Skip to content

Commit fe5145f

Browse files
authored
apps/ui: Add widget toolbar overlay and note delete action (#3409)
1 parent aca986b commit fe5145f

17 files changed

Lines changed: 836 additions & 276 deletions
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { color as colorIcon } from '@wordpress/icons';
2+
import { IconButton } from '@wordpress/ui';
3+
import * as Menu from '@/components/menu';
4+
import styles from './style.module.css';
5+
import type { ControlConfig, ControlRendererProps } from './types';
6+
import type { CSSProperties } from 'react';
7+
8+
type ColorControlConfig = Extract< ControlConfig, { type: 'color' } >;
9+
10+
type ColorControlProps = ControlRendererProps< ColorControlConfig >;
11+
12+
export function ColorControl( {
13+
control,
14+
isOpen,
15+
setIsOpen,
16+
updateProps,
17+
props,
18+
}: ColorControlProps ) {
19+
const currentValue = props[ control.property ];
20+
21+
return (
22+
<Menu.Root modal={ false } open={ isOpen } orientation="horizontal" onOpenChange={ setIsOpen }>
23+
<Menu.Trigger
24+
render={
25+
<IconButton
26+
icon={ colorIcon }
27+
label={ control.label }
28+
size="compact"
29+
tone="neutral"
30+
variant="minimal"
31+
aria-pressed={ isOpen }
32+
className={ styles.button }
33+
/>
34+
}
35+
/>
36+
<Menu.Popup side="top" align="center" sideOffset={ 18 } className={ styles.swatchMenu }>
37+
{ control.options.map( ( option ) => (
38+
<Menu.Item
39+
key={ option.value }
40+
className={ styles.swatch }
41+
style={ getSwatchStyle( option.color ) }
42+
data-active={ currentValue === option.value ? 'true' : 'false' }
43+
title={ option.label }
44+
aria-label={ option.label }
45+
label={ option.label }
46+
onClick={ () => {
47+
updateProps( { [ control.property ]: option.value } );
48+
} }
49+
/>
50+
) ) }
51+
</Menu.Popup>
52+
</Menu.Root>
53+
);
54+
}
55+
56+
function getSwatchStyle( color: string ): CSSProperties {
57+
return {
58+
'--control-swatch-color': color,
59+
} as CSSProperties;
60+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ColorControl } from './color';
2+
import type { ControlRendererProps } from './types';
3+
4+
export function ControlRenderer( props: ControlRendererProps ) {
5+
switch ( props.control.type ) {
6+
case 'color':
7+
return <ColorControl { ...props } control={ props.control } />;
8+
}
9+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
.button {
2+
--wp-ui-button-height: 36px;
3+
--wp-ui-button-background-color: transparent;
4+
--wp-ui-button-background-color-active: rgba( 15, 23, 42, 0.07 );
5+
--wp-ui-button-border-color: transparent;
6+
--wp-ui-button-border-color-active: transparent;
7+
--wp-ui-button-foreground-color: #14171a;
8+
--wp-ui-button-foreground-color-active: #14171a;
9+
10+
width: 36px;
11+
min-width: 36px;
12+
border-radius: 12px;
13+
}
14+
15+
.button[aria-pressed='true'] {
16+
--wp-ui-button-background-color: rgba( 15, 23, 42, 0.07 );
17+
--wp-ui-button-background-color-active: rgba( 15, 23, 42, 0.07 );
18+
--wp-ui-button-foreground-color: #14171a;
19+
--wp-ui-button-foreground-color-active: #14171a;
20+
}
21+
22+
.swatchMenu {
23+
min-width: auto;
24+
flex-direction: row;
25+
gap: 6px;
26+
padding: 6px;
27+
border-radius: 18px;
28+
}
29+
30+
.swatch,
31+
.swatch[data-highlighted],
32+
.swatch:hover {
33+
width: 30px;
34+
height: 30px;
35+
padding: 0;
36+
border: 1px solid rgba( 15, 23, 42, 0.12 );
37+
border-radius: 10px;
38+
background: var( --control-swatch-color );
39+
box-shadow: 0 6px 18px rgba( 15, 23, 42, 0.1 );
40+
color: transparent;
41+
transition:
42+
box-shadow 160ms ease,
43+
transform 160ms ease;
44+
}
45+
46+
.swatch[data-highlighted],
47+
.swatch:hover {
48+
box-shadow: 0 8px 22px rgba( 15, 23, 42, 0.14 );
49+
transform: translateY( -1px );
50+
}
51+
52+
.swatch[data-active='true'] {
53+
outline: 2px solid #3858e9;
54+
outline-offset: 2px;
55+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
export interface ColorControlOption< TValue extends string = string > {
2+
value: TValue;
3+
label: string;
4+
color: string;
5+
}
6+
7+
type StringControlPropKey< TProps extends Record< string, unknown > > = {
8+
[ TKey in Extract< keyof TProps, string > ]: TProps[ TKey ] extends string ? TKey : never;
9+
}[ Extract< keyof TProps, string > ];
10+
11+
export type ColorControlConfig<
12+
TProps extends Record< string, unknown > = Record< string, string >,
13+
> = {
14+
[ TProperty in StringControlPropKey< TProps > ]: {
15+
type: 'color';
16+
id: string;
17+
property: TProperty;
18+
label: string;
19+
options: Array< ColorControlOption< Extract< TProps[ TProperty ], string > > >;
20+
};
21+
}[ StringControlPropKey< TProps > ];
22+
23+
export type ControlConfig< TProps extends Record< string, unknown > = Record< string, string > > =
24+
ColorControlConfig< TProps >;
25+
26+
export interface ControlRendererProps< TControl extends ControlConfig = ControlConfig > {
27+
control: TControl;
28+
isOpen: boolean;
29+
setIsOpen: ( isOpen: boolean ) => void;
30+
updateProps: ( props: Record< string, unknown > ) => void;
31+
props: Record< string, unknown >;
32+
}

apps/ui/src/ui-desks/desk/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { __ } from '@wordpress/i18n';
22
import { DeskChats } from '../chats';
33
import { DeskChrome } from '../chrome';
4+
import { DeskWidgetToolbar } from '../widgets/toolbar';
45
import { DeskCanvas } from './canvas';
56
import { DeskProvider } from './provider';
67
import styles from './style.module.css';
@@ -27,6 +28,7 @@ function DeskShell( { siteId, children }: DeskProps & { children: ReactNode } )
2728
<main className={ styles.root } aria-label={ getDeskLabel( siteId ) } data-site-id={ siteId }>
2829
<DeskChrome siteId={ siteId } />
2930
{ children }
31+
<DeskWidgetToolbar />
3032
</main>
3133
</>
3234
);

0 commit comments

Comments
 (0)