1.19.0 (#789)
* Edit Farsi translations (#788) * Add a Ukrainian translation (#786) * Add a Ukrainian translation * Clarify some strings in the Ukrainian translation * feat: change dock position (#774) * feat: change dock position * fix grid row and column * add top position * fix responsive for the top position * change content side * fix overflowing menu * [improvement] theme on body (#790) * Update Tldraw.tsx * Add theme on body, adjust dark page options dialog * fix test * Preparing for global integration (#775) * Update translations.ts * Create en.json * Make main translation default * Remove unused locale property of translation Co-authored-by: Steve Ruiz <steveruizok@gmail.com> * Fix language menu * Update ar.json (#793) * feature/add Hebrew translations (#792) * hebrew translations * pr fixes Co-authored-by: Steve Ruiz <steveruizok@gmail.com> * fix toolspanel item position (#791) * fix toolspanel item position * add translation Co-authored-by: Steve Ruiz <steveruizok@gmail.com> * Add remote caching * Adds link to translation guide (#794) Co-authored-by: Baahar Ebrahimi <108254874+Baahaarmast@users.noreply.github.com> Co-authored-by: walking-octopus <46994949+walking-octopus@users.noreply.github.com> Co-authored-by: Judicael <46365844+judicaelandria@users.noreply.github.com> Co-authored-by: Ali Alhaidary <75235623+ali-alhaidary@users.noreply.github.com> Co-authored-by: gadi246 <gadi246@gmail.com>
This commit is contained in:
parent
4d900fb7fd
commit
88fbdacaea
35 changed files with 505 additions and 97 deletions
|
@ -1 +1 @@
|
|||
{}
|
||||
{"teamId":"team_MjXkk3MAaGtBFaHcssVUiMgd","apiUrl":"https://api.vercel.com"}
|
File diff suppressed because one or more lines are too long
|
@ -17,7 +17,7 @@
|
|||
],
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"build:www": "turbo run build:www --force",
|
||||
"build:www": "turbo run build:www",
|
||||
"build:core": "turbo run build:core",
|
||||
"build:packages": "turbo run build:packages --stream",
|
||||
"build:apps": "turbo run build:apps",
|
||||
|
|
|
@ -50,8 +50,8 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@swc-node/jest": "^1.4.3",
|
||||
"@testing-library/jest-dom": "^5.16.2",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@tldraw/intersect": "*",
|
||||
"@tldraw/vec": "*",
|
||||
"@types/node": "^17.0.14",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { mockDocument, mockUtils } from '~test'
|
||||
import { render } from '@testing-library/react'
|
||||
import { act, render } from '@testing-library/react'
|
||||
import { Renderer } from './Renderer'
|
||||
import { action, makeAutoObservable } from 'mobx'
|
||||
import type { TLBinding, TLBounds, TLPage, TLPageState } from '~types'
|
||||
|
@ -83,14 +83,15 @@ describe('When passing observables', () => {
|
|||
|
||||
const wrapper = render(<Renderer shapeUtils={mockUtils} page={page} pageState={pageState} />)
|
||||
|
||||
// PageState
|
||||
|
||||
expect(wrapper.getByTestId('layer')).toHaveProperty(
|
||||
'style.transform',
|
||||
`scale(1) translateX(0px) translateY(0px)`
|
||||
)
|
||||
|
||||
act(() => {
|
||||
// PageState
|
||||
pageState.pan([10, 10])
|
||||
})
|
||||
|
||||
expect(wrapper.getByTestId('layer')).toHaveProperty(
|
||||
'style.transform',
|
||||
|
@ -109,7 +110,9 @@ describe('When passing observables', () => {
|
|||
rotate(0rad)`
|
||||
)
|
||||
|
||||
act(() => {
|
||||
page.moveShape('box1', [10, 10])
|
||||
})
|
||||
|
||||
expect(wrapper.getByTestId('container')).toHaveProperty(
|
||||
'style.transform',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Renderer } from '@tldraw/core'
|
||||
import { defineMessages, IntlConfig, IntlProvider } from 'react-intl'
|
||||
import { IntlProvider } from 'react-intl'
|
||||
import { styled, dark } from '~styles'
|
||||
import { TDDocument, TDStatus } from '~types'
|
||||
import { TldrawApp, TDCallbacks } from '~state'
|
||||
|
@ -443,9 +443,19 @@ const InnerTldraw = React.memo(function InnerTldraw({
|
|||
|
||||
const translation = useTranslation(settings.language)
|
||||
|
||||
// Put the theme on the body. This means that components with
|
||||
// multiple editors cannot have different themes.
|
||||
React.useLayoutEffect(() => {
|
||||
if (settings.isDarkMode) {
|
||||
window.document.body.classList.add(dark)
|
||||
} else {
|
||||
window.document.body.classList.remove(dark)
|
||||
}
|
||||
}, [settings.isDarkMode])
|
||||
|
||||
return (
|
||||
<IntlProvider locale={translation.locale} messages={translation.messages}>
|
||||
<StyledLayout ref={rWrapper} tabIndex={-0} className={settings.isDarkMode ? dark : ''}>
|
||||
<StyledLayout ref={rWrapper} tabIndex={-0}>
|
||||
<Loading />
|
||||
<OneOff focusableRef={rWrapper} autofocus={autofocus} />
|
||||
<ContextMenu>
|
||||
|
|
|
@ -10,9 +10,17 @@ export interface DMContentProps {
|
|||
sideOffset?: number
|
||||
children: React.ReactNode
|
||||
id?: string
|
||||
side?: 'top' | 'left' | 'right' | 'bottom' | undefined
|
||||
}
|
||||
|
||||
export function DMContent({ sideOffset = 8, children, align, variant, id }: DMContentProps) {
|
||||
export function DMContent({
|
||||
sideOffset = 8,
|
||||
children,
|
||||
align,
|
||||
variant,
|
||||
id,
|
||||
side = 'bottom',
|
||||
}: DMContentProps) {
|
||||
return (
|
||||
<DropdownMenu.Content
|
||||
dir="ltr"
|
||||
|
@ -21,6 +29,7 @@ export function DMContent({ sideOffset = 8, children, align, variant, id }: DMCo
|
|||
onEscapeKeyDown={stopPropagation}
|
||||
asChild
|
||||
id={id}
|
||||
side={side}
|
||||
>
|
||||
<StyledContent variant={variant}>{children}</StyledContent>
|
||||
</DropdownMenu.Content>
|
||||
|
|
|
@ -20,8 +20,8 @@ export function DMSubMenu({ children, size, disabled = false, label, id }: DMSub
|
|||
{label}
|
||||
</RowButton>
|
||||
</TriggerItem>
|
||||
<Content dir="ltr" asChild sideOffset={2} alignOffset={-2}>
|
||||
<MenuContent size={size}>
|
||||
<Content dir="ltr" asChild sideOffset={2} alignOffset={-2} align="start">
|
||||
<MenuContent size={size} overflow>
|
||||
{children}
|
||||
<Arrow offset={13} />
|
||||
</MenuContent>
|
||||
|
|
|
@ -20,5 +20,17 @@ export const MenuContent = styled('div', {
|
|||
minWidth: 72,
|
||||
},
|
||||
},
|
||||
overflow: {
|
||||
true: {
|
||||
maxHeight: '60vh',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
},
|
||||
},
|
||||
},
|
||||
'-ms-overflow-style': 'none' /* for Internet Explorer, Edge */,
|
||||
scrollbarWidth: 'none',
|
||||
'&::webkit-scrollbar': {
|
||||
display: 'none',
|
||||
},
|
||||
})
|
||||
|
|
|
@ -99,7 +99,8 @@ export const StyledRowButton = styled('button', {
|
|||
background: 'none',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
height: '32px',
|
||||
height: 32,
|
||||
minHeight: 32,
|
||||
outline: 'none',
|
||||
color: '$text',
|
||||
fontFamily: '$ui',
|
||||
|
|
|
@ -30,6 +30,7 @@ const StyledInput = styled('input', {
|
|||
width: '100%',
|
||||
paddingLeft: '$3',
|
||||
paddingRight: '$6',
|
||||
backgroundColor: '$background',
|
||||
|
||||
height: '32px',
|
||||
outline: 'none',
|
||||
|
@ -48,4 +49,5 @@ const StyledInputIcon = styled(SmallIcon, {
|
|||
paddingLeft: '$3',
|
||||
paddingRight: '$3',
|
||||
pointerEvents: 'none',
|
||||
color: '$text',
|
||||
})
|
||||
|
|
|
@ -34,6 +34,8 @@ import { Divider } from '~components/Primitives/Divider'
|
|||
import { ToolButton } from '~components/Primitives/ToolButton'
|
||||
import { useIntl } from 'react-intl'
|
||||
|
||||
const dockPositionState = (s: TDSnapshot) => s.settings.dockPosition
|
||||
|
||||
const selectedShapesCountSelector = (s: TDSnapshot) =>
|
||||
s.document.pageStates[s.appState.currentPageId].selectedIds.length
|
||||
|
||||
|
@ -77,8 +79,6 @@ export function ActionButton() {
|
|||
const app = useTldrawApp()
|
||||
const intl = useIntl()
|
||||
|
||||
const isFrenchLang = navigator.language === 'fr'
|
||||
|
||||
const isAllLocked = app.useStore(isAllLockedSelector)
|
||||
|
||||
const isAllAspectLocked = app.useStore(isAllAspectLockedSelector)
|
||||
|
@ -91,6 +91,8 @@ export function ActionButton() {
|
|||
|
||||
const selectedShapesCount = app.useStore(selectedShapesCountSelector)
|
||||
|
||||
const dockPosition = app.useStore(dockPositionState)
|
||||
|
||||
const hasTwoOrMore = selectedShapesCount > 1
|
||||
|
||||
const hasThreeOrMore = selectedShapesCount > 2
|
||||
|
@ -182,6 +184,8 @@ export function ActionButton() {
|
|||
[app]
|
||||
)
|
||||
|
||||
const contentSide = dockPosition === 'bottom' || dockPosition === 'top' ? 'top' : dockPosition
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root dir="ltr" onOpenChange={handleMenuOpenChange}>
|
||||
<DropdownMenu.Trigger dir="ltr" asChild id="TD-Tools-Dots">
|
||||
|
@ -189,7 +193,7 @@ export function ActionButton() {
|
|||
<DotsHorizontalIcon />
|
||||
</ToolButton>
|
||||
</DropdownMenu.Trigger>
|
||||
<DMContent sideOffset={16}>
|
||||
<DMContent sideOffset={16} side={contentSide}>
|
||||
<>
|
||||
<ButtonsRow>
|
||||
<ToolButton variant="icon" disabled={!hasSelection} onClick={handleDuplicate}>
|
||||
|
|
|
@ -12,15 +12,33 @@ const isEmptyCanvasSelector = (s: TDSnapshot) => {
|
|||
)
|
||||
}
|
||||
|
||||
const isDebugModeSelector = (s: TDSnapshot) => s.settings.isDebugMode
|
||||
const dockPositionState = (s: TDSnapshot) => s.settings.dockPosition
|
||||
|
||||
export const BackToContent = React.memo(function BackToContent() {
|
||||
const app = useTldrawApp()
|
||||
|
||||
const isEmptyCanvas = app.useStore(isEmptyCanvasSelector)
|
||||
const dockPosition = app.useStore(dockPositionState)
|
||||
const isDebugMode = app.useStore(isDebugModeSelector)
|
||||
|
||||
const style = {
|
||||
bottom:
|
||||
dockPosition === 'bottom' && isDebugMode
|
||||
? 120
|
||||
: dockPosition === 'bottom'
|
||||
? 80
|
||||
: isDebugMode
|
||||
? 60
|
||||
: 20,
|
||||
left: '50%',
|
||||
transform: 'translate(-50%,0)',
|
||||
}
|
||||
|
||||
if (!isEmptyCanvas) return null
|
||||
|
||||
return (
|
||||
<BackToContentContainer id="TD-Tools-Back_to_content">
|
||||
<BackToContentContainer id="TD-Tools-Back_to_content" style={{ ...style }}>
|
||||
<RowButton onClick={app.zoomToContent}>Back to content</RowButton>
|
||||
</BackToContentContainer>
|
||||
)
|
||||
|
@ -30,7 +48,9 @@ const BackToContentContainer = styled(MenuContent, {
|
|||
pointerEvents: 'all',
|
||||
width: 'fit-content',
|
||||
minWidth: 0,
|
||||
gridRow: 1,
|
||||
flexGrow: 2,
|
||||
display: 'block',
|
||||
// gridRow: 1,
|
||||
// flexGrow: 2,
|
||||
// display: 'block',
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
})
|
||||
|
|
|
@ -16,6 +16,7 @@ import { EraserIcon } from '~components/Primitives/icons'
|
|||
|
||||
const activeToolSelector = (s: TDSnapshot) => s.appState.activeTool
|
||||
const toolLockedSelector = (s: TDSnapshot) => s.appState.isToolLocked
|
||||
const dockPositionState = (s: TDSnapshot) => s.settings.dockPosition
|
||||
|
||||
export const PrimaryTools = React.memo(function PrimaryTools() {
|
||||
const app = useTldrawApp()
|
||||
|
@ -24,6 +25,7 @@ export const PrimaryTools = React.memo(function PrimaryTools() {
|
|||
const activeTool = app.useStore(activeToolSelector)
|
||||
|
||||
const isToolLocked = app.useStore(toolLockedSelector)
|
||||
const dockPosition = app.useStore(dockPositionState)
|
||||
|
||||
const selectSelectTool = React.useCallback(() => {
|
||||
app.selectTool('select')
|
||||
|
@ -49,8 +51,10 @@ export const PrimaryTools = React.memo(function PrimaryTools() {
|
|||
app.selectTool(TDShapeType.Sticky)
|
||||
}, [app])
|
||||
|
||||
const panelStyle = dockPosition === 'bottom' || dockPosition === 'top' ? 'row' : 'column'
|
||||
|
||||
return (
|
||||
<Panel side="center" id="TD-PrimaryTools">
|
||||
<Panel side="center" id="TD-PrimaryTools" style={{ flexDirection: panelStyle }}>
|
||||
<ToolButtonWithTooltip
|
||||
kbd={'1'}
|
||||
label={intl.formatMessage({ id: 'select' })}
|
||||
|
|
|
@ -34,12 +34,12 @@ const shapeShapeIcons = {
|
|||
[TDShapeType.Line]: <LineIcon />,
|
||||
}
|
||||
|
||||
const statusSelector = (s: TDSnapshot) => s.appState.status
|
||||
|
||||
enum Status {
|
||||
SpacePanning = 'spacePanning',
|
||||
}
|
||||
|
||||
const dockPositionState = (s: TDSnapshot) => s.settings.dockPosition
|
||||
|
||||
export const ShapesMenu = React.memo(function ShapesMenu({
|
||||
activeTool,
|
||||
isToolLocked,
|
||||
|
@ -47,7 +47,7 @@ export const ShapesMenu = React.memo(function ShapesMenu({
|
|||
const app = useTldrawApp()
|
||||
const intl = useIntl()
|
||||
|
||||
const status = app.useStore(statusSelector)
|
||||
const dockPosition = app.useStore(dockPositionState)
|
||||
|
||||
const [lastActiveTool, setLastActiveTool] = React.useState<ShapeShape>(TDShapeType.Rectangle)
|
||||
|
||||
|
@ -74,6 +74,9 @@ export const ShapesMenu = React.memo(function ShapesMenu({
|
|||
}, [])
|
||||
|
||||
const isActive = shapeShapes.includes(activeTool as ShapeShape)
|
||||
const contentSide = dockPosition === 'bottom' || dockPosition === 'top' ? 'top' : dockPosition
|
||||
|
||||
const panelStyle = dockPosition === 'bottom' || dockPosition === 'top' ? 'row' : 'column'
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root dir="ltr" onOpenChange={selectShapeTool}>
|
||||
|
@ -89,8 +92,8 @@ export const ShapesMenu = React.memo(function ShapesMenu({
|
|||
{shapeShapeIcons[lastActiveTool]}
|
||||
</ToolButton>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content asChild dir="ltr" side="top" sideOffset={12}>
|
||||
<Panel side="center">
|
||||
<DropdownMenu.Content asChild dir="ltr" side={contentSide} sideOffset={12}>
|
||||
<Panel side="center" style={{ flexDirection: panelStyle }}>
|
||||
{shapeShapes.map((shape, i) => (
|
||||
<Tooltip
|
||||
key={shape}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { styled } from '~styles'
|
||||
import type { TDSnapshot } from '~types'
|
||||
import { useTldrawApp } from '~hooks'
|
||||
import { useMediaQuery, useTldrawApp } from '~hooks'
|
||||
import { StatusBar } from './StatusBar'
|
||||
import { BackToContent } from './BackToContent'
|
||||
import { PrimaryTools } from './PrimaryTools'
|
||||
|
@ -9,6 +9,7 @@ import { ActionButton } from './ActionButton'
|
|||
import { DeleteButton } from './DeleteButton'
|
||||
|
||||
const isDebugModeSelector = (s: TDSnapshot) => s.settings.isDebugMode
|
||||
const dockPositionState = (s: TDSnapshot) => s.settings.dockPosition
|
||||
|
||||
interface ToolsPanelProps {
|
||||
onBlur?: React.FocusEventHandler
|
||||
|
@ -17,12 +18,62 @@ interface ToolsPanelProps {
|
|||
export const ToolsPanel = React.memo(function ToolsPanel({ onBlur }: ToolsPanelProps) {
|
||||
const app = useTldrawApp()
|
||||
const isDebugMode = app.useStore(isDebugModeSelector)
|
||||
const dockPosition = app.useStore(dockPositionState)
|
||||
const isMobile = useMediaQuery('(max-width: 900px)')
|
||||
|
||||
const bottomStyle = {
|
||||
width: '100%',
|
||||
height: 'min-content',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: isDebugMode ? 40 : 0,
|
||||
}
|
||||
const topStyle = {
|
||||
width: '100%',
|
||||
height: 'min-content',
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: isMobile ? 60 : 10,
|
||||
}
|
||||
const rightStyle = { width: 'min-content', height: '100%', right: 0 }
|
||||
const leftStyle = { width: 'min-content', height: '100%', left: 10 }
|
||||
|
||||
const toolStyle = () => {
|
||||
switch (dockPosition) {
|
||||
case 'bottom':
|
||||
return bottomStyle
|
||||
case 'left':
|
||||
return leftStyle
|
||||
case 'right':
|
||||
return rightStyle
|
||||
case 'top':
|
||||
return topStyle
|
||||
default:
|
||||
return bottomStyle
|
||||
}
|
||||
}
|
||||
const style = toolStyle()
|
||||
const centerWrapStyle =
|
||||
dockPosition === 'bottom' || dockPosition === 'top'
|
||||
? { gridRow: 1, gridColumn: 2 }
|
||||
: { gridRow: 2, gridColumn: 1 }
|
||||
const primaryToolStyle = dockPosition === 'bottom' || dockPosition === 'top' ? 'row' : 'column'
|
||||
|
||||
return (
|
||||
<StyledToolsPanelContainer onBlur={onBlur}>
|
||||
<StyledCenterWrap id="TD-Tools">
|
||||
<StyledToolsPanelContainer
|
||||
style={{
|
||||
...style,
|
||||
}}
|
||||
onBlur={onBlur}
|
||||
>
|
||||
<StyledCenterWrap
|
||||
id="TD-Tools"
|
||||
style={{
|
||||
...centerWrapStyle,
|
||||
}}
|
||||
>
|
||||
<BackToContent />
|
||||
<StyledPrimaryTools>
|
||||
<StyledPrimaryTools style={{ flexDirection: primaryToolStyle }}>
|
||||
<ActionButton />
|
||||
<PrimaryTools />
|
||||
<DeleteButton />
|
||||
|
@ -39,9 +90,6 @@ export const ToolsPanel = React.memo(function ToolsPanel({ onBlur }: ToolsPanelP
|
|||
|
||||
const StyledToolsPanelContainer = styled('div', {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
width: '100%',
|
||||
minWidth: 0,
|
||||
maxWidth: '100%',
|
||||
|
@ -70,8 +118,12 @@ const StyledCenterWrap = styled('div', {
|
|||
})
|
||||
|
||||
const StyledStatusWrap = styled('div', {
|
||||
gridRow: 2,
|
||||
gridColumn: '1 / span 3',
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
width: '100%',
|
||||
maxWidth: '100%',
|
||||
})
|
||||
|
||||
const StyledPrimaryTools = styled('div', {
|
||||
|
|
|
@ -1,36 +1,52 @@
|
|||
import { ExternalLinkIcon } from '@radix-ui/react-icons'
|
||||
import * as React from 'react'
|
||||
import { useIntl } from 'react-intl'
|
||||
import { DMCheckboxItem, DMSubMenu } from '~components/Primitives/DropdownMenu'
|
||||
import { FormattedMessage, useIntl } from 'react-intl'
|
||||
import { DMCheckboxItem, DMDivider, DMItem, DMSubMenu } from '~components/Primitives/DropdownMenu'
|
||||
import { HeartIcon } from '~components/Primitives/icons/HeartIcon'
|
||||
import { SmallIcon } from '~components/Primitives/SmallIcon'
|
||||
import { useTldrawApp } from '~hooks'
|
||||
import { TDLanguage, TRANSLATIONS } from '~translations'
|
||||
import { TDSnapshot } from '~types'
|
||||
|
||||
const settingsSelector = (s: TDSnapshot) => s.settings
|
||||
const languageSelector = (s: TDSnapshot) => s.settings.language
|
||||
|
||||
export function LanguageMenu() {
|
||||
const app = useTldrawApp()
|
||||
const setting = app.useStore(settingsSelector)
|
||||
const language = app.useStore(languageSelector)
|
||||
const intl = useIntl()
|
||||
|
||||
const handleChangeLanguage = React.useCallback(
|
||||
(code: TDLanguage) => {
|
||||
app.setSetting('language', code)
|
||||
(locale: TDLanguage) => {
|
||||
app.setSetting('language', locale)
|
||||
},
|
||||
[app]
|
||||
)
|
||||
|
||||
return (
|
||||
<DMSubMenu label={intl.formatMessage({ id: 'language' })}>
|
||||
{TRANSLATIONS.map(({ code, label }) => (
|
||||
{TRANSLATIONS.map(({ locale, label }) => (
|
||||
<DMCheckboxItem
|
||||
key={code}
|
||||
checked={setting.language === code}
|
||||
onCheckedChange={() => handleChangeLanguage(code)}
|
||||
id={`TD-MenuItem-Language-${code}`}
|
||||
key={locale}
|
||||
checked={language === locale}
|
||||
onCheckedChange={() => handleChangeLanguage(locale)}
|
||||
id={`TD-MenuItem-Language-${locale}`}
|
||||
>
|
||||
{label}
|
||||
</DMCheckboxItem>
|
||||
))}
|
||||
<DMDivider />
|
||||
<a
|
||||
href="https://github.com/tldraw/tldraw/blob/develop/guides/translation.md"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
>
|
||||
<DMItem id="TD-MenuItem-Translation-Link">
|
||||
<FormattedMessage id="translation.link" />
|
||||
<SmallIcon>
|
||||
<ExternalLinkIcon />
|
||||
</SmallIcon>
|
||||
</DMItem>
|
||||
</a>
|
||||
</DMSubMenu>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ export const StyledDialogContent = styled(Dialog.Content, {
|
|||
marginTop: '-5vh',
|
||||
pointerEvents: 'all',
|
||||
backgroundColor: '$panel',
|
||||
padding: '$0',
|
||||
padding: '$1',
|
||||
borderRadius: '$2',
|
||||
font: '$ui',
|
||||
'&:focus': {
|
||||
|
|
|
@ -2,10 +2,13 @@ import * as React from 'react'
|
|||
import { FormattedMessage, useIntl } from 'react-intl'
|
||||
import { DMCheckboxItem, DMDivider, DMSubMenu } from '~components/Primitives/DropdownMenu'
|
||||
import { useTldrawApp } from '~hooks'
|
||||
import { TDSnapshot } from '~types'
|
||||
import { TDDockPosition, TDSnapshot } from '~types'
|
||||
import { styled } from '~styles'
|
||||
|
||||
const settingsSelector = (s: TDSnapshot) => s.settings
|
||||
|
||||
const DockPosition = ['bottom', 'left', 'right', 'top']
|
||||
|
||||
export function PreferencesMenu() {
|
||||
const app = useTldrawApp()
|
||||
const intl = useIntl()
|
||||
|
@ -52,6 +55,13 @@ export function PreferencesMenu() {
|
|||
app.setSetting('isCadSelectMode', (v) => !v)
|
||||
}, [app])
|
||||
|
||||
const handleChangeDockPosition = React.useCallback(
|
||||
(position: TDDockPosition) => {
|
||||
app.setSetting('dockPosition', position)
|
||||
},
|
||||
[app]
|
||||
)
|
||||
|
||||
return (
|
||||
<DMSubMenu label={intl.formatMessage({ id: 'menu.preferences' })} id="TD-MenuItem-Preferences">
|
||||
<DMCheckboxItem
|
||||
|
@ -128,6 +138,24 @@ export function PreferencesMenu() {
|
|||
>
|
||||
<FormattedMessage id="preferences.clone.handles" />
|
||||
</DMCheckboxItem>
|
||||
<DMSubMenu label={intl.formatMessage({ id: 'dock.position' })}>
|
||||
{DockPosition.map((position) => (
|
||||
<DMCheckboxItem
|
||||
key={position}
|
||||
checked={settings.dockPosition === position}
|
||||
onCheckedChange={() => handleChangeDockPosition(position as TDDockPosition)}
|
||||
id={`TD-MenuItem-DockPosition-${position}`}
|
||||
>
|
||||
<StyledText>
|
||||
<FormattedMessage id={position} />
|
||||
</StyledText>
|
||||
</DMCheckboxItem>
|
||||
))}
|
||||
</DMSubMenu>
|
||||
</DMSubMenu>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledText = styled('span', {
|
||||
textTransform: 'capitalize',
|
||||
})
|
||||
|
|
|
@ -5,3 +5,4 @@ export * from './useStylesheet'
|
|||
export * from './useFileSystemHandlers'
|
||||
export * from './useFileSystem'
|
||||
export * from './useTranslation'
|
||||
export * from './useMediaQuery'
|
||||
|
|
19
packages/tldraw/src/hooks/useMediaQuery.ts
Normal file
19
packages/tldraw/src/hooks/useMediaQuery.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import * as React from 'react'
|
||||
|
||||
export function useMediaQuery(query: string) {
|
||||
const [matches, setMatches] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
const media = window.matchMedia(query)
|
||||
if (media.matches !== matches) {
|
||||
setMatches(media.matches)
|
||||
}
|
||||
const listener = () => setMatches(media.matches)
|
||||
window.addEventListener('resize', listener)
|
||||
return () => window.removeEventListener('resize', listener)
|
||||
}, [matches, query])
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
export default useMediaQuery
|
|
@ -1,10 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { getTranslation, TDLanguage } from '../translations/translations'
|
||||
|
||||
export function useTranslation(code?: TDLanguage) {
|
||||
export function useTranslation(locale?: TDLanguage) {
|
||||
return React.useMemo(() => {
|
||||
const locale = code ?? navigator.language.split(/[-_]/)[0]
|
||||
|
||||
return getTranslation(locale)
|
||||
}, [code])
|
||||
return getTranslation(locale ?? navigator.language.split(/[-_]/)[0])
|
||||
}, [locale])
|
||||
}
|
||||
|
|
|
@ -4168,6 +4168,7 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
showCloneHandles: false,
|
||||
showGrid: false,
|
||||
language: 'en',
|
||||
dockPosition: 'bottom',
|
||||
},
|
||||
appState: {
|
||||
status: TDStatus.Idle,
|
||||
|
|
|
@ -53,6 +53,7 @@ TldrawTestApp {
|
|||
"version": 15.3,
|
||||
},
|
||||
"settings": Object {
|
||||
"dockPosition": "bottom",
|
||||
"isCadSelectMode": false,
|
||||
"isDarkMode": false,
|
||||
"isDebugMode": false,
|
||||
|
@ -203,6 +204,7 @@ TldrawTestApp {
|
|||
"version": 15.3,
|
||||
},
|
||||
"settings": Object {
|
||||
"dockPosition": "bottom",
|
||||
"isCadSelectMode": false,
|
||||
"isDarkMode": false,
|
||||
"isDebugMode": false,
|
||||
|
@ -374,6 +376,7 @@ TldrawTestApp {
|
|||
"version": 15.3,
|
||||
},
|
||||
"settings": Object {
|
||||
"dockPosition": "bottom",
|
||||
"isCadSelectMode": false,
|
||||
"isDarkMode": false,
|
||||
"isDebugMode": false,
|
||||
|
|
|
@ -86,5 +86,10 @@
|
|||
"forward": "للخلف",
|
||||
"backward": "للوراء",
|
||||
"back": "خلف",
|
||||
"language": "لغة"
|
||||
"language": "لغة",
|
||||
"dock.position": "موقع الادوات",
|
||||
"bottom": "اسفل",
|
||||
"left": "يسار",
|
||||
"right": "يمين",
|
||||
"top": "أعلى"
|
||||
}
|
1
packages/tldraw/src/translations/en.json
Normal file
1
packages/tldraw/src/translations/en.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -18,7 +18,7 @@
|
|||
"menu.preferences": "تنظیمها",
|
||||
"menu.sign.in": "ورود",
|
||||
"menu.sign.out": "خروج",
|
||||
"sponsored": "حمایتشده",
|
||||
"sponsored": "حامیان",
|
||||
"become.a.sponsor": " حامی شو",
|
||||
"zoom.to.selection": "نمایش انتخابشدهها",
|
||||
"zoom.to.fit": "نمایش کل صفحه",
|
||||
|
|
|
@ -87,5 +87,10 @@
|
|||
"forward": "Au premier plan",
|
||||
"backward": "En arrière plan",
|
||||
"back": "À l'arrière",
|
||||
"language": "Langage"
|
||||
"language": "Langage",
|
||||
"dock.position": "Position du dock",
|
||||
"bottom": "En bas",
|
||||
"left": "À Gauche",
|
||||
"right": "À Droite",
|
||||
"top": "En Haut"
|
||||
}
|
90
packages/tldraw/src/translations/he.json
Normal file
90
packages/tldraw/src/translations/he.json
Normal file
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
"style.menu.color": "צבע",
|
||||
"style.menu.fill": "מלא",
|
||||
"style.menu.dash": "גבול",
|
||||
"style.menu.size": "גודל",
|
||||
"style.menu.keep.open": "השאר פתוח",
|
||||
"style.menu.font": "גופן",
|
||||
"style.menu.align": "יישור",
|
||||
"styles": "עיצוב",
|
||||
"zoom.in": "הגדל תצוגה",
|
||||
"zoom.out": "הקטן תצוגה",
|
||||
"to": "ל",
|
||||
"to.selection": "לסימון",
|
||||
"to.fit": "להתאמה",
|
||||
"menu.file": "קובץ",
|
||||
"menu.edit": "עריכה",
|
||||
"menu.view": "תצוגה",
|
||||
"menu.preferences": "מאפיינים",
|
||||
"menu.sign.in": "הירשם",
|
||||
"menu.sign.out": "התנתק",
|
||||
"sponsored": "חסות",
|
||||
"become.a.sponsor": "מתן חסות",
|
||||
"zoom.to.selection": "זום לבחירה",
|
||||
"zoom.to.fit": "זום להתאמה",
|
||||
"zoom.to": "זום ל",
|
||||
"preferences.dark.mode": "מצב חשוך",
|
||||
"preferences.focus.mode": "מצב פוקוס",
|
||||
"preferences.debug.mode": "מצב דיבאג",
|
||||
"preferences.show.grid": "(גריד)הראה רשת עימוד",
|
||||
"preferences.use.cad.selection": "סימון CAD",
|
||||
"preferences.keep.stylemenu.open": "השאר תפריט עיצוב פתוח",
|
||||
"preferences.always.show.snaps": "הראה קווי מתאר",
|
||||
"preferences.rotate.handles": "הראה ידיות סיבוב",
|
||||
"preferences.binding.handles": "הראה ידיות קשירה",
|
||||
"preferences.clone.handles": "הראה ידיות שיכפול",
|
||||
"undo": "בטל",
|
||||
"redo": "עשה מחדש",
|
||||
"cut": "גזור",
|
||||
"copy": "העתק",
|
||||
"paste": "הדבק",
|
||||
"copy.as": "העתק כ",
|
||||
"export.as": "ייצא כ",
|
||||
"select.all": "בחר הכל",
|
||||
"select.none": "בטל בחירה",
|
||||
"delete": "מחק",
|
||||
"new.project": "פרויקט חדש",
|
||||
"open": "פתח",
|
||||
"save": "שמור",
|
||||
"save.as": "שמור כ",
|
||||
"upload.media": "העלאת מדיה",
|
||||
"create.page": "צור דף",
|
||||
"new.page": "דף חדש",
|
||||
"page.name": "שם הדף",
|
||||
"duplicate": "שכפל",
|
||||
"cancel": "בטל",
|
||||
"copy.invite.link": "העתק קישור הזמנה",
|
||||
"create.multiplayer.project": "צור פרויקט רב משתתפים",
|
||||
"copy.multiplayer.project": "העתק לפרויקט רב משתתפים",
|
||||
"select": "סמן",
|
||||
"eraser": "מחק",
|
||||
"draw": "צייר",
|
||||
"arrow": "חץ",
|
||||
"text": "טקסט",
|
||||
"sticky": "דביקי",
|
||||
"Rectangle": "מרובע",
|
||||
"Ellipse": "אליפסה",
|
||||
"Triangle": "משולש",
|
||||
"Line": "קו",
|
||||
"rotate": "סובב",
|
||||
"lock.aspect.ratio": "נעל יחס רוחב-גובה",
|
||||
"unlock.aspect.ratio": "שחרר נעילת יחס רוחב-גובה",
|
||||
"group": "קבץ",
|
||||
"ungroup": "בטל קיבוץ",
|
||||
"move.to.back": "הבא לתחתית",
|
||||
"move.backward": "הזז אחורה",
|
||||
"move.forward": "הזז קדימה",
|
||||
"move.to.front": "הבא לחזית",
|
||||
"reset.angle": "אפס זווית",
|
||||
"lock": "נעל",
|
||||
"unlock": "שחרר נעילה",
|
||||
"move.to.page": "הזז לדף",
|
||||
"flip.horizontal": "הפוך אופקית",
|
||||
"flip.vertical": "הפוך אנכית",
|
||||
"move": "הזז",
|
||||
"to.front": "הבא לקדימה",
|
||||
"forward": "קדימה",
|
||||
"backward": "אחורה",
|
||||
"back": "בחזרה",
|
||||
"language": "שפה"
|
||||
}
|
|
@ -87,5 +87,10 @@
|
|||
"forward": "Sposta avanti",
|
||||
"backward": "Sposta indietro",
|
||||
"back": "In fondo",
|
||||
"language": "Lingua"
|
||||
"language": "Lingua",
|
||||
"dock.position": "Posizione dock",
|
||||
"bottom": "In basso",
|
||||
"left": "Sinistra",
|
||||
"right": "Destra",
|
||||
"top": "In Alto"
|
||||
}
|
|
@ -51,7 +51,6 @@
|
|||
"create.page": "Create Page",
|
||||
"new.page": "New Page",
|
||||
"page.name": "Page Name",
|
||||
"page": "Page",
|
||||
"duplicate": "Duplicate",
|
||||
"cancel": "Cancel",
|
||||
"copy.invite.link": "Copy Invite Link",
|
||||
|
@ -87,5 +86,11 @@
|
|||
"forward": "Forward",
|
||||
"backward": "Backward",
|
||||
"back": "Back",
|
||||
"language": "Language"
|
||||
"language": "Language",
|
||||
"translation.link": "Learn More",
|
||||
"dock.position": "Dock Position",
|
||||
"bottom": "Bottom",
|
||||
"left": "Left",
|
||||
"right": "Right",
|
||||
"top": "Top"
|
||||
}
|
|
@ -1,19 +1,22 @@
|
|||
import ar from './ar.json'
|
||||
import da from './da.json'
|
||||
import de from './de.json'
|
||||
import en from './main.json'
|
||||
import en from './en.json'
|
||||
import es from './es.json'
|
||||
import fa from './fa.json'
|
||||
import fr from './fr.json'
|
||||
import he from './he.json'
|
||||
import it from './it.json'
|
||||
import ja from './ja.json'
|
||||
import ko_kr from './ko-kr.json'
|
||||
import main from './main.json'
|
||||
import ne from './ne.json'
|
||||
import no from './no.json'
|
||||
import pl from './pl.json'
|
||||
import pt_br from './pt-br.json'
|
||||
import ru from './ru.json'
|
||||
import tr from './tr.json'
|
||||
import uk from './uk.json'
|
||||
import zh_cn from './zh-cn.json'
|
||||
|
||||
// The default language (english) must have a value for every message.
|
||||
|
@ -22,49 +25,50 @@ import zh_cn from './zh-cn.json'
|
|||
// translation instead.
|
||||
|
||||
export const TRANSLATIONS: TDTranslations = [
|
||||
{ code: 'ar', locale: 'ar', label: 'عربي', messages: ar },
|
||||
{ code: 'en', locale: 'en', label: 'English', messages: en },
|
||||
{ code: 'es', locale: 'es', label: 'Español', messages: es },
|
||||
{ code: 'fr', locale: 'fr', label: 'Français', messages: fr },
|
||||
{ code: 'fa', locale:'fa', label: 'فارسی', messages: fa },
|
||||
{ code: 'it', locale: 'it', label: 'Italiano', messages: it },
|
||||
{ code: 'ja', locale: 'ja', label: '日本語', messages: ja },
|
||||
{ code: 'ko-kr', locale: 'ko-kr', label: '한국어', messages: ko_kr },
|
||||
{ code: 'ne', locale: 'ne', label: 'नेपाली', messages: ne },
|
||||
{ code: 'no', locale: 'no', label: 'Norwegian', messages: no },
|
||||
{ code: 'pl', locale: 'pl', label: 'Polski', messages: pl },
|
||||
{ code: 'pt-br', locale: 'pt-br', label: 'Português - Brasil', messages: pt_br },
|
||||
{ code: 'tr', locale: 'tr', label: 'Türkçe', messages: tr },
|
||||
{ code: 'zh-cn', locale: 'zh-ch', label: 'Chinese - Simplified', messages: zh_cn },
|
||||
{ code: 'da', locale: 'da', label: 'Danish', messages: da },
|
||||
{ code: 'de', locale: 'de', label: 'Deutsch', messages: de},
|
||||
{ code: 'ru', locale: 'ru', label: 'Russian', messages: ru },
|
||||
{ locale: 'ar', label: 'عربي', messages: ar },
|
||||
{ locale: 'da', label: 'Danish', messages: da },
|
||||
{ locale: 'de', label: 'Deutsch', messages: de },
|
||||
{ locale: 'en', label: 'English', messages: en },
|
||||
{ locale: 'es', label: 'Español', messages: es },
|
||||
{ locale: 'fa', label: 'فارسی', messages: fa },
|
||||
{ locale: 'fr', label: 'Français', messages: fr },
|
||||
{ locale: 'he', label: 'עברית', messages: he },
|
||||
{ locale: 'it', label: 'Italiano', messages: it },
|
||||
{ locale: 'ja', label: '日本語', messages: ja },
|
||||
{ locale: 'ko-kr', label: '한국어', messages: ko_kr },
|
||||
{ locale: 'ne', label: 'नेपाली', messages: ne },
|
||||
{ locale: 'no', label: 'Norwegian', messages: no },
|
||||
{ locale: 'pl', label: 'Polski', messages: pl },
|
||||
{ locale: 'pt-br', label: 'Português - Brasil', messages: pt_br },
|
||||
{ locale: 'ru', label: 'Russian', messages: ru },
|
||||
{ locale: 'tr', label: 'Türkçe', messages: tr },
|
||||
{ locale: 'uk', label: 'Ukrainian', messages: uk },
|
||||
{ locale: 'zh-ch', label: 'Chinese - Simplified', messages: zh_cn },
|
||||
]
|
||||
|
||||
/* ----------------- (do not change) ---------------- */
|
||||
|
||||
TRANSLATIONS.sort((a, b) => (a.code < b.code ? -1 : 1))
|
||||
TRANSLATIONS.sort((a, b) => (a.locale < b.locale ? -1 : 1))
|
||||
|
||||
export type TDTranslation = {
|
||||
readonly code: string
|
||||
readonly label: string
|
||||
readonly locale: string
|
||||
readonly label: string
|
||||
readonly messages: Partial<typeof en>
|
||||
}
|
||||
|
||||
export type TDTranslations = TDTranslation[]
|
||||
|
||||
export type TDLanguage = TDTranslations[number]['code']
|
||||
export type TDLanguage = TDTranslations[number]['locale']
|
||||
|
||||
export function getTranslation(code: TDLanguage): TDTranslation {
|
||||
const translation = TRANSLATIONS.find((t) => t.code === code)
|
||||
export function getTranslation(locale: TDLanguage): TDTranslation {
|
||||
const translation = TRANSLATIONS.find((t) => t.locale === locale)
|
||||
|
||||
const defaultTranslation = TRANSLATIONS.find((t) => t.code === 'en')!
|
||||
|
||||
const messages = {
|
||||
...defaultTranslation.messages,
|
||||
return {
|
||||
locale,
|
||||
label: translation?.label ?? locale,
|
||||
messages: {
|
||||
...main,
|
||||
...translation?.messages,
|
||||
},
|
||||
}
|
||||
|
||||
return { code, messages, locale: code, label: translation?.label ?? code }
|
||||
}
|
90
packages/tldraw/src/translations/uk.json
Normal file
90
packages/tldraw/src/translations/uk.json
Normal file
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
"style.menu.color": "Колір",
|
||||
"style.menu.fill": "Заповнювати",
|
||||
"style.menu.dash": "Штрих",
|
||||
"style.menu.size": "Розмір",
|
||||
"style.menu.keep.open": "Тримати відкритим",
|
||||
"style.menu.font": "Шрифт",
|
||||
"style.menu.align": "Вирівняти",
|
||||
"styles": "Стиль",
|
||||
"zoom.in": "Збільшити",
|
||||
"zoom.out": "Зменшити",
|
||||
"to": "до",
|
||||
"to.selection": "До виділення",
|
||||
"to.fit": "За розміром екрану",
|
||||
"menu.file": "Файл",
|
||||
"menu.edit": "Редагування",
|
||||
"menu.view": "Вигляд",
|
||||
"menu.preferences": "Налаштування",
|
||||
"menu.sign.in": "Увійти",
|
||||
"menu.sign.out": "Вийти",
|
||||
"sponsored": "Спонсовано",
|
||||
"become.a.sponsor": "Стати спонсором",
|
||||
"zoom.to.selection": "Наблизити до виділення",
|
||||
"zoom.to.fit": "Збільшити за розміром екрану",
|
||||
"zoom.to": "Наблизити до",
|
||||
"preferences.dark.mode": "Темна тема",
|
||||
"preferences.focus.mode": "Мінімалістичний режим",
|
||||
"preferences.debug.mode": "Режим налагодження",
|
||||
"preferences.show.grid": "Показати сітку",
|
||||
"preferences.use.cad.selection": "Використовувати CAD виділення",
|
||||
"preferences.keep.stylemenu.open": "Тримати меню стилів відкритим",
|
||||
"preferences.always.show.snaps": "Завжди показувати прив'язки",
|
||||
"preferences.rotate.handles": "Ручки обертання",
|
||||
"preferences.binding.handles": "Ручки прив'язки",
|
||||
"preferences.clone.handles": "Ручки клонування",
|
||||
"undo": "Скасувати",
|
||||
"redo": "Повторити",
|
||||
"cut": "Вирізати",
|
||||
"copy": "Скопіювати",
|
||||
"paste": "Вставити",
|
||||
"copy.as": "Скопіювати як",
|
||||
"export.as": "Експортувати як",
|
||||
"select.all": "Обрати все",
|
||||
"select.none": "Зняти виділення",
|
||||
"delete": "Видалити",
|
||||
"new.project": "Новий проект",
|
||||
"open": "Відкрити",
|
||||
"save": "Зберегти",
|
||||
"save.as": "Зберегти як",
|
||||
"upload.media": "Завантажити медіа",
|
||||
"create.page": "Створити сторінку",
|
||||
"new.page": "Нова сторінка",
|
||||
"page.name": "Назва сторінки",
|
||||
"duplicate": "Дублювати",
|
||||
"cancel": "Скасувати",
|
||||
"copy.invite.link": "Скопіювати посилання на запрошення",
|
||||
"create.multiplayer.project": "Створити багатокористувацький проект",
|
||||
"copy.multiplayer.project": "Скопіювати в багатокористувацький проект",
|
||||
"select": "Вибирати",
|
||||
"eraser": "Ластик",
|
||||
"draw": "Малювати",
|
||||
"arrow": "Стрілка",
|
||||
"text": "Текст",
|
||||
"sticky": "Нотатка",
|
||||
"Rectangle": "Прямокутник",
|
||||
"Ellipse": "Еліпс",
|
||||
"Triangle": "Трикутник",
|
||||
"Line": " Лінія",
|
||||
"rotate": "Повернути",
|
||||
"lock.aspect.ratio": "Заблокувати співвідношення сторін",
|
||||
"unlock.aspect.ratio": " Розблокувати співвідношення сторін",
|
||||
"group": "Згрупувати",
|
||||
"ungroup": " Розгрупувати",
|
||||
"move.to.back": "Перемістити назад",
|
||||
"move.backward": "Перемістити на задній план",
|
||||
"move.forward": "Перемістити вперед",
|
||||
"move.to.front": "Перемістити на передній план",
|
||||
"reset.angle": "Скидання кута",
|
||||
"lock": "Блокування",
|
||||
"unlock": " Розблокування",
|
||||
"move.to.page": "Перейти на сторінку",
|
||||
"flip.horizontal": "Перевернути горизонтально",
|
||||
"flip.vertical": "Перевернути вертикально",
|
||||
"move": "Перемістити",
|
||||
"to.front": "На передній план",
|
||||
"forward": " Вперед",
|
||||
"backward": "На задній план",
|
||||
"back": "Назад",
|
||||
"language": "Мова"
|
||||
}
|
|
@ -77,6 +77,8 @@ export class TDEventHandler {
|
|||
onShapeClone?: TLShapeCloneHandler
|
||||
}
|
||||
|
||||
export type TDDockPosition = 'bottom' | 'left' | 'right' | 'top'
|
||||
|
||||
// The shape of the TldrawApp's React (zustand) store
|
||||
export interface TDSnapshot {
|
||||
settings: {
|
||||
|
@ -96,6 +98,7 @@ export interface TDSnapshot {
|
|||
showCloneHandles: boolean
|
||||
showGrid: boolean
|
||||
language: TDLanguage
|
||||
dockPosition: TDDockPosition
|
||||
}
|
||||
appState: {
|
||||
currentStyle: ShapeStyles
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
import '@testing-library/jest-dom/extend-expect'
|
||||
import 'fake-indexeddb/auto'
|
||||
global.ResizeObserver = require('resize-observer-polyfill')
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // Deprecated
|
||||
removeListener: jest.fn(), // Deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue