Fix 'style panel doesn't always disappear if you switch to the hand/laser tools' (#2886)

We had some bad logic in `useRelevantStyles` explicitly allowing an
opacity-slider to be rendered at all times when there is at least one
shape selected.

This shouldn't be the case when the editor is in non-shape-focused tools
like the move tool and the laser pointer tool. I refactored the hook
slightly to make it easier to express the correct logic. See the comment
for a more detailed description.

### Change Type

- [x] `patch` — Bug fix


[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version


### Release Notes

- Fixes an bug causing the opacity slider to show up in the move tool
and laser pointer tool.
This commit is contained in:
David Sheldrick 2024-02-20 15:09:45 +00:00 committed by GitHub
parent 9b65b0e20f
commit 3e12b27728
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 30 additions and 25 deletions

View file

@ -7168,9 +7168,8 @@ export class Editor extends EventEmitter<TLEventMap> {
* *
* @internal * @internal
*/ */
private _selectionSharedStyles = computed<ReadonlySharedStyleMap>( @computed
'_selectionSharedStyles', private _getSelectionSharedStyles(): ReadonlySharedStyleMap {
() => {
const selectedShapes = this.getSelectedShapes() const selectedShapes = this.getSelectedShapes()
const sharedStyles = new SharedStyleMap() const sharedStyles = new SharedStyleMap()
@ -7180,7 +7179,6 @@ export class Editor extends EventEmitter<TLEventMap> {
return sharedStyles return sharedStyles
} }
)
/** @internal */ /** @internal */
getStyleForNextShape<T>(style: StyleProp<T>): T { getStyleForNextShape<T>(style: StyleProp<T>): T {
@ -7213,7 +7211,7 @@ export class Editor extends EventEmitter<TLEventMap> {
// If we're in selecting and if we have a selection, return the shared styles from the // If we're in selecting and if we have a selection, return the shared styles from the
// current selection // current selection
if (this.isIn('select') && this.getSelectedShapeIds().length > 0) { if (this.isIn('select') && this.getSelectedShapeIds().length > 0) {
return this._selectionSharedStyles.get() return this._getSelectionSharedStyles()
} }
// If the current tool is associated with a shape, return the styles for that shape. // If the current tool is associated with a shape, return the styles for that shape.

View file

@ -23513,7 +23513,7 @@
"text": ";" "text": ";"
} }
], ],
"fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useRevelantStyles.ts", "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useRelevantStyles.ts",
"returnTypeTokenRange": { "returnTypeTokenRange": {
"startIndex": 5, "startIndex": 5,
"endIndex": 7 "endIndex": 7

View file

@ -81,7 +81,7 @@ export { useKeyboardShortcuts } from './lib/ui/hooks/useKeyboardShortcuts'
export { useLocalStorageState } from './lib/ui/hooks/useLocalStorageState' export { useLocalStorageState } from './lib/ui/hooks/useLocalStorageState'
export { useMenuIsOpen } from './lib/ui/hooks/useMenuIsOpen' export { useMenuIsOpen } from './lib/ui/hooks/useMenuIsOpen'
export { useReadonly } from './lib/ui/hooks/useReadonly' export { useReadonly } from './lib/ui/hooks/useReadonly'
export { useRelevantStyles } from './lib/ui/hooks/useRevelantStyles' export { useRelevantStyles } from './lib/ui/hooks/useRelevantStyles'
export { export {
toolbarItem, toolbarItem,
useToolbarSchema, useToolbarSchema,

View file

@ -7,7 +7,7 @@ import {
} from '@tldraw/editor' } from '@tldraw/editor'
import { useCallback } from 'react' import { useCallback } from 'react'
import { useTldrawUiComponents } from '../context/components' import { useTldrawUiComponents } from '../context/components'
import { useRelevantStyles } from '../hooks/useRevelantStyles' import { useRelevantStyles } from '../hooks/useRelevantStyles'
import { useTranslation } from '../hooks/useTranslation/useTranslation' import { useTranslation } from '../hooks/useTranslation/useTranslation'
import { TldrawUiButton } from './primitives/Button/TldrawUiButton' import { TldrawUiButton } from './primitives/Button/TldrawUiButton'
import { TldrawUiButtonIcon } from './primitives/Button/TldrawUiButtonIcon' import { TldrawUiButtonIcon } from './primitives/Button/TldrawUiButtonIcon'

View file

@ -1,7 +1,7 @@
import { useEditor } from '@tldraw/editor' import { useEditor } from '@tldraw/editor'
import classNames from 'classnames' import classNames from 'classnames'
import { memo, useCallback } from 'react' import { memo, useCallback } from 'react'
import { useRelevantStyles } from '../../hooks/useRevelantStyles' import { useRelevantStyles } from '../../hooks/useRelevantStyles'
import { DefaultStylePanelContent } from './DefaultStylePanelContent' import { DefaultStylePanelContent } from './DefaultStylePanelContent'
/** @public */ /** @public */

View file

@ -13,7 +13,6 @@ import {
ReadonlySharedStyleMap, ReadonlySharedStyleMap,
StyleProp, StyleProp,
TLArrowShapeArrowheadStyle, TLArrowShapeArrowheadStyle,
TLDefaultVerticalAlignStyle,
minBy, minBy,
useEditor, useEditor,
useValue, useValue,
@ -21,7 +20,7 @@ import {
import React from 'react' import React from 'react'
import { STYLES } from '../../../styles' import { STYLES } from '../../../styles'
import { useUiEvents } from '../../context/events' import { useUiEvents } from '../../context/events'
import { useRelevantStyles } from '../../hooks/useRevelantStyles' import { useRelevantStyles } from '../../hooks/useRelevantStyles'
import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton' import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon' import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
@ -201,7 +200,7 @@ function TextStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
<TldrawUiButtonIcon icon="vertical-align-center" /> <TldrawUiButtonIcon icon="vertical-align-center" />
</TldrawUiButton> </TldrawUiButton>
) : ( ) : (
<DropdownPicker<TLDefaultVerticalAlignStyle> <DropdownPicker
type="icon" type="icon"
id="geo-vertical-alignment" id="geo-vertical-alignment"
uiType="verticalAlign" uiType="verticalAlign"

View file

@ -82,9 +82,9 @@ function _DoubleDropdownPicker<T extends string>({
</TldrawUiDropdownMenuTrigger> </TldrawUiDropdownMenuTrigger>
<TldrawUiDropdownMenuContent side="bottom" align="end" sideOffset={0} alignOffset={-2}> <TldrawUiDropdownMenuContent side="bottom" align="end" sideOffset={0} alignOffset={-2}>
<div className="tlui-buttons__grid"> <div className="tlui-buttons__grid">
{itemsA.map((item) => { {itemsA.map((item, i) => {
return ( return (
<TldrawUiDropdownMenuItem data-testid={`style.${uiTypeA}.${item.value}`}> <TldrawUiDropdownMenuItem key={i} data-testid={`style.${uiTypeA}.${item.value}`}>
<TldrawUiButton <TldrawUiButton
type="icon" type="icon"
key={item.value} key={item.value}

View file

@ -24,8 +24,8 @@ export function useRelevantStyles(stylesToCheck = selectToolStyles): ReadonlySha
'getRelevantStyles', 'getRelevantStyles',
() => { () => {
const styles = new SharedStyleMap(editor.getSharedStyles()) const styles = new SharedStyleMap(editor.getSharedStyles())
const hasShape = const isInShapeSpecificTool = !!editor.root.getCurrent()?.shapeType
editor.getSelectedShapeIds().length > 0 || !!editor.root.getCurrent()?.shapeType const hasShapesSelected = editor.isIn('select') && editor.getSelectedShapeIds().length > 0
if (styles.size === 0 && editor.isIn('select') && editor.getSelectedShapeIds().length === 0) { if (styles.size === 0 && editor.isIn('select') && editor.getSelectedShapeIds().length === 0) {
for (const style of stylesToCheck) { for (const style of stylesToCheck) {
@ -33,8 +33,16 @@ export function useRelevantStyles(stylesToCheck = selectToolStyles): ReadonlySha
} }
} }
if (styles.size === 0 && !hasShape) return null if (isInShapeSpecificTool || hasShapesSelected || styles.size > 0) {
// If size is 0 we may still want to return an empty styles map to allow
// the opacity slider to show up.
// This can happen in two situations:
// 1. When the user is in the select tool and has multiple shapes selected but they have no supported styles (beyond opacity).
// 2. When the user is in a shape-specific tool and the shape has no supported styles (beyond opacity obvs).
return styles return styles
}
return null
}, },
[editor] [editor]
) )