CommandPalette: Design tweaks and design fixes (#61971)

* Command palette design tweaks start

* Fix some item styling issues

* Updated placeholder

* Style fixes

* Fix gradient border pos issue, and heading padding

* Fix header top margin

* Restore padding

* Update

* Change to md modal
This commit is contained in:
Torkel Ödegaard 2023-01-25 10:38:33 +01:00 committed by GitHub
parent b54b80f473
commit 504fec46d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 46 additions and 55 deletions

View File

@ -57,10 +57,12 @@ export const CommandPalette = () => {
<FocusScope contain autoFocus restoreFocus>
<div {...overlayProps} {...dialogProps}>
<KBarSearch
defaultPlaceholder={t('command-palette.search-box.placeholder', 'Search Grafana')}
defaultPlaceholder={t('command-palette.search-box.placeholder', 'Search or jump to...')}
className={styles.search}
/>
<RenderResults dashboardResults={dashboardResults} />
<div className={styles.resultsContainer}>
<RenderResults dashboardResults={dashboardResults} />
</div>
</div>
</FocusScope>
</KBarAnimator>
@ -92,22 +94,18 @@ const RenderResults = ({ dashboardResults }: RenderResultsProps) => {
return (
<KBarResults
items={items}
maxHeight={650}
onRender={({ item, active }) => {
// These items are rendered in a container, in a virtual list, so we cannot
// use :first/last-child selectors, so we must mimic them in JS
const isFirstItem = items[0] === item;
const isLastItem = items[items.length - 1] === item;
const isFirst = items[0] === item;
const renderedItem =
typeof item === 'string' ? (
<div className={styles.sectionHeader}>
<div className={cx(styles.sectionHeaderInner, isFirstItem && styles.sectionHeaderInnerFirst)}>{item}</div>
</div>
<div className={cx(styles.sectionHeader, isFirst && styles.sectionHeaderFirst)}>{item}</div>
) : (
<ResultItem action={item} active={active} currentRootActionId={rootActionId!} />
);
return isLastItem ? <div className={styles.lastItem}>{renderedItem}</div> : renderedItem;
return renderedItem;
}}
/>
);
@ -129,47 +127,39 @@ const getSearchStyles = (theme: GrafanaTheme2) => ({
},
}),
animator: css({
maxWidth: theme.breakpoints.values.sm, // supposed to be 600...
maxWidth: theme.breakpoints.values.md,
width: '100%',
background: theme.colors.background.canvas,
background: theme.colors.background.primary,
color: theme.colors.text.primary,
borderRadius: theme.shape.borderRadius(4),
borderRadius: theme.shape.borderRadius(2),
border: `1px solid ${theme.colors.border.weak}`,
overflow: 'hidden',
boxShadow: theme.shadows.z3,
}),
search: css({
padding: theme.spacing(2, 3),
padding: theme.spacing(1.5, 2),
fontSize: theme.typography.fontSize,
width: '100%',
boxSizing: 'border-box',
outline: 'none',
border: 'none',
background: theme.colors.background.canvas,
color: theme.colors.text.primary,
borderBottom: `1px solid ${theme.colors.border.medium}`,
background: theme.components.input.background,
color: theme.components.input.text,
borderBottom: `1px solid ${theme.colors.border.weak}`,
}),
// Virtual list measures margin incorrectly, so we need to split padding before/after border
// over and inner and outer element
sectionHeader: css({
paddingTop: theme.spacing(2),
fontSize: theme.typography.h6.fontSize,
fontWeight: theme.typography.body.fontWeight,
color: theme.colors.text.secondary,
}),
sectionHeaderInner: css({
padding: theme.spacing(1, 2),
borderTop: `1px solid ${theme.colors.border.medium}`,
}),
// We don't need the header above the first section
sectionHeaderInnerFirst: css({
borderTop: 'none',
paddingTop: 0,
}),
// Last item gets extra padding so it's not clipped by the rounded corners on the container
lastItem: css({
resultsContainer: css({
paddingBottom: theme.spacing(1),
}),
sectionHeader: css({
padding: theme.spacing(1.5, 2, 1, 2),
fontSize: theme.typography.bodySmall.fontSize,
fontWeight: theme.typography.fontWeightMedium,
color: theme.colors.text.primary,
borderTop: `1px solid ${theme.colors.border.weak}`,
marginTop: theme.spacing(1),
}),
sectionHeaderFirst: css({
borderTop: 'none',
marginTop: 0,
}),
});

View File

@ -1,9 +1,9 @@
import { css } from '@emotion/css';
import { css, cx } from '@emotion/css';
import { ActionId, ActionImpl } from 'kbar';
import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { useTheme2 } from '@grafana/ui';
import { useStyles2 } from '@grafana/ui';
export const ResultItem = React.forwardRef(
(
@ -31,8 +31,7 @@ export const ResultItem = React.forwardRef(
return action.ancestors.slice(index + 1);
}, [action.ancestors, currentRootActionId]);
const theme = useTheme2();
const styles = getResultItemStyles(theme, active);
const styles = useStyles2(getResultItemStyles);
let name = action.name;
@ -42,7 +41,7 @@ export const ResultItem = React.forwardRef(
}
return (
<div ref={ref} className={styles.row}>
<div ref={ref} className={cx(styles.row, active && styles.activeRow)}>
<div className={styles.actionContainer}>
{action.icon}
<div className={styles.textContainer}>
@ -63,31 +62,33 @@ export const ResultItem = React.forwardRef(
);
}
);
ResultItem.displayName = 'ResultItem';
const getResultItemStyles = (theme: GrafanaTheme2, isActive: boolean) => {
const textColor = isActive ? theme.colors.text.maxContrast : theme.colors.text.primary;
const rowBackgroundColor = isActive ? theme.colors.background.primary : 'transparent';
const shortcutBackgroundColor = isActive ? theme.colors.background.secondary : theme.colors.background.primary;
const getResultItemStyles = (theme: GrafanaTheme2) => {
return {
row: css({
color: textColor,
padding: theme.spacing(1, 2),
background: rowBackgroundColor,
display: 'flex',
alightItems: 'center',
justifyContent: 'space-between',
cursor: 'pointer',
position: 'relative',
borderRadius: theme.shape.borderRadius(2),
margin: theme.spacing(0, 1),
}),
activeRow: css({
color: theme.colors.text.maxContrast,
background: theme.colors.emphasize(theme.colors.background.primary, 0.03),
'&:before': {
display: isActive ? 'block' : 'none',
display: 'block',
content: '" "',
position: 'absolute',
left: 0,
top: 0,
bottom: 0,
width: theme.spacing(0.5),
borderRadius: theme.shape.borderRadius(1),
borderRadius: theme.shape.borderRadius(2),
backgroundImage: theme.colors.gradients.brandVertical,
},
}),
@ -103,7 +104,7 @@ const getResultItemStyles = (theme: GrafanaTheme2, isActive: boolean) => {
}),
shortcut: css({
padding: theme.spacing(0, 1),
background: shortcutBackgroundColor,
background: theme.colors.background.secondary,
borderRadius: theme.shape.borderRadius(),
fontSize: theme.typography.fontSize,
}),

View File

@ -13,7 +13,7 @@
"search": "Search"
},
"search-box": {
"placeholder": "Search Grafana"
"placeholder": "Search or jump to..."
},
"section": {
"actions": "Actions",

View File

@ -13,7 +13,7 @@
"search": "Ŝęäřčĥ"
},
"search-box": {
"placeholder": "Ŝęäřčĥ Ğřäƒäʼnä"
"placeholder": "Ŝęäřčĥ őř ĵūmp ŧő..."
},
"section": {
"actions": "Åčŧįőʼnş",