mirror of https://github.com/logseq/logseq
feat: introduce iframe component
parent
a58e513928
commit
0d16402dca
|
@ -28,6 +28,7 @@ import {
|
|||
shapes,
|
||||
TextTool,
|
||||
YouTubeTool,
|
||||
IFrameTool,
|
||||
type Shape,
|
||||
} from './lib'
|
||||
import { LogseqContext, type LogseqContextValue } from './lib/logseq-context'
|
||||
|
@ -47,6 +48,7 @@ const tools: TLReactToolConstructor<Shape>[] = [
|
|||
PencilTool,
|
||||
TextTool,
|
||||
YouTubeTool,
|
||||
IFrameTool,
|
||||
HTMLTool,
|
||||
LogseqPortalTool,
|
||||
]
|
||||
|
|
|
@ -7,6 +7,7 @@ import type {
|
|||
LogseqPortalShape,
|
||||
TextShape,
|
||||
HTMLShape,
|
||||
IFrameShape,
|
||||
YouTubeShape,
|
||||
BoxShape,
|
||||
PolygonShape,
|
||||
|
@ -36,13 +37,14 @@ export const contextBarActionTypes = [
|
|||
'ScaleLevel',
|
||||
'TextStyle',
|
||||
'YoutubeLink',
|
||||
'IFrameSource',
|
||||
'LogseqPortalViewMode',
|
||||
'ArrowMode',
|
||||
'OpenPage',
|
||||
] as const
|
||||
|
||||
type ContextBarActionType = typeof contextBarActionTypes[number]
|
||||
const singleShapeActions: ContextBarActionType[] = ['Edit', 'YoutubeLink', 'OpenPage']
|
||||
const singleShapeActions: ContextBarActionType[] = ['Edit', 'YoutubeLink', 'IFrameSource', 'OpenPage']
|
||||
|
||||
const contextBarActionMapping = new Map<ContextBarActionType, React.FC>()
|
||||
|
||||
|
@ -51,6 +53,7 @@ type ShapeType = Shape['props']['type']
|
|||
export const shapeMapping: Partial<Record<ShapeType, ContextBarActionType[]>> = {
|
||||
'logseq-portal': ['Edit', 'LogseqPortalViewMode', 'ScaleLevel', 'OpenPage', 'AutoResizing'],
|
||||
youtube: ['YoutubeLink'],
|
||||
iframe: ['IFrameSource'],
|
||||
box: ['Swatch', 'NoFill', 'StrokeType'],
|
||||
ellipse: ['Swatch', 'NoFill', 'StrokeType'],
|
||||
polygon: ['Swatch', 'NoFill', 'StrokeType'],
|
||||
|
@ -239,6 +242,34 @@ const OpenPageAction = observer(() => {
|
|||
)
|
||||
})
|
||||
|
||||
const IFrameSourceAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const shape = filterShapeByAction<IFrameShape>(app.selectedShapesArray, 'IFrameSource')[0]
|
||||
const handleChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
shape.onIFrameSourceChange(e.target.value)
|
||||
app.persist()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<span className="flex gap-3">
|
||||
<TextInput
|
||||
title="Website Url"
|
||||
className="tl-iframe-src"
|
||||
value={`${shape.props.url}`}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<button
|
||||
title="Open website url"
|
||||
className="tl-contextbar-button"
|
||||
type="button"
|
||||
onClick={() => window.logseq?.api?.open_external_link?.(shape.props.url)}
|
||||
>
|
||||
<TablerIcon name="external-link" />
|
||||
</button>
|
||||
</span>
|
||||
)
|
||||
})
|
||||
|
||||
const YoutubeLinkAction = observer(() => {
|
||||
const app = useApp<Shape>()
|
||||
const shape = filterShapeByAction<YouTubeShape>(app.selectedShapesArray, 'YoutubeLink')[0]
|
||||
|
@ -450,6 +481,7 @@ contextBarActionMapping.set('LogseqPortalViewMode', LogseqPortalViewModeAction)
|
|||
contextBarActionMapping.set('ScaleLevel', ScaleLevelAction)
|
||||
contextBarActionMapping.set('OpenPage', OpenPageAction)
|
||||
contextBarActionMapping.set('YoutubeLink', YoutubeLinkAction)
|
||||
contextBarActionMapping.set('IFrameSource', IFrameSourceAction)
|
||||
contextBarActionMapping.set('NoFill', NoFillAction)
|
||||
contextBarActionMapping.set('Swatch', SwatchAction)
|
||||
contextBarActionMapping.set('StrokeType', StrokeTypeAction)
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
LogseqPortalShape,
|
||||
VideoShape,
|
||||
ImageShape,
|
||||
IFrameShape,
|
||||
} from '../lib'
|
||||
import type { LogseqContextValue } from '../lib/logseq-context'
|
||||
|
||||
|
@ -243,7 +244,13 @@ export function usePaste(context: LogseqContextValue) {
|
|||
) {
|
||||
return true
|
||||
}
|
||||
// ??? deal with normal URLs?
|
||||
|
||||
shapesToCreate.push({
|
||||
...IFrameShape.defaultProps,
|
||||
url: rawText,
|
||||
point: [point[0], point[1]],
|
||||
})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { TLBoxShape, TLBoxShapeProps } from '@tldraw/core'
|
||||
import { HTMLContainer, TLComponentProps } from '@tldraw/react'
|
||||
import { action, computed } from 'mobx'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
|
||||
export interface IFrameShapeProps extends TLBoxShapeProps {
|
||||
type: 'iframe'
|
||||
url: string
|
||||
}
|
||||
|
||||
export class IFrameShape extends TLBoxShape<IFrameShapeProps> {
|
||||
static id = 'iframe'
|
||||
|
||||
static defaultProps: IFrameShapeProps = {
|
||||
id: 'iframe',
|
||||
type: 'iframe',
|
||||
parentId: 'page',
|
||||
point: [0, 0],
|
||||
size: [853, 480],
|
||||
url: '',
|
||||
}
|
||||
|
||||
@computed get url() {
|
||||
return this.props.url
|
||||
}
|
||||
|
||||
@action onIFrameSourceChange = (url: string) => {
|
||||
this.update({ url })
|
||||
}
|
||||
|
||||
ReactComponent = observer(({ events, isErasing, isEditing }: TLComponentProps) => {
|
||||
return (
|
||||
<HTMLContainer
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
pointerEvents: 'all',
|
||||
opacity: isErasing ? 0.2 : 1,
|
||||
}}
|
||||
{...events}
|
||||
>
|
||||
<div
|
||||
className="rounded-lg w-full h-full relative overflow-hidden shadow-xl"
|
||||
style={{
|
||||
pointerEvents: isEditing ? 'all' : 'none',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
{this.url && (
|
||||
<div
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
className="absolute inset-0 w-full h-full m-0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
src={`${this.url}`}
|
||||
frameBorder="0"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</HTMLContainer>
|
||||
)
|
||||
})
|
||||
|
||||
ReactIndicator = observer(() => {
|
||||
const {
|
||||
props: {
|
||||
size: [w, h],
|
||||
},
|
||||
} = this
|
||||
return <rect width={w} height={h} fill="transparent" rx={8} ry={8} />
|
||||
})
|
||||
}
|
|
@ -6,6 +6,7 @@ import { HighlighterShape } from './HighlighterShape'
|
|||
import { HTMLShape } from './HTMLShape'
|
||||
import { ImageShape } from './ImageShape'
|
||||
import { VideoShape } from './VideoShape'
|
||||
import { IFrameShape } from './IFrameShape'
|
||||
import { LineShape } from './LineShape'
|
||||
import { LogseqPortalShape } from './LogseqPortalShape'
|
||||
import { PencilShape } from './PencilShape'
|
||||
|
@ -27,6 +28,7 @@ export type Shape =
|
|||
| PolygonShape
|
||||
| TextShape
|
||||
| YouTubeShape
|
||||
| IFrameShape
|
||||
| HTMLShape
|
||||
| LogseqPortalShape
|
||||
|
||||
|
@ -37,6 +39,7 @@ export * from './HighlighterShape'
|
|||
export * from './HTMLShape'
|
||||
export * from './ImageShape'
|
||||
export * from './VideoShape'
|
||||
export * from './IFrameShape'
|
||||
export * from './LineShape'
|
||||
export * from './LogseqPortalShape'
|
||||
export * from './PencilShape'
|
||||
|
@ -56,6 +59,7 @@ export const shapes: TLReactShapeConstructor<Shape>[] = [
|
|||
PolygonShape,
|
||||
TextShape,
|
||||
YouTubeShape,
|
||||
IFrameShape,
|
||||
HTMLShape,
|
||||
LogseqPortalShape,
|
||||
]
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { TLBoxTool } from '@tldraw/core'
|
||||
import type { TLReactEventMap } from '@tldraw/react'
|
||||
import { IFrameShape, type Shape } from '../shapes'
|
||||
|
||||
export class IFrameTool extends TLBoxTool<IFrameShape, Shape, TLReactEventMap> {
|
||||
static id = 'iframe'
|
||||
Shape = IFrameShape
|
||||
}
|
||||
|
||||
export {}
|
|
@ -10,3 +10,4 @@ export * from './TextTool'
|
|||
export * from './YouTubeTool'
|
||||
export * from './LogseqPortalTool'
|
||||
export * from './HTMLTool'
|
||||
export * from './IFrameTool'
|
||||
|
|
Loading…
Reference in New Issue