Merge pull request #262 from dod-ccpo/ui/cumulative-budget-chart
Ui/cumulative budget chart
This commit is contained in:
commit
776796beee
@ -196,3 +196,11 @@ class Reports:
|
||||
"projects": project_totals,
|
||||
"workspace": workspace_totals,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def cumulative_budget(cls, alternate):
|
||||
return {
|
||||
"months": CUMULATIVE_BUDGET_BELUGA
|
||||
if alternate
|
||||
else CUMULATIVE_BUDGET_AARDVARK
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ def workspace_reports(workspace_id):
|
||||
|
||||
return render_template(
|
||||
"workspaces/reports/index.html",
|
||||
cumulative_budget=Reports.cumulative_budget(alternate_reports),
|
||||
workspace_totals=Reports.workspace_totals(alternate_reports),
|
||||
monthly_totals=Reports.monthly_totals(alternate_reports),
|
||||
current_month=current_month,
|
||||
|
135
js/components/charts/budget_chart.js
Normal file
135
js/components/charts/budget_chart.js
Normal file
@ -0,0 +1,135 @@
|
||||
import { format } from 'date-fns'
|
||||
import { abbreviateDollars, formatDollars } from '../../lib/dollars'
|
||||
|
||||
const TOP_OFFSET = 20
|
||||
const BOTTOM_OFFSET = 60
|
||||
const CHART_HEIGHT = 360
|
||||
|
||||
export default {
|
||||
name: 'budget-chart',
|
||||
props: {
|
||||
currentMonth: String,
|
||||
months: Object,
|
||||
budget: String
|
||||
},
|
||||
|
||||
data: function () {
|
||||
const heightScale = this.budget / (CHART_HEIGHT - TOP_OFFSET - BOTTOM_OFFSET)
|
||||
return {
|
||||
numMonths: 10,
|
||||
focusedMonthPosition: 4,
|
||||
height: CHART_HEIGHT,
|
||||
heightScale,
|
||||
budgetHeight: CHART_HEIGHT - BOTTOM_OFFSET - (this.budget / heightScale),
|
||||
baseHeight: CHART_HEIGHT - BOTTOM_OFFSET,
|
||||
width: 0,
|
||||
displayedMonths: [],
|
||||
spendPath: '',
|
||||
projectedPath: '',
|
||||
displayBudget: formatDollars(parseFloat(this.budget))
|
||||
}
|
||||
},
|
||||
|
||||
mounted: function () {
|
||||
this._setDisplayedMonths()
|
||||
this._setMetrics()
|
||||
addEventListener('resize', this._setMetrics)
|
||||
},
|
||||
|
||||
methods: {
|
||||
_setMetrics: function () {
|
||||
this.width = this.$refs.panel.clientWidth
|
||||
this.spendPath = ''
|
||||
this.projectedPath = ''
|
||||
|
||||
let lastSpend = 0
|
||||
let lastSpendPoint = ''
|
||||
|
||||
for (let i = 0; i < this.numMonths; i++) {
|
||||
const { metrics, budget } = this.displayedMonths[i]
|
||||
const blockWidth = (this.width / this.numMonths)
|
||||
const blockX = blockWidth * i
|
||||
const spend = budget
|
||||
? budget.spend || lastSpend
|
||||
: 0
|
||||
const cumulative = budget
|
||||
? budget.cumulative || budget.projected
|
||||
: 0
|
||||
const barHeight = spend / this.heightScale
|
||||
lastSpend = spend
|
||||
const cumulativeY = this.height - (cumulative / this.heightScale) - BOTTOM_OFFSET
|
||||
const cumulativeX = blockX + blockWidth/2
|
||||
const cumulativePoint = `${cumulativeX} ${cumulativeY}`
|
||||
|
||||
this.displayedMonths[i].metrics = Object.assign(metrics, {
|
||||
blockWidth,
|
||||
blockX,
|
||||
barHeight,
|
||||
barWidth: 30,
|
||||
barX: blockX + (blockWidth/2 - 15),
|
||||
barY: this.height - barHeight - BOTTOM_OFFSET,
|
||||
cumulativeR: 2.5,
|
||||
cumulativeY,
|
||||
cumulativeX
|
||||
})
|
||||
|
||||
if (budget && budget.spend) {
|
||||
this.spendPath += this.spendPath === '' ? 'M' : ' L'
|
||||
this.spendPath += cumulativePoint
|
||||
lastSpendPoint = cumulativePoint
|
||||
}
|
||||
|
||||
if (budget && budget.projected) {
|
||||
this.projectedPath += this.projectedPath === '' ? `M${lastSpendPoint} L` : ' L'
|
||||
this.projectedPath += cumulativePoint
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_setDisplayedMonths: function () {
|
||||
const [month, year] = this.currentMonth.split('/')
|
||||
const monthsRange = []
|
||||
const monthsBack = this.focusedMonthPosition
|
||||
const monthsForward = this.numMonths - this.focusedMonthPosition - 1
|
||||
const start = new Date(year, month - 1 - monthsBack)
|
||||
|
||||
let previousAmount = 0
|
||||
|
||||
for (let i = 0; i < this.numMonths; i++) {
|
||||
const date = new Date(start.getFullYear(), start.getMonth() + i)
|
||||
const index = format(date, 'MM/YYYY')
|
||||
const budget = this.months[index] || null
|
||||
const spendAmount = budget ? budget.spend || previousAmount : 0
|
||||
const cumulativeAmount = budget ? budget.cumulative || budget.projected : 0
|
||||
previousAmount = spendAmount
|
||||
|
||||
monthsRange.push({
|
||||
budget,
|
||||
spendAmount: formatDollars(spendAmount),
|
||||
abbreviatedSpend: abbreviateDollars(spendAmount),
|
||||
cumulativeAmount: formatDollars(cumulativeAmount),
|
||||
abbreviatedCumulative: abbreviateDollars(cumulativeAmount),
|
||||
date: {
|
||||
monthIndex: format(date, 'M'),
|
||||
month: format(date, 'MMM'),
|
||||
year: format(date,'YYYY')
|
||||
},
|
||||
isHighlighted: this.currentMonth === index,
|
||||
metrics: {
|
||||
blockWidth: 0,
|
||||
blockX: 0,
|
||||
barHeight: 0,
|
||||
barWidth: 0,
|
||||
barX: 0,
|
||||
barY: 0,
|
||||
cumulativeY: 0,
|
||||
cumulativeX: 0,
|
||||
cumulativeR: 0
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
this.displayedMonths = monthsRange
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import 'svg-innerhtml'
|
||||
import 'babel-polyfill'
|
||||
|
||||
import classes from '../styles/atat.scss'
|
||||
@ -14,6 +15,7 @@ import toggler from './components/toggler'
|
||||
import NewProject from './components/forms/new_project'
|
||||
import Modal from './mixins/modal'
|
||||
import selector from './components/selector'
|
||||
import BudgetChart from './components/charts/budget_chart'
|
||||
|
||||
Vue.use(VTooltip)
|
||||
|
||||
@ -30,7 +32,8 @@ const app = new Vue({
|
||||
poc,
|
||||
financial,
|
||||
NewProject,
|
||||
selector
|
||||
selector,
|
||||
BudgetChart
|
||||
},
|
||||
mounted: function() {
|
||||
const modalOpen = document.querySelector("#modalOpen")
|
||||
|
12
js/lib/dollars.js
Normal file
12
js/lib/dollars.js
Normal file
@ -0,0 +1,12 @@
|
||||
export const formatDollars = value => `$${value.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,')}`
|
||||
|
||||
export const abbreviateDollars = (value, decimals = 1) => {
|
||||
if (value === null) { return null } // terminate early
|
||||
if (value === 0) { return '0' } // terminate early
|
||||
var b = (value).toPrecision(2).split("e"), // get power
|
||||
k = b.length === 1 ? 0 : Math.floor(Math.min(b[1].slice(1), 14) / 3), // floor at decimals, ceiling at trillions
|
||||
c = k < 1 ? value.toFixed(0 + decimals) : (value / Math.pow(10, k * 3) ).toFixed(decimals), // divide by power
|
||||
d = c < 0 ? c : Math.abs(c), // enforce -0 is 0
|
||||
e = d + ['', 'k', 'M', 'B', 'T'][k]; // append power
|
||||
return e;
|
||||
}
|
@ -13,8 +13,10 @@
|
||||
"dependencies": {
|
||||
"autoprefixer": "^9.1.3",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"date-fns": "^1.29.0",
|
||||
"npm": "^6.0.1",
|
||||
"parcel": "^1.9.7",
|
||||
"svg-innerhtml": "^1.1.0",
|
||||
"text-mask-addons": "^3.8.0",
|
||||
"uswds": "^1.6.3",
|
||||
"v-tooltip": "^2.0.0-rc.33",
|
||||
|
@ -34,7 +34,7 @@
|
||||
@import 'components/search_bar';
|
||||
@import 'components/forms';
|
||||
@import 'components/selector';
|
||||
|
||||
@import 'components/budget_chart';
|
||||
|
||||
@import 'sections/login';
|
||||
@import 'sections/request_approval';
|
||||
|
130
styles/components/_budget_chart.scss
Normal file
130
styles/components/_budget_chart.scss
Normal file
@ -0,0 +1,130 @@
|
||||
.budget-chart {
|
||||
.budget-chart__header {
|
||||
border-bottom: 1px solid $color-gray-light;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.budget-chart__legend {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
dl {
|
||||
margin: 0 0 0 ($gap * 2);
|
||||
|
||||
> div {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
|
||||
dt {
|
||||
@include small-label;
|
||||
}
|
||||
|
||||
.budget-chart__legend__dot {
|
||||
width: $gap;
|
||||
height: $gap;
|
||||
border-radius: $gap / 2;
|
||||
margin: 0 $gap;
|
||||
|
||||
&.accumulated {
|
||||
background-color: $color-gold;
|
||||
}
|
||||
&.monthly {
|
||||
background-color: $color-blue;
|
||||
}
|
||||
}
|
||||
|
||||
.budget-chart__legend__line {
|
||||
height: 2px;
|
||||
width: $gap * 3;
|
||||
border-top-width: 2px;
|
||||
border-top-style: dashed;
|
||||
margin: $gap;
|
||||
|
||||
&.spend {
|
||||
border-color: $color-blue;
|
||||
}
|
||||
&.accumulated {
|
||||
border-color: $color-gold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.budget-chart__block {
|
||||
fill: $color-white;
|
||||
cursor: pointer;
|
||||
|
||||
&--highlighted {
|
||||
fill: $color-aqua-lightest;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
fill: $color-aqua-lightest;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
&:focus {
|
||||
outline: none;
|
||||
stroke: $color-gray-light;
|
||||
stroke-dasharray: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.budget-chart__bar {
|
||||
fill: $color-blue;
|
||||
|
||||
&--projected {
|
||||
fill: transparent;
|
||||
stroke-width: 2px;
|
||||
stroke: $color-blue;
|
||||
stroke-dasharray: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.budget-chart__cumulative__dot {
|
||||
fill: $color-gold;
|
||||
}
|
||||
|
||||
.budget-chart__projected-path {
|
||||
stroke-width: 1px;
|
||||
stroke: $color-gold;
|
||||
stroke-dasharray: 4px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.budget-chart__spend-path {
|
||||
stroke-width: 1px;
|
||||
stroke: $color-gold;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.budget-chart__budget-line {
|
||||
stroke-width: 2px;
|
||||
stroke: $color-gray-light;
|
||||
stroke-dasharray: 4px;
|
||||
}
|
||||
|
||||
.budget-chart__label {
|
||||
@include small-label;
|
||||
fill: $color-gray;
|
||||
|
||||
&--strong {
|
||||
fill: $color-black;
|
||||
}
|
||||
}
|
||||
}
|
@ -42,5 +42,6 @@
|
||||
&.col--grow {
|
||||
flex: 1;
|
||||
flex-grow: 1;
|
||||
overflow: auto
|
||||
}
|
||||
}
|
||||
|
@ -62,13 +62,13 @@
|
||||
}
|
||||
|
||||
.panel__heading {
|
||||
margin: $gap * 2;
|
||||
padding: $gap * 2;
|
||||
@include media($medium-screen) {
|
||||
margin: $gap * 4;
|
||||
padding: $gap * 4;
|
||||
}
|
||||
|
||||
&--tight {
|
||||
margin: $gap*2;
|
||||
padding: $gap*2;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
|
@ -68,3 +68,9 @@ dl {
|
||||
margin-bottom: $gap * 2;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin small-label {
|
||||
font-size: $h6-font-size;
|
||||
font-weight: $font-bold;
|
||||
color: $color-black;
|
||||
}
|
||||
|
@ -134,16 +134,16 @@
|
||||
{% endcall %}
|
||||
|
||||
<div is='toggler' default-visible class='block-list project-list-item'>
|
||||
<template slot-scope='{ isVisible, toggle }'>
|
||||
<template slot-scope='props'>
|
||||
<header class='block-list__header'>
|
||||
<button v-on:click='toggle' class='icon-link icon-link--large icon-link--default spend-table__project__toggler'>
|
||||
<template v-if='isVisible'>{{ Icon('caret_down') }}</template>
|
||||
<button type='button' v-on:click='props.toggle' class='icon-link icon-link--large icon-link--default spend-table__project__toggler'>
|
||||
<template v-if='props.isVisible'>{{ Icon('caret_down') }}</template>
|
||||
<template v-else>{{ Icon('caret_right') }}</template>
|
||||
<h3 class="block-list__title">Code.mil</h3>
|
||||
</button>
|
||||
<span><a href="#" class="icon-link icon-link--danger">revoke all access</a></span>
|
||||
</header>
|
||||
<ul v-show='isVisible'>
|
||||
<ul v-show='props.isVisible'>
|
||||
<li class='block-list__item project-list-item__environment'>
|
||||
<span class='project-list-item__environment'>
|
||||
Development
|
||||
@ -173,16 +173,16 @@
|
||||
</div>
|
||||
|
||||
<div is="toggler" class='block-list project-list-item'>
|
||||
<template slot-scope='{ isVisible, toggle }'>
|
||||
<template slot-scope='props'>
|
||||
<header class='block-list__header'>
|
||||
<button v-on:click='toggle' class='icon-link icon-link--large icon-link--default spend-table__project__toggler'>
|
||||
<template v-if='isVisible'>{{ Icon('caret_down') }}</template>
|
||||
<button type='button' v-on:click='props.toggle' class='icon-link icon-link--large icon-link--default spend-table__project__toggler'>
|
||||
<template v-if='props.isVisible'>{{ Icon('caret_down') }}</template>
|
||||
<template v-else>{{ Icon('caret_right') }}</template>
|
||||
<h3 class="block-list__title">Digital Dojo</h3>
|
||||
</button>
|
||||
<span class="label">no access</span>
|
||||
</header>
|
||||
<ul v-show='isVisible'>
|
||||
<ul v-show='props.isVisible'>
|
||||
<li class='block-list__item project-list-item__environment'>
|
||||
<span class='project-list-item__environment'>
|
||||
Development
|
||||
|
@ -93,6 +93,133 @@
|
||||
{% set current_month_index = current_month.strftime('%m/%Y') %}
|
||||
{% set prev_month_index = prev_month.strftime('%m/%Y') %}
|
||||
{% set two_months_ago_index = two_months_ago.strftime('%m/%Y') %}
|
||||
{% set reports_url = url_for("workspaces.workspace_reports", workspace_id=workspace.id) %}
|
||||
|
||||
<budget-chart budget={{ budget }} current-month='{{ current_month_index }}' v-bind:months='{{ cumulative_budget.months | tojson }}' inline-template>
|
||||
<div class='budget-chart panel' ref='panel'>
|
||||
<header class='budget-chart__header panel__heading panel__heading--tight'>
|
||||
<h2 class='h3'>Cumulative Budget</h2>
|
||||
|
||||
<div class='budget-chart__legend'>
|
||||
<dl class='budget-chart__legend__spend'>
|
||||
<div>
|
||||
<dt>Monthly Spend</dt>
|
||||
<dd class='budget-chart__legend__dot monthly'><span class='usa-sr-only'>Monthly spend visual key</span></dd>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<dt>Accumulated Spend</dt>
|
||||
<dd class='budget-chart__legend__dot accumulated'><span class='usa-sr-only'>Accumulated spend visual key</span></dd>
|
||||
</div>
|
||||
</dl>
|
||||
<dl class='budget-chart__legend__projected'>
|
||||
<div>
|
||||
<dt>Projected</dt>
|
||||
<dd>
|
||||
<div class='budget-chart__legend__line spend'><span class='usa-sr-only'>Projected monthly spend visual key</span></div>
|
||||
<div class='budget-chart__legend__line accumulated'><span class='usa-sr-only'>Projected accumulated spend visual key</span></div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<svg v-bind:height='height' v-bind:width='width'>
|
||||
<g v-for='month in displayedMonths' >
|
||||
{# make this clickable to focus on that month #}
|
||||
<a v-bind:href='"{{ reports_url }}?month=" + month.date.monthIndex + "&year=" + month.date.year'>
|
||||
<title>
|
||||
<span v-html='month.date.month + " " + month.date.year'></span> | <!--
|
||||
--><template v-if='month.budget'><!--
|
||||
--><template v-if='month.budget.spend'>Spend:</template><!--
|
||||
--><template v-if='month.budget.projected'>Projected Spend:</template><!--
|
||||
--><span v-html='month.spendAmount'></span><!--
|
||||
--> | <!--
|
||||
--><template v-if='month.budget.cumulative'>Total:</template><!--
|
||||
--><template v-if='month.budget.projected'>Projected Total:</template><!--
|
||||
--><span v-html='month.cumulativeAmount'></span><!--
|
||||
--></template><!--
|
||||
|
||||
--><template v-else>No spend for this month</template>
|
||||
</title>
|
||||
|
||||
{# container block #}
|
||||
<rect
|
||||
class='budget-chart__block'
|
||||
v-bind:class='{ "budget-chart__block--highlighted": month.isHighlighted }'
|
||||
v-bind:width='month.metrics.blockWidth'
|
||||
v-bind:x='month.metrics.blockX'
|
||||
v-bind:height='height'></rect>
|
||||
|
||||
{# budget bar #}
|
||||
<rect
|
||||
v-if='month.budget'
|
||||
class='budget-chart__bar'
|
||||
v-bind:class='{ "budget-chart__bar--projected": month.budget.projected }'
|
||||
v-bind:width='month.metrics.barWidth'
|
||||
v-bind:height='month.metrics.barHeight'
|
||||
v-bind:x='month.metrics.barX'
|
||||
v-bind:y='month.metrics.barY'></rect>
|
||||
|
||||
{# cumulative dot #}
|
||||
<circle
|
||||
v-if='month.budget'
|
||||
class='budget-chart__cumulative__dot'
|
||||
v-bind:r='month.metrics.cumulativeR'
|
||||
v-bind:cx='month.metrics.cumulativeX'
|
||||
v-bind:cy='month.metrics.cumulativeY'></circle>
|
||||
|
||||
{# abbreviated cumulative label #}
|
||||
<text
|
||||
v-if='month.budget'
|
||||
v-bind:x='month.metrics.cumulativeX'
|
||||
v-bind:y='month.metrics.cumulativeY - 10'
|
||||
text-anchor='middle'
|
||||
class='budget-chart__label'
|
||||
v-html='month.abbreviatedCumulative'></text>
|
||||
|
||||
{# abbreviated spend label #}
|
||||
<text
|
||||
v-bind:x='month.metrics.cumulativeX'
|
||||
v-bind:y='baseHeight + 20'
|
||||
text-anchor='middle'
|
||||
class='budget-chart__label'
|
||||
v-html='"+" + month.abbreviatedSpend'></text>
|
||||
|
||||
{# month label #}
|
||||
<text
|
||||
v-bind:x='month.metrics.cumulativeX'
|
||||
v-bind:y='baseHeight + 40'
|
||||
text-anchor='middle'
|
||||
class='budget-chart__label budget-chart__label--strong'
|
||||
v-html='month.date.month'></text>
|
||||
</g>
|
||||
</a>
|
||||
|
||||
{# spend/projected budget path lines #}
|
||||
<path class='budget-chart__projected-path' v-bind:d='projectedPath'></path>
|
||||
<path class='budget-chart__spend-path' v-bind:d='spendPath'></path>
|
||||
|
||||
{# max budget line #}
|
||||
<line
|
||||
class='budget-chart__budget-line'
|
||||
x1='0'
|
||||
v-bind:x2='width'
|
||||
v-bind:y1='budgetHeight'
|
||||
v-bind:y2='budgetHeight'></line>
|
||||
|
||||
<text
|
||||
x='20'
|
||||
v-bind:y='budgetHeight + 20'
|
||||
class='budget-chart__label'>Total Budget</text>
|
||||
<text
|
||||
x='20'
|
||||
v-bind:y='budgetHeight + 40'
|
||||
class='budget-chart__label'
|
||||
v-html='displayBudget'></text>
|
||||
</svg>
|
||||
</div>
|
||||
</budget-chart>
|
||||
|
||||
<div class='spend-table responsive-table-wrapper'>
|
||||
<div class='spend-table__header'>
|
||||
@ -126,11 +253,11 @@
|
||||
|
||||
{% for project_name, project_totals in monthly_totals['projects'].items() %}
|
||||
<tbody is='toggler' class='spend-table__project'>
|
||||
<template slot-scope='{ isVisible, toggle }'>
|
||||
<template slot-scope='props'>
|
||||
<tr>
|
||||
<th scope='rowgroup'>
|
||||
<button v-on:click='toggle' class='icon-link icon-link--large spend-table__project__toggler'>
|
||||
<template v-if='isVisible'>{{ Icon('caret_down') }}</template>
|
||||
<button v-on:click='props.toggle' class='icon-link icon-link--large spend-table__project__toggler'>
|
||||
<template v-if='props.isVisible'>{{ Icon('caret_down') }}</template>
|
||||
<template v-else>{{ Icon('caret_right') }}</template>
|
||||
{{ project_name }}
|
||||
</button>
|
||||
@ -145,7 +272,7 @@
|
||||
</tr>
|
||||
|
||||
{% for env_name, env_totals in monthly_totals['environments'][project_name].items() %}
|
||||
<tr v-show='isVisible'>
|
||||
<tr v-show='props.isVisible'>
|
||||
<th scope='rowgroup'><a href='#' class='icon-link spend-table__project__env'>{{ Icon('link') }} {{ env_name }}</a></th>
|
||||
<td class='table-cell--align-right previous-month'>{{ env_totals.get(two_months_ago_index, 0) | dollars }}</td>
|
||||
<td class='table-cell--align-right previous-month'>{{ env_totals.get(prev_month_index, 0) | dollars }}</td>
|
||||
|
@ -1811,6 +1811,10 @@ dashdash@^1.12.0:
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
date-fns@^1.29.0:
|
||||
version "1.29.0"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6"
|
||||
|
||||
date-now@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
||||
@ -6271,6 +6275,10 @@ supports-color@^5.3.0, supports-color@^5.4.0:
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
svg-innerhtml@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/svg-innerhtml/-/svg-innerhtml-1.1.0.tgz#5e5d1efbc32596479e73a1e8e221d1222678b678"
|
||||
|
||||
svgo@^0.7.0:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
|
||||
|
Loading…
x
Reference in New Issue
Block a user