kopia lustrzana https://github.com/Tldraw/Tldraw
185 wiersze
4.7 KiB
TypeScript
185 wiersze
4.7 KiB
TypeScript
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 {
|
|
DEFAULT_BOOKMARK_HEIGHT,
|
|
DEFAULT_BOOKMARK_WIDTH,
|
|
ROTATING_SHADOWS,
|
|
} from '../../../constants'
|
|
import {
|
|
rotateBoxShadow,
|
|
stopEventPropagation,
|
|
truncateStringWithEllipsis,
|
|
} from '../../../utils/dom'
|
|
import { BaseBoxShapeUtil } from '../BaseBoxShapeUtil'
|
|
import { TLOnBeforeCreateHandler, TLOnBeforeUpdateHandler } from '../ShapeUtil'
|
|
import { HyperlinkButton } from '../shared/HyperlinkButton'
|
|
|
|
/** @public */
|
|
export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
|
static override type = 'bookmark'
|
|
|
|
override canResize = () => false
|
|
|
|
override hideSelectionBoundsBg = () => true
|
|
override hideSelectionBoundsFg = () => true
|
|
|
|
override defaultProps(): TLBookmarkShape['props'] {
|
|
return {
|
|
url: '',
|
|
w: DEFAULT_BOOKMARK_WIDTH,
|
|
h: DEFAULT_BOOKMARK_HEIGHT,
|
|
assetId: null,
|
|
}
|
|
}
|
|
|
|
override render(shape: TLBookmarkShape) {
|
|
const asset = (
|
|
shape.props.assetId ? this.editor.getAssetById(shape.props.assetId) : null
|
|
) as TLBookmarkAsset
|
|
|
|
const pageRotation = this.editor.getPageRotation(shape)
|
|
|
|
const address = this.getHumanReadableAddress(shape)
|
|
|
|
return (
|
|
<HTMLContainer>
|
|
<div
|
|
className="tl-bookmark__container tl-hitarea-stroke"
|
|
style={{
|
|
boxShadow: rotateBoxShadow(pageRotation, ROTATING_SHADOWS),
|
|
}}
|
|
>
|
|
<div className="tl-bookmark__image_container">
|
|
{asset?.props.image ? (
|
|
<img
|
|
className="tl-bookmark__image"
|
|
draggable={false}
|
|
src={asset?.props.image}
|
|
alt={asset?.props.title || ''}
|
|
/>
|
|
) : (
|
|
<div className="tl-bookmark__placeholder" />
|
|
)}
|
|
<HyperlinkButton url={shape.props.url} zoomLevel={this.editor.zoomLevel} />
|
|
</div>
|
|
<div className="tl-bookmark__copy_container">
|
|
{asset?.props.title && (
|
|
<h2 className="tl-bookmark__heading">
|
|
{truncateStringWithEllipsis(asset?.props.title || '', 54)}
|
|
</h2>
|
|
)}
|
|
{asset?.props.description && (
|
|
<p className="tl-bookmark__description">
|
|
{truncateStringWithEllipsis(asset?.props.description || '', 128)}
|
|
</p>
|
|
)}
|
|
<a
|
|
className="tl-bookmark__link"
|
|
href={shape.props.url || ''}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
onPointerDown={stopEventPropagation}
|
|
onPointerUp={stopEventPropagation}
|
|
onClick={stopEventPropagation}
|
|
>
|
|
{truncateStringWithEllipsis(address, 45)}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</HTMLContainer>
|
|
)
|
|
}
|
|
|
|
override indicator(shape: TLBookmarkShape) {
|
|
return (
|
|
<rect
|
|
width={toDomPrecision(shape.props.w)}
|
|
height={toDomPrecision(shape.props.h)}
|
|
rx="8"
|
|
ry="8"
|
|
/>
|
|
)
|
|
}
|
|
|
|
override onBeforeCreate?: TLOnBeforeCreateHandler<TLBookmarkShape> = (shape) => {
|
|
this.updateBookmarkAsset(shape)
|
|
}
|
|
|
|
override onBeforeUpdate?: TLOnBeforeUpdateHandler<TLBookmarkShape> = (prev, shape) => {
|
|
if (prev.props.url !== shape.props.url) {
|
|
this.updateBookmarkAsset(shape)
|
|
}
|
|
}
|
|
|
|
getHumanReadableAddress(shape: TLBookmarkShape) {
|
|
try {
|
|
const url = new URL(shape.props.url)
|
|
const path = url.pathname.replace(/\/*$/, '')
|
|
return `${url.hostname}${path}`
|
|
} catch (e) {
|
|
return shape.props.url
|
|
}
|
|
}
|
|
|
|
protected updateBookmarkAsset = debounce((shape: TLBookmarkShape) => {
|
|
const { url } = shape.props
|
|
const assetId: TLAssetId = AssetRecordType.createId(getHashForString(url))
|
|
const existing = this.editor.getAssetById(assetId)
|
|
|
|
if (existing) {
|
|
// If there's an existing asset with the same URL, use
|
|
// its asset id instead.
|
|
if (shape.props.assetId !== existing.id) {
|
|
this.editor.updateShapes([
|
|
{
|
|
id: shape.id,
|
|
type: shape.type,
|
|
props: { assetId },
|
|
},
|
|
])
|
|
}
|
|
} else if (this.editor.onCreateBookmarkFromUrl) {
|
|
// Create a bookmark asset for the URL. First get its meta
|
|
// data, then create the asset and update the shape.
|
|
this.editor.onCreateBookmarkFromUrl(url).then((meta) => {
|
|
if (!meta) {
|
|
this.editor.updateShapes([
|
|
{
|
|
id: shape.id,
|
|
type: shape.type,
|
|
props: { assetId: undefined },
|
|
},
|
|
])
|
|
return
|
|
}
|
|
|
|
this.editor.batch(() => {
|
|
this.editor
|
|
.createAssets([
|
|
{
|
|
id: assetId,
|
|
typeName: 'asset',
|
|
type: 'bookmark',
|
|
props: {
|
|
src: url,
|
|
description: meta.description,
|
|
image: meta.image,
|
|
title: meta.title,
|
|
},
|
|
},
|
|
])
|
|
.updateShapes([
|
|
{
|
|
id: shape.id,
|
|
type: shape.type,
|
|
props: { assetId },
|
|
},
|
|
])
|
|
})
|
|
})
|
|
}
|
|
}, 500)
|
|
}
|