ui: refactor breakpoints to fit in an enum (#2843)

@SomeHats i feel like my naming sucks here. halp.

### Change Type

- [x] `patch` — Bug fix

### Release Notes

- Refactor breakpoints into an enum.
This commit is contained in:
Mime Čuvalo 2024-02-15 17:39:17 +00:00 committed by GitHub
parent 31a2b2115f
commit 5d87804a76
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 58 additions and 23 deletions

View file

@ -9,6 +9,7 @@ import { FollowingIndicator } from './components/FollowingIndicator'
import { MenuZone } from './components/MenuZone'
import { ToastViewport, Toasts } from './components/Toasts'
import { Button } from './components/primitives/Button'
import { PORTRAIT_BREAKPOINT } from './constants'
import {
TldrawUiContextProvider,
TldrawUiContextProviderProps,
@ -142,7 +143,7 @@ const TldrawUiContent = React.memo(function TldrawUI({ shareZone, topZone }: Tld
<ToastProvider>
<div
className={classNames('tlui-layout', {
'tlui-layout__mobile': breakpoint < 5,
'tlui-layout__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
})}
data-breakpoint={breakpoint}
>
@ -166,7 +167,9 @@ const TldrawUiContent = React.memo(function TldrawUI({ shareZone, topZone }: Tld
<div className="tlui-layout__top__center">{topZone}</div>
<div className="tlui-layout__top__right">
{shareZone}
{StylePanel && breakpoint >= 5 && !isReadonlyMode && <_StylePanel />}
{StylePanel && breakpoint >= PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && (
<_StylePanel />
)}
</div>
</div>
<div className="tlui-layout__bottom">

View file

@ -1,5 +1,6 @@
import { useEditor, useValue } from '@tldraw/editor'
import { memo } from 'react'
import { PORTRAIT_BREAKPOINT } from '../../constants'
import { useBreakpoint } from '../../context/breakpoints'
import { useReadonly } from '../../hooks/useReadonly'
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
@ -45,7 +46,10 @@ export const DefaultActionsMenu = memo(function DefaultActionsMenu({
type="icon" // needs to be here because the trigger also passes down type="button"
smallIcon
/>
<PopoverContent side={breakpoint >= 6 ? 'bottom' : 'top'} sideOffset={6}>
<PopoverContent
side={breakpoint >= PORTRAIT_BREAKPOINT.TABLET ? 'bottom' : 'top'}
sideOffset={6}
>
<div className="tlui-actions-menu tlui-buttons__grid">
<TldrawUiMenuContextProvider type="icons" sourceId="actions-menu">
{content}

View file

@ -1,4 +1,5 @@
import { useEditor, useValue } from '@tldraw/editor'
import { PORTRAIT_BREAKPOINT } from '../../constants'
import { useActions } from '../../context/actions'
import { useBreakpoint } from '../../context/breakpoints'
import {
@ -84,7 +85,7 @@ function ReorderMenuItems() {
function ZoomOrRotateMenuItem() {
const breakpoint = useBreakpoint()
return breakpoint < 5 ? <ZoomTo100MenuItem /> : <RotateCCWMenuItem />
return breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM ? <ZoomTo100MenuItem /> : <RotateCCWMenuItem />
}
function ZoomTo100MenuItem() {

View file

@ -1,4 +1,5 @@
import { memo } from 'react'
import { PORTRAIT_BREAKPOINT } from '../../constants'
import { useBreakpoint } from '../../context/breakpoints'
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
@ -24,7 +25,7 @@ export const DefaultHelpMenu = memo(function DefaultHelpMenu({ children }: TLUiH
// so skip rendering the menu.
const content = children ?? <DefaultHelpMenuContent />
if (breakpoint < 4) return null
if (breakpoint < PORTRAIT_BREAKPOINT.MOBILE) return null
return (
<div className="tlui-help-menu">

View file

@ -1,4 +1,5 @@
import { memo } from 'react'
import { PORTRAIT_BREAKPOINT } from '../constants'
import { useBreakpoint } from '../context/breakpoints'
import { useTldrawUiComponents } from '../context/components'
@ -7,14 +8,14 @@ export const MenuZone = memo(function MenuZone() {
const { MainMenu, QuickActions, ActionsMenu, PageMenu } = useTldrawUiComponents()
if (!MainMenu && !PageMenu && breakpoint < 6) return null
if (!MainMenu && !PageMenu && breakpoint < PORTRAIT_BREAKPOINT.TABLET) return null
return (
<div className="tlui-menu-zone">
<div className="tlui-buttons__horizontal">
{MainMenu && <MainMenu />}
{PageMenu && <PageMenu />}
{breakpoint < 6 ? null : (
{breakpoint < PORTRAIT_BREAKPOINT.TABLET ? null : (
<>
{QuickActions && <QuickActions />}
{ActionsMenu && <ActionsMenu />}

View file

@ -1,4 +1,5 @@
import { memo, useCallback } from 'react'
import { PORTRAIT_BREAKPOINT } from '../../constants'
import { unwrapLabel, useActions } from '../../context/actions'
import { useBreakpoint } from '../../context/breakpoints'
import { useTldrawUiComponents } from '../../context/components'
@ -21,14 +22,14 @@ export const DefaultNavigationPanel = memo(function DefaultNavigationPanel() {
const { ZoomMenu, Minimap } = useTldrawUiComponents()
if (breakpoint < 4) {
if (breakpoint < PORTRAIT_BREAKPOINT.MOBILE) {
return null
}
return (
<div className="tlui-navigation-panel">
<div className="tlui-buttons__horizontal">
{ZoomMenu && breakpoint < 6 ? (
{ZoomMenu && breakpoint < PORTRAIT_BREAKPOINT.TABLET ? (
<ZoomMenu />
) : collapsed ? (
<>
@ -74,7 +75,7 @@ export const DefaultNavigationPanel = memo(function DefaultNavigationPanel() {
</>
)}
</div>
{Minimap && breakpoint >= 6 && !collapsed && <Minimap />}
{Minimap && breakpoint >= PORTRAIT_BREAKPOINT.TABLET && !collapsed && <Minimap />}
</div>
)
})

View file

@ -8,6 +8,7 @@ import {
useValue,
} from '@tldraw/editor'
import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
import { PORTRAIT_BREAKPOINT } from '../../constants'
import { useBreakpoint } from '../../context/breakpoints'
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
import { useReadonly } from '../../hooks/useReadonly'
@ -325,7 +326,7 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
data-id={page.id}
data-index={index}
/>
{breakpoint < 5 && isCoarsePointer ? (
{breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && isCoarsePointer ? (
// sigh, this is a workaround for iOS Safari
// because the device and the radix popover seem
// to be fighting over scroll position. Nothing

View file

@ -2,6 +2,7 @@ import { GeoShapeGeoStyle, preventDefault, track, useEditor, useValue } from '@t
import classNames from 'classnames'
import hotkeys from 'hotkeys-js'
import React, { memo, useEffect, useMemo } from 'react'
import { PORTRAIT_BREAKPOINT } from '../../constants'
import { useBreakpoint } from '../../context/breakpoints'
import { useTldrawUiComponents } from '../../context/components'
import { areShortcutsDisabled } from '../../hooks/useKeyboardShortcuts'
@ -102,7 +103,7 @@ export const DefaultToolbar = memo(function DefaultToolbar() {
<div className="tlui-toolbar__left">
{!isReadonlyMode && (
<div className="tlui-toolbar__extras">
{breakpoint < 6 && (
{breakpoint < PORTRAIT_BREAKPOINT.TABLET && (
<div className="tlui-toolbar__extras__controls tlui-buttons__horizontal">
{QuickActions && <QuickActions />}
{ActionsMenu && <ActionsMenu />}
@ -113,7 +114,7 @@ export const DefaultToolbar = memo(function DefaultToolbar() {
)}
<div
className={classNames('tlui-toolbar__tools', {
'tlui-toolbar__tools__mobile': breakpoint < 5,
'tlui-toolbar__tools__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
})}
>
{/* Main panel items */}
@ -158,7 +159,7 @@ export const DefaultToolbar = memo(function DefaultToolbar() {
) : null}
</div>
</div>
{breakpoint < 5 && !isReadonlyMode && (
{breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && (
<div className="tlui-toolbar__tools">
<MobileStylePanel />
</div>

View file

@ -1,5 +1,6 @@
import { useEditor, useValue } from '@tldraw/editor'
import classNames from 'classnames'
import { PORTRAIT_BREAKPOINT } from '../../constants'
import { useBreakpoint } from '../../context/breakpoints'
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { Button } from '../primitives/Button'
@ -35,7 +36,7 @@ export function ToggleToolLockedButton({ activeToolId }: ToggleToolLockedButtonP
type="normal"
title={msg('action.toggle-tool-lock')}
className={classNames('tlui-toolbar__lock-button', {
'tlui-toolbar__lock-button__mobile': breakpoint < 5,
'tlui-toolbar__lock-button__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
})}
icon={isToolLocked ? 'lock' : 'unlock'}
onClick={() => editor.updateInstanceState({ isToolLocked: !isToolLocked })}

View file

@ -1,6 +1,7 @@
import * as _Dropdown from '@radix-ui/react-dropdown-menu'
import { ANIMATION_MEDIUM_MS, useContainer, useEditor, useValue } from '@tldraw/editor'
import { forwardRef, memo, useCallback } from 'react'
import { PORTRAIT_BREAKPOINT } from '../../constants'
import { useBreakpoint } from '../../context/breakpoints'
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
@ -64,11 +65,15 @@ const ZoomTriggerButton = forwardRef<HTMLButtonElement, any>(
type="icon"
title={`${msg('navigation-zone.zoom')}`}
data-testid="minimap.zoom-menu"
className={breakpoint < 5 ? 'tlui-zoom-menu__button' : 'tlui-zoom-menu__button__pct'}
className={
breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM
? 'tlui-zoom-menu__button'
: 'tlui-zoom-menu__button__pct'
}
onDoubleClick={handleDoubleClick}
icon={breakpoint < 4 ? 'zoom-in' : undefined}
icon={breakpoint < PORTRAIT_BREAKPOINT.MOBILE ? 'zoom-in' : undefined}
>
{breakpoint < 4 ? null : (
{breakpoint < PORTRAIT_BREAKPOINT.MOBILE ? null : (
<span style={{ flexGrow: 0, textAlign: 'center' }}>{Math.floor(zoom * 100)}%</span>
)}
</Button>

View file

@ -1,3 +1,4 @@
import { PORTRAIT_BREAKPOINT } from '../../constants'
import { useBreakpoint } from '../../context/breakpoints'
import { kbd } from './shared'
@ -9,7 +10,7 @@ export interface KbdProps {
/** @internal */
export function Kbd({ children }: KbdProps) {
const breakpoint = useBreakpoint()
if (breakpoint < 4) return null
if (breakpoint < PORTRAIT_BREAKPOINT.MOBILE) return null
return (
<kbd className="tlui-kbd">
{kbd(children).map((k, i) => (

View file

@ -1,2 +1,14 @@
// Breakpoints for portrait
// Breakpoints for portrait, keep in sync with PORTRAIT_BREAKPOINT enum below!
export const PORTRAIT_BREAKPOINTS = [0, 390, 428, 468, 580, 640, 840, 1023]
// Mapping for above array -- needs to be kept in sync!
export enum PORTRAIT_BREAKPOINT {
ZERO = 0,
MOBILE_XXS = 1,
MOBILE_XS = 2,
MOBILE_SM = 3,
MOBILE = 4,
TABLET_SM = 5,
TABLET = 6,
DESKTOP = 7,
}

View file

@ -1,6 +1,6 @@
import { useEditor, useValue } from '@tldraw/editor'
import React, { useContext } from 'react'
import { PORTRAIT_BREAKPOINTS } from '../constants'
import { PORTRAIT_BREAKPOINT, PORTRAIT_BREAKPOINTS } from '../constants'
const BreakpointContext = React.createContext(0)
@ -20,7 +20,9 @@ export function BreakPointProvider({
// This will recompute the viewport screen bounds changes...
const { width } = editor.getViewportScreenBounds()
const maxBreakpoint = forceMobile ? 3 : PORTRAIT_BREAKPOINTS.length - 1
const maxBreakpoint = forceMobile
? PORTRAIT_BREAKPOINT.MOBILE_SM
: PORTRAIT_BREAKPOINTS.length - 1
for (let i = 0; i < maxBreakpoint; i++) {
if (width > PORTRAIT_BREAKPOINTS[i] && width <= PORTRAIT_BREAKPOINTS[i + 1]) {

View file

@ -1,5 +1,6 @@
import { Editor, objectMapEntries } from '@tldraw/editor'
import { useMemo } from 'react'
import { PORTRAIT_BREAKPOINT } from './constants'
import { ActionsProviderProps } from './context/actions'
import { useBreakpoint } from './context/breakpoints'
import { useDialogs } from './context/dialogs'
@ -13,7 +14,7 @@ export function useDefaultHelpers() {
const { addToast, removeToast, clearToasts } = useToasts()
const { addDialog, clearDialogs, removeDialog, updateDialog } = useDialogs()
const breakpoint = useBreakpoint()
const isMobile = breakpoint < 5
const isMobile = breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM
const msg = useTranslation()
return useMemo(
() => ({