[improvement] Embed shape cleanup (#1569)
This PR does some cleanup around our Embed Shape. It: - removes used `doesResize` and `overridePermissions` props - removes the no-longer-needed `tmpOldUrl` prop - adds a `canUnmount` property to embed definitions, so that some embeds can unmount when desired ### Change Type - [x] `patch` — Bug Fix ### Test Plan 1. Create embed shapes 2. Migrate old data that includes embed shapes? - [x] Unit Tests ### Release Notes - [editor] Remove unused props for `TLEditorShape` - [editor] Adds `canUnmount` property to embed definitions
This commit is contained in:
parent
439a2ed3bc
commit
b65aef5cf2
10 changed files with 231 additions and 98 deletions
|
@ -1051,6 +1051,9 @@ export function getPointerInfo(e: PointerEvent | React.PointerEvent, container:
|
|||
// @public
|
||||
export function getResizedImageDataUrl(dataURLForImage: string, width: number, height: number): Promise<string>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function getRotatedBoxShadow(rotation: number): string;
|
||||
|
||||
// @public (undocumented)
|
||||
export function getSplineForLineShape(shape: TLLineShape): NonNullable<CubicSpline2d | Polyline2d>;
|
||||
|
||||
|
@ -1324,6 +1327,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1335,7 +1339,8 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 520;
|
||||
readonly height: 400;
|
||||
readonly doesResize: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined; /** @public */
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
readonly type: "codesandbox";
|
||||
|
@ -1346,6 +1351,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1355,6 +1361,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly isAspectRatioLocked: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -1365,6 +1372,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1374,6 +1382,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1383,6 +1392,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1394,6 +1404,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly minWidth: 460;
|
||||
readonly minHeight: 360;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly instructionLink: "https://support.google.com/calendar/answer/41207?hl=en";
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -1404,6 +1415,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1415,6 +1427,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly minWidth: 460;
|
||||
readonly minHeight: 360;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1424,6 +1437,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly isAspectRatioLocked: false;
|
||||
readonly backgroundColor: "#fff";
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -1435,6 +1449,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1444,6 +1459,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 520;
|
||||
readonly height: 400;
|
||||
readonly doesResize: false;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1455,6 +1471,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly minHeight: 500;
|
||||
readonly overrideOutlineRadius: 12;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1464,6 +1481,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 640;
|
||||
readonly height: 360;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly isAspectRatioLocked: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -1474,6 +1492,7 @@ export function matchEmbedUrl(url: string): {
|
|||
readonly width: 800;
|
||||
readonly height: 450;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly overridePermissions: {
|
||||
readonly 'allow-presentation': true;
|
||||
};
|
||||
|
@ -1496,6 +1515,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1507,7 +1527,8 @@ export function matchUrl(url: string): {
|
|||
readonly width: 520;
|
||||
readonly height: 400;
|
||||
readonly doesResize: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined; /** @public */
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
readonly type: "codesandbox";
|
||||
|
@ -1518,6 +1539,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1527,6 +1549,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly isAspectRatioLocked: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -1537,6 +1560,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1546,6 +1570,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1555,6 +1580,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1566,6 +1592,7 @@ export function matchUrl(url: string): {
|
|||
readonly minWidth: 460;
|
||||
readonly minHeight: 360;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly instructionLink: "https://support.google.com/calendar/answer/41207?hl=en";
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -1576,6 +1603,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1587,6 +1615,7 @@ export function matchUrl(url: string): {
|
|||
readonly minWidth: 460;
|
||||
readonly minHeight: 360;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1596,6 +1625,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly isAspectRatioLocked: false;
|
||||
readonly backgroundColor: "#fff";
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -1607,6 +1637,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1616,6 +1647,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 520;
|
||||
readonly height: 400;
|
||||
readonly doesResize: false;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1627,6 +1659,7 @@ export function matchUrl(url: string): {
|
|||
readonly minHeight: 500;
|
||||
readonly overrideOutlineRadius: 12;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
} | {
|
||||
|
@ -1636,6 +1669,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 640;
|
||||
readonly height: 360;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly isAspectRatioLocked: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -1646,6 +1680,7 @@ export function matchUrl(url: string): {
|
|||
readonly width: 800;
|
||||
readonly height: 450;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly overridePermissions: {
|
||||
readonly 'allow-presentation': true;
|
||||
};
|
||||
|
@ -1826,24 +1861,6 @@ export type RequiredKeys<T, K extends keyof T> = Pick<T, K> & Partial<T>;
|
|||
// @internal (undocumented)
|
||||
export const RICH_TYPES: Record<string, boolean>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function rotateBoxShadow(rotation: number, shadows: {
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
blur: number;
|
||||
spread: number;
|
||||
color: string;
|
||||
}[]): string;
|
||||
|
||||
// @public (undocumented)
|
||||
export const ROTATING_SHADOWS: {
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
blur: number;
|
||||
spread: number;
|
||||
color: string;
|
||||
}[];
|
||||
|
||||
// @public (undocumented)
|
||||
export const runtime: {
|
||||
openWindow: (url: string, target: string) => void;
|
||||
|
|
|
@ -78,7 +78,6 @@ export {
|
|||
MULTI_CLICK_DURATION,
|
||||
REMOVE_SYMBOL,
|
||||
RICH_TYPES,
|
||||
ROTATING_SHADOWS,
|
||||
STYLES,
|
||||
SVG_PADDING,
|
||||
TEXT_PROPS,
|
||||
|
@ -226,10 +225,10 @@ export {
|
|||
} from './lib/utils/data'
|
||||
export { debugFlags, featureFlags, type DebugFlag } from './lib/utils/debug-flags'
|
||||
export {
|
||||
getRotatedBoxShadow,
|
||||
loopToHtmlElement,
|
||||
preventDefault,
|
||||
releasePointerCapture,
|
||||
rotateBoxShadow,
|
||||
setPointerCapture,
|
||||
truncateStringWithEllipsis,
|
||||
usePrefersReducedMotion,
|
||||
|
|
|
@ -100,24 +100,6 @@ export const DEFAULT_BOOKMARK_WIDTH = 300
|
|||
/** @internal */
|
||||
export const DEFAULT_BOOKMARK_HEIGHT = 320
|
||||
|
||||
/** @public */
|
||||
export const ROTATING_SHADOWS = [
|
||||
{
|
||||
offsetX: 0,
|
||||
offsetY: 2,
|
||||
blur: 4,
|
||||
spread: 0,
|
||||
color: '#00000029',
|
||||
},
|
||||
{
|
||||
offsetX: 0,
|
||||
offsetY: 3,
|
||||
blur: 6,
|
||||
spread: 0,
|
||||
color: '#0000001f',
|
||||
},
|
||||
]
|
||||
|
||||
/** @public */
|
||||
export const GRID_STEPS = [
|
||||
{ min: -1, mid: 0.15, step: 100 },
|
||||
|
|
|
@ -2,14 +2,13 @@ import { toDomPrecision } from '@tldraw/primitives'
|
|||
import { AssetRecordType, TLAssetId, TLBookmarkAsset, TLBookmarkShape } from '@tldraw/tlschema'
|
||||
import { debounce, getHashForString } from '@tldraw/utils'
|
||||
import { HTMLContainer } from '../../../components/HTMLContainer'
|
||||
import { ROTATING_SHADOWS } from '../../../constants'
|
||||
|
||||
const DEFAULT_BOOKMARK_WIDTH = 300
|
||||
const DEFAULT_BOOKMARK_HEIGHT = 320
|
||||
|
||||
import { isValidUrl } from '../../../utils/data'
|
||||
import {
|
||||
rotateBoxShadow,
|
||||
getRotatedBoxShadow,
|
||||
stopEventPropagation,
|
||||
truncateStringWithEllipsis,
|
||||
} from '../../../utils/dom'
|
||||
|
@ -50,7 +49,7 @@ export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
|||
<div
|
||||
className="tl-bookmark__container tl-hitarea-stroke"
|
||||
style={{
|
||||
boxShadow: rotateBoxShadow(pageRotation, ROTATING_SHADOWS),
|
||||
boxShadow: getRotatedBoxShadow(pageRotation),
|
||||
}}
|
||||
>
|
||||
<div className="tl-bookmark__image_container">
|
||||
|
|
|
@ -10,9 +10,8 @@ import { useMemo } from 'react'
|
|||
import { useValue } from 'signia-react'
|
||||
import { DefaultSpinner } from '../../../components/DefaultSpinner'
|
||||
import { HTMLContainer } from '../../../components/HTMLContainer'
|
||||
import { ROTATING_SHADOWS } from '../../../constants'
|
||||
import { useIsEditing } from '../../../hooks/useIsEditing'
|
||||
import { rotateBoxShadow } from '../../../utils/dom'
|
||||
import { getRotatedBoxShadow } from '../../../utils/dom'
|
||||
import { getEmbedInfo, getEmbedInfoUnsafely } from '../../../utils/embeds'
|
||||
import { BaseBoxShapeUtil } from '../BaseBoxShapeUtil'
|
||||
import { TLOnResizeHandler, TLShapeUtilFlag } from '../ShapeUtil'
|
||||
|
@ -29,32 +28,30 @@ const getSandboxPermissions = (permissions: TLEmbedShapePermissions) => {
|
|||
export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
||||
static override type = 'embed' as const
|
||||
|
||||
override canUnmount: TLShapeUtilFlag<TLEmbedShape> = () => false
|
||||
override canResize = (shape: TLEmbedShape) => {
|
||||
const result = getEmbedInfo(shape.props.url)
|
||||
return !!result?.definition?.doesResize
|
||||
}
|
||||
|
||||
override hideSelectionBoundsBg: TLShapeUtilFlag<TLEmbedShape> = (shape) => !this.canResize(shape)
|
||||
override hideSelectionBoundsFg: TLShapeUtilFlag<TLEmbedShape> = (shape) => !this.canResize(shape)
|
||||
|
||||
override canEdit: TLShapeUtilFlag<TLEmbedShape> = () => true
|
||||
override canUnmount: TLShapeUtilFlag<TLEmbedShape> = (shape: TLEmbedShape) => {
|
||||
return !!getEmbedInfo(shape.props.url)?.definition?.canUnmount
|
||||
}
|
||||
override canResize = (shape: TLEmbedShape) => {
|
||||
return !!getEmbedInfo(shape.props.url)?.definition?.doesResize
|
||||
}
|
||||
|
||||
override defaultProps(): TLEmbedShape['props'] {
|
||||
return {
|
||||
w: 300,
|
||||
h: 300,
|
||||
url: '',
|
||||
doesResize: true,
|
||||
}
|
||||
}
|
||||
|
||||
isAspectRatioLocked: TLShapeUtilFlag<TLEmbedShape> = (shape) => {
|
||||
override isAspectRatioLocked: TLShapeUtilFlag<TLEmbedShape> = (shape) => {
|
||||
const embedInfo = getEmbedInfo(shape.props.url)
|
||||
return embedInfo?.definition.isAspectRatioLocked ?? false
|
||||
}
|
||||
|
||||
onResize: TLOnResizeHandler<TLEmbedShape> = (shape, info) => {
|
||||
override onResize: TLOnResizeHandler<TLEmbedShape> = (shape, info) => {
|
||||
const isAspectRatioLocked = this.isAspectRatioLocked(shape)
|
||||
const embedInfo = getEmbedInfo(shape.props.url)
|
||||
let minWidth = embedInfo?.definition.minWidth ?? 200
|
||||
|
@ -75,7 +72,7 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
|||
return resizeBox(shape, info, { minWidth, minHeight })
|
||||
}
|
||||
|
||||
render(shape: TLEmbedShape) {
|
||||
override render(shape: TLEmbedShape) {
|
||||
const { w, h, url } = shape.props
|
||||
const isEditing = useIsEditing(shape.id)
|
||||
const embedInfo = useMemo(() => getEmbedInfoUnsafely(url), [url])
|
||||
|
@ -91,6 +88,7 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
[]
|
||||
|
@ -102,19 +100,19 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
|||
|
||||
if (embedInfo?.definition.type === 'github_gist') {
|
||||
const idFromGistUrl = embedInfo.url.split('/').pop()
|
||||
if (idFromGistUrl) {
|
||||
return (
|
||||
<HTMLContainer className="tl-embed-container" id={shape.id}>
|
||||
<Gist
|
||||
id={idFromGistUrl}
|
||||
width={toDomPrecision(w)!}
|
||||
height={toDomPrecision(h)!}
|
||||
isInteractive={isInteractive}
|
||||
pageRotation={pageRotation}
|
||||
/>
|
||||
</HTMLContainer>
|
||||
)
|
||||
}
|
||||
if (!idFromGistUrl) throw Error('No gist id!')
|
||||
|
||||
return (
|
||||
<HTMLContainer className="tl-embed-container" id={shape.id}>
|
||||
<Gist
|
||||
id={idFromGistUrl}
|
||||
width={toDomPrecision(w)!}
|
||||
height={toDomPrecision(h)!}
|
||||
isInteractive={isInteractive}
|
||||
pageRotation={pageRotation}
|
||||
/>
|
||||
</HTMLContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const sandbox = getSandboxPermissions({
|
||||
|
@ -139,7 +137,7 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
|||
pointerEvents: isInteractive ? 'auto' : 'none',
|
||||
// Fix for safari <https://stackoverflow.com/a/49150908>
|
||||
zIndex: isInteractive ? '' : '-1',
|
||||
boxShadow: rotateBoxShadow(pageRotation, ROTATING_SHADOWS),
|
||||
boxShadow: getRotatedBoxShadow(pageRotation),
|
||||
borderRadius: embedInfo?.definition.overrideOutlineRadius ?? 8,
|
||||
background: embedInfo?.definition.backgroundColor,
|
||||
}}
|
||||
|
@ -153,7 +151,7 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
|||
)
|
||||
}
|
||||
|
||||
indicator(shape: TLEmbedShape) {
|
||||
override indicator(shape: TLEmbedShape) {
|
||||
const embedInfo = useMemo(() => getEmbedInfo(shape.props.url), [shape.props.url])
|
||||
return (
|
||||
<rect
|
||||
|
@ -183,14 +181,8 @@ function Gist({
|
|||
pageRotation: number
|
||||
style?: React.CSSProperties
|
||||
}) {
|
||||
const rIframe = React.useRef<HTMLIFrameElement>(null)
|
||||
|
||||
const fileArg = file ? `?file=${file}` : ''
|
||||
const gistLink = `https://gist.github.com/${id}.js${fileArg}`
|
||||
|
||||
return (
|
||||
<iframe
|
||||
ref={rIframe}
|
||||
className="tl-embed"
|
||||
draggable={false}
|
||||
width={toDomPrecision(width)}
|
||||
|
@ -204,7 +196,7 @@ function Gist({
|
|||
pointerEvents: isInteractive ? 'all' : 'none',
|
||||
// Fix for safari <https://stackoverflow.com/a/49150908>
|
||||
zIndex: isInteractive ? '' : '-1',
|
||||
boxShadow: rotateBoxShadow(pageRotation, ROTATING_SHADOWS),
|
||||
boxShadow: getRotatedBoxShadow(pageRotation),
|
||||
}}
|
||||
srcDoc={`
|
||||
<html>
|
||||
|
@ -212,7 +204,7 @@ function Gist({
|
|||
<base target="_blank">
|
||||
</head>
|
||||
<body>
|
||||
<script src=${gistLink}></script>
|
||||
<script src=${`https://gist.github.com/${id}.js${file ? `?file=${file}` : ''}`}></script>
|
||||
<style type="text/css">
|
||||
* { margin: 0px; }
|
||||
table { height: 100%; background-color: red; }
|
||||
|
|
|
@ -406,7 +406,7 @@ describe('When in readonly mode', () => {
|
|||
x: 100,
|
||||
y: 100,
|
||||
opacity: 1,
|
||||
props: { w: 100, h: 100, url: '', doesResize: false },
|
||||
props: { w: 100, h: 100, url: '' },
|
||||
},
|
||||
])
|
||||
editor.setReadOnly(true)
|
||||
|
|
|
@ -83,12 +83,26 @@ export function releasePointerCapture(
|
|||
}
|
||||
}
|
||||
|
||||
export const ROTATING_BOX_SHADOWS = [
|
||||
{
|
||||
offsetX: 0,
|
||||
offsetY: 2,
|
||||
blur: 4,
|
||||
spread: 0,
|
||||
color: '#00000029',
|
||||
},
|
||||
{
|
||||
offsetX: 0,
|
||||
offsetY: 3,
|
||||
blur: 6,
|
||||
spread: 0,
|
||||
color: '#0000001f',
|
||||
},
|
||||
]
|
||||
|
||||
/** @public */
|
||||
export function rotateBoxShadow(
|
||||
rotation: number,
|
||||
shadows: { offsetX: number; offsetY: number; blur: number; spread: number; color: string }[]
|
||||
) {
|
||||
const cssStrings = shadows.map((shadow) => {
|
||||
export function getRotatedBoxShadow(rotation: number) {
|
||||
const cssStrings = ROTATING_BOX_SHADOWS.map((shadow) => {
|
||||
const { offsetX, offsetY, blur, spread, color } = shadow
|
||||
const vec = new Vec2d(offsetX, offsetY)
|
||||
const { x, y } = vec.rot(-rotation)
|
||||
|
|
|
@ -128,6 +128,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -139,6 +140,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -150,6 +152,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 520;
|
||||
readonly height: 400;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -159,6 +162,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 520;
|
||||
readonly height: 400;
|
||||
readonly doesResize: false;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -168,6 +172,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 800;
|
||||
readonly height: 450;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly overridePermissions: {
|
||||
readonly 'allow-presentation': true;
|
||||
};
|
||||
|
@ -181,6 +186,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -190,6 +196,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -201,6 +208,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly minWidth: 460;
|
||||
readonly minHeight: 360;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly instructionLink: "https://support.google.com/calendar/answer/41207?hl=en";
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -213,6 +221,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly minWidth: 460;
|
||||
readonly minHeight: 360;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -222,6 +231,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -231,6 +241,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -240,6 +251,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -251,6 +263,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly minHeight: 500;
|
||||
readonly overrideOutlineRadius: 12;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
}, {
|
||||
|
@ -260,6 +273,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 640;
|
||||
readonly height: 360;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly isAspectRatioLocked: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -270,6 +284,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly isAspectRatioLocked: true;
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -280,6 +295,7 @@ export const EMBED_DEFINITIONS: readonly [{
|
|||
readonly width: 720;
|
||||
readonly height: 500;
|
||||
readonly doesResize: true;
|
||||
readonly canUnmount: false;
|
||||
readonly isAspectRatioLocked: false;
|
||||
readonly backgroundColor: "#fff";
|
||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||
|
@ -296,6 +312,7 @@ export type EmbedDefinition = {
|
|||
readonly width: number;
|
||||
readonly height: number;
|
||||
readonly doesResize: boolean;
|
||||
readonly canUnmount: boolean;
|
||||
readonly isAspectRatioLocked?: boolean;
|
||||
readonly overridePermissions?: TLEmbedShapePermissions;
|
||||
readonly instructionLink?: string;
|
||||
|
|
|
@ -1110,6 +1110,51 @@ describe('hoist opacity', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('Removes does resize from embed', () => {
|
||||
const { up, down } = embedShapeMigrations.migrators[2]
|
||||
test('up works as expected', () => {
|
||||
expect(up({ props: { url: 'https://tldraw.com', doesResize: true } })).toEqual({
|
||||
props: { url: 'https://tldraw.com' },
|
||||
})
|
||||
})
|
||||
test('down works as expected', () => {
|
||||
expect(down({ props: { url: 'https://tldraw.com' } })).toEqual({
|
||||
props: { url: 'https://tldraw.com', doesResize: true },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Removes tmpOldUrl from embed', () => {
|
||||
const { up, down } = embedShapeMigrations.migrators[3]
|
||||
test('up works as expected', () => {
|
||||
expect(up({ props: { url: 'https://tldraw.com', tmpOldUrl: 'https://tldraw.com' } })).toEqual({
|
||||
props: { url: 'https://tldraw.com' },
|
||||
})
|
||||
})
|
||||
test('down works as expected', () => {
|
||||
expect(down({ props: { url: 'https://tldraw.com' } })).toEqual({
|
||||
props: { url: 'https://tldraw.com' },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Removes overridePermissions from embed', () => {
|
||||
const { up, down } = embedShapeMigrations.migrators[4]
|
||||
|
||||
test('up works as expected', () => {
|
||||
expect(
|
||||
up({ props: { url: 'https://tldraw.com', overridePermissions: { display: 'maybe' } } })
|
||||
).toEqual({
|
||||
props: { url: 'https://tldraw.com' },
|
||||
})
|
||||
})
|
||||
test('down works as expected', () => {
|
||||
expect(down({ props: { url: 'https://tldraw.com' } })).toEqual({
|
||||
props: { url: 'https://tldraw.com' },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
/* --- PUT YOUR MIGRATIONS TESTS ABOVE HERE --- */
|
||||
|
||||
for (const migrator of allMigrators) {
|
||||
|
|
|
@ -24,6 +24,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 720,
|
||||
height: 500,
|
||||
doesResize: true,
|
||||
canUnmount: true,
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
if (urlObj && urlObj.pathname.match(TLDRAW_APP_RE)) {
|
||||
|
@ -48,6 +49,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 720,
|
||||
height: 500,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
const matches = urlObj && urlObj.pathname.match(/\/s\/([^/]+)\/?/)
|
||||
|
@ -74,6 +76,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 520,
|
||||
height: 400,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
toEmbedUrl: (url) => {
|
||||
const CODEPEN_URL_REGEXP = /https:\/\/codepen.io\/([^/]+)\/pen\/([^/]+)/
|
||||
const matches = url.match(CODEPEN_URL_REGEXP)
|
||||
|
@ -100,6 +103,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 520,
|
||||
height: 400,
|
||||
doesResize: false,
|
||||
canUnmount: false,
|
||||
toEmbedUrl: (url) => {
|
||||
const SCRATCH_URL_REGEXP = /https?:\/\/scratch.mit.edu\/projects\/([^/]+)/
|
||||
const matches = url.match(SCRATCH_URL_REGEXP)
|
||||
|
@ -126,6 +130,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 800,
|
||||
height: 450,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
overridePermissions: {
|
||||
'allow-presentation': true,
|
||||
},
|
||||
|
@ -168,6 +173,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 720,
|
||||
height: 500,
|
||||
doesResize: true,
|
||||
canUnmount: true,
|
||||
toEmbedUrl: (url) => {
|
||||
if (
|
||||
!!url.match(
|
||||
|
@ -198,6 +204,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 720,
|
||||
height: 500,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
toEmbedUrl: (url) => {
|
||||
if (url.includes('/maps/')) {
|
||||
const match = url.match(/@(.*),(.*),(.*)z/)
|
||||
|
@ -236,6 +243,7 @@ export const EMBED_DEFINITIONS = [
|
|||
minWidth: 460,
|
||||
minHeight: 360,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
instructionLink: 'https://support.google.com/calendar/answer/41207?hl=en',
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
|
@ -278,6 +286,7 @@ export const EMBED_DEFINITIONS = [
|
|||
minWidth: 460,
|
||||
minHeight: 360,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
|
||||
|
@ -312,9 +321,11 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 720,
|
||||
height: 500,
|
||||
doesResize: true,
|
||||
canUnmount: true,
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
if (urlObj && urlObj.pathname.match(/\/([^/]+)\/([^/]+)/)) {
|
||||
if (!url.split('/').pop()) return
|
||||
return url
|
||||
}
|
||||
return
|
||||
|
@ -322,6 +333,7 @@ export const EMBED_DEFINITIONS = [
|
|||
fromEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
if (urlObj && urlObj.pathname.match(/\/([^/]+)\/([^/]+)/)) {
|
||||
if (!url.split('/').pop()) return
|
||||
return url
|
||||
}
|
||||
return
|
||||
|
@ -334,6 +346,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 720,
|
||||
height: 500,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
if (urlObj && urlObj.pathname.match(/\/@([^/]+)\/([^/]+)/)) {
|
||||
|
@ -361,6 +374,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 720,
|
||||
height: 500,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
if (urlObj && urlObj.pathname.match(/^\/map\//)) {
|
||||
|
@ -386,6 +400,7 @@ export const EMBED_DEFINITIONS = [
|
|||
minHeight: 500,
|
||||
overrideOutlineRadius: 12,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
if (urlObj && urlObj.pathname.match(/^\/(artist|album)\//)) {
|
||||
|
@ -408,6 +423,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 640,
|
||||
height: 360,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
isAspectRatioLocked: true,
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
|
@ -438,6 +454,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 720,
|
||||
height: 500,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
isAspectRatioLocked: true,
|
||||
toEmbedUrl: (url) => {
|
||||
const urlObj = safeParseUrl(url)
|
||||
|
@ -461,6 +478,7 @@ export const EMBED_DEFINITIONS = [
|
|||
width: 720,
|
||||
height: 500,
|
||||
doesResize: true,
|
||||
canUnmount: false,
|
||||
isAspectRatioLocked: false,
|
||||
backgroundColor: '#fff',
|
||||
toEmbedUrl: (url) => {
|
||||
|
@ -551,10 +569,6 @@ export type TLEmbedShapeProps = {
|
|||
w: number
|
||||
h: number
|
||||
url: string
|
||||
// TODO: This is to store during migration in case anything goes wrong so we can revert.
|
||||
tmpOldUrl?: string
|
||||
doesResize: boolean
|
||||
overridePermissions?: TLEmbedShapePermissions
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
@ -565,14 +579,6 @@ export const embedShapeProps: ShapeProps<TLEmbedShape> = {
|
|||
w: T.nonZeroNumber,
|
||||
h: T.nonZeroNumber,
|
||||
url: T.string,
|
||||
tmpOldUrl: T.string.optional(),
|
||||
doesResize: T.boolean,
|
||||
overridePermissions: T.dict(
|
||||
T.setEnum(
|
||||
new Set(Object.keys(embedShapePermissionDefaults) as (keyof TLEmbedShapePermissions)[])
|
||||
),
|
||||
T.boolean.optional()
|
||||
).optional(),
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
@ -585,6 +591,7 @@ export type EmbedDefinition = {
|
|||
readonly width: number
|
||||
readonly height: number
|
||||
readonly doesResize: boolean
|
||||
readonly canUnmount: boolean
|
||||
readonly isAspectRatioLocked?: boolean
|
||||
readonly overridePermissions?: TLEmbedShapePermissions
|
||||
readonly instructionLink?: string
|
||||
|
@ -597,11 +604,14 @@ export type EmbedDefinition = {
|
|||
|
||||
const Versions = {
|
||||
GenOriginalUrlInEmbed: 1,
|
||||
RemoveDoesResize: 2,
|
||||
RemoveTmpOldUrl: 3,
|
||||
RemovePermissionOverrides: 4,
|
||||
} as const
|
||||
|
||||
/** @internal */
|
||||
export const embedShapeMigrations = defineMigrations({
|
||||
currentVersion: Versions.GenOriginalUrlInEmbed,
|
||||
currentVersion: Versions.RemovePermissionOverrides,
|
||||
migrators: {
|
||||
[Versions.GenOriginalUrlInEmbed]: {
|
||||
// add tmpOldUrl property
|
||||
|
@ -657,5 +667,63 @@ export const embedShapeMigrations = defineMigrations({
|
|||
}
|
||||
},
|
||||
},
|
||||
[Versions.RemoveDoesResize]: {
|
||||
up: (shape) => {
|
||||
const { doesResize: _, ...props } = shape.props
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (shape) => {
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...shape.props,
|
||||
doesResize: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
[Versions.RemoveTmpOldUrl]: {
|
||||
up: (shape) => {
|
||||
const { tmpOldUrl: _, ...props } = shape.props
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (shape) => {
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
[Versions.RemovePermissionOverrides]: {
|
||||
up: (shape) => {
|
||||
const { overridePermissions: _, ...props } = shape.props
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (shape) => {
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue