Commit 642374de by David Kaltschmidt

Explore: Metrics chooser for prometheus

- load all histogrammable metrics on start, based on `{le!=''}` series
  query
- select dropdown shows all those metrics as well as histograms in a
  special group
- select a metric will write fill the metric in the query box
- if a histogram is chosen, it will write a complete
  `histogram_quantile` query
- added new dependency: rc-cascader
parent 307248f7
......@@ -166,6 +166,7 @@
"mousetrap-global-bind": "^1.1.0",
"prismjs": "^1.6.0",
"prop-types": "^15.6.0",
"rc-cascader": "^0.14.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-grid-layout": "0.16.6",
......
......@@ -208,7 +208,7 @@ export class Explore extends React.Component<any, IExploreState> {
};
const nextQueries = [...queries];
nextQueries[index] = nextQuery;
this.setState({ queries: nextQueries });
this.setState({ queries: nextQueries }, override ? () => this.onSubmit() : undefined);
};
onChangeTime = nextRange => {
......
......@@ -2,6 +2,7 @@ import _ from 'lodash';
import moment from 'moment';
import React from 'react';
import { Value } from 'slate';
import Cascader from 'rc-cascader';
// dom also includes Element polyfills
import { getNextCharacter, getPreviousCousin } from './utils/dom';
......@@ -21,12 +22,14 @@ import TypeaheadField, {
const DEFAULT_KEYS = ['job', 'instance'];
const EMPTY_SELECTOR = '{}';
const HISTOGRAM_GROUP = '__histograms__';
const HISTOGRAM_SELECTOR = '{le!=""}'; // Returns all timeseries for histograms
const HISTORY_ITEM_COUNT = 5;
const HISTORY_COUNT_CUTOFF = 1000 * 60 * 60 * 24; // 24h
const METRIC_MARK = 'metric';
const PRISM_LANGUAGE = 'promql';
export const wrapLabel = label => ({ label });
export const wrapLabel = (label: string) => ({ label });
export const setFunctionMove = (suggestion: Suggestion): Suggestion => {
suggestion.move = -1;
return suggestion;
......@@ -48,6 +51,22 @@ export function addHistoryMetadata(item: Suggestion, history: any[]): Suggestion
};
}
export function groupMetricsByPrefix(metrics: string[], delimiter = '_'): CascaderOption[] {
return _.chain(metrics)
.groupBy(metric => metric.split(delimiter)[0])
.map((metricsForPrefix: string[], prefix: string): CascaderOption => {
const prefixIsMetric = metricsForPrefix.length === 1 && metricsForPrefix[0] === prefix;
const children = prefixIsMetric ? [] : metricsForPrefix.sort().map(m => ({ label: m, value: m }));
return {
children,
label: prefix,
value: prefix,
};
})
.sortBy('label')
.value();
}
export function willApplySuggestion(
suggestion: string,
{ typeaheadContext, typeaheadText }: TypeaheadFieldState
......@@ -78,22 +97,33 @@ export function willApplySuggestion(
return suggestion;
}
interface CascaderOption {
label: string;
value: string;
children?: CascaderOption[];
disabled?: boolean;
}
interface PromQueryFieldProps {
history?: any[];
histogramMetrics?: string[];
initialQuery?: string | null;
labelKeys?: { [index: string]: string[] }; // metric -> [labelKey,...]
labelValues?: { [index: string]: { [index: string]: string[] } }; // metric -> labelKey -> [labelValue,...]
metrics?: string[];
metricsByPrefix?: CascaderOption[];
onPressEnter?: () => void;
onQueryChange?: (value: string) => void;
onQueryChange?: (value: string, override?: boolean) => void;
portalPrefix?: string;
request?: (url: string) => any;
}
interface PromQueryFieldState {
histogramMetrics: string[];
labelKeys: { [index: string]: string[] }; // metric -> [labelKey,...]
labelValues: { [index: string]: { [index: string]: string[] } }; // metric -> labelKey -> [labelValue,...]
metrics: string[];
metricsByPrefix: CascaderOption[];
}
interface PromTypeaheadInput {
......@@ -107,7 +137,7 @@ interface PromTypeaheadInput {
class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryFieldState> {
plugins: any[];
constructor(props, context) {
constructor(props: PromQueryFieldProps, context) {
super(props, context);
this.plugins = [
......@@ -117,21 +147,45 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField
];
this.state = {
histogramMetrics: props.histogramMetrics || [],
labelKeys: props.labelKeys || {},
labelValues: props.labelValues || {},
metrics: props.metrics || [],
metricsByPrefix: props.metricsByPrefix || [],
};
}
componentDidMount() {
this.fetchMetricNames();
this.fetchHistogramMetrics();
}
onChangeMetrics = (values: string[], selectedOptions: CascaderOption[]) => {
let query;
if (selectedOptions.length === 1) {
if (selectedOptions[0].children.length === 0) {
query = selectedOptions[0].value;
} else {
// Ignore click on group
return;
}
} else {
const prefix = selectedOptions[0].value;
const metric = selectedOptions[1].value;
if (prefix === HISTOGRAM_GROUP) {
query = `histogram_quantile(0.95, sum(rate(${metric}[5m])) by (le))`;
} else {
query = metric;
}
}
this.onChangeQuery(query, true);
};
onChangeQuery = value => {
onChangeQuery = (value: string, override?: boolean) => {
// Send text change to parent
const { onQueryChange } = this.props;
if (onQueryChange) {
onQueryChange(value);
onQueryChange(value, override);
}
};
......@@ -317,7 +371,17 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField
return fetch(url);
};
async fetchLabelValues(key) {
fetchHistogramMetrics() {
this.fetchSeriesLabels(HISTOGRAM_SELECTOR, true, () => {
const histogramSeries = this.state.labelValues[HISTOGRAM_SELECTOR];
if (histogramSeries && histogramSeries['__name__']) {
const histogramMetrics = histogramSeries['__name__'].slice().sort();
this.setState({ histogramMetrics });
}
});
}
async fetchLabelValues(key: string) {
const url = `/api/v1/label/${key}/values`;
try {
const res = await this.request(url);
......@@ -337,7 +401,7 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField
}
}
async fetchSeriesLabels(name, withName?) {
async fetchSeriesLabels(name: string, withName?: boolean, callback?: () => void) {
const url = `/api/v1/series?match[]=${name}`;
try {
const res = await this.request(url);
......@@ -351,7 +415,7 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField
...this.state.labelValues,
[name]: values,
};
this.setState({ labelKeys, labelValues });
this.setState({ labelKeys, labelValues }, callback);
} catch (e) {
console.error(e);
}
......@@ -362,14 +426,30 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField
try {
const res = await this.request(url);
const body = await (res.data || res.json());
this.setState({ metrics: body.data }, this.onReceiveMetrics);
const metrics = body.data;
const metricsByPrefix = groupMetricsByPrefix(metrics);
this.setState({ metrics, metricsByPrefix }, this.onReceiveMetrics);
} catch (error) {
console.error(error);
}
}
render() {
const { histogramMetrics, metricsByPrefix } = this.state;
const histogramOptions = histogramMetrics.map(hm => ({ label: hm, value: hm }));
const metricsOptions = [
{ label: 'Histograms', value: HISTOGRAM_GROUP, children: histogramOptions },
...metricsByPrefix,
];
return (
<div className="prom-query-field">
<div className="prom-query-field-tools">
<Cascader options={metricsOptions} onChange={this.onChangeMetrics}>
<button className="btn navbar-button navbar-button--tight">Metrics</button>
</Cascader>
</div>
<div className="slate-query-field-wrapper">
<TypeaheadField
additionalPlugins={this.plugins}
cleanText={cleanText}
......@@ -379,6 +459,8 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField
onValueChanged={this.onChangeQuery}
placeholder="Enter a PromQL query"
/>
</div>
</div>
);
}
}
......
......@@ -3,10 +3,10 @@ import React, { PureComponent } from 'react';
import QueryField from './PromQueryField';
class QueryRow extends PureComponent<any, {}> {
onChangeQuery = value => {
onChangeQuery = (value, override?: boolean) => {
const { index, onChangeQuery } = this.props;
if (onChangeQuery) {
onChangeQuery(value, index);
onChangeQuery(value, index, override);
}
};
......@@ -18,10 +18,7 @@ class QueryRow extends PureComponent<any, {}> {
};
onClickClearButton = () => {
const { index, onChangeQuery } = this.props;
if (onChangeQuery) {
onChangeQuery('', index, true);
}
this.onChangeQuery('', true);
};
onClickRemoveButton = () => {
......@@ -42,18 +39,7 @@ class QueryRow extends PureComponent<any, {}> {
const { edited, history, query, request } = this.props;
return (
<div className="query-row">
<div className="query-row-tools">
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickAddButton}>
<i className="fa fa-plus" />
</button>
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickRemoveButton}>
<i className="fa fa-minus" />
</button>
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickClearButton}>
<i className="fa fa-times" />
</button>
</div>
<div className="slate-query-field-wrapper">
<div className="query-row-field">
<QueryField
initialQuery={edited ? null : query}
history={history}
......@@ -63,6 +49,17 @@ class QueryRow extends PureComponent<any, {}> {
request={request}
/>
</div>
<div className="query-row-tools">
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickClearButton}>
<i className="fa fa-times" />
</button>
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickAddButton}>
<i className="fa fa-plus" />
</button>
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickRemoveButton}>
<i className="fa fa-minus" />
</button>
</div>
</div>
);
}
......
// vendor
@import '../vendor/css/timepicker.css';
@import '../vendor/css/spectrum.css';
@import '../vendor/css/rc-cascader.css';
// MIXINS
@import 'mixins/mixins';
......
......@@ -110,6 +110,11 @@
width: 6rem;
}
.query-row-field {
margin-right: 3px;
width: 100%;
}
.explore {
.logs {
.logs-entries {
......@@ -146,3 +151,11 @@
}
}
}
// Prometheus-specifics, to be extracted to datasource soon
.explore {
.prom-query-field {
display: flex;
}
}
.rc-cascader {
font-size: 12px;
}
.rc-cascader-menus {
font-size: 12px;
overflow: hidden;
background: #fff;
position: absolute;
border: 1px solid #d9d9d9;
border-radius: 6px;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.17);
white-space: nowrap;
}
.rc-cascader-menus-hidden {
display: none;
}
.rc-cascader-menus.slide-up-enter,
.rc-cascader-menus.slide-up-appear {
animation-duration: .3s;
animation-fill-mode: both;
transform-origin: 0 0;
opacity: 0;
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
animation-play-state: paused;
}
.rc-cascader-menus.slide-up-leave {
animation-duration: .3s;
animation-fill-mode: both;
transform-origin: 0 0;
opacity: 1;
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
animation-play-state: paused;
}
.rc-cascader-menus.slide-up-enter.slide-up-enter-active.rc-cascader-menus-placement-bottomLeft,
.rc-cascader-menus.slide-up-appear.slide-up-appear-active.rc-cascader-menus-placement-bottomLeft {
animation-name: SlideUpIn;
animation-play-state: running;
}
.rc-cascader-menus.slide-up-enter.slide-up-enter-active.rc-cascader-menus-placement-topLeft,
.rc-cascader-menus.slide-up-appear.slide-up-appear-active.rc-cascader-menus-placement-topLeft {
animation-name: SlideDownIn;
animation-play-state: running;
}
.rc-cascader-menus.slide-up-leave.slide-up-leave-active.rc-cascader-menus-placement-bottomLeft {
animation-name: SlideUpOut;
animation-play-state: running;
}
.rc-cascader-menus.slide-up-leave.slide-up-leave-active.rc-cascader-menus-placement-topLeft {
animation-name: SlideDownOut;
animation-play-state: running;
}
.rc-cascader-menu {
display: inline-block;
/* width: 100px; */
max-width: 50vw;
height: 192px;
list-style: none;
margin: 0;
padding: 0;
border-right: 1px solid #e9e9e9;
overflow: auto;
}
.rc-cascader-menu:last-child {
border-right: 0;
}
.rc-cascader-menu-item {
height: 32px;
line-height: 32px;
padding: 0 16px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: all 0.3s ease;
position: relative;
}
.rc-cascader-menu-item:hover {
background: #eaf8fe;
}
.rc-cascader-menu-item-disabled {
cursor: not-allowed;
color: #ccc;
}
.rc-cascader-menu-item-disabled:hover {
background: transparent;
}
.rc-cascader-menu-item-loading:after {
position: absolute;
right: 12px;
content: 'loading';
color: #aaa;
font-style: italic;
}
.rc-cascader-menu-item-active {
background: #d5f1fd;
}
.rc-cascader-menu-item-active:hover {
background: #d5f1fd;
}
.rc-cascader-menu-item-expand {
position: relative;
}
.rc-cascader-menu-item-expand:after {
content: '>';
font-size: 12px;
color: #999;
position: absolute;
right: 16px;
line-height: 32px;
}
@keyframes SlideUpIn {
0% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0.8);
}
100% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
}
@keyframes SlideUpOut {
0% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0.8);
}
}
@keyframes SlideDownIn {
0% {
opacity: 0;
transform-origin: 0% 100%;
transform: scaleY(0.8);
}
100% {
opacity: 1;
transform-origin: 0% 100%;
transform: scaleY(1);
}
}
@keyframes SlideDownOut {
0% {
opacity: 1;
transform-origin: 0% 100%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 100%;
transform: scaleY(0.8);
}
}
......@@ -478,6 +478,12 @@ acorn@~2.6.4:
version "2.6.4"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.6.4.tgz#eb1f45b4a43fa31d03701a5ec46f3b52673e90ee"
add-dom-event-listener@1.x:
version "1.0.2"
resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.0.2.tgz#8faed2c41008721cf111da1d30d995b85be42bed"
dependencies:
object-assign "4.x"
after@0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
......@@ -771,6 +777,10 @@ array-slice@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5"
array-tree-filter@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-1.0.1.tgz#0a8ad1eefd38ce88858632f9cc0423d7634e4d5d"
array-union@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
......@@ -1514,7 +1524,7 @@ babel-register@^6.26.0, babel-register@^6.9.0:
mkdirp "^0.5.1"
source-map-support "^0.4.15"
babel-runtime@^6.0.0, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
babel-runtime@6.x, babel-runtime@^6.0.0, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
......@@ -2246,6 +2256,10 @@ classnames@2.x, classnames@^2.2.4, classnames@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
classnames@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
clean-css@3.4.x, clean-css@~3.4.2:
version "3.4.28"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-3.4.28.tgz#bf1945e82fc808f55695e6ddeaec01400efd03ff"
......@@ -2553,6 +2567,12 @@ component-bind@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
component-classes@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/component-classes/-/component-classes-1.2.6.tgz#c642394c3618a4d8b0b8919efccbbd930e5cd691"
dependencies:
component-indexof "0.0.3"
component-emitter@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3"
......@@ -2561,6 +2581,10 @@ component-emitter@1.2.1, component-emitter@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
component-indexof@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24"
component-inherit@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
......@@ -2841,6 +2865,13 @@ crypto-random-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
css-animation@^1.3.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.4.1.tgz#5b8813125de0fbbbb0bbe1b472ae84221469b7a8"
dependencies:
babel-runtime "6.x"
component-classes "^1.2.5"
css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
......@@ -3515,6 +3546,10 @@ doctrine@^1.2.2:
esutils "^2.0.2"
isarray "^1.0.0"
dom-align@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.8.0.tgz#c0e89b5b674c6e836cd248c52c2992135f093654"
dom-converter@~0.1:
version "0.1.4"
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b"
......@@ -7354,6 +7389,10 @@ lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
lodash._root@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
......@@ -7386,6 +7425,14 @@ lodash.flattendeep@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
lodash.isarguments@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
lodash.isarray@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
lodash.isequal@^4.0.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
......@@ -7406,6 +7453,14 @@ lodash.kebabcase@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
lodash.keys@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
dependencies:
lodash._getnative "^3.0.0"
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
......@@ -8651,7 +8706,7 @@ object-assign@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0"
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
......@@ -9981,6 +10036,54 @@ raw-body@2.3.3:
iconv-lite "0.4.23"
unpipe "1.0.0"
rc-align@^2.4.0:
version "2.4.3"
resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-2.4.3.tgz#b9b3c2a6d68adae71a8e1d041cd5e3b2a655f99a"
dependencies:
babel-runtime "^6.26.0"
dom-align "^1.7.0"
prop-types "^15.5.8"
rc-util "^4.0.4"
rc-animate@2.x:
version "2.4.4"
resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.4.4.tgz#a05a784c747beef140d99ff52b6117711bef4b1e"
dependencies:
babel-runtime "6.x"
css-animation "^1.3.2"
prop-types "15.x"
rc-cascader@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-0.14.0.tgz#a956c99896f10883bf63d46fb894d0cb326842a4"
dependencies:
array-tree-filter "^1.0.0"
prop-types "^15.5.8"
rc-trigger "^2.2.0"
rc-util "^4.0.4"
shallow-equal "^1.0.0"
warning "^4.0.1"
rc-trigger@^2.2.0:
version "2.5.4"
resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-2.5.4.tgz#9088a24ba5a811b254f742f004e38a9e2f8843fb"
dependencies:
babel-runtime "6.x"
classnames "^2.2.6"
prop-types "15.x"
rc-align "^2.4.0"
rc-animate "2.x"
rc-util "^4.4.0"
rc-util@^4.0.4, rc-util@^4.4.0:
version "4.5.1"
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.5.1.tgz#0e435057174c024901c7600ba8903dd03da3ab39"
dependencies:
add-dom-event-listener "1.x"
babel-runtime "6.x"
prop-types "^15.5.10"
shallowequal "^0.2.2"
rc@^1.0.1, rc@^1.1.6, rc@^1.1.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
......@@ -10980,6 +11083,16 @@ shallow-clone@^1.0.0:
kind-of "^5.0.0"
mixin-object "^2.0.1"
shallow-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.0.0.tgz#508d1838b3de590ab8757b011b25e430900945f7"
shallowequal@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-0.2.2.tgz#1e32fd5bcab6ad688a4812cb0cc04efc75c7014e"
dependencies:
lodash.keys "^3.1.2"
shallowequal@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.0.2.tgz#1561dbdefb8c01408100319085764da3fcf83f8f"
......@@ -12555,6 +12668,12 @@ walker@~1.0.5:
dependencies:
makeerror "1.0.x"
warning@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.1.tgz#66ce376b7fbfe8a887c22bdf0e7349d73d397745"
dependencies:
loose-envify "^1.0.0"
watch@~0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986"
......
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