import {Fragment} from 'react'; import {css} from '@emotion/react'; import styled from '@emotion/styled'; import space from 'app/styles/space'; import {ParseResult, Token, TokenResult} from './parser'; class ResultRenderer { renderFilter = (filter: TokenResult) => ( {filter.negated && !} {this.renderKey(filter.key, filter.negated)} {filter.operator && {filter.operator}} {this.renderToken(filter.value)} ); renderKey = ( key: TokenResult, negated?: boolean ) => { let value: React.ReactNode = key.text; if (key.type === Token.KeyExplicitTag) { value = ( {key.key.quoted ? `"${key.key.value}"` : key.key.value} ); } return {value}:; }; renderList = (token: TokenResult) => ( {token.items.map(({value, separator}) => [ {separator}, this.renderToken(value), ])} ); renderNumber = (token: TokenResult) => ( {token.value} {token.unit} ); renderToken = (token: TokenResult) => { switch (token.type) { case Token.Spaces: return token.value; case Token.Filter: return this.renderFilter(token); case Token.LogicGroup: return {this.renderResult(token.inner)}; case Token.LogicBoolean: return {token.value}; case Token.ValueBoolean: return {token.text}; case Token.ValueIso8601Date: return {token.text}; case Token.ValueTextList: case Token.ValueNumberList: return this.renderList(token); case Token.ValueNumber: return this.renderNumber(token); default: return token.text; } }; renderResult = (result: ParseResult) => result .map(this.renderToken) .map((renderedToken, i) => {renderedToken}); } const renderer = new ResultRenderer(); type Props = { /** * The result from parsing the search query string */ parsedQuery: ParseResult; /** * The current location of the cursror within the query. This is used to * highligh active tokens and trigger error tooltips. */ cursorPosition?: number; }; /** * Renders the parsed query with syntax highlighting. */ export default function HighlightQuery({parsedQuery}: Props) { const rendered = renderer.renderResult(parsedQuery); return {rendered}; } const FilterToken = styled('span')` --token-bg: ${p => p.theme.searchTokenBackground}; --token-border: ${p => p.theme.searchTokenBorder}; --token-value-color: ${p => p.theme.blue300}; `; const filterCss = css` background: var(--token-bg); border: 0.5px solid var(--token-border); padding: ${space(0.25)} 0; `; const Negation = styled('span')` ${filterCss}; border-right: none; padding-left: 1px; margin-left: -2px; font-weight: bold; border-radius: 2px 0 0 2px; color: ${p => p.theme.red300}; `; const Key = styled('span')<{negated: boolean}>` ${filterCss}; border-right: none; font-weight: bold; ${p => !p.negated ? css` border-radius: 2px 0 0 2px; padding-left: 1px; margin-left: -2px; ` : css` border-left: none; margin-left: 0; `}; `; const ExplicitKey = styled('span')<{prefix: string}>` &:before, &:after { color: ${p => p.theme.subText}; } &:before { content: '${p => p.prefix}['; } &:after { content: ']'; } `; const Operator = styled('span')` ${filterCss}; border-left: none; border-right: none; margin: -1px 0; color: ${p => p.theme.orange400}; `; const Value = styled('span')` ${filterCss}; border-left: none; border-radius: 0 2px 2px 0; color: var(--token-value-color); margin: -1px -2px -1px 0; padding-right: 1px; `; const Unit = styled('span')` font-weight: bold; color: ${p => p.theme.green300}; `; const LogicBoolean = styled('span')` font-weight: bold; color: ${p => p.theme.red300}; `; const Boolean = styled('span')` color: ${p => p.theme.pink300}; `; const DateTime = styled('span')` color: ${p => p.theme.green300}; `; const ListComma = styled('span')` color: ${p => p.theme.gray300}; `; const InList = styled('span')` &:before { content: '['; font-weight: bold; color: ${p => p.theme.purple300}; } &:after { content: ']'; font-weight: bold; color: ${p => p.theme.purple300}; } ${Value} { color: ${p => p.theme.purple300}; } `; const LogicGroup = styled('span')` &:before, &:after { position: relative; font-weight: bold; color: ${p => p.theme.white}; padding: 3px 0; background: ${p => p.theme.red200}; border-radius: 1px; } &:before { left: -3px; content: '('; } &:after { right: -3px; content: ')'; } `;