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:
Mitja Bezenšek 2024-01-24 10:38:35 +01:00 committed by GitHub
parent 71b5fdd48a
commit d1bc456162
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 147 additions and 0 deletions

View 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.
*/

View 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.