Skip to content

Commit f0a9a9c

Browse files
[Dashboard usability] Shiny add panel highlight (#223614)
Adds a neat shiny Borealis-ish effect that shows up when a new panel is added to a Dashboard Co-authored-by: Marta Bondyra <[email protected]>
1 parent 6300b14 commit f0a9a9c

File tree

6 files changed

+124
-24
lines changed

6 files changed

+124
-24
lines changed

src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/use_hover_actions_styles.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ export const useHoverActionStyles = (isEditMode: boolean, showBorder?: boolean)
5050
`
5151
: css`
5252
.embPanel {
53-
outline: 1px solid transparent; // necessary for outline-color transition
53+
outline: var(--internalBorderStyle); // necessary for outline-color transition
54+
outline-color: transparent; // necessary for outline-color transition
5455
z-index: ${euiTheme.levels.content}; // necessary for z-index transition
5556
// delay hiding border on hover out to match delay on hover actions
5657
transition: outline-color ${euiTheme.animation.extraFast},

src/platform/plugins/shared/dashboard/public/dashboard_api/track_panel.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import { BehaviorSubject, Subject } from 'rxjs';
1111

12+
export const highlightAnimationDuration = 2000;
13+
1214
export function initializeTrackPanel(untilLoaded: (id: string) => Promise<undefined>) {
1315
const expandedPanelId$ = new BehaviorSubject<string | undefined>(undefined);
1416
const focusedPanelId$ = new BehaviorSubject<string | undefined>(undefined);
@@ -51,7 +53,7 @@ export function initializeTrackPanel(untilLoaded: (id: string) => Promise<undefi
5153
// Removes the class after the highlight animation finishes
5254
setTimeout(() => {
5355
panelRef.classList.remove('dshDashboardGrid__item--highlighted');
54-
}, 5000);
56+
}, highlightAnimationDuration);
5557
});
5658
}
5759
highlightPanelId$.next(undefined);

src/platform/plugins/shared/dashboard/public/dashboard_renderer/grid/dashboard_grid.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,9 @@ const dashboardGridStyles = {
285285
'.embPanel__content, .embPanel, .embPanel__hoverActionsAnchor, .lnsExpressionRenderer': {
286286
borderRadius: 0,
287287
},
288+
'.embPanel__content, .embPanel__header': {
289+
backgroundColor: euiTheme.colors.backgroundBasePlain,
290+
},
288291
},
289292
// drag handle visibility when dashboard is in edit mode or a panel is expanded
290293
'&.dshLayout-withoutMargins:not(.dshLayout--editing), .dshDashboardGrid__item--expanded, .dshDashboardGrid__item--blurred, .dshDashboardGrid__item--focused':

src/platform/plugins/shared/dashboard/public/dashboard_renderer/grid/dashboard_grid_item.tsx

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,19 @@
88
*/
99

1010
import { EuiLoadingChart, UseEuiTheme } from '@elastic/eui';
11-
import { css, keyframes } from '@emotion/react';
11+
import { css } from '@emotion/react';
12+
import { useMemoizedStyles } from '@kbn/core/public';
1213
import { EmbeddableRenderer } from '@kbn/embeddable-plugin/public';
1314
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
1415
import classNames from 'classnames';
1516
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
16-
import { useMemoizedStyles } from '@kbn/core/public';
1717
import { DashboardPanelState } from '../../../common';
1818
import { useDashboardApi } from '../../dashboard_api/use_dashboard_api';
1919
import { useDashboardInternalApi } from '../../dashboard_api/use_dashboard_internal_api';
2020
import { presentationUtilService } from '../../services/kibana_services';
21-
import { DASHBOARD_MARGIN_SIZE } from './constants';
2221
import { printViewportVisStyles } from '../print_styles';
22+
import { DASHBOARD_MARGIN_SIZE } from './constants';
23+
import { getHighlightStyles } from './highlight_styles';
2324

2425
type DivProps = Pick<React.HTMLAttributes<HTMLDivElement>, 'className' | 'style' | 'children'>;
2526

@@ -219,12 +220,6 @@ const dashboardGridItemStyles = {
219220
css([
220221
{
221222
height: '100%',
222-
'&.dshDashboardGrid__item--highlighted .embPanel': {
223-
borderRadius: context.euiTheme.border.radius.small,
224-
animationName: highlightOutline(context.euiTheme),
225-
animationDuration: '4s',
226-
animationTimingFunction: 'ease-out',
227-
},
228223
// Remove padding in fullscreen mode
229224
'.kbnAppWrapper--hiddenChrome &.dshDashboardGrid__item--expanded': {
230225
padding: 0,
@@ -233,23 +228,11 @@ const dashboardGridItemStyles = {
233228
padding: 0,
234229
},
235230
},
231+
getHighlightStyles(context),
236232
printViewportVisStyles(context),
237233
]),
238234
focusPanelBlur: css({
239235
pointerEvents: 'none',
240236
opacity: '0.25',
241237
}),
242238
};
243-
244-
const highlightOutline = (euiTheme: UseEuiTheme['euiTheme']) =>
245-
keyframes({
246-
'0%': {
247-
outline: `solid ${euiTheme.size.xs} transparent`,
248-
},
249-
'25%': {
250-
outline: `solid ${euiTheme.size.xs} ${euiTheme.colors.backgroundLightSuccess}`,
251-
},
252-
'100%': {
253-
outline: `solid ${euiTheme.size.xs} transparent`,
254-
},
255-
});
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
import { UseEuiTheme } from '@elastic/eui';
11+
import { css, keyframes } from '@emotion/react';
12+
import { highlightAnimationDuration } from '../../dashboard_api/track_panel';
13+
14+
const borderSpinKeyframes = keyframes({
15+
'0%': {
16+
'--highlight-rotate': '0deg',
17+
opacity: 0,
18+
},
19+
'10%, 60%': {
20+
opacity: 1,
21+
},
22+
'100%': {
23+
'--highlight-rotate': '180deg',
24+
opacity: 0,
25+
},
26+
});
27+
28+
const getOutlineFadeKeyframes = ({ euiTheme }: UseEuiTheme) =>
29+
keyframes({
30+
'0%, 70%': {
31+
outline: `${euiTheme.border.width.thin} dashed transparent`,
32+
},
33+
'100%': {
34+
outline: `${euiTheme.border.width.thin} dashed ${euiTheme.colors.borderBaseFormsControl}`,
35+
},
36+
});
37+
38+
const shineKeyframes = keyframes({
39+
'0%': {
40+
'--highlight-rotate': '0deg',
41+
opacity: 0,
42+
},
43+
'10%': {
44+
opacity: 0.7,
45+
},
46+
'100%': {
47+
'--highlight-rotate': '180deg',
48+
opacity: 0,
49+
},
50+
});
51+
52+
const highlightPropertyStyles = css`
53+
@property --highlight-rotate {
54+
syntax: '<angle>';
55+
inherits: false;
56+
initial-value: 0deg;
57+
}
58+
`;
59+
60+
export const getHighlightStyles = (context: UseEuiTheme) => {
61+
const { euiTheme } = context;
62+
const rotatingGradient = `
63+
linear-gradient(var(--highlight-rotate),
64+
${euiTheme.colors.borderBaseSuccess} 0%,
65+
${euiTheme.colors.borderBaseAccent} 46%,
66+
${euiTheme.colors.borderBaseAccentSecondary} 100%
67+
)`;
68+
69+
const brightenInDarkMode = (brightness: number) =>
70+
context.colorMode === 'DARK' ? `brightness(${brightness})` : '';
71+
72+
return css([
73+
highlightPropertyStyles,
74+
{
75+
'&.dshDashboardGrid__item--highlighted .embPanel': {
76+
position: 'relative',
77+
overflow: 'visible !important',
78+
backgroundColor: euiTheme.colors.backgroundBasePlain,
79+
animation: `${getOutlineFadeKeyframes(context)} ${highlightAnimationDuration}ms ease-out`,
80+
},
81+
'&.dshDashboardGrid__item--highlighted .embPanel::before': {
82+
content: `""`,
83+
opacity: 0,
84+
position: 'absolute',
85+
left: '-5px',
86+
top: '-5px',
87+
'z-index': -1,
88+
width: 'calc(100% + 10px)',
89+
height: 'calc(100% + 10px)',
90+
'background-image': rotatingGradient,
91+
filter: brightenInDarkMode(1.5),
92+
'border-radius': euiTheme.border.radius.medium,
93+
animation: `${borderSpinKeyframes} ${highlightAnimationDuration}ms ease-out`,
94+
},
95+
'&.dshDashboardGrid__item--highlighted .embPanel::after': {
96+
content: `""`,
97+
opacity: 0,
98+
position: 'absolute',
99+
left: '-15px',
100+
top: '-15px',
101+
'z-index': -2,
102+
width: 'calc(100% + 30px)',
103+
height: 'calc(100% + 30px)',
104+
'background-image': rotatingGradient,
105+
filter: `${brightenInDarkMode(1.3)} blur(25px)`,
106+
animation: `${shineKeyframes} ${highlightAnimationDuration}ms ease-out`,
107+
},
108+
},
109+
]);
110+
};

src/platform/plugins/shared/embeddable/public/add_from_library/open_add_from_library_flyout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const openAddFromLibraryFlyout = ({
4141
core
4242
),
4343
{
44+
type: 'push',
4445
ownFocus: true,
4546
onClose: (overlayRef) => {
4647
if (onClose) onClose();

0 commit comments

Comments
 (0)