Commit 0425b477 by David Kaltschmidt

refactor Explore query field

parent 01074911
......@@ -9,7 +9,7 @@ import { getNextCharacter, getPreviousCousin } from './utils/dom';
import BracesPlugin from './slate-plugins/braces';
import ClearPlugin from './slate-plugins/clear';
import NewlinePlugin from './slate-plugins/newline';
import PluginPrism, { configurePrismMetricsTokens } from './slate-plugins/prism/index';
import PluginPrism, { setPrismTokens } from './slate-plugins/prism/index';
import RunnerPlugin from './slate-plugins/runner';
import debounce from './utils/debounce';
import { processLabels, RATE_RANGES, cleanText } from './utils/prometheus';
......@@ -17,13 +17,13 @@ import { processLabels, RATE_RANGES, cleanText } from './utils/prometheus';
import Typeahead from './Typeahead';
const EMPTY_METRIC = '';
const TYPEAHEAD_DEBOUNCE = 300;
export const TYPEAHEAD_DEBOUNCE = 300;
function flattenSuggestions(s) {
return s ? s.reduce((acc, g) => acc.concat(g.items), []) : [];
}
const getInitialValue = query =>
export const getInitialValue = query =>
Value.fromJSON({
document: {
nodes: [
......@@ -45,12 +45,14 @@ const getInitialValue = query =>
},
});
class Portal extends React.Component {
class Portal extends React.Component<any, any> {
node: any;
constructor(props) {
super(props);
const { index = 0, prefix = 'query' } = props;
this.node = document.createElement('div');
this.node.classList.add('explore-typeahead', `explore-typeahead-${props.index}`);
this.node.classList.add(`slate-typeahead`, `slate-typeahead-${prefix}-${index}`);
document.body.appendChild(this.node);
}
......@@ -71,12 +73,14 @@ class QueryField extends React.Component<any, any> {
constructor(props, context) {
super(props, context);
const { prismDefinition = {}, prismLanguage = 'promql' } = props;
this.plugins = [
BracesPlugin(),
ClearPlugin(),
RunnerPlugin({ handler: props.onPressEnter }),
NewlinePlugin(),
PluginPrism(),
PluginPrism({ definition: prismDefinition, language: prismLanguage }),
];
this.state = {
......@@ -131,7 +135,8 @@ class QueryField extends React.Component<any, any> {
if (!this.state.metrics) {
return;
}
configurePrismMetricsTokens(this.state.metrics);
setPrismTokens(this.props.language, 'metrics', this.state.metrics);
// Trigger re-render
window.requestAnimationFrame(() => {
// Bogus edit to trigger highlighting
......@@ -162,7 +167,7 @@ class QueryField extends React.Component<any, any> {
const selection = window.getSelection();
if (selection.anchorNode) {
const wrapperNode = selection.anchorNode.parentElement;
const editorNode = wrapperNode.closest('.query-field');
const editorNode = wrapperNode.closest('.slate-query-field');
if (!editorNode || this.state.value.isBlurred) {
// Not inside this editor
return;
......@@ -514,6 +519,7 @@ class QueryField extends React.Component<any, any> {
};
renderMenu = () => {
const { portalPrefix } = this.props;
const { suggestions } = this.state;
const hasSuggesstions = suggestions && suggestions.length > 0;
if (!hasSuggesstions) {
......@@ -528,7 +534,7 @@ class QueryField extends React.Component<any, any> {
// Create typeahead in DOM root so we can later position it absolutely
return (
<Portal>
<Portal prefix={portalPrefix}>
<Typeahead
menuRef={this.menuRef}
selectedItems={selectedKeys}
......@@ -541,7 +547,7 @@ class QueryField extends React.Component<any, any> {
render() {
return (
<div className="query-field">
<div className="slate-query-field">
{this.renderMenu()}
<Editor
autoCorrect={false}
......
import React, { PureComponent } from 'react';
import promql from './slate-plugins/prism/promql';
import QueryField from './QueryField';
class QueryRow extends PureComponent<any, any> {
......@@ -58,9 +59,12 @@ class QueryRow extends PureComponent<any, any> {
<div className="query-field-wrapper">
<QueryField
initialQuery={edited ? null : query}
portalPrefix="explore"
onPressEnter={this.handlePressEnter}
onQueryChange={this.handleChangeQuery}
placeholder="Enter a PromQL query"
prismLanguage="promql"
prismDefinition={promql}
request={request}
/>
</div>
......
import React from 'react';
import Prism from 'prismjs';
import Promql from './promql';
Prism.languages.promql = Promql;
const TOKEN_MARK = 'prism-token';
export function configurePrismMetricsTokens(metrics) {
Prism.languages.promql.metric = {
alias: 'variable',
pattern: new RegExp(`(?:^|\\s)(${metrics.join('|')})(?:$|\\s)`),
export function setPrismTokens(language, field, values, alias = 'variable') {
Prism.languages[language][field] = {
alias,
pattern: new RegExp(`(?:^|\\s)(${values.join('|')})(?:$|\\s)`),
};
}
......@@ -21,7 +17,12 @@ export function configurePrismMetricsTokens(metrics) {
* (Adapted to handle nested grammar definitions.)
*/
export default function PrismPlugin() {
export default function PrismPlugin({ definition, language }) {
if (definition) {
// Don't override exising modified definitions
Prism.languages[language] = Prism.languages[language] || definition;
}
return {
/**
* Render a Slate mark with appropiate CSS class names
......@@ -54,7 +55,7 @@ export default function PrismPlugin() {
const texts = node.getTexts().toArray();
const tstring = texts.map(t => t.text).join('\n');
const grammar = Prism.languages.promql;
const grammar = Prism.languages[language];
const tokens = Prism.tokenize(tstring, grammar);
const decorations = [];
let startText = texts.shift();
......
......@@ -67,6 +67,7 @@
@import 'components/filter-list';
@import 'components/filter-table';
@import 'components/old_stuff';
@import 'components/slate_editor';
@import 'components/typeahead';
@import 'components/modals';
@import 'components/dropdown';
......
.slate-query-field {
font-size: $font-size-root;
font-family: $font-family-monospace;
height: auto;
}
.slate-query-field-wrapper {
position: relative;
display: inline-block;
padding: 6px 7px 4px;
width: 100%;
cursor: text;
line-height: $line-height-base;
color: $text-color-weak;
background-color: $panel-bg;
background-image: none;
border: $panel-border;
border-radius: $border-radius;
transition: all 0.3s;
}
.slate-typeahead {
.typeahead {
position: absolute;
z-index: auto;
top: -10000px;
left: -10000px;
opacity: 0;
border-radius: $border-radius;
transition: opacity 0.75s;
border: $panel-border;
max-height: calc(66vh);
overflow-y: scroll;
max-width: calc(66%);
overflow-x: hidden;
outline: none;
list-style: none;
background: $panel-bg;
color: $text-color;
transition: opacity 0.4s ease-out;
box-shadow: $typeahead-shadow;
}
.typeahead-group__title {
color: $text-color-weak;
font-size: $font-size-sm;
line-height: $line-height-base;
padding: $input-padding-y $input-padding-x;
}
.typeahead-item {
height: auto;
font-family: $font-family-monospace;
padding: $input-padding-y $input-padding-x;
padding-left: $input-padding-x-lg;
font-size: $font-size-sm;
text-overflow: ellipsis;
overflow: hidden;
z-index: 1;
display: block;
white-space: nowrap;
cursor: pointer;
transition: color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
background 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), padding 0.15s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.typeahead-item__selected {
background-color: $typeahead-selected-bg;
color: $typeahead-selected-color;
}
}
/* SYNTAX */
.slate-query-field {
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: $text-color-weak;
}
.token.punctuation {
color: $text-color-weak;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.function-name,
.token.constant,
.token.symbol,
.token.deleted {
color: $query-red;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.function,
.token.builtin,
.token.inserted {
color: $query-green;
}
.token.operator,
.token.entity,
.token.url,
.token.variable {
color: $query-purple;
}
.token.atrule,
.token.attr-value,
.token.keyword,
.token.class-name {
color: $query-blue;
}
.token.regex,
.token.important {
color: $query-orange;
}
.token.important {
font-weight: normal;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.namespace {
opacity: 0.7;
}
}
......@@ -93,150 +93,3 @@
.query-row-tools {
width: 4rem;
}
.query-field {
font-size: $font-size-root;
font-family: $font-family-monospace;
height: auto;
}
.query-field-wrapper {
position: relative;
display: inline-block;
padding: 6px 7px 4px;
width: 100%;
cursor: text;
line-height: $line-height-base;
color: $text-color-weak;
background-color: $panel-bg;
background-image: none;
border: $panel-border;
border-radius: $border-radius;
transition: all 0.3s;
}
.explore-typeahead {
.typeahead {
position: absolute;
z-index: auto;
top: -10000px;
left: -10000px;
opacity: 0;
border-radius: $border-radius;
transition: opacity 0.75s;
border: $panel-border;
max-height: calc(66vh);
overflow-y: scroll;
max-width: calc(66%);
overflow-x: hidden;
outline: none;
list-style: none;
background: $panel-bg;
color: $text-color;
transition: opacity 0.4s ease-out;
box-shadow: $typeahead-shadow;
}
.typeahead-group__title {
color: $text-color-weak;
font-size: $font-size-sm;
line-height: $line-height-base;
padding: $input-padding-y $input-padding-x;
}
.typeahead-item {
height: auto;
font-family: $font-family-monospace;
padding: $input-padding-y $input-padding-x;
padding-left: $input-padding-x-lg;
font-size: $font-size-sm;
text-overflow: ellipsis;
overflow: hidden;
z-index: 1;
display: block;
white-space: nowrap;
cursor: pointer;
transition: color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
background 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), padding 0.15s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.typeahead-item__selected {
background-color: $typeahead-selected-bg;
color: $typeahead-selected-color;
}
}
/* SYNTAX */
.explore {
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: $text-color-weak;
}
.token.punctuation {
color: $text-color-weak;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.function-name,
.token.constant,
.token.symbol,
.token.deleted {
color: $query-red;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.function,
.token.builtin,
.token.inserted {
color: $query-green;
}
.token.operator,
.token.entity,
.token.url,
.token.variable {
color: $query-purple;
}
.token.atrule,
.token.attr-value,
.token.keyword,
.token.class-name {
color: $query-blue;
}
.token.regex,
.token.important {
color: $query-orange;
}
.token.important {
font-weight: normal;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.namespace {
opacity: 0.7;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment