Skip to content

Commit

Permalink
Convert extensions' Dropdown tool to Flyout
Browse files Browse the repository at this point in the history
And use the style of a regular context Theatre.js menu.
  • Loading branch information
AriaMinaei committed Jan 23, 2023
1 parent 3d343cc commit a30bba0
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 167 deletions.
34 changes: 19 additions & 15 deletions packages/playground/src/shared/hello-world-extension/index.tsx
Expand Up @@ -41,28 +41,32 @@ studio.extend({
},
},
{
type: 'Downdown',
svgSource: '🫠',
onChange: (value: any) => {
console.log('Change:', value)
},
selectable: false,
options: [
type: 'Flyout',
label: '🫠',
items: [
{
label: 'Option 1',
value: 0,
label: 'Item 1',
onClick: () => {
console.log('Item 1 clicked')
},
},
{
label: 'Option 2',
value: 1,
label: 'Item 2',
onClick: () => {
console.log('Item 2 clicked')
},
},
{
label: 'Option 3',
value: 2,
label: 'Item 3',
onClick: () => {
console.log('Item 3 clicked')
},
},
{
label: 'Option 4',
value: 3,
label: 'Item 4',
onClick: () => {
console.log('Item 4 clicked')
},
},
],
},
Expand Down
26 changes: 16 additions & 10 deletions theatre/studio/src/TheatreStudio.ts
Expand Up @@ -102,21 +102,27 @@ export type ToolConfigSwitch = {
options: ToolConfigOption[]
}

export type ToolConfigDowndownOption = {
export type ToolconfigFlyoutMenuItem = {
label: string
value: any
onClick?: () => void
}

export type ToolConfigDowndown = {
type: 'Downdown'
index?: number
svgSource: string
selectable: boolean
onChange: (option: ToolConfigDowndownOption | null) => void
options: ToolConfigDowndownOption[]
export type ToolConfigFlyoutMenu = {
/**
* A flyout menu
*/
type: 'Flyout'
/**
* The label of the trigger button
*/
label: string
items: ToolconfigFlyoutMenuItem[]
}

export type ToolConfig = ToolConfigIcon | ToolConfigSwitch | ToolConfigDowndown
export type ToolConfig =
| ToolConfigIcon
| ToolConfigSwitch
| ToolConfigFlyoutMenu

export type ToolsetConfig = Array<ToolConfig>

Expand Down
4 changes: 2 additions & 2 deletions theatre/studio/src/toolbars/ExtensionToolbar/Toolset.tsx
Expand Up @@ -5,7 +5,7 @@ import type {ToolConfig, ToolsetConfig} from '@theatre/studio/TheatreStudio'
import React from 'react'
import IconButton from './tools/IconButton'
import Switch from './tools/Switch'
import Downdown from './tools/Downdown'
import ExtensionFlyoutMenu from './tools/ExtensionFlyoutMenu'

const Toolset: React.FC<{
config: ToolsetConfig
Expand All @@ -26,7 +26,7 @@ const toolByType: {
} = {
Icon: IconButton,
Switch: Switch,
Downdown: Downdown,
Flyout: ExtensionFlyoutMenu,
}

function getToolByType<Type extends ToolConfig['type']>(
Expand Down
85 changes: 0 additions & 85 deletions theatre/studio/src/toolbars/ExtensionToolbar/tools/Downdown.tsx

This file was deleted.

@@ -0,0 +1,81 @@
import React, {useRef} from 'react'
import styled from 'styled-components'
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
import type {
ToolConfigFlyoutMenu,
ToolconfigFlyoutMenuItem,
} from '@theatre/studio/TheatreStudio'
import ToolbarIconButton from '@theatre/studio/uiComponents/toolbar/ToolbarIconButton'
import BaseMenu from '@theatre/studio/uiComponents/simpleContextMenu/ContextMenu/BaseMenu'
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
import type {$IntentionalAny} from '@theatre/shared/utils/types'

const Container = styled.div`
${pointerEventsAutoInNormalMode};
& > svg {
width: 1em;
height: 1em;
pointer-events: none;
}
`

const ExtensionFlyoutMenu: React.FC<{
config: ToolConfigFlyoutMenu
}> = ({config}) => {
const triggerRef = useRef<null | HTMLElement>(null)

const popover = usePopover(
() => {
const triggerBounds = triggerRef.current!.getBoundingClientRect()
return {
debugName: 'ExtensionFlyoutMenu:' + config.label,

constraints: {
maxX: triggerBounds.right,
maxY: 8,
minX: triggerBounds.left,
minY: 8,
},
verticalGap: 2,
}
},
() => {
return (
<BaseMenu
items={config.items.map(
(option: ToolconfigFlyoutMenuItem, index: number) => ({
label: option.label,
callback: () => {
// this is a user-defined function, so we need to wrap it in a try/catch
try {
option.onClick?.()
} catch (e) {
console.error(e)
}
},
}),
)}
onRequestClose={() => {
popover.close('clicked')
}}
/>
)
},
)

return (
<Container>
{popover.node}
<ToolbarIconButton
ref={triggerRef as $IntentionalAny}
onClick={(e) => {
popover.open(e, triggerRef.current!)
}}
>
{config.label}
</ToolbarIconButton>
</Container>
)
}

export default ExtensionFlyoutMenu
@@ -0,0 +1,72 @@
import type {ElementType} from 'react'
import React from 'react'
import Item from './Item'
import type {$FixMe} from '@theatre/shared/utils/types'
import styled from 'styled-components'
import {transparentize} from 'polished'
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'

const minWidth = 190

const SHOW_OPTIONAL_MENU_TITLE = true

const MenuContainer = styled.ul`
position: absolute;
min-width: ${minWidth}px;
z-index: 10000;
background: ${transparentize(0.2, '#111')};
backdrop-filter: blur(2px);
color: white;
list-style-type: none;
padding: 2px 0;
margin: 0;
border-radius: 1px;
cursor: default;
${pointerEventsAutoInNormalMode};
border-radius: 3px;
`

const MenuTitle = styled.div`
padding: 4px 10px;
border-bottom: 1px solid #6262626d;
color: #adadadb3;
font-size: 11px;
font-weight: 500;
`

type MenuItem = {
label: string | ElementType
callback?: (e: React.MouseEvent) => void
enabled?: boolean
// subs?: Item[]
}

const BaseMenu: React.FC<{
items: MenuItem[]
ref?: $FixMe
displayName?: string
onRequestClose: () => void
}> = React.forwardRef((props, ref: $FixMe) => {
return (
<MenuContainer ref={ref}>
{SHOW_OPTIONAL_MENU_TITLE && props.displayName ? (
<MenuTitle>{props.displayName}</MenuTitle>
) : null}
{props.items.map((item, i) => (
<Item
key={`item-${i}`}
label={item.label}
enabled={item.enabled === false ? false : true}
onClick={(e) => {
if (item.callback) {
item.callback(e)
}
props.onRequestClose()
}}
/>
))}
</MenuContainer>
)
})

export default BaseMenu

0 comments on commit a30bba0

Please sign in to comment.