Add context toolbar example. (#2596)
Adds a context toolbar example which allows you to change shape's size. https://github.com/tldraw/tldraw/assets/2523721/527af280-af19-4142-ac2e-46dd07559d14 Works when one or more shapes (which support size property) are selected. Solves #2549 ### Change Type - [ ] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [x] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Release Notes - Add context toolbar example. --------- Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
This commit is contained in:
parent
71b5fdd48a
commit
d1bc456162
2 changed files with 147 additions and 0 deletions
135
apps/examples/src/examples/context-toolbar/ContextToolbar.tsx
Normal file
135
apps/examples/src/examples/context-toolbar/ContextToolbar.tsx
Normal file
|
@ -0,0 +1,135 @@
|
|||
import {
|
||||
DefaultSizeStyle,
|
||||
Icon,
|
||||
SharedStyleMap,
|
||||
Tldraw,
|
||||
TLEditorComponents,
|
||||
track,
|
||||
useEditor,
|
||||
Vec,
|
||||
} from '@tldraw/tldraw'
|
||||
import '@tldraw/tldraw/tldraw.css'
|
||||
|
||||
const SIZES = [
|
||||
{ value: 's', icon: 'size-small' },
|
||||
{ value: 'm', icon: 'size-medium' },
|
||||
{ value: 'l', icon: 'size-large' },
|
||||
{ value: 'xl', icon: 'size-extra-large' },
|
||||
]
|
||||
|
||||
// There's a guide at the bottom of this file!
|
||||
|
||||
// [1]
|
||||
const ContextToolbarComponent = track(() => {
|
||||
const editor = useEditor()
|
||||
const showToolbar = editor.isIn('select.idle')
|
||||
if (!showToolbar) return null
|
||||
const selectionRotatedPageBounds = editor.getSelectionRotatedPageBounds()
|
||||
if (!selectionRotatedPageBounds) return null
|
||||
|
||||
// [2]
|
||||
const styles = new SharedStyleMap(editor.getSharedStyles())
|
||||
const size = styles.get(DefaultSizeStyle)
|
||||
if (!size) return null
|
||||
const currentSize = size.type === 'shared' ? size.value : undefined
|
||||
|
||||
const pageCoordinates = Vec.Sub(
|
||||
editor.pageToScreen(selectionRotatedPageBounds.point),
|
||||
editor.getViewportScreenBounds()
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: Math.max(16, pageCoordinates.y - 48),
|
||||
left: Math.max(16, pageCoordinates.x),
|
||||
pointerEvents: 'all',
|
||||
// [3]
|
||||
width: selectionRotatedPageBounds.width,
|
||||
}}
|
||||
// [4]
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
display: 'flex',
|
||||
boxShadow: '0 0 0 1px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.1)',
|
||||
background: 'var(--color-panel)',
|
||||
width: 'fit-content',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{SIZES.map(({ value, icon }) => {
|
||||
const isActive = value === currentSize
|
||||
return (
|
||||
<div
|
||||
key={value}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: 32,
|
||||
width: 32,
|
||||
background: isActive ? 'var(--color-muted-2)' : 'transparent',
|
||||
}}
|
||||
onClick={() =>
|
||||
editor.setStyleForSelectedShapes(DefaultSizeStyle, value, { squashing: false })
|
||||
}
|
||||
>
|
||||
<Icon icon={icon} />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const components: TLEditorComponents = {
|
||||
InFrontOfTheCanvas: ContextToolbarComponent,
|
||||
}
|
||||
|
||||
export default function ContextToolbar() {
|
||||
return (
|
||||
<div className="tldraw__editor">
|
||||
<Tldraw persistenceKey="things-on-the-canvas-example" components={components} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
This example shows how you can implement a context toolbar that appears when you select shapes.
|
||||
It's using the `InFrontOfTheCanvas` component to render the toolbar. This allows us to render
|
||||
the toolbar on top of the shapes, but behind the existing UI. The toolbar is only rendered when
|
||||
we are in the `select.idle` state (so we are not rotating, moving, resizing, drawing,...).
|
||||
You can also check the `Things on the canvas` example for more info on how to use `TLEditorComponents`.
|
||||
|
||||
[1]
|
||||
This is our context toolbar. It's positioned absolutely on top of the selected shapes.
|
||||
|
||||
[2]
|
||||
Different shapes support different style properties and this is how we get the styles that are
|
||||
supported by all selected shapes. If none of the selected shapes supported the `DefaultSizeStyle`
|
||||
we wouldn't show the toolbar at all.
|
||||
We also get the current value of the size property. If all the shapes have the same size then the
|
||||
type of the size property is `shared`. This will allow us to show the currently selected size in the
|
||||
toolbar. If the shapes have different sizes then the type of the size property is `mixed` and none
|
||||
of the sizes will be highlighted.
|
||||
|
||||
[3]
|
||||
We will show the toolbar in the middle of the selected shapes. We'll achieve this by making the parent
|
||||
take the whole width, then use flexbox to center the toolbar.
|
||||
|
||||
[4]
|
||||
We stop the event propagation so that clicking on the toolbar doesn't deselect the shapes.
|
||||
*/
|
12
apps/examples/src/examples/context-toolbar/README.md
Normal file
12
apps/examples/src/examples/context-toolbar/README.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
title: Context Toolbar
|
||||
component: ./ContextToolbar.tsx
|
||||
---
|
||||
|
||||
Show a contextual toolbar above the shapes when they are selected.
|
||||
|
||||
---
|
||||
|
||||
An example of how to show a contextual toolbar which appears on top of the selected shapes. It is displayed above the canvas, but below other UI elements.
|
||||
|
||||
Select shapes of different types and adjust the size of the shapes - this works just like adjusting the size from the Style panel on the right side.
|
Loading…
Reference in a new issue