123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- import { css } from '@emotion/css';
- import { FocusScope } from '@react-aria/focus';
- import {
- KBarAnimator,
- KBarPortal,
- KBarPositioner,
- KBarResults,
- KBarSearch,
- useMatches,
- Action,
- VisualState,
- useRegisterActions,
- useKBar,
- } from 'kbar';
- import React, { useEffect, useState } from 'react';
- import { useSelector } from 'react-redux';
- import { GrafanaTheme2 } from '@grafana/data';
- import { reportInteraction, locationService } from '@grafana/runtime';
- import { useStyles2 } from '@grafana/ui';
- import { StoreState } from 'app/types';
- import { keybindingSrv } from '../../core/services/keybindingSrv';
- import { ResultItem } from './ResultItem';
- import getDashboardNavActions from './actions/dashboard.nav.actions';
- import getGlobalActions from './actions/global.static.actions';
- /**
- * Wrap all the components from KBar here.
- * @constructor
- */
- export const CommandPalette = () => {
- const styles = useStyles2(getSearchStyles);
- const [actions, setActions] = useState<Action[]>([]);
- const { query, showing } = useKBar((state) => ({
- showing: state.visualState === VisualState.showing,
- }));
- const isNotLogin = locationService.getLocation().pathname !== '/login';
- const { navBarTree } = useSelector((state: StoreState) => {
- return {
- navBarTree: state.navBarTree,
- };
- });
- useEffect(() => {
- (async () => {
- if (isNotLogin) {
- const staticActions = getGlobalActions(navBarTree);
- const dashAct = await getDashboardNavActions('go/dashboard');
- setActions([...staticActions, ...dashAct]);
- }
- })();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isNotLogin]);
- useEffect(() => {
- if (showing) {
- reportInteraction('commandPalette_opened');
- keybindingSrv.bindGlobal('esc', () => {
- query.setVisualState(VisualState.animatingOut);
- });
- }
- return () => {
- keybindingSrv.bindGlobal('esc', () => {
- keybindingSrv.globalEsc();
- });
- };
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [showing]);
- useRegisterActions(actions, [actions]);
- return actions.length > 0 ? (
- <KBarPortal>
- <KBarPositioner className={styles.positioner}>
- <KBarAnimator className={styles.animator}>
- <FocusScope contain>
- <KBarSearch className={styles.search} />
- <RenderResults />
- </FocusScope>
- </KBarAnimator>
- </KBarPositioner>
- </KBarPortal>
- ) : null;
- };
- const RenderResults = () => {
- const { results, rootActionId } = useMatches();
- const styles = useStyles2(getSearchStyles);
- return (
- <div className={styles.resultsContainer}>
- <KBarResults
- items={results}
- onRender={({ item, active }) =>
- typeof item === 'string' ? (
- <div className={styles.sectionHeader}>{item}</div>
- ) : (
- <ResultItem action={item} active={active} currentRootActionId={rootActionId!} />
- )
- }
- />
- </div>
- );
- };
- const getSearchStyles = (theme: GrafanaTheme2) => ({
- positioner: css({
- zIndex: theme.zIndex.portal,
- marginTop: '0px',
- '&::before': {
- content: '""',
- position: 'fixed',
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
- background: theme.components.overlay.background,
- backdropFilter: 'blur(1px)',
- },
- }),
- animator: css({
- maxWidth: theme.breakpoints.values.sm, // supposed to be 600...
- width: '100%',
- background: theme.colors.background.canvas,
- color: theme.colors.text.primary,
- borderRadius: theme.shape.borderRadius(4),
- overflow: 'hidden',
- boxShadow: theme.shadows.z3,
- }),
- search: css({
- padding: theme.spacing(2, 3),
- 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.weak}`,
- }),
- sectionHeader: css({
- padding: theme.spacing(1, 2),
- fontSize: theme.typography.h6.fontSize,
- fontWeight: theme.typography.body.fontWeight,
- color: theme.colors.text.secondary,
- }),
- resultsContainer: css({
- padding: theme.spacing(2, 0),
- }),
- });
|