mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-09 23:24:05 -08:00
ui (histograms): Add native histogram chart to Table view (#13658)
* feat: initial add histogram table Signed-off-by: Manik Rana <manikrana54@gmail.com> * feat: add x-axis scale toggle Signed-off-by: Manik Rana <manikrana54@gmail.com> * refac: adjust histogram summary styles Signed-off-by: Manik Rana <manikrana54@gmail.com> * feat: initial add histogram table Signed-off-by: Manik Rana <manikrana54@gmail.com> * feat: add x-axis scale toggle Signed-off-by: Manik Rana <manikrana54@gmail.com> * refac: adjust histogram summary styles Signed-off-by: Manik Rana <manikrana54@gmail.com> * chore: remove unused code Signed-off-by: Manik Rana <manikrana54@gmail.com> * tests: test for HistogramChart instead of HistogramString Signed-off-by: Manik Rana <manikrana54@gmail.com> * fix: remove histogramChart from a test Signed-off-by: Manik Rana <manikrana54@gmail.com> * chore: rename style classes Signed-off-by: Manik Rana <manikrana54@gmail.com> * refac: suppress test errors Signed-off-by: Manik Rana <manikrana54@gmail.com> --------- Signed-off-by: Manik Rana <manikrana54@gmail.com>
This commit is contained in:
parent
a09465baee
commit
ab9b770aea
|
@ -71,7 +71,7 @@ describe('DataTable', () => {
|
|||
const table = dataTable.find(Table);
|
||||
table.find('tr').forEach((row, idx) => {
|
||||
expect(row.find(SeriesName)).toHaveLength(1);
|
||||
expect(row.find('td').at(1).text()).toEqual(`${idx} <HistogramString />`);
|
||||
expect(row.find('td').at(1).text()).toEqual(`${idx}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -129,42 +129,21 @@ describe('DataTable', () => {
|
|||
},
|
||||
useLocalTime: false,
|
||||
};
|
||||
const dataTable = mount(<DataTable {...dataTableProps} />);
|
||||
const dataTable = shallow(<DataTable {...dataTableProps} />);
|
||||
|
||||
it('renders a table', () => {
|
||||
const table = dataTable.find(Table);
|
||||
const table = dataTable.find(Table).first();
|
||||
expect(table.prop('hover')).toBe(true);
|
||||
expect(table.prop('size')).toEqual('sm');
|
||||
expect(table.prop('className')).toEqual('data-table');
|
||||
expect(table.find('tbody')).toHaveLength(1);
|
||||
expect(table.find('tbody')).toHaveLength(dataTableProps.data?.result.length as number);
|
||||
});
|
||||
|
||||
it('renders rows', () => {
|
||||
const table = dataTable.find(Table);
|
||||
const histogramData = [{
|
||||
count: '10',
|
||||
sum: '3.3',
|
||||
buckets: [
|
||||
[1, '-1', '-0.5', '2'],
|
||||
[3, '-0.5', '0.5', '3'],
|
||||
[0, '0.5', '1', '5'],
|
||||
]
|
||||
},
|
||||
{
|
||||
count: '5',
|
||||
sum: '1.11',
|
||||
buckets: [
|
||||
[0, '0.5', '1', '2'],
|
||||
[0, '1', '2', '3'],
|
||||
],
|
||||
}];
|
||||
table.find('tr').forEach((row, idx) => {
|
||||
const seriesNameComponent = dataTable.find('SeriesName');
|
||||
expect(seriesNameComponent).toHaveLength(dataTableProps.data?.result.length as number);
|
||||
|
||||
const histogramStringComponent = row.find('HistogramString');
|
||||
expect(histogramStringComponent).toHaveLength(1);
|
||||
expect(histogramStringComponent.prop('h')).toEqual(histogramData[idx]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import React, { FC, ReactNode } from 'react';
|
||||
|
||||
import { Alert, Table } from 'reactstrap';
|
||||
import { Alert, Button, ButtonGroup, Table } from 'reactstrap';
|
||||
|
||||
import SeriesName from './SeriesName';
|
||||
import { Metric, Histogram } from '../../types/types';
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
import HistogramChart from './HistogramChart';
|
||||
|
||||
export interface DataTableProps {
|
||||
data:
|
||||
| null
|
||||
|
@ -54,6 +56,8 @@ const limitSeries = <S extends InstantSample | RangeSamples>(series: S[]): S[] =
|
|||
};
|
||||
|
||||
const DataTable: FC<DataTableProps> = ({ data, useLocalTime }) => {
|
||||
const [scale, setScale] = React.useState<'linear' | 'exponential'>('exponential');
|
||||
|
||||
if (data === null) {
|
||||
return <Alert color="light">No data queried yet</Alert>;
|
||||
}
|
||||
|
@ -75,7 +79,42 @@ const DataTable: FC<DataTableProps> = ({ data, useLocalTime }) => {
|
|||
<SeriesName labels={s.metric} format={doFormat} />
|
||||
</td>
|
||||
<td>
|
||||
{s.value && s.value[1]} <HistogramString h={s.histogram && s.histogram[1]} />
|
||||
{s.value && s.value[1]}
|
||||
{s.histogram && (
|
||||
<>
|
||||
<HistogramChart histogram={s.histogram[1]} index={index} scale={scale} />
|
||||
<div className="histogram-summary-wrapper">
|
||||
<div className="histogram-summary">
|
||||
<span>
|
||||
<strong>Total count:</strong> {s.histogram[1].count}
|
||||
</span>
|
||||
<span>
|
||||
<strong>Sum:</strong> {s.histogram[1].sum}
|
||||
</span>
|
||||
</div>
|
||||
<div className="histogram-summary">
|
||||
<span>x-axis scale:</span>
|
||||
<ButtonGroup className="stacked-input" size="sm">
|
||||
<Button
|
||||
title="Show histogram on exponential scale"
|
||||
onClick={() => setScale('exponential')}
|
||||
active={scale === 'exponential'}
|
||||
>
|
||||
Exponential
|
||||
</Button>
|
||||
<Button
|
||||
title="Show histogram on linear scale"
|
||||
onClick={() => setScale('linear')}
|
||||
active={scale === 'linear'}
|
||||
>
|
||||
Linear
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
{histogramTable(s.histogram[1])}
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
@ -100,7 +139,7 @@ const DataTable: FC<DataTableProps> = ({ data, useLocalTime }) => {
|
|||
const printedDatetime = moment.unix(h[0]).toISOString(useLocalTime);
|
||||
return (
|
||||
<React.Fragment key={-hisIdx}>
|
||||
<HistogramString h={h[1]} /> @{<span title={printedDatetime}>{h[0]}</span>}
|
||||
{histogramTable(h[1])} @{<span title={printedDatetime}>{h[0]}</span>}
|
||||
<br />
|
||||
</React.Fragment>
|
||||
);
|
||||
|
@ -159,29 +198,39 @@ const DataTable: FC<DataTableProps> = ({ data, useLocalTime }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export interface HistogramStringProps {
|
||||
h?: Histogram;
|
||||
}
|
||||
const leftDelim = (br: number): string => (br === 3 || br === 1 ? '[' : '(');
|
||||
const rightDelim = (br: number): string => (br === 3 || br === 0 ? ']' : ')');
|
||||
|
||||
export const HistogramString: FC<HistogramStringProps> = ({ h }) => {
|
||||
if (!h) {
|
||||
return <></>;
|
||||
}
|
||||
const buckets: string[] = [];
|
||||
|
||||
if (h.buckets) {
|
||||
for (const bucket of h.buckets) {
|
||||
const left = bucket[0] === 3 || bucket[0] === 1 ? '[' : '(';
|
||||
const right = bucket[0] === 3 || bucket[0] === 0 ? ']' : ')';
|
||||
buckets.push(left + bucket[1] + ',' + bucket[2] + right + ':' + bucket[3] + ' ');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{'{'} count:{h.count} sum:{h.sum} {buckets} {'}'}
|
||||
</>
|
||||
);
|
||||
export const bucketRangeString = ([boundaryRule, leftBoundary, rightBoundary, _]: [
|
||||
number,
|
||||
string,
|
||||
string,
|
||||
string
|
||||
]): string => {
|
||||
return `${leftDelim(boundaryRule)}${leftBoundary} -> ${rightBoundary}${rightDelim(boundaryRule)}`;
|
||||
};
|
||||
|
||||
export const histogramTable = (h: Histogram): ReactNode => (
|
||||
<Table size="xs" responsive bordered>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ textAlign: 'center' }} colSpan={2}>
|
||||
Histogram Sample
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Range</th>
|
||||
<th>Count</th>
|
||||
</tr>
|
||||
{h.buckets?.map((b, i) => (
|
||||
<tr key={i}>
|
||||
<td>{bucketRangeString(b)}</td>
|
||||
<td>{b[3]}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
);
|
||||
export default DataTable;
|
||||
|
|
92
web/ui/react-app/src/pages/graph/HistogramChart.tsx
Normal file
92
web/ui/react-app/src/pages/graph/HistogramChart.tsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import React, { FC } from 'react';
|
||||
import { UncontrolledTooltip } from 'reactstrap';
|
||||
import { Histogram } from '../../types/types';
|
||||
import { bucketRangeString } from './DataTable';
|
||||
|
||||
type ScaleType = 'linear' | 'exponential';
|
||||
|
||||
const HistogramChart: FC<{ histogram: Histogram; index: number; scale: ScaleType }> = ({ index, histogram, scale }) => {
|
||||
const { buckets } = histogram;
|
||||
const rangeMax = buckets ? parseFloat(buckets[buckets.length - 1][2]) : 0;
|
||||
const countMax = buckets ? buckets.map((b) => parseFloat(b[3])).reduce((a, b) => Math.max(a, b)) : 0;
|
||||
const formatter = Intl.NumberFormat('en', { notation: 'compact' });
|
||||
const positiveBuckets = buckets?.filter((b) => parseFloat(b[1]) >= 0); // we only want to show buckets with range >= 0
|
||||
const xLabelTicks = scale === 'linear' ? [0.25, 0.5, 0.75, 1] : [1];
|
||||
return (
|
||||
<div className="histogram-y-wrapper">
|
||||
<div className="histogram-y-labels">
|
||||
{[1, 0.75, 0.5, 0.25].map((i) => (
|
||||
<div key={i} className="histogram-y-label">
|
||||
{formatter.format(countMax * i)}
|
||||
</div>
|
||||
))}
|
||||
<div key={0} className="histogram-y-label" style={{ height: 0 }}>
|
||||
0
|
||||
</div>
|
||||
</div>
|
||||
<div className="histogram-x-wrapper">
|
||||
<div className="histogram-container">
|
||||
{[0, 0.25, 0.5, 0.75, 1].map((i) => (
|
||||
<React.Fragment key={i}>
|
||||
<div className="histogram-y-grid" style={{ bottom: i * 100 + '%' }}></div>
|
||||
<div className="histogram-y-tick" style={{ bottom: i * 100 + '%' }}></div>
|
||||
<div className="histogram-x-grid" style={{ left: i * 100 + '%' }}></div>
|
||||
<div className="histogram-x-tick" style={{ left: i * 100 + '%' }}></div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
{positiveBuckets?.map((b, bIdx) => {
|
||||
const bucketIdx = `bucket-${index}-${bIdx}-${Math.ceil(parseFloat(b[3]) * 100)}`;
|
||||
const bucketLeft =
|
||||
scale === 'linear' ? (parseFloat(b[1]) / rangeMax) * 100 + '%' : (bIdx / positiveBuckets.length) * 100 + '%';
|
||||
const bucketWidth =
|
||||
scale === 'linear'
|
||||
? ((parseFloat(b[2]) - parseFloat(b[1])) / rangeMax) * 100 + '%'
|
||||
: 100 / positiveBuckets.length + '%';
|
||||
return (
|
||||
<React.Fragment key={bIdx}>
|
||||
<div
|
||||
id={bucketIdx}
|
||||
className="histogram-bucket-slot"
|
||||
style={{
|
||||
left: bucketLeft,
|
||||
width: bucketWidth,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
id={bucketIdx}
|
||||
className="histogram-bucket"
|
||||
style={{
|
||||
height: (parseFloat(b[3]) / countMax) * 100 + '%',
|
||||
}}
|
||||
></div>
|
||||
<UncontrolledTooltip
|
||||
style={{ maxWidth: 'unset', padding: 10, textAlign: 'left' }}
|
||||
placement="bottom"
|
||||
target={bucketIdx}
|
||||
>
|
||||
<strong>range:</strong> {bucketRangeString(b)}
|
||||
<br />
|
||||
<strong>count:</strong> {b[3]}
|
||||
</UncontrolledTooltip>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
<div className="histogram-axes"></div>
|
||||
</div>
|
||||
<div className="histogram-x-labels">
|
||||
<div key={0} className="histogram-x-label" style={{ width: 0 }}>
|
||||
0
|
||||
</div>
|
||||
{xLabelTicks.map((i) => (
|
||||
<div key={i} className="histogram-x-label">
|
||||
<div style={{ position: 'absolute', right: i === 1 ? 0 : -18 }}>{formatter.format(rangeMax * i)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HistogramChart;
|
|
@ -111,7 +111,7 @@ button.execute-btn {
|
|||
}
|
||||
|
||||
.navbar-brand svg.animate path {
|
||||
animation: flamecolor 4s ease-in 1 forwards,flame 1s ease-in infinite;
|
||||
animation: flamecolor 4s ease-in 1 forwards, flame 1s ease-in infinite;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
|
@ -122,6 +122,126 @@ input[type='checkbox']:checked + label {
|
|||
color: $checked-checkbox-color;
|
||||
}
|
||||
|
||||
.histogram-summary-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.histogram-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.histogram-y-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: flex-start;
|
||||
box-sizing: border-box;
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.histogram-y-labels {
|
||||
height: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.histogram-y-label {
|
||||
margin-right: 8px;
|
||||
height: 25%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.histogram-x-wrapper {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.histogram-x-labels {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.histogram-x-label {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.histogram-container {
|
||||
margin-top: 9px;
|
||||
position: relative;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.histogram-axes {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-bottom: 1px solid $histogram-chart-axis-color;
|
||||
border-left: 1px solid $histogram-chart-axis-color;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.histogram-y-grid {
|
||||
position: absolute;
|
||||
border-bottom: 1px dashed $histogram-chart-grid-color;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.histogram-y-tick {
|
||||
position: absolute;
|
||||
border-bottom: 1px solid $histogram-chart-axis-color;
|
||||
left: -5px;
|
||||
height: 0px;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.histogram-x-grid {
|
||||
position: absolute;
|
||||
border-left: 1px dashed $histogram-chart-grid-color;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.histogram-x-tick {
|
||||
position: absolute;
|
||||
border-left: 1px solid $histogram-chart-axis-color;
|
||||
height: 5px;
|
||||
width: 0;
|
||||
bottom: -5px;
|
||||
}
|
||||
|
||||
.histogram-bucket-slot {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.histogram-bucket {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
background-color: #2db453;
|
||||
border: 1px solid #77de94;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.histogram-bucket-slot:hover {
|
||||
background-color: $histogram-chart-hover-color;
|
||||
}
|
||||
|
||||
.histogram-bucket-slot:hover .histogram-bucket {
|
||||
background-color: #88e1a1;
|
||||
border: 1px solid #77de94;
|
||||
}
|
||||
|
||||
.custom-control-label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -151,7 +271,7 @@ input[type='checkbox']:checked + label {
|
|||
|
||||
.alert {
|
||||
padding: 10px;
|
||||
margin-bottom: .2rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
|
@ -364,14 +484,24 @@ input[type='checkbox']:checked + label {
|
|||
}
|
||||
|
||||
@keyframes flamecolor {
|
||||
100% {
|
||||
fill: #e95224;
|
||||
}
|
||||
100% {
|
||||
fill: #e95224;
|
||||
}
|
||||
}
|
||||
@keyframes flame {
|
||||
0% { d: path("M 56.667,0.667 C 25.372,0.667 0,26.036 0,57.332 c 0,31.295 25.372,56.666 56.667,56.666 31.295,0 56.666,-25.371 56.666,-56.666 0,-31.296 -25.372,-56.665 -56.666,-56.665 z m 0,106.055 c -8.904,0 -16.123,-5.948 -16.123,-13.283 H 72.79 c 0,7.334 -7.219,13.283 -16.123,13.283 z M 83.297,89.04 H 30.034 V 79.382 H 83.298 V 89.04 Z M 83.106,74.411 H 30.186 C 30.01,74.208 29.83,74.008 29.66,73.802 24.208,67.182 22.924,63.726 21.677,60.204 c -0.021,-0.116 6.611,1.355 11.314,2.413 0,0 2.42,0.56 5.958,1.205 -3.397,-3.982 -5.414,-9.044 -5.414,-14.218 0,-11.359 8.712,-21.285 5.569,-29.308 3.059,0.249 6.331,6.456 6.552,16.161 3.252,-4.494 4.613,-12.701 4.613,-17.733 0,-5.21 3.433,-11.262 6.867,-11.469 -3.061,5.045 0.793,9.37 4.219,20.099 1.285,4.03 1.121,10.812 2.113,15.113 C 63.797,33.534 65.333,20.5 71,16 c -2.5,5.667 0.37,12.758 2.333,16.167 3.167,5.5 5.087,9.667 5.087,17.548 0,5.284 -1.951,10.259 -5.242,14.148 3.742,-0.702 6.326,-1.335 6.326,-1.335 l 12.152,-2.371 c 10e-4,-10e-4 -1.765,7.261 -8.55,14.254 z");
|
||||
}
|
||||
50% { d: path("M 56.667,0.667 C 25.372,0.667 0,26.036 0,57.332 c 0,31.295 25.372,56.666 56.667,56.666 31.295,0 56.666,-25.371 56.666,-56.666 0,-31.296 -25.372,-56.665 -56.666,-56.665 z m 0,106.055 c -8.904,0 -16.123,-5.948 -16.123,-13.283 H 72.79 c 0,7.334 -7.219,13.283 -16.123,13.283 z M 83.297,89.04 H 30.034 V 79.382 H 83.298 V 89.04 Z M 83.106,74.411 H 30.186 C 30.01,74.208 29.83,74.008 29.66,73.802 24.208,67.182 22.924,63.726 21.677,60.204 c -0.021,-0.116 6.611,1.355 11.314,2.413 0,0 2.42,0.56 5.958,1.205 -3.397,-3.982 -5.414,-9.044 -5.414,-14.218 0,-11.359 1.640181,-23.047128 7.294982,-29.291475 C 39.391377,29.509803 45.435,26.752 45.656,36.457 c 3.252,-4.494 7.100362,-8.366957 7.100362,-13.398957 0,-5.21 0.137393,-8.650513 -3.479689,-15.0672265 7.834063,1.6180944 8.448052,4.2381285 11.874052,14.9671285 1.285,4.03 1.325275,15.208055 2.317275,19.509055 0.329,-8.933 6.441001,-14.01461 5.163951,-21.391003 5.755224,5.771457 4.934508,10.495521 7.126537,14.288218 3.167,5.5 2.382625,7.496239 2.382625,15.377239 0,5.284 -1.672113,9.232546 -4.963113,13.121546 3.742,-0.702 6.326,-1.335 6.326,-1.335 l 12.152,-2.371 c 10e-4,-10e-4 -1.765,7.261 -8.55,14.254 z"); }
|
||||
100% {
|
||||
d: path("M 56.667,0.667 C 25.372,0.667 0,26.036 0,57.332 c 0,31.295 25.372,56.666 56.667,56.666 31.295,0 56.666,-25.371 56.666,-56.666 0,-31.296 -25.372,-56.665 -56.666,-56.665 z m 0,106.055 c -8.904,0 -16.123,-5.948 -16.123,-13.283 H 72.79 c 0,7.334 -7.219,13.283 -16.123,13.283 z M 83.297,89.04 H 30.034 V 79.382 H 83.298 V 89.04 Z M 83.106,74.411 H 30.186 C 30.01,74.208 29.83,74.008 29.66,73.802 24.208,67.182 22.924,63.726 21.677,60.204 c -0.021,-0.116 6.611,1.355 11.314,2.413 0,0 2.42,0.56 5.958,1.205 -3.397,-3.982 -5.414,-9.044 -5.414,-14.218 0,-11.359 8.712,-21.285 5.569,-29.308 3.059,0.249 6.331,6.456 6.552,16.161 3.252,-4.494 4.613,-12.701 4.613,-17.733 0,-5.21 3.433,-11.262 6.867,-11.469 -3.061,5.045 0.793,9.37 4.219,20.099 1.285,4.03 1.121,10.812 2.113,15.113 C 63.797,33.534 65.333,20.5 71,16 c -2.5,5.667 0.37,12.758 2.333,16.167 3.167,5.5 5.087,9.667 5.087,17.548 0,5.284 -1.951,10.259 -5.242,14.148 3.742,-0.702 6.326,-1.335 6.326,-1.335 l 12.152,-2.371 c 10e-4,-10e-4 -1.765,7.261 -8.55,14.254 z"); }
|
||||
0% {
|
||||
d: path(
|
||||
'M 56.667,0.667 C 25.372,0.667 0,26.036 0,57.332 c 0,31.295 25.372,56.666 56.667,56.666 31.295,0 56.666,-25.371 56.666,-56.666 0,-31.296 -25.372,-56.665 -56.666,-56.665 z m 0,106.055 c -8.904,0 -16.123,-5.948 -16.123,-13.283 H 72.79 c 0,7.334 -7.219,13.283 -16.123,13.283 z M 83.297,89.04 H 30.034 V 79.382 H 83.298 V 89.04 Z M 83.106,74.411 H 30.186 C 30.01,74.208 29.83,74.008 29.66,73.802 24.208,67.182 22.924,63.726 21.677,60.204 c -0.021,-0.116 6.611,1.355 11.314,2.413 0,0 2.42,0.56 5.958,1.205 -3.397,-3.982 -5.414,-9.044 -5.414,-14.218 0,-11.359 8.712,-21.285 5.569,-29.308 3.059,0.249 6.331,6.456 6.552,16.161 3.252,-4.494 4.613,-12.701 4.613,-17.733 0,-5.21 3.433,-11.262 6.867,-11.469 -3.061,5.045 0.793,9.37 4.219,20.099 1.285,4.03 1.121,10.812 2.113,15.113 C 63.797,33.534 65.333,20.5 71,16 c -2.5,5.667 0.37,12.758 2.333,16.167 3.167,5.5 5.087,9.667 5.087,17.548 0,5.284 -1.951,10.259 -5.242,14.148 3.742,-0.702 6.326,-1.335 6.326,-1.335 l 12.152,-2.371 c 10e-4,-10e-4 -1.765,7.261 -8.55,14.254 z'
|
||||
);
|
||||
}
|
||||
50% {
|
||||
d: path(
|
||||
'M 56.667,0.667 C 25.372,0.667 0,26.036 0,57.332 c 0,31.295 25.372,56.666 56.667,56.666 31.295,0 56.666,-25.371 56.666,-56.666 0,-31.296 -25.372,-56.665 -56.666,-56.665 z m 0,106.055 c -8.904,0 -16.123,-5.948 -16.123,-13.283 H 72.79 c 0,7.334 -7.219,13.283 -16.123,13.283 z M 83.297,89.04 H 30.034 V 79.382 H 83.298 V 89.04 Z M 83.106,74.411 H 30.186 C 30.01,74.208 29.83,74.008 29.66,73.802 24.208,67.182 22.924,63.726 21.677,60.204 c -0.021,-0.116 6.611,1.355 11.314,2.413 0,0 2.42,0.56 5.958,1.205 -3.397,-3.982 -5.414,-9.044 -5.414,-14.218 0,-11.359 1.640181,-23.047128 7.294982,-29.291475 C 39.391377,29.509803 45.435,26.752 45.656,36.457 c 3.252,-4.494 7.100362,-8.366957 7.100362,-13.398957 0,-5.21 0.137393,-8.650513 -3.479689,-15.0672265 7.834063,1.6180944 8.448052,4.2381285 11.874052,14.9671285 1.285,4.03 1.325275,15.208055 2.317275,19.509055 0.329,-8.933 6.441001,-14.01461 5.163951,-21.391003 5.755224,5.771457 4.934508,10.495521 7.126537,14.288218 3.167,5.5 2.382625,7.496239 2.382625,15.377239 0,5.284 -1.672113,9.232546 -4.963113,13.121546 3.742,-0.702 6.326,-1.335 6.326,-1.335 l 12.152,-2.371 c 10e-4,-10e-4 -1.765,7.261 -8.55,14.254 z'
|
||||
);
|
||||
}
|
||||
100% {
|
||||
d: path(
|
||||
'M 56.667,0.667 C 25.372,0.667 0,26.036 0,57.332 c 0,31.295 25.372,56.666 56.667,56.666 31.295,0 56.666,-25.371 56.666,-56.666 0,-31.296 -25.372,-56.665 -56.666,-56.665 z m 0,106.055 c -8.904,0 -16.123,-5.948 -16.123,-13.283 H 72.79 c 0,7.334 -7.219,13.283 -16.123,13.283 z M 83.297,89.04 H 30.034 V 79.382 H 83.298 V 89.04 Z M 83.106,74.411 H 30.186 C 30.01,74.208 29.83,74.008 29.66,73.802 24.208,67.182 22.924,63.726 21.677,60.204 c -0.021,-0.116 6.611,1.355 11.314,2.413 0,0 2.42,0.56 5.958,1.205 -3.397,-3.982 -5.414,-9.044 -5.414,-14.218 0,-11.359 8.712,-21.285 5.569,-29.308 3.059,0.249 6.331,6.456 6.552,16.161 3.252,-4.494 4.613,-12.701 4.613,-17.733 0,-5.21 3.433,-11.262 6.867,-11.469 -3.061,5.045 0.793,9.37 4.219,20.099 1.285,4.03 1.121,10.812 2.113,15.113 C 63.797,33.534 65.333,20.5 71,16 c -2.5,5.667 0.37,12.758 2.333,16.167 3.167,5.5 5.087,9.667 5.087,17.548 0,5.284 -1.951,10.259 -5.242,14.148 3.742,-0.702 6.326,-1.335 6.326,-1.335 l 12.152,-2.371 c 10e-4,-10e-4 -1.765,7.261 -8.55,14.254 z'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,10 @@ $clear-time-btn-bg: $secondary;
|
|||
|
||||
$checked-checkbox-color: #60a5fa;
|
||||
|
||||
$histogram-chart-axis-color: $gray-700;
|
||||
$histogram-chart-grid-color: $gray-600;
|
||||
$histogram-chart-hover-color: $gray-400;
|
||||
|
||||
.bootstrap-dark {
|
||||
@import './shared';
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ $clear-time-btn-bg: $white;
|
|||
|
||||
$checked-checkbox-color: #286090;
|
||||
|
||||
$histogram-chart-axis-color: $gray-700;
|
||||
$histogram-chart-grid-color: $gray-600;
|
||||
$histogram-chart-hover-color: $gray-400;
|
||||
|
||||
.bootstrap {
|
||||
@import './shared';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue