[feature] Iframe warning (#800)
This commit is contained in:
parent
1e80d1ac21
commit
2352985e94
31 changed files with 325 additions and 205 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -18,3 +18,4 @@ apps/www/public/sw.js.map
|
|||
.env
|
||||
firebase.config.*.turbo
|
||||
.turbo
|
||||
apps/new-tab-extension/tsconfig.tsbuildinfo
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
# @tldraw/new-tab-extension
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @tldraw/tldraw@1.19.1
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@tldraw/new-tab-extension",
|
||||
"private": true,
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.0",
|
||||
"description": "A tiny little new tab extension for tldraw.",
|
||||
"author": "@steveruizok",
|
||||
"repository": {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,11 +1,5 @@
|
|||
# @tldraw/vscode-editor
|
||||
|
||||
## 1.11.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fix toolbar placement.
|
||||
|
||||
## 1.11.0
|
||||
|
||||
### Minor Changes
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@tldraw/vscode-editor",
|
||||
"version": "1.11.1",
|
||||
"version": "1.11.0",
|
||||
"private": true,
|
||||
"description": "An an editor for the tldraw vscode extension.",
|
||||
"author": "@steveruizok",
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
# @tldraw/www
|
||||
|
||||
## 1.7.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @tldraw/core@1.14.2
|
||||
- @tldraw/tldraw@1.19.1
|
||||
|
||||
## 1.7.8
|
||||
|
||||
### Patch Changes
|
||||
|
|
127
apps/www/components/IFrameWarning.tsx
Normal file
127
apps/www/components/IFrameWarning.tsx
Normal file
|
@ -0,0 +1,127 @@
|
|||
import React from 'react'
|
||||
import { styled } from 'styles'
|
||||
|
||||
export function IFrameWarning({ url = 'https://tldraw.com' }: { url?: string }) {
|
||||
const [copied, setCopied] = React.useState(false)
|
||||
const rTimeout = React.useRef<any>(0)
|
||||
|
||||
const handleCopy = React.useCallback(() => {
|
||||
setCopied(true)
|
||||
clearTimeout(rTimeout.current)
|
||||
rTimeout.current = setTimeout(() => {
|
||||
setCopied(false)
|
||||
}, 1200)
|
||||
|
||||
const textarea = document.createElement('textarea')
|
||||
textarea.setAttribute('position', 'fixed')
|
||||
textarea.setAttribute('top', '0')
|
||||
textarea.setAttribute('readonly', 'true')
|
||||
textarea.setAttribute('contenteditable', 'true')
|
||||
textarea.style.position = 'fixed'
|
||||
textarea.value = url
|
||||
document.body.appendChild(textarea)
|
||||
textarea.focus()
|
||||
textarea.select()
|
||||
//tldraw.com/r/hasdasdsad sad sadaasdasdasdsdasdadssdadadsd as ello
|
||||
https: try {
|
||||
const range = document.createRange()
|
||||
range.selectNodeContents(textarea)
|
||||
const sel = window.getSelection()
|
||||
if (sel) {
|
||||
sel.removeAllRanges()
|
||||
sel.addRange(range)
|
||||
textarea.setSelectionRange(0, textarea.value.length)
|
||||
}
|
||||
document.execCommand('copy')
|
||||
} catch (err) {
|
||||
console.log('nope')
|
||||
|
||||
null // Could not copy to clipboard
|
||||
} finally {
|
||||
document.body.removeChild(textarea)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledUrlLink href={url} target="_parent">
|
||||
Visit this page on tldraw.com{' '}
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 15 15"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3 2C2.44772 2 2 2.44772 2 3V12C2 12.5523 2.44772 13 3 13H12C12.5523 13 13 12.5523 13 12V8.5C13 8.22386 12.7761 8 12.5 8C12.2239 8 12 8.22386 12 8.5V12H3V3L6.5 3C6.77614 3 7 2.77614 7 2.5C7 2.22386 6.77614 2 6.5 2H3ZM12.8536 2.14645C12.9015 2.19439 12.9377 2.24964 12.9621 2.30861C12.9861 2.36669 12.9996 2.4303 13 2.497L13 2.5V2.50049V5.5C13 5.77614 12.7761 6 12.5 6C12.2239 6 12 5.77614 12 5.5V3.70711L6.85355 8.85355C6.65829 9.04882 6.34171 9.04882 6.14645 8.85355C5.95118 8.65829 5.95118 8.34171 6.14645 8.14645L11.2929 3H9.5C9.22386 3 9 2.77614 9 2.5C9 2.22386 9.22386 2 9.5 2H12.4999H12.5C12.5678 2 12.6324 2.01349 12.6914 2.03794C12.7504 2.06234 12.8056 2.09851 12.8536 2.14645Z"
|
||||
fill="currentColor"
|
||||
stroke="black"
|
||||
strokeWidth=".5"
|
||||
></path>
|
||||
</svg>
|
||||
</StyledUrlLink>
|
||||
<StyledUrlContainer onClick={handleCopy}>
|
||||
<span>{url}</span>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 15 15"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
{copied ? (
|
||||
<path d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z" />
|
||||
) : (
|
||||
<path d="M5 2V1H10V2H5ZM4.75 0C4.33579 0 4 0.335786 4 0.75V1H3.5C2.67157 1 2 1.67157 2 2.5V12.5C2 13.3284 2.67157 14 3.5 14H7V13H3.5C3.22386 13 3 12.7761 3 12.5V2.5C3 2.22386 3.22386 2 3.5 2H4V2.25C4 2.66421 4.33579 3 4.75 3H10.25C10.6642 3 11 2.66421 11 2.25V2H11.5C11.7761 2 12 2.22386 12 2.5V7H13V2.5C13 1.67157 12.3284 1 11.5 1H11V0.75C11 0.335786 10.6642 0 10.25 0H4.75ZM9 8.5C9 8.77614 8.77614 9 8.5 9C8.22386 9 8 8.77614 8 8.5C8 8.22386 8.22386 8 8.5 8C8.77614 8 9 8.22386 9 8.5ZM10.5 9C10.7761 9 11 8.77614 11 8.5C11 8.22386 10.7761 8 10.5 8C10.2239 8 10 8.22386 10 8.5C10 8.77614 10.2239 9 10.5 9ZM13 8.5C13 8.77614 12.7761 9 12.5 9C12.2239 9 12 8.77614 12 8.5C12 8.22386 12.2239 8 12.5 8C12.7761 8 13 8.22386 13 8.5ZM14.5 9C14.7761 9 15 8.77614 15 8.5C15 8.22386 14.7761 8 14.5 8C14.2239 8 14 8.22386 14 8.5C14 8.77614 14.2239 9 14.5 9ZM15 10.5C15 10.7761 14.7761 11 14.5 11C14.2239 11 14 10.7761 14 10.5C14 10.2239 14.2239 10 14.5 10C14.7761 10 15 10.2239 15 10.5ZM14.5 13C14.7761 13 15 12.7761 15 12.5C15 12.2239 14.7761 12 14.5 12C14.2239 12 14 12.2239 14 12.5C14 12.7761 14.2239 13 14.5 13ZM14.5 15C14.7761 15 15 14.7761 15 14.5C15 14.2239 14.7761 14 14.5 14C14.2239 14 14 14.2239 14 14.5C14 14.7761 14.2239 15 14.5 15ZM8.5 11C8.77614 11 9 10.7761 9 10.5C9 10.2239 8.77614 10 8.5 10C8.22386 10 8 10.2239 8 10.5C8 10.7761 8.22386 11 8.5 11ZM9 12.5C9 12.7761 8.77614 13 8.5 13C8.22386 13 8 12.7761 8 12.5C8 12.2239 8.22386 12 8.5 12C8.77614 12 9 12.2239 9 12.5ZM8.5 15C8.77614 15 9 14.7761 9 14.5C9 14.2239 8.77614 14 8.5 14C8.22386 14 8 14.2239 8 14.5C8 14.7761 8.22386 15 8.5 15ZM11 14.5C11 14.7761 10.7761 15 10.5 15C10.2239 15 10 14.7761 10 14.5C10 14.2239 10.2239 14 10.5 14C10.7761 14 11 14.2239 11 14.5ZM12.5 15C12.7761 15 13 14.7761 13 14.5C13 14.2239 12.7761 14 12.5 14C12.2239 14 12 14.2239 12 14.5C12 14.7761 12.2239 15 12.5 15Z" />
|
||||
)}
|
||||
</svg>
|
||||
</StyledUrlContainer>
|
||||
</StyledContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledContainer = styled('div', {
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontFamily: '$body',
|
||||
fontWeight: 'normal',
|
||||
fontSize: '$3',
|
||||
gap: '$5',
|
||||
flexDirection: 'column',
|
||||
})
|
||||
|
||||
const StyledUrlLink = styled('a', {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '$2',
|
||||
padding: '$5',
|
||||
})
|
||||
|
||||
const StyledUrlContainer = styled('a', {
|
||||
backgroundColor: '$hover',
|
||||
borderRadius: '$2',
|
||||
maxWidth: '62%',
|
||||
minWidth: 320,
|
||||
gap: '$4',
|
||||
padding: '$4 $5',
|
||||
fontSize: '$2',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
|
||||
'& span': {
|
||||
flexGrow: 2,
|
||||
overflow: 'auto',
|
||||
whiteSpace: 'nowrap',
|
||||
'-ms-overflow-style': 'none' /* for Internet Explorer, Edge */,
|
||||
scrollbarWidth: 'none',
|
||||
'&::webkit-scrollbar': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
})
|
|
@ -1,6 +1,9 @@
|
|||
import { RoomProvider } from '../utils/liveblocks'
|
||||
import { Tldraw, useFileSystem } from '@tldraw/tldraw'
|
||||
import { useAccountHandlers } from 'hooks/useAccountHandlers'
|
||||
import { useMultiplayerAssets } from 'hooks/useMultiplayerAssets'
|
||||
import { useMultiplayerState } from 'hooks/useMultiplayerState'
|
||||
import { useUploadAssets } from 'hooks/useUploadAssets'
|
||||
import React, { FC } from 'react'
|
||||
import { styled } from 'styles'
|
||||
import { useReadOnlyMultiplayerState } from 'hooks/useReadOnlyMultiplayerState'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@tldraw/www",
|
||||
"version": "1.7.9",
|
||||
"version": "1.7.8",
|
||||
"private": true,
|
||||
"description": "A tiny little drawing app (site).",
|
||||
"repository": {
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as React from 'react'
|
|||
import type { GetServerSideProps } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { IFrameWarning } from 'components/IFrameWarning'
|
||||
const MultiplayerEditor = dynamic(() => import('components/MultiplayerEditor'), {
|
||||
ssr: false,
|
||||
}) as any
|
||||
|
@ -13,6 +14,10 @@ interface RoomProps {
|
|||
}
|
||||
|
||||
export default function Room({ id, isUser, isSponsor }: RoomProps) {
|
||||
if (typeof window !== 'undefined') {
|
||||
return <IFrameWarning url={`https://tldraw.com/r/${id}`} />
|
||||
}
|
||||
|
||||
return <MultiplayerEditor isUser={isUser} isSponsor={isSponsor} roomId={id} />
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { GetServerSideProps } from 'next'
|
|||
import { getSession } from 'next-auth/react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import { IFrameWarning } from 'components/IFrameWarning'
|
||||
const ReadOnlyMultiplayerEditor = dynamic(() => import('components/ReadOnlyMultiplayerEditor'), {
|
||||
ssr: false,
|
||||
}) as any
|
||||
|
@ -14,6 +15,10 @@ interface RoomProps {
|
|||
}
|
||||
|
||||
export default function Room({ id, isUser, isSponsor }: RoomProps) {
|
||||
if (typeof window !== 'undefined') {
|
||||
return <IFrameWarning url={`https://tldraw.com/v/${id}`} />
|
||||
}
|
||||
|
||||
return <ReadOnlyMultiplayerEditor isUser={isUser} isSponsor={isSponsor} roomId={id} />
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import UIOptions from './ui-options'
|
|||
import { Multiplayer } from './multiplayer'
|
||||
import './styles.css'
|
||||
import Export from '~export'
|
||||
import IFrame from '~iframe'
|
||||
|
||||
const pages: ({ path: string; component: any; title: string } | '---')[] = [
|
||||
{ path: '/develop', component: Develop, title: 'Develop' },
|
||||
|
@ -42,6 +43,7 @@ const pages: ({ path: string; component: any; title: string } | '---')[] = [
|
|||
{ path: '/export', component: Export, title: 'Export' },
|
||||
{ path: '/scroll', component: Scroll, title: 'In a scrolling container' },
|
||||
{ path: '/multiplayer', component: Multiplayer, title: 'Multiplayer' },
|
||||
{ path: '/iframe', component: IFrame, title: 'IFrame' },
|
||||
]
|
||||
|
||||
export default function App() {
|
||||
|
|
9
examples/tldraw-example/src/iframe.tsx
Normal file
9
examples/tldraw-example/src/iframe.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import * as React from 'react'
|
||||
|
||||
export default function IFrame() {
|
||||
return (
|
||||
<div className="tldraw">
|
||||
<iframe src="http://localhost:3000/r/hello" style={{ width: '100%', height: '50%' }} />
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
],
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"build:www": "turbo run build:www",
|
||||
"build:www": "turbo run build:www --force",
|
||||
"build:core": "turbo run build:core",
|
||||
"build:packages": "turbo run build:packages --stream",
|
||||
"build:apps": "turbo run build:apps",
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
## 1.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fix toolbar placement.
|
||||
|
||||
## 1.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.14.2",
|
||||
"version": "1.14.1",
|
||||
"name": "@tldraw/core",
|
||||
"description": "The tldraw core renderer and utilities.",
|
||||
"author": "@steveruizok",
|
||||
|
|
|
@ -1491,15 +1491,14 @@ left past the initial left edge) then swap points on that axis.
|
|||
* @param str string
|
||||
*/
|
||||
static lns(str: string) {
|
||||
const result = str.split('')
|
||||
const result = str
|
||||
.split('')
|
||||
.map((n) => (Number.isNaN(+n) ? n : +n < 5 ? 5 + +n : +n > 5 ? +n - 5 : +n))
|
||||
result.push(...result.splice(0, Math.round(result.length / 5)))
|
||||
result.push(...result.splice(0, Math.round(result.length / 4)))
|
||||
result.push(...result.splice(0, Math.round(result.length / 3)))
|
||||
result.push(...result.splice(0, Math.round(result.length / 2)))
|
||||
return result
|
||||
.reverse()
|
||||
.map((n) => (+n ? (+n < 5 ? 5 + +n : +n > 5 ? +n - 5 : n) : n))
|
||||
.join('')
|
||||
return result.reverse().join('')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
## 1.19.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fix toolbar placement.
|
||||
- Updated dependencies
|
||||
- @tldraw/core@1.14.2
|
||||
|
||||
## 1.19.0
|
||||
|
||||
### Minor Changes
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@tldraw/tldraw",
|
||||
"version": "1.19.1",
|
||||
"version": "1.19.0",
|
||||
"description": "A tiny little drawing app (editor)",
|
||||
"author": "@steveruizok",
|
||||
"repository": {
|
||||
|
@ -47,7 +47,7 @@
|
|||
"@radix-ui/react-icons": "^1.1.1",
|
||||
"@radix-ui/react-tooltip": "^0.1.7",
|
||||
"@stitches/react": "^1.2.8",
|
||||
"@tldraw/core": "^1.14.2",
|
||||
"@tldraw/core": "^1.14.1",
|
||||
"@tldraw/intersect": "^1.7.1",
|
||||
"@tldraw/vec": "^1.7.1",
|
||||
"idb-keyval": "^6.1.0",
|
||||
|
|
|
@ -618,7 +618,7 @@ const StyledUI = styled('div', {
|
|||
left: 0,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
padding: '0',
|
||||
padding: '8px 8px 0 8px',
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'flex-start',
|
||||
|
|
|
@ -119,7 +119,7 @@ export const StyledToolButton = styled('button', {
|
|||
color: '$text',
|
||||
fontSize: '$0',
|
||||
background: 'none',
|
||||
margin: 0,
|
||||
margin: '0',
|
||||
padding: '$2',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
|
@ -158,9 +158,7 @@ export const StyledToolButton = styled('button', {
|
|||
},
|
||||
},
|
||||
circle: {
|
||||
padding: '0',
|
||||
height: 32,
|
||||
width: 32,
|
||||
padding: '$2',
|
||||
[`& ${StyledToolButtonInner}`]: {
|
||||
border: '1px solid $panelContrast',
|
||||
borderRadius: '100%',
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
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'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import { DeleteButton } from './DeleteButton'
|
||||
import { breakpoints } from '~components/breakpoints'
|
||||
|
||||
const isDebugModeSelector = (s: TDSnapshot) => s.settings.isDebugMode
|
||||
const dockPositionState = (s: TDSnapshot) => s.settings.dockPosition
|
||||
|
@ -18,29 +17,74 @@ interface ToolsPanelProps {
|
|||
|
||||
export const ToolsPanel = React.memo(function ToolsPanel({ onBlur }: ToolsPanelProps) {
|
||||
const app = useTldrawApp()
|
||||
const side = app.useStore(dockPositionState)
|
||||
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 side={side} onBlur={onBlur} bp={breakpoints} debug={isDebugMode}>
|
||||
<StyledCenterWrap id="TD-Tools">
|
||||
<BackToContent />
|
||||
<StyledPrimaryTools
|
||||
orientation={side === 'bottom' || side === 'top' ? 'horizontal' : 'vertical'}
|
||||
<StyledToolsPanelContainer
|
||||
style={{
|
||||
...style,
|
||||
}}
|
||||
onBlur={onBlur}
|
||||
>
|
||||
<StyledCenterWrap
|
||||
id="TD-Tools"
|
||||
style={{
|
||||
...centerWrapStyle,
|
||||
}}
|
||||
>
|
||||
<BackToContent />
|
||||
<StyledPrimaryTools style={{ flexDirection: primaryToolStyle }}>
|
||||
<ActionButton />
|
||||
<PrimaryTools />
|
||||
<DeleteButton />
|
||||
</StyledPrimaryTools>
|
||||
</StyledCenterWrap>
|
||||
</StyledToolsPanelContainer>
|
||||
{isDebugMode && (
|
||||
<StyledStatusWrap>
|
||||
<StatusBar />
|
||||
</StyledStatusWrap>
|
||||
)}
|
||||
</>
|
||||
</StyledToolsPanelContainer>
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -49,68 +93,24 @@ const StyledToolsPanelContainer = styled('div', {
|
|||
width: '100%',
|
||||
minWidth: 0,
|
||||
maxWidth: '100%',
|
||||
height: '64px',
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'auto auto auto',
|
||||
gridTemplateRows: 'auto auto',
|
||||
justifyContent: 'space-between',
|
||||
padding: '0',
|
||||
gap: '$4',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
zIndex: 200,
|
||||
overflow: 'hidden',
|
||||
pointerEvents: 'none',
|
||||
'& > div > *': {
|
||||
pointerEvents: 'all',
|
||||
},
|
||||
variants: {
|
||||
debug: {
|
||||
true: {},
|
||||
false: {},
|
||||
},
|
||||
bp: {
|
||||
mobile: {},
|
||||
small: {},
|
||||
medium: {},
|
||||
large: {},
|
||||
},
|
||||
side: {
|
||||
top: {
|
||||
width: '100%',
|
||||
height: 64,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 45,
|
||||
},
|
||||
right: { width: 64, height: '100%', top: 0, right: 0 },
|
||||
bottom: {
|
||||
width: '100%',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
left: { width: 64, height: '100%', left: 0 },
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
side: 'top',
|
||||
bp: 'large',
|
||||
css: {
|
||||
top: '10px',
|
||||
},
|
||||
},
|
||||
{
|
||||
side: 'bottom',
|
||||
debug: true,
|
||||
css: {
|
||||
bottom: 40,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const StyledCenterWrap = styled('div', {
|
||||
gridRow: 1,
|
||||
gridColumn: 2,
|
||||
display: 'flex',
|
||||
width: 'fit-content',
|
||||
height: 'fit-content',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
|
@ -118,11 +118,10 @@ const StyledCenterWrap = styled('div', {
|
|||
})
|
||||
|
||||
const StyledStatusWrap = styled('div', {
|
||||
position: 'absolute',
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: '40px',
|
||||
width: '100%',
|
||||
maxWidth: '100%',
|
||||
})
|
||||
|
@ -131,16 +130,5 @@ const StyledPrimaryTools = styled('div', {
|
|||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: 'fit-content',
|
||||
gap: '$3',
|
||||
variants: {
|
||||
orientation: {
|
||||
horizontal: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
vertical: {
|
||||
flexDirection: 'column',
|
||||
},
|
||||
},
|
||||
},
|
||||
gap: '$2',
|
||||
})
|
||||
|
|
|
@ -119,7 +119,7 @@ export const MultiplayerMenu = React.memo(function MultiplayerMenu() {
|
|||
<DMItem
|
||||
id="TD-Multiplayer-CopyReadOnlyLink"
|
||||
onClick={handleCopyReadOnlySelect}
|
||||
disabled={!room}
|
||||
disabled={false}
|
||||
>
|
||||
<FormattedMessage id="copy.readonly.link" />
|
||||
<SmallIcon>{copied ? <CheckIcon /> : <ClipboardIcon />}</SmallIcon>
|
||||
|
|
|
@ -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
|
|
@ -1015,6 +1015,7 @@ export class TLDR {
|
|||
sel.addRange(range)
|
||||
textarea.setSelectionRange(0, textarea.value.length)
|
||||
}
|
||||
document.execCommand('copy')
|
||||
} catch (err) {
|
||||
null // Could not copy to clipboard
|
||||
} finally {
|
||||
|
|
|
@ -267,13 +267,13 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
|
||||
constructor(id?: string, callbacks = {} as TDCallbacks) {
|
||||
super(TldrawApp.defaultState, id, TldrawApp.version, (prev, next, prevVersion) => {
|
||||
return migrate(
|
||||
{
|
||||
return {
|
||||
...next,
|
||||
document: { ...next.document, ...prev.document, version: prevVersion },
|
||||
},
|
||||
document: migrate(
|
||||
{ ...next.document, ...prev.document, version: prevVersion },
|
||||
TldrawApp.version
|
||||
)
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
this.callbacks = callbacks
|
||||
|
@ -282,7 +282,10 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
/* -------------------- Internal -------------------- */
|
||||
|
||||
protected migrate = (state: TDSnapshot): TDSnapshot => {
|
||||
return migrate(state, TldrawApp.version)
|
||||
return {
|
||||
...state,
|
||||
document: migrate(state.document, TldrawApp.version),
|
||||
}
|
||||
}
|
||||
|
||||
protected onReady = () => {
|
||||
|
@ -294,10 +297,10 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
|
||||
try {
|
||||
this.patchState({
|
||||
...migrate(this.state, TldrawApp.version),
|
||||
appState: {
|
||||
status: TDStatus.Idle,
|
||||
},
|
||||
document: migrate(this.document, TldrawApp.version),
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('The data appears to be corrupted. Resetting!', e)
|
||||
|
@ -1229,7 +1232,10 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
// Set the default page name to the localized version of "Page"
|
||||
doc.pages['page'].name = 'Page 1'
|
||||
|
||||
this.resetHistory().clearSelectHistory().loadDocument(TldrawApp.defaultDocument).persist({})
|
||||
this.resetHistory()
|
||||
.clearSelectHistory()
|
||||
.loadDocument(migrate(doc, TldrawApp.version))
|
||||
.persist({})
|
||||
|
||||
return this
|
||||
}
|
||||
|
@ -1267,17 +1273,12 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
// If it's a new document, do a full change.
|
||||
if (this.document.id !== document.id) {
|
||||
this.replaceState({
|
||||
...migrate(
|
||||
{
|
||||
...this.state,
|
||||
document,
|
||||
},
|
||||
TldrawApp.version
|
||||
),
|
||||
appState: {
|
||||
...this.appState,
|
||||
currentPageId: Object.keys(document.pages)[0],
|
||||
},
|
||||
document: migrate(document, TldrawApp.version),
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
@ -1339,11 +1340,12 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
|
||||
return this.replaceState(
|
||||
{
|
||||
...migrate(
|
||||
{ ...this.state, document: { ...document, pageStates: currentPageStates } },
|
||||
TldrawApp.version
|
||||
),
|
||||
...this.state,
|
||||
appState: nextAppState,
|
||||
document: {
|
||||
...migrate(document, TldrawApp.version),
|
||||
pageStates: currentPageStates,
|
||||
},
|
||||
},
|
||||
'merge'
|
||||
)
|
||||
|
@ -1356,7 +1358,7 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
updateDocument = (document: TDDocument, reason = 'updated_document'): this => {
|
||||
const prevState = this.state
|
||||
|
||||
let nextState = { ...prevState, document: { ...prevState.document } }
|
||||
const nextState = { ...prevState, document: { ...prevState.document } }
|
||||
|
||||
if (!document.pages[this.currentPageId]) {
|
||||
nextState.appState = {
|
||||
|
@ -1397,10 +1399,9 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
}
|
||||
}
|
||||
|
||||
return this.replaceState(
|
||||
migrate(nextState, nextState.document.version || 0),
|
||||
`${reason}:${document.id}`
|
||||
)
|
||||
nextState.document = migrate(nextState.document, nextState.document.version || 0)
|
||||
|
||||
return this.replaceState(nextState, `${reason}:${document.id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1436,21 +1437,22 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
this.clearSelectHistory()
|
||||
this.session = undefined
|
||||
|
||||
const state = {
|
||||
this.replaceState(
|
||||
{
|
||||
...TldrawApp.defaultState,
|
||||
settings: {
|
||||
...this.state.settings,
|
||||
},
|
||||
document,
|
||||
document: migrate(document, TldrawApp.version),
|
||||
appState: {
|
||||
...TldrawApp.defaultState.appState,
|
||||
...this.state.appState,
|
||||
currentPageId: Object.keys(document.pages)[0],
|
||||
disableAssets: this.disableAssets,
|
||||
},
|
||||
}
|
||||
|
||||
this.replaceState(migrate(state, TldrawApp.version), 'loaded_document')
|
||||
},
|
||||
'loaded_document'
|
||||
)
|
||||
const { point, zoom } = this.camera
|
||||
this.updateViewport(point, zoom)
|
||||
return this
|
||||
|
@ -1474,7 +1476,7 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
if (this.readOnly) return
|
||||
try {
|
||||
const fileHandle = await saveToFileSystem(
|
||||
migrate(this.state, TldrawApp.version).document,
|
||||
migrate(this.document, TldrawApp.version),
|
||||
this.fileSystemHandle
|
||||
)
|
||||
this.fileSystemHandle = fileHandle
|
||||
|
@ -4119,7 +4121,7 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
|
||||
getShapeUtil = TLDR.getShapeUtil
|
||||
|
||||
static version = 15.4
|
||||
static version = 15.3
|
||||
|
||||
static defaultDocument: TDDocument = {
|
||||
id: 'doc',
|
||||
|
|
|
@ -50,7 +50,7 @@ TldrawTestApp {
|
|||
"shapes": Object {},
|
||||
},
|
||||
},
|
||||
"version": 15.4,
|
||||
"version": 15.3,
|
||||
},
|
||||
"settings": Object {
|
||||
"dockPosition": "bottom",
|
||||
|
@ -201,7 +201,7 @@ TldrawTestApp {
|
|||
},
|
||||
},
|
||||
},
|
||||
"version": 15.4,
|
||||
"version": 15.3,
|
||||
},
|
||||
"settings": Object {
|
||||
"dockPosition": "bottom",
|
||||
|
@ -373,7 +373,7 @@ TldrawTestApp {
|
|||
"shapes": Object {},
|
||||
},
|
||||
},
|
||||
"version": 15.4,
|
||||
"version": 15.3,
|
||||
},
|
||||
"settings": Object {
|
||||
"dockPosition": "bottom",
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { Decoration, FontStyle, TDDocument, TDShapeType, TDSnapshot, TextShape } from '~types'
|
||||
import { Decoration, FontStyle, TDDocument, TDShapeType, TextShape } from '~types'
|
||||
|
||||
export function migrate(state: TDSnapshot, newVersion: number): TDSnapshot {
|
||||
const { document, settings } = state
|
||||
export function migrate(document: TDDocument, newVersion: number): TDDocument {
|
||||
const { version = 0 } = document
|
||||
|
||||
if (!('assets' in document)) {
|
||||
|
@ -12,8 +11,8 @@ export function migrate(state: TDSnapshot, newVersion: number): TDSnapshot {
|
|||
// Remove unused assets when loading a document
|
||||
const assetIdsInUse = new Set<string>()
|
||||
|
||||
Object.values(document.pages).forEach((page) =>
|
||||
Object.values(page.shapes).forEach((shape) => {
|
||||
Object.values(document.pages).forEach(page =>
|
||||
Object.values(page.shapes).forEach(shape => {
|
||||
const { parentId, children, assetId } = shape
|
||||
|
||||
if (assetId) {
|
||||
|
@ -27,7 +26,7 @@ export function migrate(state: TDSnapshot, newVersion: number): TDSnapshot {
|
|||
}
|
||||
|
||||
if (shape.type === TDShapeType.Group && children) {
|
||||
children.forEach((childId) => {
|
||||
children.forEach(childId => {
|
||||
if (!page.shapes[childId]) {
|
||||
console.warn('Encountered a parent with a missing child!', shape.id, childId)
|
||||
children?.splice(children.indexOf(childId), 1)
|
||||
|
@ -39,31 +38,31 @@ export function migrate(state: TDSnapshot, newVersion: number): TDSnapshot {
|
|||
})
|
||||
)
|
||||
|
||||
Object.keys(document.assets).forEach((assetId) => {
|
||||
Object.keys(document.assets).forEach(assetId => {
|
||||
if (!assetIdsInUse.has(assetId)) {
|
||||
delete document.assets[assetId]
|
||||
}
|
||||
})
|
||||
|
||||
if (version === newVersion) return state
|
||||
if (version === newVersion) return document
|
||||
|
||||
if (version < 14) {
|
||||
Object.values(document.pages).forEach((page) => {
|
||||
Object.values(document.pages).forEach(page => {
|
||||
Object.values(page.shapes)
|
||||
.filter((shape) => shape.type === TDShapeType.Text)
|
||||
.forEach((shape) => (shape as TextShape).style.font === FontStyle.Script)
|
||||
.filter(shape => shape.type === TDShapeType.Text)
|
||||
.forEach(shape => (shape as TextShape).style.font === FontStyle.Script)
|
||||
})
|
||||
}
|
||||
|
||||
// Lowercase styles, move binding meta to binding
|
||||
if (version <= 13) {
|
||||
Object.values(document.pages).forEach((page) => {
|
||||
Object.values(page.bindings).forEach((binding) => {
|
||||
Object.values(document.pages).forEach(page => {
|
||||
Object.values(page.bindings).forEach(binding => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Object.assign(binding, (binding as any).meta)
|
||||
})
|
||||
|
||||
Object.values(page.shapes).forEach((shape) => {
|
||||
Object.values(page.shapes).forEach(shape => {
|
||||
Object.entries(shape.style).forEach(([id, style]) => {
|
||||
if (typeof style === 'string') {
|
||||
// @ts-ignore
|
||||
|
@ -96,8 +95,8 @@ export function migrate(state: TDSnapshot, newVersion: number): TDSnapshot {
|
|||
document.assets = {}
|
||||
}
|
||||
|
||||
Object.values(document.pages).forEach((page) => {
|
||||
Object.values(page.shapes).forEach((shape) => {
|
||||
Object.values(document.pages).forEach(page => {
|
||||
Object.values(page.shapes).forEach(shape => {
|
||||
if (version < 15.2) {
|
||||
if (shape.type === TDShapeType.Image || shape.type === TDShapeType.Video) {
|
||||
shape.style.isFilled = true
|
||||
|
@ -118,13 +117,9 @@ export function migrate(state: TDSnapshot, newVersion: number): TDSnapshot {
|
|||
})
|
||||
})
|
||||
|
||||
if (version < 15.4) {
|
||||
settings.dockPosition = 'bottom'
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
Object.values(document.pageStates).forEach((pageState) => {
|
||||
pageState.selectedIds = pageState.selectedIds.filter((id) => {
|
||||
Object.values(document.pageStates).forEach(pageState => {
|
||||
pageState.selectedIds = pageState.selectedIds.filter(id => {
|
||||
return document.pages[pageState.id].shapes[id] !== undefined
|
||||
})
|
||||
pageState.bindingId = undefined
|
||||
|
@ -135,5 +130,5 @@ export function migrate(state: TDSnapshot, newVersion: number): TDSnapshot {
|
|||
|
||||
document.version = newVersion
|
||||
|
||||
return state
|
||||
return document
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
"duplicate": "Duplicate",
|
||||
"cancel": "Cancel",
|
||||
"copy.invite.link": "Copy Invite Link",
|
||||
"copy.readonly.link": "Copy Read-only Link",
|
||||
"copy.readonly.link": "Copy ReadOnly Link",
|
||||
"create.multiplayer.project": "Create a Multiplayer Project",
|
||||
"copy.multiplayer.project": "Copy to Multiplayer Project",
|
||||
"select": "Select",
|
||||
|
|
Loading…
Reference in a new issue