- {{ $tc('User Playlists.AddVideoPrompt.Select a playlist to add your N videos to', toBeAddedToPlaylistVideoCount, {
- videoCount: toBeAddedToPlaylistVideoCount,
- }) }}
+ {{ title }}
{{ $tc('User Playlists.AddVideoPrompt.N playlists selected', selectedPlaylistCount, {
diff --git a/src/renderer/components/ft-prompt/ft-prompt.css b/src/renderer/components/ft-prompt/ft-prompt.css
index 06312c5a864ac..2e303bca90ed8 100644
--- a/src/renderer/components/ft-prompt/ft-prompt.css
+++ b/src/renderer/components/ft-prompt/ft-prompt.css
@@ -7,7 +7,7 @@
background-color: rgb(0 0 0 / 70%);
/* Higher than components like playlist info */
- z-index: 200;
+ z-index: 201;
padding: 15px;
box-sizing: border-box;
display: flex;
@@ -17,6 +17,7 @@
.promptCard {
overflow-y: scroll;
+ max-block-size: 95%;
}
.promptCard.autosize {
@@ -34,6 +35,31 @@
box-sizing: border-box;
}
+.promptCard.flex-column {
+ /* Some child(ren) will grow vertically */
+ display: flex;
+ flex-direction: column;
+}
+
+.promptCard.slim {
+ max-inline-size: 70%;
+ inset-inline-start: 15%;
+ padding-block: 10px;
+}
+
.center {
text-align: center;
}
+
+@media only screen and (width <= 680px) {
+ .promptCard.slim {
+ padding-block: 5px;
+ }
+}
+
+@media only screen and (width <= 500px) {
+ .promptCard.slim {
+ max-inline-size: 80%;
+ inset-inline-start: 10%;
+ }
+}
diff --git a/src/renderer/components/ft-prompt/ft-prompt.js b/src/renderer/components/ft-prompt/ft-prompt.js
index 5ecdd208e6772..c28bb3d4cda87 100644
--- a/src/renderer/components/ft-prompt/ft-prompt.js
+++ b/src/renderer/components/ft-prompt/ft-prompt.js
@@ -1,4 +1,4 @@
-import { defineComponent } from 'vue'
+import { defineComponent, nextTick } from 'vue'
import { mapActions } from 'vuex'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
@@ -15,7 +15,7 @@ export default defineComponent({
props: {
label: {
type: String,
- default: ''
+ required: true
},
extraLabels: {
type: Array,
@@ -36,6 +36,10 @@ export default defineComponent({
autosize: {
type: Boolean,
default: false
+ },
+ theme: {
+ type: String,
+ default: 'base'
}
},
emits: ['click'],
@@ -50,21 +54,22 @@ export default defineComponent({
return sanitizeForHtmlId(this.label)
}
},
- beforeDestroy: function () {
- document.removeEventListener('keydown', this.closeEventFunction, true)
- this.lastActiveElement?.focus()
- },
mounted: function () {
this.lastActiveElement = document.activeElement
-
- document.addEventListener('keydown', this.closeEventFunction, true)
- document.querySelector('.prompt').addEventListener('keydown', this.arrowKeys, true)
- this.promptButtons = Array.from(
- document.querySelector('.prompt .promptCard .ft-flex-box').childNodes
- ).filter((e) => {
- return e.id && e.id.startsWith('prompt')
+ this.$nextTick(() => {
+ document.addEventListener('keydown', this.closeEventFunction, true)
+ document.querySelector('.prompt').addEventListener('keydown', this.arrowKeys, true)
+ this.promptButtons = Array.from(
+ document.querySelector('.prompt .promptCard .ft-flex-box').childNodes
+ ).filter((e) => {
+ return e.id && e.id.startsWith('prompt')
+ })
+ this.focusItem(0)
})
- this.focusItem(0)
+ },
+ beforeDestroy: function () {
+ document.removeEventListener('keydown', this.closeEventFunction, true)
+ nextTick(() => this.lastActiveElement?.focus())
},
methods: {
click: function (value) {
diff --git a/src/renderer/components/ft-prompt/ft-prompt.vue b/src/renderer/components/ft-prompt/ft-prompt.vue
index d0cd00b451359..9ea9e47412fa7 100644
--- a/src/renderer/components/ft-prompt/ft-prompt.vue
+++ b/src/renderer/components/ft-prompt/ft-prompt.vue
@@ -1,52 +1,57 @@
-
-
+
-
-
- {{ label }}
-
-
-
- {{ extraLabel }}
-
-
-
-
-
-
-
-
-
+
+
+
+ {{ label }}
+
+
+
+ {{ extraLabel }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/components/ft-radio-button/ft-radio-button.css b/src/renderer/components/ft-radio-button/ft-radio-button.css
index 7a5e14ad680b8..a6777d511173e 100644
--- a/src/renderer/components/ft-radio-button/ft-radio-button.css
+++ b/src/renderer/components/ft-radio-button/ft-radio-button.css
@@ -11,7 +11,7 @@ pure-checkbox input[type="checkbox"], .pure-radiobutton input[type="checkbox"],
}
.pure-checkbox input[type="checkbox"]:focus + label::before, .pure-radiobutton input[type="checkbox"]:focus + label::before, .pure-checkbox input[type="radio"]:focus + label::before, .pure-radiobutton input[type="radio"]:focus + label::before, .pure-checkbox input[type="checkbox"]:hover + label::before, .pure-radiobutton input[type="checkbox"]:hover + label::before, .pure-checkbox input[type="radio"]:hover + label::before, .pure-radiobutton input[type="radio"]:hover + label::before {
- border-color: var(--primary-color);
+ border: 2px solid var(--primary-color);
}
.pure-checkbox input[type="checkbox"]:active + label::before, .pure-radiobutton input[type="checkbox"]:active + label::before, .pure-checkbox input[type="radio"]:active + label::before, .pure-radiobutton input[type="radio"]:active + label::before { transition-duration: 0s; }
@@ -22,20 +22,20 @@ pure-checkbox input[type="checkbox"], .pure-radiobutton input[type="checkbox"],
user-select: none;
cursor: pointer;
display: block;
- margin-block-end: -20px;
+ margin-block-start: 10px;
}
.pure-checkbox input[type="checkbox"] + label::before, .pure-radiobutton input[type="checkbox"] + label::before, .pure-checkbox input[type="radio"] + label::before, .pure-radiobutton input[type="radio"] + label::before {
box-sizing: content-box;
content: '';
- color: var(--primary-color);
+ color: var(--primary-text-color);
position: absolute;
inset-block-start: 50%;
inset-inline-start: 0;
inline-size: 14px;
block-size: 14px;
margin-block-start: -9px;
- border: 2px solid var(--primary-color);
+ border: 2px solid var(--primary-text-color);
text-align: center;
transition: all 0.4s ease;
}
@@ -43,7 +43,7 @@ pure-checkbox input[type="checkbox"], .pure-radiobutton input[type="checkbox"],
.pure-checkbox input[type="checkbox"] + label::after, .pure-radiobutton input[type="checkbox"] + label::after, .pure-checkbox input[type="radio"] + label::after, .pure-radiobutton input[type="radio"] + label::after {
box-sizing: content-box;
content: '';
- background-color: var(--primary-color);
+ background-color: var(--primary-text-color);
position: absolute;
inset-block-start: 50%;
inset-inline-start: 4px;
@@ -84,23 +84,33 @@ pure-checkbox input[type="checkbox"], .pure-radiobutton input[type="checkbox"],
animation: borderscale 300ms ease-in;
}
+.pure-radiobutton input[type="radio"]:focus + label::after{
+ background-color: var(--primary-color);
+}
+
.pure-checkbox input[type="radio"]:checked + label::after, .pure-radiobutton input[type="radio"]:checked + label::after { transform: scale(1); }
.pure-checkbox input[type="radio"] + label::before, .pure-radiobutton input[type="radio"] + label::before, .pure-checkbox input[type="radio"] + label::after, .pure-radiobutton input[type="radio"] + label::after { border-radius: 50%; }
.pure-checkbox input[type="checkbox"]:checked + label::before, .pure-radiobutton input[type="checkbox"]:checked + label::before {
animation: borderscale 200ms ease-in;
- background: var(--primary-color);
+ background: var(--primary-text-color);
}
.pure-radiobutton input[type="checkbox"]:checked + label::after { transform: rotate(-45deg) scale(1); }
@keyframes
borderscale { 50% {
- box-shadow: 0 0 0 2px var(--primary-color);
+ box-shadow: 0 0 0 2px var(--primary-text-color);
}
}
.radioTitle {
- margin-block-end: -20px;
+ margin-block: 0;
+}
+
+@media only screen and (width <= 680px) {
+ .pure-checkbox input[type="checkbox"] + label, .pure-radiobutton input[type="checkbox"] + label, .pure-checkbox input[type="radio"] + label, .pure-radiobutton input[type="radio"] + label {
+ margin-block-start: 3px;
+ }
}
diff --git a/src/renderer/components/ft-radio-button/ft-radio-button.js b/src/renderer/components/ft-radio-button/ft-radio-button.js
index 44626da1d7e54..3aa5922e69590 100644
--- a/src/renderer/components/ft-radio-button/ft-radio-button.js
+++ b/src/renderer/components/ft-radio-button/ft-radio-button.js
@@ -18,6 +18,10 @@ export default defineComponent({
disabled: {
type: Boolean,
default: false
+ },
+ initialValueIndex: {
+ type: Number,
+ default: 0
}
},
emits: ['change'],
@@ -35,7 +39,7 @@ export default defineComponent({
},
mounted: function () {
this.id = this._uid
- this.selectedValue = this.values[0]
+ this.selectedValue = this.values[this.initialValueIndex]
},
methods: {
updateSelectedValue: function (value) {
diff --git a/src/renderer/components/ft-radio-button/ft-radio-button.vue b/src/renderer/components/ft-radio-button/ft-radio-button.vue
index ef6f1c2791e67..e7974f9f94e11 100644
--- a/src/renderer/components/ft-radio-button/ft-radio-button.vue
+++ b/src/renderer/components/ft-radio-button/ft-radio-button.vue
@@ -14,7 +14,7 @@
v-model="selectedValue"
:name="inputName"
:value="values[index]"
- :checked="index === 0"
+ :checked="index === initialValueIndex"
:disabled="disabled"
class="radio"
type="radio"
diff --git a/src/renderer/components/ft-search-filters/ft-search-filters.css b/src/renderer/components/ft-search-filters/ft-search-filters.css
index 33ba9f6448f50..071a9b42c3f55 100644
--- a/src/renderer/components/ft-search-filters/ft-search-filters.css
+++ b/src/renderer/components/ft-search-filters/ft-search-filters.css
@@ -1,17 +1,7 @@
-.searchFilterInner {
- max-inline-size: 800px;
- margin-inline: auto;
- padding-block: 20px 70px;
- padding-inline: 20px;
- max-block-size: 410px;
- overflow-y: auto;
- background-color: var(--card-bg-color);
- box-shadow: 0 1px 2px rgb(0 0 0 / 10%);
- opacity: 0.9;
-}
-
.center {
+ margin-block: 5px 10px;
text-align: center;
+ user-select: none;
}
.searchRadio {
@@ -19,13 +9,22 @@
}
.radioFlexBox {
- max-inline-size: 1000px;
margin-block: 0;
margin-inline: auto;
}
-@media only screen and (width <= 600px) {
+.searchFilterCloseButtonContainer {
+ display: flex;
+ justify-content: center;
+}
+
+@media only screen and (width <= 680px) {
.searchRadio {
border-inline-end: 0;
+ padding-block-start: 0;
+ }
+
+ .radioFlexBox {
+ flex-flow: column;
}
}
diff --git a/src/renderer/components/ft-search-filters/ft-search-filters.js b/src/renderer/components/ft-search-filters/ft-search-filters.js
index bebeafcdcc67a..49aa6efcfb886 100644
--- a/src/renderer/components/ft-search-filters/ft-search-filters.js
+++ b/src/renderer/components/ft-search-filters/ft-search-filters.js
@@ -1,16 +1,24 @@
import { defineComponent } from 'vue'
+import { mapActions } from 'vuex'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtRadioButton from '../ft-radio-button/ft-radio-button.vue'
+import FtPrompt from '../ft-prompt/ft-prompt.vue'
+import FtButton from '../ft-button/ft-button.vue'
export default defineComponent({
name: 'FtSearchFilters',
components: {
'ft-flex-box': FtFlexBox,
- 'ft-radio-button': FtRadioButton
+ 'ft-radio-button': FtRadioButton,
+ 'ft-prompt': FtPrompt,
+ 'ft-button': FtButton
},
- emits: ['filterValueUpdated'],
data: function () {
return {
+ searchSortByStartIndex: 0,
+ searchTimeStartIndex: 0,
+ searchTypeStartIndex: 0,
+ searchDurationStartIndex: 0,
sortByValues: [
'relevance',
'rating',
@@ -41,11 +49,15 @@ export default defineComponent({
}
},
computed: {
+ title: function () {
+ return this.$t('Search Filters.Search Filters')
+ },
+
searchSettings: function () {
return this.$store.getters.getSearchSettings
},
- filterValueChanged: function() {
+ searchFilterValueChanged: function() {
return [
this.$refs.sortByRadio.selectedValue !== this.sortByValues[0],
this.$refs.timeRadio.selectedValue !== this.timeValues[0],
@@ -93,6 +105,12 @@ export default defineComponent({
]
}
},
+ created: function () {
+ this.searchSortByStartIndex = this.sortByValues.indexOf(this.searchSettings.sortBy)
+ this.searchTimeStartIndex = this.timeValues.indexOf(this.searchSettings.time)
+ this.searchTypeStartIndex = this.typeValues.indexOf(this.searchSettings.type)
+ this.searchDurationStartIndex = this.durationValues.indexOf(this.searchSettings.duration)
+ },
methods: {
isVideoOrMovieOrAll(type) {
return type === 'video' || type === 'movie' || type === 'all'
@@ -100,7 +118,7 @@ export default defineComponent({
updateSortBy: function (value) {
this.$store.commit('setSearchSortBy', value)
- this.$emit('filterValueUpdated', this.filterValueChanged)
+ this.$store.commit('setSearchFilterValueChanged', this.searchFilterValueChanged)
},
updateTime: function (value) {
@@ -110,7 +128,7 @@ export default defineComponent({
this.$store.commit('setSearchType', 'all')
}
this.$store.commit('setSearchTime', value)
- this.$emit('filterValueUpdated', this.filterValueChanged)
+ this.$store.commit('setSearchFilterValueChanged', this.searchFilterValueChanged)
},
updateType: function (value) {
@@ -126,7 +144,7 @@ export default defineComponent({
this.$store.commit('setSearchSortBy', this.sortByValues[0])
}
this.$store.commit('setSearchType', value)
- this.$emit('filterValueUpdated', this.filterValueChanged)
+ this.$store.commit('setSearchFilterValueChanged', this.searchFilterValueChanged)
},
updateDuration: function (value) {
@@ -136,7 +154,11 @@ export default defineComponent({
this.updateType('all')
}
this.$store.commit('setSearchDuration', value)
- this.$emit('filterValueUpdated', this.filterValueChanged)
- }
+ this.$store.commit('setSearchFilterValueChanged', this.searchFilterValueChanged)
+ },
+
+ ...mapActions([
+ 'hideSearchFilters'
+ ])
}
})
diff --git a/src/renderer/components/ft-search-filters/ft-search-filters.vue b/src/renderer/components/ft-search-filters/ft-search-filters.vue
index 47ebe6229642d..fce3bc3429ca3 100644
--- a/src/renderer/components/ft-search-filters/ft-search-filters.vue
+++ b/src/renderer/components/ft-search-filters/ft-search-filters.vue
@@ -1,8 +1,15 @@
-
-
-
- {{ $t("Search Filters.Search Filters") }}
+
+
+
+ {{ title }}
@@ -18,6 +26,7 @@
:title="$t('Search Filters.Time.Time')"
:labels="timeLabels"
:values="timeValues"
+ :initial-value-index="searchTimeStartIndex"
class="searchRadio"
@change="updateTime"
/>
@@ -26,6 +35,7 @@
:title="$t('Search Filters.Type.Type')"
:labels="typeLabels"
:values="typeValues"
+ :initial-value-index="searchTypeStartIndex"
class="searchRadio"
@change="updateType"
/>
@@ -34,12 +44,21 @@
:title="$t('Search Filters.Duration.Duration')"
:labels="durationLabels"
:values="durationValues"
+ :initial-value-index="searchDurationStartIndex"
class="searchRadio"
@change="updateDuration"
/>
+
+
+
-
+
diff --git a/src/renderer/components/top-nav/top-nav.js b/src/renderer/components/top-nav/top-nav.js
index f6b02d609ae29..8e9d2433c4f8e 100644
--- a/src/renderer/components/top-nav/top-nav.js
+++ b/src/renderer/components/top-nav/top-nav.js
@@ -1,7 +1,6 @@
import { defineComponent } from 'vue'
import { mapActions } from 'vuex'
import FtInput from '../ft-input/ft-input.vue'
-import FtSearchFilters from '../ft-search-filters/ft-search-filters.vue'
import FtProfileSelector from '../ft-profile-selector/ft-profile-selector.vue'
import debounce from 'lodash.debounce'
@@ -15,15 +14,12 @@ export default defineComponent({
name: 'TopNav',
components: {
FtInput,
- FtSearchFilters,
FtProfileSelector
},
data: () => {
return {
component: this,
showSearchContainer: true,
- showFilters: false,
- searchFilterValueChanged: false,
historyIndex: 1,
isForwardOrBack: false,
isArrowBackwardDisabled: true,
@@ -84,6 +80,10 @@ export default defineComponent({
return this.$store.getters.getExpandSideBar
},
+ searchFilterValueChanged: function () {
+ return this.$store.getters.getSearchFilterValueChanged
+ },
+
forwardText: function () {
return this.$t('Forward')
},
@@ -216,9 +216,6 @@ export default defineComponent({
}
}
})
-
- // Close the filter panel
- this.showFilters = false
},
focusSearch: function () {
@@ -295,11 +292,6 @@ export default defineComponent({
toggleSearchContainer: function () {
this.showSearchContainer = !this.showSearchContainer
- this.showFilters = false
- },
-
- handleSearchFilterValueChanged: function (filterValueChanged) {
- this.searchFilterValueChanged = filterValueChanged
},
navigateHistory: function () {
@@ -354,14 +346,12 @@ export default defineComponent({
navigate: function (route) {
this.$router.push('/' + route)
},
- hideFilters: function () {
- this.showFilters = false
- },
updateSearchInputText: function (text) {
this.$refs.searchInput.updateInputData(text)
},
...mapActions([
'getYoutubeUrlInfo',
+ 'showSearchFilters'
])
}
})
diff --git a/src/renderer/components/top-nav/top-nav.scss b/src/renderer/components/top-nav/top-nav.scss
index ef62946c1e520..55e87ec84725b 100644
--- a/src/renderer/components/top-nav/top-nav.scss
+++ b/src/renderer/components/top-nav/top-nav.scss
@@ -89,15 +89,17 @@
}
.navFilterIcon {
- $effect-distance: 10px;
+ $effect-distance: 20px;
margin-inline-start: $effect-distance;
&.filterChanged {
box-shadow: 0 0 $effect-distance var(--primary-color);
+ color: var(--primary-color);
@include top-nav-is-colored {
box-shadow: 0 0 $effect-distance var(--text-with-main-color);
+ color: var(--text-with-main-color);
}
}
}
@@ -209,18 +211,4 @@
flex: 1;
}
}
-
- .searchFilters {
- inset-inline: 0;
- margin-block: 10px 20px;
- margin-inline: 220px 20px;
- position: absolute;
- transition: margin 150ms ease-in-out;
-
- @media only screen and (width <= 680px) {
- inset-inline: 0;
- margin-block: 95px 0;
- margin-inline: 10px;
- }
- }
}
diff --git a/src/renderer/components/top-nav/top-nav.vue b/src/renderer/components/top-nav/top-nav.vue
index 69a54ab70355b..ca023d47795b1 100644
--- a/src/renderer/components/top-nav/top-nav.vue
+++ b/src/renderer/components/top-nav/top-nav.vue
@@ -94,18 +94,13 @@
class="navFilterIcon navIcon"
:class="{ filterChanged: searchFilterValueChanged }"
:icon="['fas', 'filter']"
+ :title="$t('Search Filters.Search Filters')"
role="button"
tabindex="0"
- @click="showFilters = !showFilters"
- @keydown.enter.prevent="showFilters = !showFilters"
+ @click="showSearchFilters"
+ @keydown.enter.prevent="showSearchFilters"
/>
-
diff --git a/src/renderer/main.js b/src/renderer/main.js
index 79f8971cd5fd0..dca405d6ccf67 100644
--- a/src/renderer/main.js
+++ b/src/renderer/main.js
@@ -95,6 +95,7 @@ import {
faMonero
} from '@fortawesome/free-brands-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
+import PortalVue from 'portal-vue'
Vue.config.devtools = process.env.NODE_ENV === 'development'
Vue.config.performance = process.env.NODE_ENV === 'development'
@@ -201,6 +202,7 @@ new Vue({
i18n,
render: h => h(App)
})
+Vue.use(PortalVue)
// to avoid accessing electron api from web app build
if (process.env.IS_ELECTRON) {
diff --git a/src/renderer/store/modules/utils.js b/src/renderer/store/modules/utils.js
index 8184395860a92..a4163aeed851a 100644
--- a/src/renderer/store/modules/utils.js
+++ b/src/renderer/store/modules/utils.js
@@ -33,6 +33,8 @@ const state = {
showProgressBar: false,
showAddToPlaylistPrompt: false,
showCreatePlaylistPrompt: false,
+ showSearchFilters: false,
+ searchFilterValueChanged: false,
progressBarPercentage: 0,
toBeAddedToPlaylistVideoList: [],
newPlaylistDefaultProperties: {},
@@ -94,6 +96,10 @@ const getters = {
return state.searchSettings
},
+ getSearchFilterValueChanged () {
+ return state.searchFilterValueChanged
+ },
+
getShowAddToPlaylistPrompt () {
return state.showAddToPlaylistPrompt
},
@@ -102,6 +108,10 @@ const getters = {
return state.showCreatePlaylistPrompt
},
+ getShowSearchFilters () {
+ return state.showSearchFilters
+ },
+
getToBeAddedToPlaylistVideoList () {
return state.toBeAddedToPlaylistVideoList
},
@@ -168,7 +178,7 @@ const getters = {
getLastVideoRefreshTimestampByProfile: (state) => (profileId) => {
return state.lastVideoRefreshTimestampByProfile[profileId]
- }
+ },
}
const actions = {
@@ -378,6 +388,14 @@ const actions = {
commit('setShowCreatePlaylistPrompt', false)
},
+ showSearchFilters ({ commit }) {
+ commit('setShowSearchFilters', true)
+ },
+
+ hideSearchFilters ({ commit }) {
+ commit('setShowSearchFilters', false)
+ },
+
updateShowProgressBar ({ commit }, value) {
commit('setShowProgressBar', value)
},
@@ -839,6 +857,10 @@ const mutations = {
state.showCreatePlaylistPrompt = payload
},
+ setShowSearchFilters (state, payload) {
+ state.showSearchFilters = payload
+ },
+
setToBeAddedToPlaylistVideoList (state, payload) {
state.toBeAddedToPlaylistVideoList = payload
},
@@ -899,6 +921,10 @@ const mutations = {
state.cachedPlaylist = value
},
+ setSearchFilterValueChanged (state, value) {
+ state.searchFilterValueChanged = value
+ },
+
setSearchSortBy (state, value) {
state.searchSettings.sortBy = value
},
diff --git a/yarn.lock b/yarn.lock
index 12cf0aff1c467..833f80642f9ec 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6681,6 +6681,11 @@ pluralize@^8.0.0:
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
+portal-vue@^2.1.7:
+ version "2.1.7"
+ resolved "https://registry.yarnpkg.com/portal-vue/-/portal-vue-2.1.7.tgz#ea08069b25b640ca08a5b86f67c612f15f4e4ad4"
+ integrity sha512-+yCno2oB3xA7irTt0EU5Ezw22L2J51uKAacE/6hMPMoO/mx3h4rXFkkBkT4GFsMDv/vEe8TNKC3ujJJ0PTwb6g==
+
postcss-calc@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-9.0.1.tgz#a744fd592438a93d6de0f1434c572670361eb6c6"