diff --git a/Cargo.toml b/Cargo.toml index 5a1d86f..17b0212 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,4 @@ members = [ "yopa", "yopa-web", - #"yopa-test", ] diff --git a/yopa-test/Cargo.toml b/yopa-test/Cargo.toml deleted file mode 100644 index e0de717..0000000 --- a/yopa-test/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "yopa-test" -version = "0.1.0" -authors = ["Ondřej Hruška "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -log = "0.4.13" -simple-logging = "2.0.2" - -yopa = { path = "../yopa", features = [ "uuid-ids" ] } - -serde_json = "1.0.61" -serde = { version = "1.0.120", features = ["derive"] } - -anyhow = "1.0.38" -thiserror = "1.0.23" diff --git a/yopa-test/src/main.rs b/yopa-test/src/main.rs deleted file mode 100644 index 8802107..0000000 --- a/yopa-test/src/main.rs +++ /dev/null @@ -1,120 +0,0 @@ -#[macro_use] extern crate log; - -use log::LevelFilter; - -use yopa::{model, InMemoryStorage}; -use yopa::model::DataType; -use yopa::insert::{InsertObj, InsertValue, InsertRel}; -use yopa::data::TypedValue; - -fn main() { - simple_logging::log_to_stderr(LevelFilter::Debug); - - main_test_recipes().unwrap(); -} - -#[allow(non_snake_case)] -fn main_test_recipes() -> anyhow::Result<()> { - simple_logging::log_to_stderr(LevelFilter::Debug); - - let mut store = yopa::InMemoryStorage::new(); - - let Recipe = store.define_object(model::ObjectModel { - id: Default::default(), - name: "recipe".to_string(), - parent: None - })?; - - let RecipeTitle = store.define_property(model::PropertyModel { - id: Default::default(), - object: Recipe, - name: "title".to_string(), - optional: false, - multiple: false, - data_type: DataType::String, - default: None - })?; - - let _PrepHours = store.define_property(model::PropertyModel { - id: Default::default(), - object: Recipe, - name: "prep_hours".to_string(), - optional: true, - multiple: false, - data_type: DataType::Decimal, - default: None - })?; - - let Book = store.define_object(model::ObjectModel { - id: Default::default(), - name: "book".to_string(), - parent: None - })?; - - let BookName = store.define_property(model::PropertyModel { - id: Default::default(), - object: Book, - name: "name".to_string(), - optional: false, - multiple: false, - data_type: DataType::String, - default: None - })?; - - let BookToRecipe = store.define_relation(model::RelationModel { - id: Default::default(), - object: Recipe, - name: "book reference".to_string(), - optional: true, - multiple: true, - related: Book - })?; - - let BookToRecipePage = store.define_property(model::PropertyModel { - id: Default::default(), - object: BookToRecipe, - name: "page".to_string(), - optional: false, - multiple: false, - data_type: DataType::Integer, - default: None - })?; - - debug!("{:#?}", store); - - let MyBook1 = store.insert_object(InsertObj { - model_id: Book, - values: vec![ - InsertValue::new(BookName, TypedValue::String("Recipe Book 1".into())), - ], - relations: vec![], - })?; - - store.insert_object(InsertObj { - model_id: Recipe, - values: vec![ - InsertValue::new(RecipeTitle, TypedValue::String("Pancakes".into())), - ], - relations: vec![ - InsertRel { - model_id: BookToRecipe, - related_id: MyBook1, - values: vec![ - InsertValue::new(BookToRecipePage, TypedValue::Integer(123)) - ] - } - ], - })?; - - debug!("{:#?}", store); - - let as_s = serde_json::to_string_pretty(&store).unwrap(); - - println!("{}", as_s); - - let _back : InMemoryStorage = serde_json::from_str(&as_s)?; - - debug!("After unpack: {:#?}", store); - - Ok(()) -} diff --git a/yopa-web/resources/src/components/EditObjectForm.vue b/yopa-web/resources/src/components/EditObjectForm.vue index e5b8e5c..c8df88d 100644 --- a/yopa-web/resources/src/components/EditObjectForm.vue +++ b/yopa-web/resources/src/components/EditObjectForm.vue @@ -134,13 +134,21 @@ export default { diff --git a/yopa-web/resources/src/components/BooleanValue.vue b/yopa-web/resources/src/components/value/BooleanValue.vue similarity index 71% rename from yopa-web/resources/src/components/BooleanValue.vue rename to yopa-web/resources/src/components/value/BooleanValue.vue index 5df3c4e..8d71be4 100644 --- a/yopa-web/resources/src/components/BooleanValue.vue +++ b/yopa-web/resources/src/components/value/BooleanValue.vue @@ -27,5 +27,8 @@ export default { diff --git a/yopa-web/resources/src/components/DecimalValue.vue b/yopa-web/resources/src/components/value/DecimalValue.vue similarity index 79% rename from yopa-web/resources/src/components/DecimalValue.vue rename to yopa-web/resources/src/components/value/DecimalValue.vue index 125966d..773ff4f 100644 --- a/yopa-web/resources/src/components/DecimalValue.vue +++ b/yopa-web/resources/src/components/value/DecimalValue.vue @@ -27,5 +27,5 @@ export default { diff --git a/yopa-web/resources/src/components/IntegerValue.vue b/yopa-web/resources/src/components/value/IntegerValue.vue similarity index 79% rename from yopa-web/resources/src/components/IntegerValue.vue rename to yopa-web/resources/src/components/value/IntegerValue.vue index b14e6c9..e1d1618 100644 --- a/yopa-web/resources/src/components/IntegerValue.vue +++ b/yopa-web/resources/src/components/value/IntegerValue.vue @@ -27,5 +27,5 @@ export default { diff --git a/yopa-web/resources/src/components/StringValue.vue b/yopa-web/resources/src/components/value/StringValue.vue similarity index 80% rename from yopa-web/resources/src/components/StringValue.vue rename to yopa-web/resources/src/components/value/StringValue.vue index a95e472..a262676 100644 --- a/yopa-web/resources/src/components/StringValue.vue +++ b/yopa-web/resources/src/components/value/StringValue.vue @@ -27,5 +27,5 @@ export default { diff --git a/yopa-web/resources/src/main.js b/yopa-web/resources/src/main.js index 91ea2fa..dd75e95 100644 --- a/yopa-web/resources/src/main.js +++ b/yopa-web/resources/src/main.js @@ -2,10 +2,10 @@ import * as Vue from "vue"; import './style/app.scss'; -import StringValue from "./components/StringValue.vue"; -import DecimalValue from "./components/DecimalValue.vue"; -import BooleanValue from "./components/BooleanValue.vue"; -import IntegerValue from "./components/IntegerValue.vue"; +import StringValue from "./components/value/StringValue.vue"; +import DecimalValue from "./components/value/DecimalValue.vue"; +import BooleanValue from "./components/value/BooleanValue.vue"; +import IntegerValue from "./components/value/IntegerValue.vue"; import PropertyField from "./components/PropertyField.vue"; import NewObjectForm from "./components/NewObjectForm.vue"; diff --git a/yopa-web/resources/src/style/_icons-extra.scss b/yopa-web/resources/src/style/_icons-extra.scss new file mode 100644 index 0000000..d17b57f --- /dev/null +++ b/yopa-web/resources/src/style/_icons-extra.scss @@ -0,0 +1,21 @@ +@import "spectre/src/variables"; + +$icon-border-width: $border-width-lg; + +.icon-home { + &::before { + border: $icon-border-width solid currentColor; + border-bottom: 0; + border-right: 0; + transform: translate(-50%, -40%) rotate(45deg); + width: .8em; + height: .8em; + } + + &::after { + transform: translate(-50%, -50%); + border-bottom: $icon-border-width solid currentColor; + height: 1em; + width: .8em; + } +} diff --git a/yopa-web/resources/src/style/_spectre-patches.scss b/yopa-web/resources/src/style/_spectre-patches.scss new file mode 100644 index 0000000..1c75c65 --- /dev/null +++ b/yopa-web/resources/src/style/_spectre-patches.scss @@ -0,0 +1,27 @@ +@import "spectre/src/variables"; + +.form-select.input-inline { + display: inline-block; + vertical-align: middle; + width: auto; +} + +$error-color-faint: lighten($error-color, 50%) !default; +$error-color-dark: darken($error-color, 3%) !default; + +.btn.btn-delete { + color: $error-color; + border-color: $error-color; + + &:focus, + &:hover { + background: $error-color-faint; + border-color: $error-color-dark; + } + &:active, + &.active { + color: $light-color; + background: $error-color; + border-color: darken($error-color-dark, 5%); + } +} diff --git a/yopa-web/resources/src/style/app.scss b/yopa-web/resources/src/style/app.scss index 5b76bc3..8c07cf2 100644 --- a/yopa-web/resources/src/style/app.scss +++ b/yopa-web/resources/src/style/app.scss @@ -1,5 +1,12 @@ -@import "common"; +//@import "common"; +//@import "bulma/bulma"; +@import "spectre/src/spectre"; +@import "spectre/src/spectre-exp"; +@import "spectre/src/spectre-icons"; +@import "icons-extra"; +@import "spectre-patches"; +/* .EditForm th { text-align: left; vertical-align: top; @@ -8,3 +15,9 @@ label { cursor: pointer; } +*/ + +//.navbar-brand { +// font-size: 120%; +// font-weight: bold; +//} diff --git a/yopa-web/resources/src/style/spectre/LICENSE b/yopa-web/resources/src/style/spectre/LICENSE new file mode 100644 index 0000000..7d4eb70 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 - 2020 Yan Zhu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/yopa-web/resources/src/style/spectre/src/_accordions.scss b/yopa-web/resources/src/style/spectre/src/_accordions.scss new file mode 100644 index 0000000..ee57ac0 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_accordions.scss @@ -0,0 +1,38 @@ +// Accordions +.accordion { + input:checked ~, + &[open] { + & .accordion-header > { + .icon:first-child { + transform: rotate(90deg); + } + } + + & .accordion-body { + max-height: 50rem; + } + } + + .accordion-header { + display: block; + padding: $unit-1 $unit-2; + + .icon { + transition: transform .25s; + } + } + + .accordion-body { + margin-bottom: $layout-spacing; + max-height: 0; + overflow: hidden; + transition: max-height .25s; + } +} + +// Remove default details marker in Webkit +summary.accordion-header { + &::-webkit-details-marker { + display: none; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_animations.scss b/yopa-web/resources/src/style/spectre/src/_animations.scss new file mode 100644 index 0000000..e7fde1a --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_animations.scss @@ -0,0 +1,20 @@ +// Animations +@keyframes loading { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +@keyframes slide-down { + 0% { + opacity: 0; + transform: translateY(-$unit-8); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_asian.scss b/yopa-web/resources/src/style/spectre/src/_asian.scss new file mode 100644 index 0000000..e426f39 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_asian.scss @@ -0,0 +1,43 @@ +// Optimized for East Asian CJK +html:lang(zh), +html:lang(zh-Hans), +.lang-zh, +.lang-zh-hans { + font-family: $cjk-zh-hans-font-family; +} + +html:lang(zh-Hant), +.lang-zh-hant { + font-family: $cjk-zh-hant-font-family; +} + +html:lang(ja), +.lang-ja { + font-family: $cjk-jp-font-family; +} + +html:lang(ko), +.lang-ko { + font-family: $cjk-ko-font-family; +} + +:lang(zh), +:lang(ja), +.lang-cjk { + ins, + u { + border-bottom: $border-width solid; + text-decoration: none; + } + + del + del, + del + s, + ins + ins, + ins + u, + s + del, + s + s, + u + ins, + u + u { + margin-left: .125em; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_autocomplete.scss b/yopa-web/resources/src/style/spectre/src/_autocomplete.scss new file mode 100644 index 0000000..279fa03 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_autocomplete.scss @@ -0,0 +1,47 @@ +// Autocomplete +.form-autocomplete { + position: relative; + + .form-autocomplete-input { + align-content: flex-start; + display: flex; + flex-wrap: wrap; + height: auto; + min-height: $unit-8; + padding: $unit-h; + + &.is-focused { + @include control-shadow(); + border-color: $primary-color; + } + + .form-input { + border-color: transparent; + box-shadow: none; + display: inline-block; + flex: 1 0 auto; + height: $unit-6; + line-height: $unit-4; + margin: $unit-h; + width: auto; + } + } + + .menu { + left: 0; + position: absolute; + top: 100%; + width: 100%; + } + + &.autocomplete-oneline { + .form-autocomplete-input { + flex-wrap: nowrap; + overflow-x: auto; + } + + .chip { + flex: 1 0 auto; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_avatars.scss b/yopa-web/resources/src/style/spectre/src/_avatars.scss new file mode 100644 index 0000000..b203aa2 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_avatars.scss @@ -0,0 +1,77 @@ +// Avatars +.avatar { + @include avatar-base(); + background: $primary-color; + border-radius: 50%; + color: rgba($light-color, .85); + display: inline-block; + font-weight: 300; + line-height: 1.25; + margin: 0; + position: relative; + vertical-align: middle; + + &.avatar-xs { + @include avatar-base($unit-4); + } + &.avatar-sm { + @include avatar-base($unit-6); + } + &.avatar-lg { + @include avatar-base($unit-12); + } + &.avatar-xl { + @include avatar-base($unit-16); + } + + img { + border-radius: 50%; + height: 100%; + position: relative; + width: 100%; + z-index: $zindex-0; + } + + .avatar-icon, + .avatar-presence { + background: $bg-color-light; + bottom: 14.64%; + height: 50%; + padding: $border-width-lg; + position: absolute; + right: 14.64%; + transform: translate(50%, 50%); + width: 50%; + z-index: $zindex-0 + 1; + } + + .avatar-presence { + background: $gray-color; + box-shadow: 0 0 0 $border-width-lg $light-color; + border-radius: 50%; + height: .5em; + width: .5em; + + &.online { + background: $success-color; + } + + &.busy { + background: $error-color; + } + + &.away { + background: $warning-color; + } + } + + &[data-initial]::before { + color: currentColor; + content: attr(data-initial); + left: 50%; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + z-index: $zindex-0; + } +} \ No newline at end of file diff --git a/yopa-web/resources/src/style/spectre/src/_badges.scss b/yopa-web/resources/src/style/spectre/src/_badges.scss new file mode 100644 index 0000000..d67f6d1 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_badges.scss @@ -0,0 +1,60 @@ +// Badges +.badge { + position: relative; + white-space: nowrap; + + &[data-badge], + &:not([data-badge]) { + &::after { + background: $primary-color; + background-clip: padding-box; + border-radius: .5rem; + box-shadow: 0 0 0 .1rem $bg-color-light; + color: $light-color; + content: attr(data-badge); + display: inline-block; + transform: translate(-.05rem, -.5rem); + } + } + &[data-badge] { + &::after { + font-size: $font-size-sm; + height: .9rem; + line-height: 1; + min-width: .9rem; + padding: .1rem .2rem; + text-align: center; + white-space: nowrap; + } + } + &:not([data-badge]), + &[data-badge=""] { + &::after { + height: 6px; + min-width: 6px; + padding: 0; + width: 6px; + } + } + + // Badges for Buttons + &.btn { + &::after { + position: absolute; + top: 0; + right: 0; + transform: translate(50%, -50%); + } + } + + // Badges for Avatars + &.avatar { + &::after { + position: absolute; + top: 14.64%; + right: 14.64%; + transform: translate(50%, -50%); + z-index: $zindex-1; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_bars.scss b/yopa-web/resources/src/style/spectre/src/_bars.scss new file mode 100644 index 0000000..47e21c9 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_bars.scss @@ -0,0 +1,71 @@ +// Bars +.bar { + background: $bg-color-dark; + border-radius: $border-radius; + display: flex; + flex-wrap: nowrap; + height: $unit-4; + width: 100%; + + &.bar-sm { + height: $unit-1; + } + + // TODO: attr() support + .bar-item { + background: $primary-color; + color: $light-color; + display: block; + font-size: $font-size-sm; + flex-shrink: 0; + line-height: $unit-4; + height: 100%; + position: relative; + text-align: center; + width: 0; + + &:first-child { + border-bottom-left-radius: $border-radius; + border-top-left-radius: $border-radius; + } + &:last-child { + border-bottom-right-radius: $border-radius; + border-top-right-radius: $border-radius; + flex-shrink: 1; + } + } +} + +// Slider bar +.bar-slider { + height: $border-width-lg; + margin: $layout-spacing 0; + position: relative; + + .bar-item { + left: 0; + padding: 0; + position: absolute; + &:not(:last-child):first-child { + background: $bg-color-dark; + z-index: $zindex-0; + } + } + + .bar-slider-btn { + background: $primary-color; + border: 0; + border-radius: 50%; + height: $unit-3; + padding: 0; + position: absolute; + right: 0; + top: 50%; + transform: translate(50%, -50%); + width: $unit-3; + + &:active { + box-shadow: 0 0 0 .1rem $primary-color; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_base.scss b/yopa-web/resources/src/style/spectre/src/_base.scss new file mode 100644 index 0000000..4e01b20 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_base.scss @@ -0,0 +1,44 @@ +// Base +*, +*::before, +*::after { + box-sizing: inherit; +} + +html { + box-sizing: border-box; + font-size: $html-font-size; + line-height: $html-line-height; + -webkit-tap-highlight-color: transparent; +} + +body { + background: $body-bg; + color: $body-font-color; + font-family: $body-font-family; + font-size: $font-size; + overflow-x: hidden; + text-rendering: optimizeLegibility; +} + +a { + color: $link-color; + outline: none; + text-decoration: none; + + &:focus { + @include control-shadow(); + } + + &:focus, + &:hover, + &:active, + &.active { + color: $link-color-dark; + text-decoration: underline; + } + + &:visited { + color: $link-color-light; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_breadcrumbs.scss b/yopa-web/resources/src/style/spectre/src/_breadcrumbs.scss new file mode 100644 index 0000000..6a5af31 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_breadcrumbs.scss @@ -0,0 +1,29 @@ +// Breadcrumbs +.breadcrumb { + list-style: none; + margin: $unit-1 0; + padding: $unit-1 0; + + .breadcrumb-item { + color: $gray-color-dark; + display: inline-block; + margin: 0; + padding: $unit-1 0; + + &:not(:last-child) { + margin-right: $unit-1; + + a { + color: $gray-color-dark; + } + } + + &:not(:first-child) { + &::before { + color: $gray-color-dark; + content: "/"; + padding-right: $unit-2; + } + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_buttons.scss b/yopa-web/resources/src/style/spectre/src/_buttons.scss new file mode 100644 index 0000000..9158f0f --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_buttons.scss @@ -0,0 +1,193 @@ +// Buttons +.btn { + appearance: none; + background: $bg-color-light; + border: $border-width solid $primary-color; + border-radius: $border-radius; + color: $primary-color; + cursor: pointer; + display: inline-block; + font-size: $font-size; + height: $control-size; + line-height: $line-height; + outline: none; + padding: $control-padding-y $control-padding-x; + text-align: center; + text-decoration: none; + transition: background .2s, border .2s, box-shadow .2s, color .2s; + user-select: none; + vertical-align: middle; + white-space: nowrap; + &:focus { + @include control-shadow(); + } + &:focus, + &:hover { + background: $secondary-color; + border-color: $primary-color-dark; + text-decoration: none; + } + &:active, + &.active { + background: $primary-color-dark; + border-color: darken($primary-color-dark, 5%); + color: $light-color; + text-decoration: none; + &.loading { + &::after { + border-bottom-color: $light-color; + border-left-color: $light-color; + } + } + } + &[disabled], + &:disabled, + &.disabled { + cursor: default; + opacity: .5; + pointer-events: none; + } + + // Button Primary + &.btn-primary { + background: $primary-color; + border-color: $primary-color-dark; + color: $light-color; + &:focus, + &:hover { + background: darken($primary-color-dark, 2%); + border-color: darken($primary-color-dark, 5%); + color: $light-color; + } + &:active, + &.active { + background: darken($primary-color-dark, 4%); + border-color: darken($primary-color-dark, 7%); + color: $light-color; + } + &.loading { + &::after { + border-bottom-color: $light-color; + border-left-color: $light-color; + } + } + } + + // Button Colors + &.btn-success { + @include button-variant($success-color); + } + + &.btn-error { + @include button-variant($error-color); + } + + // Button Link + &.btn-link { + background: transparent; + border-color: transparent; + color: $link-color; + &:focus, + &:hover, + &:active, + &.active { + color: $link-color-dark; + } + } + + // Button Sizes + &.btn-sm { + font-size: $font-size-sm; + height: $control-size-sm; + padding: $control-padding-y-sm $control-padding-x-sm; + } + + &.btn-lg { + font-size: $font-size-lg; + height: $control-size-lg; + padding: $control-padding-y-lg $control-padding-x-lg; + } + + // Button Block + &.btn-block { + display: block; + width: 100%; + } + + // Button Action + &.btn-action { + width: $control-size; + padding-left: 0; + padding-right: 0; + + &.btn-sm { + width: $control-size-sm; + } + + &.btn-lg { + width: $control-size-lg; + } + } + + // Button Clear + &.btn-clear { + background: transparent; + border: 0; + color: currentColor; + height: $unit-5; + line-height: $unit-4; + margin-left: $unit-1; + margin-right: -2px; + opacity: 1; + padding: $unit-h; + text-decoration: none; + width: $unit-5; + + &:focus, + &:hover { + background: rgba($bg-color, .5); + opacity: .95; + } + + &::before { + content: "\2715"; + } + } +} + +// Button groups +.btn-group { + display: inline-flex; + flex-wrap: wrap; + + .btn { + flex: 1 0 auto; + &:first-child:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + } + &:not(:first-child):not(:last-child) { + border-radius: 0; + margin-left: -$border-width; + } + &:last-child:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + margin-left: -$border-width; + } + &:focus, + &:hover, + &:active, + &.active { + z-index: $zindex-0; + } + } + + &.btn-group-block { + display: flex; + + .btn { + flex: 1 0 0; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_calendars.scss b/yopa-web/resources/src/style/spectre/src/_calendars.scss new file mode 100644 index 0000000..1e9fd15 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_calendars.scss @@ -0,0 +1,222 @@ +// Calendars +.calendar { + border: $border-width solid $border-color; + border-radius: $border-radius; + display: block; + min-width: 280px; + + .calendar-nav { + align-items: center; + background: $bg-color; + border-top-left-radius: $border-radius; + border-top-right-radius: $border-radius; + display: flex; + font-size: $font-size-lg; + padding: $layout-spacing; + } + + .calendar-header, + .calendar-body { + display: flex; + flex-wrap: wrap; + justify-content: center; + padding: $layout-spacing 0; + + .calendar-date { + flex: 0 0 14.28%; // 7 calendar-items each row + max-width: 14.28%; + } + } + + .calendar-header { + background: $bg-color; + border-bottom: $border-width solid $border-color; + color: $gray-color; + font-size: $font-size-sm; + text-align: center; + } + + .calendar-body { + color: $gray-color-dark; + } + + .calendar-date { + border: 0; + padding: $unit-1; + + .date-item { + appearance: none; + background: transparent; + border: $border-width solid transparent; + border-radius: 50%; + color: $gray-color-dark; + cursor: pointer; + font-size: $font-size-sm; + height: $unit-7; + line-height: $unit-5; + outline: none; + padding: $unit-h; + position: relative; + text-align: center; + text-decoration: none; + transition: background .2s, border .2s, box-shadow .2s, color .2s; + vertical-align: middle; + white-space: nowrap; + width: $unit-7; + + &.date-today { + border-color: $secondary-color-dark; + color: $primary-color; + } + + &:focus { + @include control-shadow(); + } + + &:focus, + &:hover { + background: $secondary-color-light; + border-color: $secondary-color-dark; + color: $primary-color; + text-decoration: none; + } + &:active, + &.active { + background: $primary-color-dark; + border-color: darken($primary-color-dark, 5%); + color: $light-color; + } + + // Calendar badge support + &.badge { + &::after { + position: absolute; + top: 3px; + right: 3px; + transform: translate(50%, -50%); + } + } + } + + .date-item, + .calendar-event { + &:disabled, + &.disabled { + cursor: default; + opacity: .25; + pointer-events: none; + } + } + + &.prev-month, + &.next-month { + .date-item, + .calendar-event { + opacity: .25; + } + } + } + + .calendar-range { + position: relative; + + &::before { + background: $secondary-color; + content: ""; + height: $unit-7; + left: 0; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + } + &.range-start { + &::before { + left: 50%; + } + } + &.range-end { + &::before { + right: 50%; + } + } + + &.range-start, + &.range-end { + .date-item { + background: $primary-color-dark; + border-color: darken($primary-color-dark, 5%); + color: $light-color; + } + } + + .date-item { + color: $primary-color; + } + } + + // Calendars size + &.calendar-lg { + .calendar-body { + padding: 0; + + .calendar-date { + border-bottom: $border-width solid $border-color; + border-right: $border-width solid $border-color; + display: flex; + flex-direction: column; + height: 5.5rem; + padding: 0; + + &:nth-child(7n) { + border-right: 0; + } + &:nth-last-child(-n+7) { + border-bottom: 0; + } + } + } + + .date-item { + align-self: flex-end; + height: $unit-7; + margin-right: $layout-spacing-sm; + margin-top: $layout-spacing-sm; + } + + .calendar-range { + &::before { + top: 19px; + } + &.range-start { + &::before { + left: auto; + width: 19px; + } + } + &.range-end { + &::before { + right: 19px; + } + } + } + + .calendar-events { + flex-grow: 1; + line-height: 1; + overflow-y: auto; + padding: $layout-spacing-sm; + } + + .calendar-event { + border-radius: $border-radius; + font-size: $font-size-sm; + display: block; + margin: $unit-h auto; + overflow: hidden; + padding: 3px 4px; + text-overflow: ellipsis; + white-space: nowrap; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_cards.scss b/yopa-web/resources/src/style/spectre/src/_cards.scss new file mode 100644 index 0000000..6b712e1 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_cards.scss @@ -0,0 +1,43 @@ +// Cards +.card { + background: $bg-color-light; + border: $border-width solid $border-color; + border-radius: $border-radius; + display: flex; + flex-direction: column; + + .card-header, + .card-body, + .card-footer { + padding: $layout-spacing-lg; + padding-bottom: 0; + + &:last-child { + padding-bottom: $layout-spacing-lg; + } + } + + .card-body { + flex: 1 1 auto; + } + + .card-image { + padding-top: $layout-spacing-lg; + + &:first-child { + padding-top: 0; + + img { + border-top-left-radius: $border-radius; + border-top-right-radius: $border-radius; + } + } + + &:last-child { + img { + border-bottom-left-radius: $border-radius; + border-bottom-right-radius: $border-radius; + } + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_carousels.scss b/yopa-web/resources/src/style/spectre/src/_carousels.scss new file mode 100644 index 0000000..66dc51b --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_carousels.scss @@ -0,0 +1,136 @@ +// Carousels +// The number of carousel images +$carousel-number: 8; + +%carousel-image-checked { + animation: carousel-slidein .75s ease-in-out 1; + opacity: 1; + z-index: $zindex-1; +} + +%carousel-nav-checked { + color: $gray-color-light; +} + +.carousel { + background: $bg-color; + display: block; + overflow: hidden; + position: relative; + width: 100%; + -webkit-overflow-scrolling: touch; + z-index: $zindex-0; + + .carousel-container { + height: 100%; + left: 0; + position: relative; + &::before { + content: ""; + display: block; + padding-bottom: 56.25%; + } + + .carousel-item { + animation: carousel-slideout 1s ease-in-out 1; + height: 100%; + left: 0; + margin: 0; + opacity: 0; + position: absolute; + top: 0; + width: 100%; + + &:hover { + .item-prev, + .item-next { + opacity: 1; + } + } + } + + .item-prev, + .item-next { + background: rgba($gray-color-light, .25); + border-color: rgba($gray-color-light, .5); + color: $gray-color-light; + opacity: 0; + position: absolute; + top: 50%; + transition: all .4s; + transform: translateY(-50%); + z-index: $zindex-1; + } + .item-prev { + left: 1rem; + } + .item-next { + right: 1rem; + } + } + + .carousel-locator { + @for $i from 1 through ($carousel-number) { + &:nth-of-type(#{$i}):checked ~ .carousel-container .carousel-item:nth-of-type(#{$i}) { + @extend %carousel-image-checked; + } + } + + @for $i from 1 through ($carousel-number) { + &:nth-of-type(#{$i}):checked ~ .carousel-nav .nav-item:nth-of-type(#{$i}) { + @extend %carousel-nav-checked; + } + } + } + + .carousel-nav { + bottom: $layout-spacing; + display: flex; + justify-content: center; + left: 50%; + position: absolute; + transform: translateX(-50%); + width: 10rem; + z-index: $zindex-1; + + .nav-item { + color: rgba($gray-color-light, .5); + display: block; + flex: 1 0 auto; + height: $unit-8; + margin: $unit-1; + max-width: 2.5rem; + position: relative; + + &::before { + background: currentColor; + content: ""; + display: block; + height: $unit-h; + position: absolute; + top: .5rem; + width: 100%; + } + } + } +} + +@keyframes carousel-slidein { + 0% { + transform: translateX(100%); + } + 100% { + transform: translateX(0); + } +} + +@keyframes carousel-slideout { + 0% { + opacity: 1; + transform: translateX(0); + } + 100% { + opacity: 1; + transform: translateX(-50%); + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_chips.scss b/yopa-web/resources/src/style/spectre/src/_chips.scss new file mode 100644 index 0000000..6729c56 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_chips.scss @@ -0,0 +1,33 @@ +// Chips +.chip { + align-items: center; + background: $bg-color-dark; + border-radius: 5rem; + display: inline-flex; + font-size: 90%; + height: $unit-6; + line-height: $unit-4; + margin: $unit-h; + max-width: $control-width-sm; + overflow: hidden; + padding: $unit-1 $unit-2; + text-decoration: none; + text-overflow: ellipsis; + vertical-align: middle; + white-space: nowrap; + + &.active { + background: $primary-color; + color: $light-color; + } + + .avatar { + margin-left: -$unit-2; + margin-right: $unit-1; + } + + .btn-clear { + border-radius: 50%; + transform: scale(.75); + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_codes.scss b/yopa-web/resources/src/style/spectre/src/_codes.scss new file mode 100644 index 0000000..817452b --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_codes.scss @@ -0,0 +1,31 @@ +// Codes +code { + @include label-base(); + @include label-variant($code-color, lighten($code-color, 42.5%)); + font-size: 85%; +} + +.code { + border-radius: $border-radius; + color: $body-font-color; + position: relative; + + &::before { + color: $gray-color; + content: attr(data-lang); + font-size: $font-size-sm; + position: absolute; + right: $layout-spacing; + top: $unit-h; + } + + code { + background: $bg-color; + color: inherit; + display: block; + line-height: 1.5; + overflow-x: auto; + padding: 1rem; + width: 100%; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_comparison-sliders.scss b/yopa-web/resources/src/style/spectre/src/_comparison-sliders.scss new file mode 100644 index 0000000..ec3609b --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_comparison-sliders.scss @@ -0,0 +1,116 @@ +// Image comparison slider +// Credit: http://codepen.io/solipsistacp/pen/Gpmaq +.comparison-slider { + height: 50vh; + overflow: hidden; + position: relative; + width: 100%; + -webkit-overflow-scrolling: touch; + + .comparison-before, + .comparison-after { + height: 100%; + left: 0; + margin: 0; + overflow: hidden; + position: absolute; + top: 0; + + img { + height: 100%; + object-fit: cover; + object-position: left center; + position: absolute; + width: 100%; + } + } + + .comparison-before { + width: 100%; + z-index: 1; + + .comparison-label { + right: $unit-4; + } + } + + .comparison-after { + max-width: 100%; + min-width: 0; + z-index: 2; + + &::before { + background: transparent; + content: ""; + cursor: default; + height: 100%; + left: 0; + position: absolute; + right: $unit-4; + top: 0; + z-index: $zindex-0; + } + + &::after { + background: currentColor; + border-radius: 50%; + box-shadow: 0 -5px, 0 5px; + color: $light-color; + content: ""; + height: 3px; + pointer-events: none; + position: absolute; + right: $unit-2; + top: 50%; + transform: translate(50%, -50%); + width: 3px; + } + + .comparison-label { + left: $unit-4; + } + } + + .comparison-resizer { + animation: first-run 1.5s 1 ease-in-out; + cursor: ew-resize; + height: $unit-4; + left: 0; + max-width: 100%; + min-width: $unit-4; + opacity: 0; + outline: none; + position: relative; + resize: horizontal; + top: 50%; + transform: translateY(-50%) scaleY(30); + width: 0; + } + + .comparison-label { + background: rgba($dark-color, .5); + bottom: $unit-4; + color: $light-color; + padding: $unit-1 $unit-2; + position: absolute; + user-select: none; + } +} + +@keyframes first-run { + 0% { + width: 0; + } + 25% { + width: $unit-12; + } + 50% { + width: $unit-4; + } + 75% { + width: $unit-6; + } + 100% { + width: 0; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_dropdowns.scss b/yopa-web/resources/src/style/spectre/src/_dropdowns.scss new file mode 100644 index 0000000..324440b --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_dropdowns.scss @@ -0,0 +1,36 @@ +// Dropdown +.dropdown { + display: inline-block; + position: relative; + + .menu { + animation: slide-down .15s ease 1; + display: none; + left: 0; + max-height: 50vh; + overflow-y: auto; + position: absolute; + top: 100%; + } + + &.dropdown-right { + .menu { + left: auto; + right: 0; + } + } + + &.active .menu, + .dropdown-toggle:focus + .menu, + .menu:hover { + display: block; + } + + // Fix dropdown-toggle border radius in button groups + .btn-group { + .dropdown-toggle:nth-last-child(2) { + border-bottom-right-radius: $border-radius; + border-top-right-radius: $border-radius; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_empty.scss b/yopa-web/resources/src/style/spectre/src/_empty.scss new file mode 100644 index 0000000..accba9c --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_empty.scss @@ -0,0 +1,21 @@ +// Empty states (or Blank slates) +.empty { + background: $bg-color; + border-radius: $border-radius; + color: $gray-color-dark; + text-align: center; + padding: $unit-16 $unit-8; + + .empty-icon { + margin-bottom: $layout-spacing-lg; + } + + .empty-title, + .empty-subtitle { + margin: $layout-spacing auto; + } + + .empty-action { + margin-top: $layout-spacing-lg; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_filters.scss b/yopa-web/resources/src/style/spectre/src/_filters.scss new file mode 100644 index 0000000..37ccc89 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_filters.scss @@ -0,0 +1,37 @@ +// Filters +// The number of filter options +$filter-number: 8 !default; + +%filter-checked-nav { + background: $primary-color; + color: $light-color; +} + +%filter-checked-body { + display: none; +} + +.filter { + .filter-nav { + margin: $layout-spacing 0; + } + + .filter-body { + display: flex; + flex-wrap: wrap; + } + + .filter-tag { + @for $i from 0 through ($filter-number) { + &#tag-#{$i}:checked ~ .filter-nav .chip[for="tag-#{$i}"] { + @extend %filter-checked-nav; + } + } + + @for $i from 1 through ($filter-number) { + &#tag-#{$i}:checked ~ .filter-body .filter-item:not([data-tag~="tag-#{$i}"]) { + @extend %filter-checked-body; + } + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_forms.scss b/yopa-web/resources/src/style/spectre/src/_forms.scss new file mode 100644 index 0000000..20a6b4f --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_forms.scss @@ -0,0 +1,555 @@ +// Forms +.form-group { + &:not(:last-child) { + margin-bottom: $layout-spacing; + } +} + +fieldset { + margin-bottom: $layout-spacing-lg; +} + +legend { + font-size: $font-size-lg; + font-weight: 500; + margin-bottom: $layout-spacing-lg; +} + +// Form element: Label +.form-label { + display: block; + line-height: $line-height; + padding: $control-padding-y + $border-width 0; + + &.label-sm { + font-size: $font-size-sm; + padding: $control-padding-y-sm + $border-width 0; + } + + &.label-lg { + font-size: $font-size-lg; + padding: $control-padding-y-lg + $border-width 0; + } +} + +// Form element: Input +.form-input { + appearance: none; + background: $bg-color-light; + background-image: none; + border: $border-width solid $border-color-dark; + border-radius: $border-radius; + color: $body-font-color; + display: block; + font-size: $font-size; + height: $control-size; + line-height: $line-height; + max-width: 100%; + outline: none; + padding: $control-padding-y $control-padding-x; + position: relative; + transition: background .2s, border .2s, box-shadow .2s, color .2s; + width: 100%; + &:focus { + @include control-shadow(); + border-color: $primary-color; + } + &::placeholder { + color: $gray-color; + } + + // Input sizes + &.input-sm { + font-size: $font-size-sm; + height: $control-size-sm; + padding: $control-padding-y-sm $control-padding-x-sm; + } + + &.input-lg { + font-size: $font-size-lg; + height: $control-size-lg; + padding: $control-padding-y-lg $control-padding-x-lg; + } + + &.input-inline { + display: inline-block; + vertical-align: middle; + width: auto; + } + + // Input types + &[type="file"] { + height: auto; + } +} + +// Form element: Textarea +textarea.form-input { + &, + &.input-lg, + &.input-sm { + height: auto; + } +} + +// Form element: Input hint +.form-input-hint { + color: $gray-color; + font-size: $font-size-sm; + margin-top: $unit-1; + + .has-success &, + .is-success + & { + color: $success-color; + } + + .has-error &, + .is-error + & { + color: $error-color; + } +} + +// Form element: Select +.form-select { + appearance: none; + border: $border-width solid $border-color-dark; + border-radius: $border-radius; + color: inherit; + font-size: $font-size; + height: $control-size; + line-height: $line-height; + outline: none; + padding: $control-padding-y $control-padding-x; + vertical-align: middle; + width: 100%; + background: $bg-color-light; + &:focus { + @include control-shadow(); + border-color: $primary-color; + } + &::-ms-expand { + display: none; + } + + // Select sizes + &.select-sm { + font-size: $font-size-sm; + height: $control-size-sm; + padding: $control-padding-y-sm ($control-icon-size + $control-padding-x-sm) $control-padding-y-sm $control-padding-x-sm; + } + + &.select-lg { + font-size: $font-size-lg; + height: $control-size-lg; + padding: $control-padding-y-lg ($control-icon-size + $control-padding-x-lg) $control-padding-y-lg $control-padding-x-lg; + } + + // Multiple select + &[size], + &[multiple] { + height: auto; + padding: $control-padding-y $control-padding-x; + + option { + padding: $unit-h $unit-1; + } + } + &:not([multiple]):not([size]) { + background: $bg-color-light url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%204%205'%3E%3Cpath%20fill='%23667189'%20d='M2%200L0%202h4zm0%205L0%203h4z'/%3E%3C/svg%3E") no-repeat right .35rem center / .4rem .5rem; + padding-right: $control-icon-size + $control-padding-x; + } +} + +// Form Icons +.has-icon-left, +.has-icon-right { + position: relative; + + .form-icon { + height: $control-icon-size; + margin: 0 $control-padding-y; + position: absolute; + top: 50%; + transform: translateY(-50%); + width: $control-icon-size; + z-index: $zindex-0 + 1; + } +} + +.has-icon-left { + .form-icon { + left: $border-width; + } + + .form-input { + padding-left: $control-icon-size + $control-padding-y * 2; + } +} + +.has-icon-right { + .form-icon { + right: $border-width; + } + + .form-input { + padding-right: $control-icon-size + $control-padding-y * 2; + } +} + +// Form element: Checkbox and Radio +.form-checkbox, +.form-radio, +.form-switch { + display: block; + line-height: $line-height; + margin: ($control-size - $control-size-sm) / 2 0; + min-height: $control-size-sm; + padding: (($control-size-sm - $line-height) / 2) $control-padding-x (($control-size-sm - $line-height) / 2) ($control-icon-size + $control-padding-x); + position: relative; + + input { + clip: rect(0, 0, 0, 0); + height: 1px; + margin: -1px; + overflow: hidden; + position: absolute; + width: 1px; + &:focus + .form-icon { + @include control-shadow(); + border-color: $primary-color; + } + &:checked + .form-icon { + background: $primary-color; + border-color: $primary-color; + } + } + + .form-icon { + border: $border-width solid $border-color-dark; + cursor: pointer; + display: inline-block; + position: absolute; + transition: background .2s, border .2s, box-shadow .2s, color .2s; + } + + // Input checkbox, radio and switch sizes + &.input-sm { + font-size: $font-size-sm; + margin: 0; + } + + &.input-lg { + font-size: $font-size-lg; + margin: ($control-size-lg - $control-size-sm) / 2 0; + } +} + +.form-checkbox, +.form-radio { + .form-icon { + background: $bg-color-light; + height: $control-icon-size; + left: 0; + top: ($control-size-sm - $control-icon-size) / 2; + width: $control-icon-size; + } + + input { + &:active + .form-icon { + background: $bg-color-dark; + } + } +} +.form-checkbox { + .form-icon { + border-radius: $border-radius; + } + + input { + &:checked + .form-icon { + &::before { + background-clip: padding-box; + border: $border-width-lg solid $light-color; + border-left-width: 0; + border-top-width: 0; + content: ""; + height: 9px; + left: 50%; + margin-left: -3px; + margin-top: -6px; + position: absolute; + top: 50%; + transform: rotate(45deg); + width: 6px; + } + } + &:indeterminate + .form-icon { + background: $primary-color; + border-color: $primary-color; + &::before { + background: $bg-color-light; + content: ""; + height: 2px; + left: 50%; + margin-left: -5px; + margin-top: -1px; + position: absolute; + top: 50%; + width: 10px; + } + } + } +} +.form-radio { + .form-icon { + border-radius: 50%; + } + + input { + &:checked + .form-icon { + &::before { + background: $bg-color-light; + border-radius: 50%; + content: ""; + height: 6px; + left: 50%; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + width: 6px; + } + } + } +} + +// Form element: Switch +.form-switch { + padding-left: ($unit-8 + $control-padding-x); + + .form-icon { + background: $gray-color; + background-clip: padding-box; + border-radius: $unit-2 + $border-width; + height: $unit-4 + $border-width * 2; + left: 0; + top: ($control-size-sm - $unit-4) / 2 - $border-width; + width: $unit-8; + &::before { + background: $bg-color-light; + border-radius: 50%; + content: ""; + display: block; + height: $unit-4; + left: 0; + position: absolute; + top: 0; + transition: background .2s, border .2s, box-shadow .2s, color .2s, left .2s; + width: $unit-4; + } + } + + input { + &:checked + .form-icon { + &::before { + left: 14px; + } + } + &:active + .form-icon { + &::before { + background: $bg-color; + } + } + } +} + +// Form element: Input groups +.input-group { + display: flex; + + .input-group-addon { + background: $bg-color; + border: $border-width solid $border-color-dark; + border-radius: $border-radius; + line-height: $line-height; + padding: $control-padding-y $control-padding-x; + white-space: nowrap; + + &.addon-sm { + font-size: $font-size-sm; + padding: $control-padding-y-sm $control-padding-x-sm; + } + + &.addon-lg { + font-size: $font-size-lg; + padding: $control-padding-y-lg $control-padding-x-lg; + } + } + + .form-input, + .form-select { + flex: 1 1 auto; + width: 1%; + } + + .input-group-btn { + z-index: $zindex-0; + } + + .form-input, + .form-select, + .input-group-addon, + .input-group-btn { + &:first-child:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + } + &:not(:first-child):not(:last-child) { + border-radius: 0; + margin-left: -$border-width; + } + &:last-child:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + margin-left: -$border-width; + } + &:focus { + z-index: $zindex-0 + 1; + } + } + + .form-select { + width: auto; + } + + &.input-inline { + display: inline-flex; + } +} + +// Form validation states +.form-input, +.form-select { + .has-success &, + &.is-success { + background: lighten($success-color, 53%); + border-color: $success-color; + &:focus { + @include control-shadow($success-color); + } + } + + .has-error &, + &.is-error { + background: lighten($error-color, 53%); + border-color: $error-color; + &:focus { + @include control-shadow($error-color); + } + } +} + +.form-checkbox, +.form-radio, +.form-switch { + .has-error &, + &.is-error { + .form-icon { + border-color: $error-color; + } + + input { + &:checked + .form-icon { + background: $error-color; + border-color: $error-color; + } + + &:focus + .form-icon { + @include control-shadow($error-color); + border-color: $error-color; + } + } + } +} + +.form-checkbox { + .has-error &, + &.is-error { + input { + &:indeterminate + .form-icon { + background: $error-color; + border-color: $error-color; + } + } + } +} + +// validation based on :placeholder-shown (Edge doesn't support it yet) +.form-input { + &:not(:placeholder-shown) { + &:invalid { + border-color: $error-color; + &:focus { + @include control-shadow($error-color); + background: lighten($error-color, 53%); + } + + & + .form-input-hint { + color: $error-color; + } + } + } +} + +// Form disabled and readonly +.form-input, +.form-select { + &:disabled, + &.disabled { + background-color: $bg-color-dark; + cursor: not-allowed; + opacity: .5; + } +} + +.form-input { + &[readonly] { + background-color: $bg-color; + } +} + +input { + &:disabled, + &.disabled { + & + .form-icon { + background: $bg-color-dark; + cursor: not-allowed; + opacity: .5; + } + } +} + +.form-switch { + input { + &:disabled, + &.disabled { + & + .form-icon::before { + background: $bg-color-light; + } + } + } +} + +// Form horizontal +.form-horizontal { + padding: $layout-spacing 0; + + .form-group { + display: flex; + flex-wrap: wrap; + } +} + +// Form inline +.form-inline { + display: inline-block; +} diff --git a/yopa-web/resources/src/style/spectre/src/_hero.scss b/yopa-web/resources/src/style/spectre/src/_hero.scss new file mode 100644 index 0000000..0044461 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_hero.scss @@ -0,0 +1,22 @@ +// Hero +.hero { + display: flex; + flex-direction: column; + justify-content: space-between; + padding-bottom: 4rem; + padding-top: 4rem; + + &.hero-sm { + padding-bottom: 2rem; + padding-top: 2rem; + } + + &.hero-lg { + padding-bottom: 8rem; + padding-top: 8rem; + } + + .hero-body { + padding: $layout-spacing; + } +} \ No newline at end of file diff --git a/yopa-web/resources/src/style/spectre/src/_icons.scss b/yopa-web/resources/src/style/spectre/src/_icons.scss new file mode 100644 index 0000000..4f3c5ce --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_icons.scss @@ -0,0 +1,5 @@ +// CSS Icons +@import "icons/icons-core"; +@import "icons/icons-navigation"; +@import "icons/icons-action"; +@import "icons/icons-object"; \ No newline at end of file diff --git a/yopa-web/resources/src/style/spectre/src/_labels.scss b/yopa-web/resources/src/style/spectre/src/_labels.scss new file mode 100644 index 0000000..ca693cd --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_labels.scss @@ -0,0 +1,34 @@ +// Labels +.label { + @include label-base(); + @include label-variant(lighten($body-font-color, 5%), $bg-color-dark); + display: inline-block; + + // Label rounded + &.label-rounded { + border-radius: 5rem; + padding-left: .4rem; + padding-right: .4rem; + } + + // Label colors + &.label-primary { + @include label-variant($light-color, $primary-color); + } + + &.label-secondary { + @include label-variant($primary-color, $secondary-color); + } + + &.label-success { + @include label-variant($light-color, $success-color); + } + + &.label-warning { + @include label-variant($light-color, $warning-color); + } + + &.label-error { + @include label-variant($light-color, $error-color); + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_layout.scss b/yopa-web/resources/src/style/spectre/src/_layout.scss new file mode 100644 index 0000000..e6bad9a --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_layout.scss @@ -0,0 +1,446 @@ +// Layout +.container { + margin-left: auto; + margin-right: auto; + padding-left: $layout-spacing; + padding-right: $layout-spacing; + width: 100%; + + $grid-spacing: ($layout-spacing / ($layout-spacing * 0 + 1)) * $html-font-size; + + &.grid-xl { + max-width: $grid-spacing * 2 + $size-xl; + } + + &.grid-lg { + max-width: $grid-spacing * 2 + $size-lg; + } + + &.grid-md { + max-width: $grid-spacing * 2 + $size-md; + } + + &.grid-sm { + max-width: $grid-spacing * 2 + $size-sm; + } + + &.grid-xs { + max-width: $grid-spacing * 2 + $size-xs; + } +} + +// Responsive breakpoint system +.show-xs, +.show-sm, +.show-md, +.show-lg, +.show-xl { + display: none !important; +} + +// Responsive grid system +.cols, +.columns { + display: flex; + flex-wrap: wrap; + margin-left: -$layout-spacing; + margin-right: -$layout-spacing; + + &.col-gapless { + margin-left: 0; + margin-right: 0; + + & > .column { + padding-left: 0; + padding-right: 0; + } + } + &.col-oneline { + flex-wrap: nowrap; + overflow-x: auto; + } +} +[class~="col-"], +.column { + flex: 1; + max-width: 100%; + padding-left: $layout-spacing; + padding-right: $layout-spacing; + + &.col-12, + &.col-11, + &.col-10, + &.col-9, + &.col-8, + &.col-7, + &.col-6, + &.col-5, + &.col-4, + &.col-3, + &.col-2, + &.col-1, + &.col-auto { + flex: none; + } +} +.col-12 { + width: 100%; +} +.col-11 { + width: 91.66666667%; +} +.col-10 { + width: 83.33333333%; +} +.col-9 { + width: 75%; +} +.col-8 { + width: 66.66666667%; +} +.col-7 { + width: 58.33333333%; +} +.col-6 { + width: 50%; +} +.col-5 { + width: 41.66666667%; +} +.col-4 { + width: 33.33333333%; +} +.col-3 { + width: 25%; +} +.col-2 { + width: 16.66666667%; +} +.col-1 { + width: 8.33333333%; +} +.col-auto { + flex: 0 0 auto; + max-width: none; + width: auto; +} +.col-mx-auto { + margin-left: auto; + margin-right: auto; +} +.col-ml-auto { + margin-left: auto; +} +.col-mr-auto { + margin-right: auto; +} +@media (max-width: $size-xl) { + .col-xl-12, + .col-xl-11, + .col-xl-10, + .col-xl-9, + .col-xl-8, + .col-xl-7, + .col-xl-6, + .col-xl-5, + .col-xl-4, + .col-xl-3, + .col-xl-2, + .col-xl-1, + .col-xl-auto { + flex: none; + } + .col-xl-12 { + width: 100%; + } + .col-xl-11 { + width: 91.66666667%; + } + .col-xl-10 { + width: 83.33333333%; + } + .col-xl-9 { + width: 75%; + } + .col-xl-8 { + width: 66.66666667%; + } + .col-xl-7 { + width: 58.33333333%; + } + .col-xl-6 { + width: 50%; + } + .col-xl-5 { + width: 41.66666667%; + } + .col-xl-4 { + width: 33.33333333%; + } + .col-xl-3 { + width: 25%; + } + .col-xl-2 { + width: 16.66666667%; + } + .col-xl-1 { + width: 8.33333333%; + } + .col-xl-auto { + width: auto; + } + .hide-xl { + display: none !important; + } + .show-xl { + display: block !important; + } +} +@media (max-width: $size-lg) { + .col-lg-12, + .col-lg-11, + .col-lg-10, + .col-lg-9, + .col-lg-8, + .col-lg-7, + .col-lg-6, + .col-lg-5, + .col-lg-4, + .col-lg-3, + .col-lg-2, + .col-lg-1, + .col-lg-auto { + flex: none; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-auto { + width: auto; + } + .hide-lg { + display: none !important; + } + .show-lg { + display: block !important; + } +} +@media (max-width: $size-md) { + .col-md-12, + .col-md-11, + .col-md-10, + .col-md-9, + .col-md-8, + .col-md-7, + .col-md-6, + .col-md-5, + .col-md-4, + .col-md-3, + .col-md-2, + .col-md-1, + .col-md-auto { + flex: none; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-auto { + width: auto; + } + .hide-md { + display: none !important; + } + .show-md { + display: block !important; + } +} +@media (max-width: $size-sm) { + .col-sm-12, + .col-sm-11, + .col-sm-10, + .col-sm-9, + .col-sm-8, + .col-sm-7, + .col-sm-6, + .col-sm-5, + .col-sm-4, + .col-sm-3, + .col-sm-2, + .col-sm-1, + .col-sm-auto { + flex: none; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-auto { + width: auto; + } + .hide-sm { + display: none !important; + } + .show-sm { + display: block !important; + } +} +@media (max-width: $size-xs) { + .col-xs-12, + .col-xs-11, + .col-xs-10, + .col-xs-9, + .col-xs-8, + .col-xs-7, + .col-xs-6, + .col-xs-5, + .col-xs-4, + .col-xs-3, + .col-xs-2, + .col-xs-1, + .col-xs-auto { + flex: none; + } + .col-xs-12 { + width: 100%; + } + .col-xs-11 { + width: 91.66666667%; + } + .col-xs-10 { + width: 83.33333333%; + } + .col-xs-9 { + width: 75%; + } + .col-xs-8 { + width: 66.66666667%; + } + .col-xs-7 { + width: 58.33333333%; + } + .col-xs-6 { + width: 50%; + } + .col-xs-5 { + width: 41.66666667%; + } + .col-xs-4 { + width: 33.33333333%; + } + .col-xs-3 { + width: 25%; + } + .col-xs-2 { + width: 16.66666667%; + } + .col-xs-1 { + width: 8.33333333%; + } + .col-xs-auto { + width: auto; + } + .hide-xs { + display: none !important; + } + .show-xs { + display: block !important; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_media.scss b/yopa-web/resources/src/style/spectre/src/_media.scss new file mode 100644 index 0000000..4029e4c --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_media.scss @@ -0,0 +1,75 @@ +// Media +// Image responsive +.img-responsive { + display: block; + height: auto; + max-width: 100%; +} + +// object-fit support is coming to Microsoft Edge +// https://developer.microsoft.com/en-us/microsoft-edge/platform/status/objectfitandobjectposition/ +.img-fit-cover { + object-fit: cover; +} + +.img-fit-contain { + object-fit: contain; +} + +// Video responsive +.video-responsive { + display: block; + overflow: hidden; + padding: 0; + position: relative; + width: 100%; + &::before { + content: ""; + display: block; + padding-bottom: 56.25%; // Default ratio 16:9, you can calculate this value by dividing 9 by 16 + } + + iframe, + object, + embed { + border: 0; + bottom: 0; + height: 100%; + left: 0; + position: absolute; + right: 0; + top: 0; + width: 100%; + } +} + +video.video-responsive { + height: auto; + max-width: 100%; + + &::before { + content: none; + } +} + +.video-responsive-4-3 { + &::before { + padding-bottom: 75%; // Ratio 4:3 + } +} + +.video-responsive-1-1 { + &::before { + padding-bottom: 100%; // Ratio 1:1 + } +} + +// Figure +.figure { + margin: 0 0 $layout-spacing 0; + + .figure-caption { + color: $gray-color-dark; + margin-top: $layout-spacing; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_menus.scss b/yopa-web/resources/src/style/spectre/src/_menus.scss new file mode 100644 index 0000000..411cada --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_menus.scss @@ -0,0 +1,66 @@ +// Menus +.menu { + @include shadow-variant(.05rem); + background: $bg-color-light; + border-radius: $border-radius; + list-style: none; + margin: 0; + min-width: $control-width-xs; + padding: $unit-2; + transform: translateY($layout-spacing-sm); + z-index: $zindex-3; + + &.menu-nav { + background: transparent; + box-shadow: none; + } + + .menu-item { + margin-top: 0; + padding: 0 $unit-2; + position: relative; + text-decoration: none; + + & > a { + border-radius: $border-radius; + color: inherit; + display: block; + margin: 0 (-$unit-2); + padding: $unit-1 $unit-2; + text-decoration: none; + &:focus, + &:hover { + background: $secondary-color; + color: $primary-color; + } + &:active, + &.active { + background: $secondary-color; + color: $primary-color; + } + } + + .form-checkbox, + .form-radio, + .form-switch { + margin: $unit-h 0; + } + + & + .menu-item { + margin-top: $unit-1; + } + } + + .menu-badge { + align-items: center; + display: flex; + height: 100%; + position: absolute; + right: 0; + top: 0; + + .label { + margin-right: $unit-2; + } + } +} \ No newline at end of file diff --git a/yopa-web/resources/src/style/spectre/src/_meters.scss b/yopa-web/resources/src/style/spectre/src/_meters.scss new file mode 100644 index 0000000..9fd98b0 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_meters.scss @@ -0,0 +1,57 @@ +// Meters +// Credit: https://css-tricks.com/html5-meter-element/ +.meter { + appearance: none; + background: $bg-color; + border: 0; + border-radius: $border-radius; + display: block; + width: 100%; + height: $unit-4; + + &::-webkit-meter-inner-element { + display: block; + } + + &::-webkit-meter-bar, + &::-webkit-meter-optimum-value, + &::-webkit-meter-suboptimum-value, + &::-webkit-meter-even-less-good-value { + border-radius: $border-radius; + } + + &::-webkit-meter-bar { + background: $bg-color; + } + + &::-webkit-meter-optimum-value { + background: $success-color; + } + + &::-webkit-meter-suboptimum-value { + background: $warning-color; + } + + &::-webkit-meter-even-less-good-value { + background: $error-color; + } + + &::-moz-meter-bar, + &:-moz-meter-optimum, + &:-moz-meter-sub-optimum, + &:-moz-meter-sub-sub-optimum { + border-radius: $border-radius; + } + + &:-moz-meter-optimum::-moz-meter-bar { + background: $success-color; + } + + &:-moz-meter-sub-optimum::-moz-meter-bar { + background: $warning-color; + } + + &:-moz-meter-sub-sub-optimum::-moz-meter-bar { + background: $error-color; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_mixins.scss b/yopa-web/resources/src/style/spectre/src/_mixins.scss new file mode 100644 index 0000000..d3a28d5 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_mixins.scss @@ -0,0 +1,10 @@ +// Mixins +@import "mixins/avatar"; +@import "mixins/button"; +@import "mixins/clearfix"; +@import "mixins/color"; +@import "mixins/label"; +@import "mixins/position"; +@import "mixins/shadow"; +@import "mixins/text"; +@import "mixins/toast"; \ No newline at end of file diff --git a/yopa-web/resources/src/style/spectre/src/_modals.scss b/yopa-web/resources/src/style/spectre/src/_modals.scss new file mode 100644 index 0000000..a7b3f10 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_modals.scss @@ -0,0 +1,87 @@ +// Modals +.modal { + align-items: center; + bottom: 0; + display: none; + justify-content: center; + left: 0; + opacity: 0; + overflow: hidden; + padding: $layout-spacing; + position: fixed; + right: 0; + top: 0; + + &:target, + &.active { + display: flex; + opacity: 1; + z-index: $zindex-4; + + .modal-overlay { + background: rgba($bg-color, .75); + bottom: 0; + cursor: default; + display: block; + left: 0; + position: absolute; + right: 0; + top: 0; + } + + .modal-container { + animation: slide-down .2s ease 1; + z-index: $zindex-0; + } + } + + &.modal-sm { + .modal-container { + max-width: $control-width-sm; + padding: 0 $unit-2; + } + } + + &.modal-lg { + .modal-overlay { + background: $bg-color-light; + } + + .modal-container { + box-shadow: none; + max-width: $control-width-lg; + } + } +} + +.modal-container { + @include shadow-variant(.2rem); + background: $bg-color-light; + border-radius: $border-radius; + display: flex; + flex-direction: column; + max-height: 75vh; + max-width: $control-width-md; + padding: 0 $unit-4; + width: 100%; + + &.modal-fullheight { + max-height: 100vh; + } + + .modal-header { + color: $dark-color; + padding: $unit-4; + } + + .modal-body { + overflow-y: auto; + padding: $unit-4; + position: relative; + } + + .modal-footer { + padding: $unit-4; + text-align: right; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_navbar.scss b/yopa-web/resources/src/style/spectre/src/_navbar.scss new file mode 100755 index 0000000..1164296 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_navbar.scss @@ -0,0 +1,28 @@ +// Navbar +.navbar { + align-items: stretch; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + + .navbar-section { + align-items: center; + display: flex; + flex: 1 0 0; + + &:not(:first-child):last-child { + justify-content: flex-end; + } + } + + .navbar-center { + align-items: center; + display: flex; + flex: 0 0 auto; + } + + .navbar-brand { + font-size: $font-size-lg; + text-decoration: none; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_navs.scss b/yopa-web/resources/src/style/spectre/src/_navs.scss new file mode 100644 index 0000000..4bedc27 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_navs.scss @@ -0,0 +1,34 @@ +// Navs +.nav { + display: flex; + flex-direction: column; + list-style: none; + margin: $unit-1 0; + + .nav-item { + a { + color: $gray-color-dark; + padding: $unit-1 $unit-2; + text-decoration: none; + &:focus, + &:hover { + color: $primary-color; + } + } + &.active { + & > a { + color: darken($gray-color-dark, 10%); + font-weight: bold; + &:focus, + &:hover { + color: $primary-color; + } + } + } + } + + & .nav { + margin-bottom: $unit-2; + margin-left: $unit-4; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_normalize.scss b/yopa-web/resources/src/style/spectre/src/_normalize.scss new file mode 100644 index 0000000..a098a84 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_normalize.scss @@ -0,0 +1,446 @@ +/* Manually forked from Normalize.css */ +/* normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ + +/** + * 1. Change the default font family in all browsers (opinionated). + * 2. Correct the line height in all browsers. + * 3. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +/* Document + ========================================================================== */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 3 */ + -webkit-text-size-adjust: 100%; /* 3 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers (opinionated). + */ + +body { + margin: 0; +} + +/** + * Add the correct display in IE 9-. + */ + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8 (removed). + */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. (removed) + * 2. Correct the odd `em` font sizing in all browsers. + */ + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ +} + +/** + * Remove the outline on focused links when they are also active or hovered + * in all browsers (opinionated). + */ + +a:active, +a:hover { + outline-width: 0; +} + +/** + * Modify default styling of address. + */ + +address { + font-style: normal; +} + +/** + * 1. Remove the bottom border in Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. (removed) + */ + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +pre, +samp { + font-family: $mono-font-family; /* 1 (changed) */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. (Removed) + */ + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; + font-weight: 400; /* (added) */ +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 (changed) */ + font-size: inherit; /* 1 (changed) */ + line-height: inherit; /* 1 (changed) */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule (removed). + */ + + +/** + * Change the border, margin, and padding in all browsers (opinionated) (changed). + */ + +fieldset { + border: 0; + margin: 0; + padding: 0; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; + outline: none; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} diff --git a/yopa-web/resources/src/style/spectre/src/_off-canvas.scss b/yopa-web/resources/src/style/spectre/src/_off-canvas.scss new file mode 100644 index 0000000..f3b8b9f --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_off-canvas.scss @@ -0,0 +1,95 @@ +// Off canvas menus +$off-canvas-breakpoint: $size-lg !default; + +.off-canvas { + display: flex; + flex-flow: nowrap; + height: 100%; + position: relative; + width: 100%; + + .off-canvas-toggle { + display: block; + position: absolute; + top: $layout-spacing; + transition: none; + z-index: $zindex-0; + @if $rtl == true { + right: $layout-spacing; + } @else { + left: $layout-spacing; + } + } + + .off-canvas-sidebar { + background: $bg-color; + bottom: 0; + min-width: 10rem; + overflow-y: auto; + position: fixed; + top: 0; + transition: transform .25s; + z-index: $zindex-2; + @if $rtl == true { + right: 0; + transform: translateX(100%); + } @else { + left: 0; + transform: translateX(-100%); + } + } + + .off-canvas-content { + flex: 1 1 auto; + height: 100%; + padding: $layout-spacing $layout-spacing $layout-spacing 4rem; + } + + .off-canvas-overlay { + background: rgba($dark-color, .1); + border-color: transparent; + border-radius: 0; + bottom: 0; + display: none; + height: 100%; + left: 0; + position: fixed; + right: 0; + top: 0; + width: 100%; + } + + .off-canvas-sidebar { + &:target, + &.active { + transform: translateX(0); + } + + &:target ~ .off-canvas-overlay, + &.active ~ .off-canvas-overlay { + display: block; + z-index: $zindex-1; + } + } +} + +// Responsive layout +@media (min-width: $off-canvas-breakpoint) { + .off-canvas { + &.off-canvas-sidebar-show { + .off-canvas-toggle { + display: none; + } + + .off-canvas-sidebar { + flex: 0 0 auto; + position: relative; + transform: none; + } + + .off-canvas-overlay { + display: none !important; + } + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_pagination.scss b/yopa-web/resources/src/style/spectre/src/_pagination.scss new file mode 100644 index 0000000..4c0e011 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_pagination.scss @@ -0,0 +1,60 @@ +// Pagination +.pagination { + display: flex; + list-style: none; + margin: $unit-1 0; + padding: $unit-1 0; + + .page-item { + margin: $unit-1 $unit-o; + + span { + display: inline-block; + padding: $unit-1 $unit-1; + } + + a { + border-radius: $border-radius; + display: inline-block; + padding: $unit-1 $unit-2; + text-decoration: none; + &:focus, + &:hover { + color: $primary-color; + } + } + + &.disabled { + a { + cursor: default; + opacity: .5; + pointer-events: none; + } + } + + &.active { + a { + background: $primary-color; + color: $light-color; + } + } + + &.page-prev, + &.page-next { + flex: 1 0 50%; + } + + &.page-next { + text-align: right; + } + + .page-item-title { + margin: 0; + } + + .page-item-subtitle { + margin: 0; + opacity: .5; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_panels.scss b/yopa-web/resources/src/style/spectre/src/_panels.scss new file mode 100644 index 0000000..386f96e --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_panels.scss @@ -0,0 +1,23 @@ +// Panels +.panel { + border: $border-width solid $border-color; + border-radius: $border-radius; + display: flex; + flex-direction: column; + + .panel-header, + .panel-footer { + flex: 0 0 auto; + padding: $layout-spacing-lg; + } + + .panel-nav { + flex: 0 0 auto; + } + + .panel-body { + flex: 1 1 auto; + overflow-y: auto; + padding: 0 $layout-spacing-lg; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_parallax.scss b/yopa-web/resources/src/style/spectre/src/_parallax.scss new file mode 100644 index 0000000..ea244e5 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_parallax.scss @@ -0,0 +1,135 @@ +// Parallax +$parallax-deg: 3deg !default; +$parallax-offset: 4.5px !default; +$parallax-offset-z: 50px !default; +$parallax-perspective: 1000px !default; +$parallax-scale: .95 !default; +$parallax-fade-color: rgba(255, 255, 255, .35) !default; + +// Mixin: Parallax direction +@mixin parallax-dir() { + height: 50%; + outline: none; + position: absolute; + width: 50%; + z-index: $zindex-1; +} + +.parallax { + display: block; + height: auto; + position: relative; + width: auto; + + .parallax-content { + @include shadow-variant(1rem); + height: auto; + transform: perspective($parallax-perspective); + transform-style: preserve-3d; + transition: all .4s ease; + width: 100%; + + &::before { + content: ""; + display: block; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + } + } + + .parallax-front { + align-items: center; + color: $light-color; + display: flex; + height: 100%; + justify-content: center; + left: 0; + position: absolute; + text-align: center; + text-shadow: 0 0 20px rgba($dark-color, .75); + top: 0; + transform: translateZ($parallax-offset-z) scale($parallax-scale); + transition: transform .4s; + width: 100%; + z-index: $zindex-0; + } + + .parallax-top-left { + @include parallax-dir(); + left: 0; + top: 0; + + &:focus ~ .parallax-content, + &:hover ~ .parallax-content { + transform: perspective($parallax-perspective) rotateX($parallax-deg) rotateY(-$parallax-deg); + + &::before { + background: linear-gradient(135deg, $parallax-fade-color 0%, transparent 50%); + } + + .parallax-front { + transform: translate3d($parallax-offset, $parallax-offset, $parallax-offset-z) scale($parallax-scale); + } + } + } + + .parallax-top-right { + @include parallax-dir(); + right: 0; + top: 0; + + &:focus ~ .parallax-content, + &:hover ~ .parallax-content { + transform: perspective($parallax-perspective) rotateX($parallax-deg) rotateY($parallax-deg); + + &::before { + background: linear-gradient(-135deg, $parallax-fade-color 0%, transparent 50%); + } + + .parallax-front { + transform: translate3d(-$parallax-offset, $parallax-offset, $parallax-offset-z) scale($parallax-scale); + } + } + } + + .parallax-bottom-left { + @include parallax-dir(); + bottom: 0; + left: 0; + + &:focus ~ .parallax-content, + &:hover ~ .parallax-content { + transform: perspective($parallax-perspective) rotateX(-$parallax-deg) rotateY(-$parallax-deg); + + &::before { + background: linear-gradient(45deg, $parallax-fade-color 0%, transparent 50%); + } + + .parallax-front { + transform: translate3d($parallax-offset, -$parallax-offset, $parallax-offset-z) scale($parallax-scale); + } + } + } + + .parallax-bottom-right { + @include parallax-dir(); + bottom: 0; + right: 0; + + &:focus ~ .parallax-content, + &:hover ~ .parallax-content { + transform: perspective($parallax-perspective) rotateX(-$parallax-deg) rotateY($parallax-deg); + + &::before { + background: linear-gradient(-45deg, $parallax-fade-color 0%, transparent 50%); + } + + .parallax-front { + transform: translate3d(-$parallax-offset, -$parallax-offset, $parallax-offset-z) scale($parallax-scale); + } + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_popovers.scss b/yopa-web/resources/src/style/spectre/src/_popovers.scss new file mode 100644 index 0000000..35b6bcd --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_popovers.scss @@ -0,0 +1,65 @@ +// Popovers +.popover { + display: inline-block; + position: relative; + + .popover-container { + left: 50%; + opacity: 0; + padding: $layout-spacing; + position: absolute; + top: 0; + transform: translate(-50%, -50%) scale(0); + transition: transform .2s; + width: $control-width-sm; + z-index: $zindex-3; + } + + *:focus + .popover-container, + &:hover .popover-container { + display: block; + opacity: 1; + transform: translate(-50%, -100%) scale(1); + } + + &.popover-right { + .popover-container { + left: 100%; + top: 50%; + } + + *:focus + .popover-container, + &:hover .popover-container { + transform: translate(0, -50%) scale(1); + } + } + + &.popover-bottom { + .popover-container { + left: 50%; + top: 100%; + } + + *:focus + .popover-container, + &:hover .popover-container { + transform: translate(-50%, 0) scale(1); + } + } + + &.popover-left { + .popover-container { + left: 0; + top: 50%; + } + + *:focus + .popover-container, + &:hover .popover-container { + transform: translate(-100%, -50%) scale(1); + } + } + + .card { + @include shadow-variant(.2rem); + border: 0; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_progress.scss b/yopa-web/resources/src/style/spectre/src/_progress.scss new file mode 100644 index 0000000..f173772 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_progress.scss @@ -0,0 +1,45 @@ +// Progress +// Credit: https://css-tricks.com/html5-progress-element/ +.progress { + appearance: none; + background: $bg-color-dark; + border: 0; + border-radius: $border-radius; + color: $primary-color; + height: $unit-1; + position: relative; + width: 100%; + + &::-webkit-progress-bar { + background: transparent; + border-radius: $border-radius; + } + + &::-webkit-progress-value { + background: $primary-color; + border-radius: $border-radius; + } + + &::-moz-progress-bar { + background: $primary-color; + border-radius: $border-radius; + } + + &:indeterminate { + animation: progress-indeterminate 1.5s linear infinite; + background: $bg-color-dark linear-gradient(to right, $primary-color 30%, $bg-color-dark 30%) top left / 150% 150% no-repeat; + + &::-moz-progress-bar { + background: transparent; + } + } +} + +@keyframes progress-indeterminate { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_sliders.scss b/yopa-web/resources/src/style/spectre/src/_sliders.scss new file mode 100644 index 0000000..3ff38e8 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_sliders.scss @@ -0,0 +1,99 @@ +// Sliders +// Credit: https://css-tricks.com/styling-cross-browser-compatible-range-inputs-css/ +.slider { + appearance: none; + background: transparent; + display: block; + width: 100%; + height: $unit-6; + + &:focus { + @include control-shadow(); + outline: none; + } + + &.tooltip:not([data-tooltip]) { + &::after { + content: attr(value); + } + } + + // Slider Thumb + &::-webkit-slider-thumb { + -webkit-appearance: none; + background: $primary-color; + border: 0; + border-radius: 50%; + height: $unit-3; + margin-top: -($unit-3 - $unit-h) / 2; + transition: transform .2s; + width: $unit-3; + } + &::-moz-range-thumb { + background: $primary-color; + border: 0; + border-radius: 50%; + height: $unit-3; + transition: transform .2s; + width: $unit-3; + } + &::-ms-thumb { + background: $primary-color; + border: 0; + border-radius: 50%; + height: $unit-3; + transition: transform .2s; + width: $unit-3; + } + + &:active { + &::-webkit-slider-thumb { + transform: scale(1.25); + } + &::-moz-range-thumb { + transform: scale(1.25); + } + &::-ms-thumb { + transform: scale(1.25); + } + } + + &:disabled, + &.disabled { + &::-webkit-slider-thumb { + background: $gray-color-light; + transform: scale(1); + } + &::-moz-range-thumb { + background: $gray-color-light; + transform: scale(1); + } + &::-ms-thumb { + background: $gray-color-light; + transform: scale(1); + } + } + + // Slider Track + &::-webkit-slider-runnable-track { + background: $bg-color-dark; + border-radius: $border-radius; + height: $unit-h; + width: 100%; + } + &::-moz-range-track { + background: $bg-color-dark; + border-radius: $border-radius; + height: $unit-h; + width: 100%; + } + &::-ms-track { + background: $bg-color-dark; + border-radius: $border-radius; + height: $unit-h; + width: 100%; + } + &::-ms-fill-lower { + background: $primary-color; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_steps.scss b/yopa-web/resources/src/style/spectre/src/_steps.scss new file mode 100644 index 0000000..f642ff8 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_steps.scss @@ -0,0 +1,71 @@ +// Steps +.step { + display: flex; + flex-wrap: nowrap; + list-style: none; + margin: $unit-1 0; + width: 100%; + + .step-item { + flex: 1 1 0; + margin-top: 0; + min-height: 1rem; + text-align: center; + position: relative; + + &:not(:first-child)::before { + background: $primary-color; + content: ""; + height: 2px; + left: -50%; + position: absolute; + top: 9px; + width: 100%; + } + + a { + color: $primary-color; + display: inline-block; + padding: 20px 10px 0; + text-decoration: none; + + &::before { + background: $primary-color; + border: $border-width-lg solid $light-color; + border-radius: 50%; + content: ""; + display: block; + height: $unit-3; + left: 50%; + position: absolute; + top: $unit-1; + transform: translateX(-50%); + width: $unit-3; + z-index: $zindex-0; + } + } + + &.active { + a { + &::before { + background: $light-color; + border: $border-width-lg solid $primary-color; + } + } + + & ~ .step-item { + &::before { + background: $border-color; + } + + a { + color: $gray-color; + + &::before { + background: $border-color; + } + } + } + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_tables.scss b/yopa-web/resources/src/style/spectre/src/_tables.scss new file mode 100644 index 0000000..656c03e --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_tables.scss @@ -0,0 +1,57 @@ +// Tables +.table { + border-collapse: collapse; + border-spacing: 0; + width: 100%; + @if $rtl == true { + text-align: right; + } @else { + text-align: left; + } + + &.table-striped { + tbody { + tr:nth-of-type(odd) { + background: $bg-color; + } + } + } + + &, + &.table-striped { + tbody { + tr { + &.active { + background: $bg-color-dark; + } + } + } + } + + &.table-hover { + tbody { + tr { + &:hover { + background: $bg-color-dark; + } + } + } + } + + // Scollable tables + &.table-scroll { + display: block; + overflow-x: auto; + padding-bottom: .75rem; + white-space: nowrap; + } + + td, + th { + border-bottom: $border-width solid $border-color; + padding: $unit-3 $unit-2; + } + th { + border-bottom-width: $border-width-lg; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_tabs.scss b/yopa-web/resources/src/style/spectre/src/_tabs.scss new file mode 100644 index 0000000..0dcbaf3 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_tabs.scss @@ -0,0 +1,66 @@ +// Tabs +.tab { + align-items: center; + border-bottom: $border-width solid $border-color; + display: flex; + flex-wrap: wrap; + list-style: none; + margin: $unit-1 0 ($unit-1 - $border-width) 0; + + .tab-item { + margin-top: 0; + + a { + border-bottom: $border-width-lg solid transparent; + color: inherit; + display: block; + margin: 0 $unit-2 0 0; + padding: $unit-2 $unit-1 $unit-2 - $border-width-lg $unit-1; + text-decoration: none; + &:focus, + &:hover { + color: $link-color; + } + } + &.active a, + a.active { + border-bottom-color: $primary-color; + color: $link-color; + } + + &.tab-action { + flex: 1 0 auto; + text-align: right; + } + + .btn-clear { + margin-top: -$unit-1; + } + } + + &.tab-block { + .tab-item { + flex: 1 0 0; + text-align: center; + + a { + margin: 0; + } + + .badge { + &[data-badge]::after { + position: absolute; + right: $unit-h; + top: $unit-h; + transform: translate(0, 0); + } + } + } + } + + &:not(.tab-block) { + .badge { + padding-right: 0; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_tiles.scss b/yopa-web/resources/src/style/spectre/src/_tiles.scss new file mode 100644 index 0000000..742bbae --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_tiles.scss @@ -0,0 +1,38 @@ +// Tiles +.tile { + align-content: space-between; + align-items: flex-start; + display: flex; + + .tile-icon, + .tile-action { + flex: 0 0 auto; + } + .tile-content { + flex: 1 1 auto; + &:not(:first-child) { + padding-left: $unit-2; + } + &:not(:last-child) { + padding-right: $unit-2; + } + } + .tile-title, + .tile-subtitle { + line-height: $line-height; + } + + &.tile-centered { + align-items: center; + + .tile-content { + overflow: hidden; + } + + .tile-title, + .tile-subtitle { + @include text-ellipsis(); + margin-bottom: 0; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_timelines.scss b/yopa-web/resources/src/style/spectre/src/_timelines.scss new file mode 100644 index 0000000..c56746d --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_timelines.scss @@ -0,0 +1,56 @@ +// Timelines +.timeline { + .timeline-item { + display: flex; + margin-bottom: $unit-6; + position: relative; + &::before { + background: $border-color; + content: ""; + height: 100%; + left: 11px; + position: absolute; + top: $unit-6; + width: 2px; + } + + .timeline-left { + flex: 0 0 auto; + } + + .timeline-content { + flex: 1 1 auto; + padding: 2px 0 2px $layout-spacing-lg; + } + + .timeline-icon { + align-items: center; + border-radius: 50%; + color: $light-color; + display: flex; + height: $unit-6; + justify-content: center; + text-align: center; + width: $unit-6; + &::before { + border: $border-width-lg solid $primary-color; + border-radius: 50%; + content: ""; + display: block; + height: $unit-2; + left: $unit-2; + position: absolute; + top: $unit-2; + width: $unit-2; + } + + &.icon-lg { + background: $primary-color; + line-height: $line-height; + &::before { + content: none; + } + } + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_toasts.scss b/yopa-web/resources/src/style/spectre/src/_toasts.scss new file mode 100644 index 0000000..fef15f8 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_toasts.scss @@ -0,0 +1,48 @@ +// Toasts +.toast { + @include toast-variant($dark-color); + border: $border-width solid $dark-color; + border-radius: $border-radius; + color: $light-color; + display: block; + padding: $layout-spacing; + width: 100%; + + &.toast-primary { + @include toast-variant($primary-color); + } + + &.toast-success { + @include toast-variant($success-color); + } + + &.toast-warning { + @include toast-variant($warning-color); + } + + &.toast-error { + @include toast-variant($error-color); + } + + a { + color: $light-color; + text-decoration: underline; + + &:focus, + &:hover, + &:active, + &.active { + opacity: .75; + } + } + + .btn-clear { + margin: $unit-h; + } + + p { + &:last-child { + margin-bottom: 0; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_tooltips.scss b/yopa-web/resources/src/style/spectre/src/_tooltips.scss new file mode 100644 index 0000000..8693b67 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_tooltips.scss @@ -0,0 +1,79 @@ +// Tooltips +.tooltip { + position: relative; + &::after { + background: rgba($dark-color, .95); + border-radius: $border-radius; + bottom: 100%; + color: $light-color; + content: attr(data-tooltip); + display: block; + font-size: $font-size-sm; + left: 50%; + max-width: $control-width-sm; + opacity: 0; + overflow: hidden; + padding: $unit-1 $unit-2; + pointer-events: none; + position: absolute; + text-overflow: ellipsis; + transform: translate(-50%, $unit-2); + transition: opacity .2s, transform .2s; + white-space: pre; + z-index: $zindex-3; + } + &:focus, + &:hover { + &::after { + opacity: 1; + transform: translate(-50%, -$unit-1); + } + } + &[disabled], + &.disabled { + pointer-events: auto; + } + + &.tooltip-right { + &::after { + bottom: 50%; + left: 100%; + transform: translate(-$unit-1, 50%); + } + &:focus, + &:hover { + &::after { + transform: translate($unit-1, 50%); + } + } + } + + &.tooltip-bottom { + &::after { + bottom: auto; + top: 100%; + transform: translate(-50%, -$unit-2); + } + &:focus, + &:hover { + &::after { + transform: translate(-50%, $unit-1); + } + } + } + + &.tooltip-left { + &::after { + bottom: 50%; + left: auto; + right: 100%; + transform: translate($unit-2, 50%); + } + &:focus, + &:hover { + &::after { + transform: translate(-$unit-1, 50%); + } + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_typography.scss b/yopa-web/resources/src/style/spectre/src/_typography.scss new file mode 100644 index 0000000..bbeb876 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_typography.scss @@ -0,0 +1,129 @@ +// Typography +// Headings +h1, +h2, +h3, +h4, +h5, +h6 { + color: inherit; + font-weight: 500; + line-height: 1.2; + margin-bottom: .5em; + margin-top: 0; +} +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-weight: 500; +} +h1, +.h1 { + font-size: 2rem; +} +h2, +.h2 { + font-size: 1.6rem; +} +h3, +.h3 { + font-size: 1.4rem; +} +h4, +.h4 { + font-size: 1.2rem; +} +h5, +.h5 { + font-size: 1rem; +} +h6, +.h6 { + font-size: .8rem; +} + +// Paragraphs +p { + margin: 0 0 $line-height; +} + +// Semantic text elements +a, +ins, +u { + text-decoration-skip: ink edges; +} + +abbr[title] { + border-bottom: $border-width dotted; + cursor: help; + text-decoration: none; +} + +kbd { + @include label-base(); + @include label-variant($light-color, $dark-color); + font-size: $font-size-sm; +} + +mark { + @include label-variant($body-font-color, $highlight-color); + border-bottom: $unit-o solid darken($highlight-color, 15%); + border-radius: $border-radius; + padding: $unit-o $unit-h 0; +} + +// Blockquote +blockquote { + border-left: $border-width-lg solid $border-color; + margin-left: 0; + padding: $unit-2 $unit-4; + + p:last-child { + margin-bottom: 0; + } +} + +// Lists +ul, +ol { + margin: $unit-4 0 $unit-4 $unit-4; + padding: 0; + + ul, + ol { + margin: $unit-4 0 $unit-4 $unit-4; + } + + li { + margin-top: $unit-2; + } +} + +ul { + list-style: disc inside; + + ul { + list-style-type: circle; + } +} + +ol { + list-style: decimal inside; + + ol { + list-style-type: lower-alpha; + } +} + +dl { + dt { + font-weight: bold; + } + dd { + margin: $unit-2 0 $unit-4 0; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/_utilities.scss b/yopa-web/resources/src/style/spectre/src/_utilities.scss new file mode 100644 index 0000000..80f1e0b --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_utilities.scss @@ -0,0 +1,8 @@ +@import "utilities/colors"; +@import "utilities/cursors"; +@import "utilities/display"; +@import "utilities/divider"; +@import "utilities/loading"; +@import "utilities/position"; +@import "utilities/shapes"; +@import "utilities/text"; diff --git a/yopa-web/resources/src/style/spectre/src/_variables.scss b/yopa-web/resources/src/style/spectre/src/_variables.scss new file mode 100644 index 0000000..604c006 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_variables.scss @@ -0,0 +1,117 @@ +// Core variables +$version: "0.5.9"; + +// Core features +$rtl: false !default; + +// Core colors +$primary-color: #5755d9 !default; +$primary-color-dark: darken($primary-color, 3%) !default; +$primary-color-light: lighten($primary-color, 3%) !default; +$secondary-color: lighten($primary-color, 37.5%) !default; +$secondary-color-dark: darken($secondary-color, 3%) !default; +$secondary-color-light: lighten($secondary-color, 3%) !default; + +// Gray colors +$dark-color: #303742 !default; +$light-color: #fff !default; +$gray-color: lighten($dark-color, 55%) !default; +$gray-color-dark: darken($gray-color, 30%) !default; +$gray-color-light: lighten($gray-color, 20%) !default; + +$border-color: lighten($dark-color, 65%) !default; +$border-color-dark: darken($border-color, 10%) !default; +$border-color-light: lighten($border-color, 8%) !default; +$bg-color: lighten($dark-color, 75%) !default; +$bg-color-dark: darken($bg-color, 3%) !default; +$bg-color-light: $light-color !default; + +// Control colors +$success-color: #32b643 !default; +$warning-color: #ffb700 !default; +$error-color: #e85600 !default; + +// Other colors +$code-color: #d73e48 !default; +$highlight-color: #ffe9b3 !default; +$body-bg: $bg-color-light !default; +$body-font-color: lighten($dark-color, 5%) !default; +$link-color: $primary-color !default; +$link-color-dark: darken($link-color, 10%) !default; +$link-color-light: lighten($link-color, 10%) !default; + +// Fonts +// Credit: https://www.smashingmagazine.com/2015/11/using-system-ui-fonts-practical-guide/ +$base-font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto !default; +$mono-font-family: "SF Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Courier, monospace !default; +$fallback-font-family: "Helvetica Neue", sans-serif !default; +$cjk-zh-hans-font-family: $base-font-family, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", $fallback-font-family !default; +$cjk-zh-hant-font-family: $base-font-family, "PingFang TC", "Hiragino Sans CNS", "Microsoft JhengHei", $fallback-font-family !default; +$cjk-jp-font-family: $base-font-family, "Hiragino Sans", "Hiragino Kaku Gothic Pro", "Yu Gothic", YuGothic, Meiryo, $fallback-font-family !default; +$cjk-ko-font-family: $base-font-family, "Malgun Gothic", $fallback-font-family !default; +$body-font-family: $base-font-family, $fallback-font-family !default; + +// Unit sizes +$unit-o: .05rem !default; +$unit-h: .1rem !default; +$unit-1: .2rem !default; +$unit-2: .4rem !default; +$unit-3: .6rem !default; +$unit-4: .8rem !default; +$unit-5: 1rem !default; +$unit-6: 1.2rem !default; +$unit-7: 1.4rem !default; +$unit-8: 1.6rem !default; +$unit-9: 1.8rem !default; +$unit-10: 2rem !default; +$unit-12: 2.4rem !default; +$unit-16: 3.2rem !default; + +// Font sizes +$html-font-size: 20px !default; +$html-line-height: 1.5 !default; +$font-size: .8rem !default; +$font-size-sm: .7rem !default; +$font-size-lg: .9rem !default; +$line-height: 1.2rem !default; + +// Sizes +$layout-spacing: $unit-2 !default; +$layout-spacing-sm: $unit-1 !default; +$layout-spacing-lg: $unit-4 !default; +$border-radius: $unit-h !default; +$border-width: $unit-o !default; +$border-width-lg: $unit-h !default; +$control-size: $unit-9 !default; +$control-size-sm: $unit-7 !default; +$control-size-lg: $unit-10 !default; +$control-padding-x: $unit-2 !default; +$control-padding-x-sm: $unit-2 * .75 !default; +$control-padding-x-lg: $unit-2 * 1.5 !default; +$control-padding-y: ($control-size - $line-height) / 2 - $border-width !default; +$control-padding-y-sm: ($control-size-sm - $line-height) / 2 - $border-width !default; +$control-padding-y-lg: ($control-size-lg - $line-height) / 2 - $border-width !default; +$control-icon-size: .8rem !default; + +$control-width-xs: 180px !default; +$control-width-sm: 320px !default; +$control-width-md: 640px !default; +$control-width-lg: 960px !default; +$control-width-xl: 1280px !default; + +// Responsive breakpoints +$size-xs: 480px !default; +$size-sm: 600px !default; +$size-md: 840px !default; +$size-lg: 960px !default; +$size-xl: 1280px !default; +$size-2x: 1440px !default; + +$responsive-breakpoint: $size-xs !default; + +// Z-index +$zindex-0: 1 !default; +$zindex-1: 100 !default; +$zindex-2: 200 !default; +$zindex-3: 300 !default; +$zindex-4: 400 !default; diff --git a/yopa-web/resources/src/style/spectre/src/_viewer-360.scss b/yopa-web/resources/src/style/spectre/src/_viewer-360.scss new file mode 100644 index 0000000..c1b8928 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/_viewer-360.scss @@ -0,0 +1,34 @@ +// 360 Degree Viewer + +// Mixin: Viewer slider sizes +@mixin viewer-slider-size($image-number: 36) { + @for $s from 1 through ($image-number) { + .viewer-slider[max='#{$image-number}'][value='#{$s}'] + .viewer-image { + background-position-y: percentage((($s)-1) * 1/(($image-number)-1)); + } + } +} + +.viewer-360 { + align-items: center; + display: flex; + flex-direction: column; + + // Copy and add more numbers if you need + @include viewer-slider-size(36); + + .viewer-slider { + cursor: ew-resize; + margin: 1rem; + order: 2; + width: 60%; + } + + .viewer-image { + background-position-y: 0; + background-repeat: no-repeat; + background-size: 100%; + max-width: 100%; + order: 1; + } +} \ No newline at end of file diff --git a/yopa-web/resources/src/style/spectre/src/icons/_icons-action.scss b/yopa-web/resources/src/style/spectre/src/icons/_icons-action.scss new file mode 100644 index 0000000..1b952ea --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/icons/_icons-action.scss @@ -0,0 +1,315 @@ +// Icon resize +.icon-resize-horiz, +.icon-resize-vert { + &::before, + &::after { + border: $icon-border-width solid currentColor; + border-bottom: 0; + border-right: 0; + height: .45em; + width: .45em; + } + &::before { + transform: translate(-50%, -90%) rotate(45deg); + } + &::after { + transform: translate(-50%, -10%) rotate(225deg); + } +} + +.icon-resize-horiz { + &::before { + transform: translate(-90%, -50%) rotate(-45deg); + } + &::after { + transform: translate(-10%, -50%) rotate(135deg); + } +} + +// Icon more +.icon-more-horiz, +.icon-more-vert { + &::before { + background: currentColor; + box-shadow: -.4em 0, .4em 0; + border-radius: 50%; + height: 3px; + width: 3px; + } +} + +.icon-more-vert { + &::before { + box-shadow: 0 -.4em, 0 .4em; + } +} + +// Icon plus, minus, cross +.icon-plus, +.icon-minus, +.icon-cross { + &::before { + background: currentColor; + height: $icon-border-width; + width: 100%; + } +} + +.icon-plus, +.icon-cross { + &::after { + background: currentColor; + height: 100%; + width: $icon-border-width; + } +} + +.icon-cross { + &::before { + width: 100%; + } + &::after { + height: 100%; + } + &::before, + &::after { + transform: translate(-50%, -50%) rotate(45deg); + } +} + +// Icon check +.icon-check { + &::before { + border: $icon-border-width solid currentColor; + border-right: 0; + border-top: 0; + height: .5em; + width: .9em; + transform: translate(-50%, -75%) rotate(-45deg); + } +} + +// Icon stop +.icon-stop { + border: $icon-border-width solid currentColor; + border-radius: 50%; + &::before { + background: currentColor; + height: $icon-border-width; + transform: translate(-50%, -50%) rotate(45deg); + width: 1em; + } +} + +// Icon shutdown +.icon-shutdown { + border: $icon-border-width solid currentColor; + border-radius: 50%; + border-top-color: transparent; + &::before { + background: currentColor; + content: ""; + height: .5em; + top: .1em; + width: $icon-border-width; + } +} + +// Icon refresh +.icon-refresh { + &::before { + border: $icon-border-width solid currentColor; + border-radius: 50%; + border-right-color: transparent; + height: 1em; + width: 1em; + } + &::after { + border: .2em solid currentColor; + border-top-color: transparent; + border-left-color: transparent; + height: 0; + left: 80%; + top: 20%; + width: 0; + } +} + +// Icon search +.icon-search { + &::before { + border: $icon-border-width solid currentColor; + border-radius: 50%; + height: .75em; + left: 5%; + top: 5%; + transform: translate(0, 0) rotate(45deg); + width: .75em; + } + &::after { + background: currentColor; + height: $icon-border-width; + left: 80%; + top: 80%; + transform: translate(-50%, -50%) rotate(45deg); + width: .4em; + } +} + +// Icon edit +.icon-edit { + &::before { + border: $icon-border-width solid currentColor; + height: .4em; + transform: translate(-40%, -60%) rotate(-45deg); + width: .85em; + } + &::after { + border: .15em solid currentColor; + border-top-color: transparent; + border-right-color: transparent; + height: 0; + left: 5%; + top: 95%; + transform: translate(0, -100%); + width: 0; + } +} + +// Icon delete +.icon-delete { + &::before { + border: $icon-border-width solid currentColor; + border-bottom-left-radius: $border-radius; + border-bottom-right-radius: $border-radius; + border-top: 0; + height: .75em; + top: 60%; + width: .75em; + } + &::after { + background: currentColor; + box-shadow: -.25em .2em, .25em .2em; + height: $icon-border-width; + top: $icon-border-width/2; + width: .5em; + } +} + +// Icon share +.icon-share { + border: $icon-border-width solid currentColor; + border-radius: $border-radius; + border-right: 0; + border-top: 0; + &::before { + border: $icon-border-width solid currentColor; + border-left: 0; + border-top: 0; + height: .4em; + left: 100%; + top: .25em; + transform: translate(-125%, -50%) rotate(-45deg); + width: .4em; + } + &::after { + border: $icon-border-width solid currentColor; + border-bottom: 0; + border-right: 0; + border-radius: 75% 0; + height: .5em; + width: .6em; + } +} + +// Icon flag +.icon-flag { + &::before { + background: currentColor; + height: 1em; + left: 15%; + width: $icon-border-width; + } + &::after { + border: $icon-border-width solid currentColor; + border-bottom-right-radius: $border-radius; + border-left: 0; + border-top-right-radius: $border-radius; + height: .65em; + top: 35%; + left: 60%; + width: .8em; + } +} + +// Icon bookmark +.icon-bookmark { + &::before { + border: $icon-border-width solid currentColor; + border-bottom: 0; + border-top-left-radius: $border-radius; + border-top-right-radius: $border-radius; + height: .9em; + width: .8em; + } + &::after { + border: $icon-border-width solid currentColor; + border-bottom: 0; + border-left: 0; + border-radius: $border-radius; + height: .5em; + transform: translate(-50%, 35%) rotate(-45deg) skew(15deg, 15deg); + width: .5em; + } +} + +// Icon download & upload +.icon-download, +.icon-upload { + border-bottom: $icon-border-width solid currentColor; + &::before { + border: $icon-border-width solid currentColor; + border-bottom: 0; + border-right: 0; + height: .5em; + width: .5em; + transform: translate(-50%, -60%) rotate(-135deg); + } + &::after { + background: currentColor; + height: .6em; + top: 40%; + width: $icon-border-width; + } +} + +.icon-upload { + &::before { + transform: translate(-50%, -60%) rotate(45deg); + } + &::after { + top: 50%; + } +} + +// Icon copy +.icon-copy { + &::before { + border: $icon-border-width solid currentColor; + border-radius: $border-radius; + border-right: 0; + border-bottom: 0; + height: .8em; + left: 40%; + top: 35%; + width: .8em; + } + &::after { + border: $icon-border-width solid currentColor; + border-radius: $border-radius; + height: .8em; + left: 60%; + top: 60%; + width: .8em; + } +} \ No newline at end of file diff --git a/yopa-web/resources/src/style/spectre/src/icons/_icons-core.scss b/yopa-web/resources/src/style/spectre/src/icons/_icons-core.scss new file mode 100644 index 0000000..9a67ae4 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/icons/_icons-core.scss @@ -0,0 +1,54 @@ +// Icon variables +$icon-border-width: $border-width-lg; +$icon-prefix: "icon"; + +// Icon base style +.#{$icon-prefix} { + box-sizing: border-box; + display: inline-block; + font-size: inherit; + font-style: normal; + height: 1em; + position: relative; + text-indent: -9999px; + vertical-align: middle; + width: 1em; + &::before, + &::after { + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + } + + // Icon sizes + &.icon-2x { + font-size: 1.6rem; + } + + &.icon-3x { + font-size: 2.4rem; + } + + &.icon-4x { + font-size: 3.2rem; + } +} + +// Component icon support +.accordion, +.btn, +.toast, +.menu { + .#{$icon-prefix} { + vertical-align: -10%; + } +} + +.btn-lg { + .#{$icon-prefix} { + vertical-align: -15%; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/icons/_icons-navigation.scss b/yopa-web/resources/src/style/spectre/src/icons/_icons-navigation.scss new file mode 100644 index 0000000..b6dc9ad --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/icons/_icons-navigation.scss @@ -0,0 +1,128 @@ +// Icon arrows +.icon-arrow-down, +.icon-arrow-left, +.icon-arrow-right, +.icon-arrow-up, +.icon-downward, +.icon-back, +.icon-forward, +.icon-upward, +.icon-home { + &::before { + border: $icon-border-width solid currentColor; + border-bottom: 0; + border-right: 0; + height: .65em; + width: .65em; + } +} + +.icon-arrow-down { + &::before { + transform: translate(-50%, -75%) rotate(225deg); + } +} + +.icon-arrow-left { + &::before { + transform: translate(-25%, -50%) rotate(-45deg); + } +} + +.icon-arrow-right { + &::before { + transform: translate(-75%, -50%) rotate(135deg); + } +} + +.icon-arrow-up { + &::before { + transform: translate(-50%, -25%) rotate(45deg); + } +} + +.icon-back, +.icon-forward { + &::after { + background: currentColor; + height: $icon-border-width; + width: .8em; + } +} + +.icon-downward, +.icon-upward { + &::after { + background: currentColor; + height: .8em; + width: $icon-border-width; + } +} + +.icon-back { + &::after { + left: 55%; + } + &::before { + transform: translate(-50%, -50%) rotate(-45deg); + } +} + +.icon-downward { + &::after { + top: 45%; + } + &::before { + transform: translate(-50%, -50%) rotate(-135deg); + } +} + +.icon-forward { + &::after { + left: 45%; + } + &::before { + transform: translate(-50%, -50%) rotate(135deg); + } +} + +.icon-upward { + &::after { + top: 55%; + } + &::before { + transform: translate(-50%, -50%) rotate(45deg); + } +} + +// Icon caret +.icon-caret { + &::before { + border-top: .3em solid currentColor; + border-right: .3em solid transparent; + border-left: .3em solid transparent; + height: 0; + transform: translate(-50%, -25%); + width: 0; + } +} + +// Icon menu +.icon-menu { + &::before { + background: currentColor; + box-shadow: 0 -.35em, 0 .35em; + height: $icon-border-width; + width: 100%; + } +} + +// Icon apps +.icon-apps { + &::before { + background: currentColor; + box-shadow: -.35em -.35em, -.35em 0, -.35em .35em, 0 -.35em, 0 .35em, .35em -.35em, .35em 0, .35em .35em; + height: 3px; + width: 3px; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/icons/_icons-object.scss b/yopa-web/resources/src/style/spectre/src/icons/_icons-object.scss new file mode 100644 index 0000000..00597d8 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/icons/_icons-object.scss @@ -0,0 +1,161 @@ +// Icon time +.icon-time { + border: $icon-border-width solid currentColor; + border-radius: 50%; + &::before { + background: currentColor; + height: .4em; + transform: translate(-50%, -75%); + width: $icon-border-width; + } + &::after { + background: currentColor; + height: .3em; + transform: translate(-50%, -75%) rotate(90deg); + transform-origin: 50% 90%; + width: $icon-border-width; + } +} + +// Icon mail +.icon-mail { + &::before { + border: $icon-border-width solid currentColor; + border-radius: $border-radius; + height: .8em; + width: 1em; + } + &::after { + border: $icon-border-width solid currentColor; + border-right: 0; + border-top: 0; + height: .5em; + transform: translate(-50%, -90%) rotate(-45deg) skew(10deg, 10deg); + width: .5em; + } +} + +// Icon people +.icon-people { + &::before { + border: $icon-border-width solid currentColor; + border-radius: 50%; + height: .45em; + top: 25%; + width: .45em; + } + &::after { + border: $icon-border-width solid currentColor; + border-radius: 50% 50% 0 0; + height: .4em; + top: 75%; + width: .9em; + } +} + +// Icon message +.icon-message { + border: $icon-border-width solid currentColor; + border-bottom: 0; + border-radius: $border-radius; + border-right: 0; + &::before { + border: $icon-border-width solid currentColor; + border-bottom-right-radius: $border-radius; + border-left: 0; + border-top: 0; + height: .8em; + left: 65%; + top: 40%; + width: .7em; + } + &::after { + background: currentColor; + border-radius: $border-radius; + height: .3em; + left: 10%; + top: 100%; + transform: translate(0, -90%) rotate(45deg); + width: $icon-border-width; + } +} + +// Icon photo +.icon-photo { + border: $icon-border-width solid currentColor; + border-radius: $border-radius; + &::before { + border: $icon-border-width solid currentColor; + border-radius: 50%; + height: .25em; + left: 35%; + top: 35%; + width: .25em; + } + &::after { + border: $icon-border-width solid currentColor; + border-bottom: 0; + border-left: 0; + height: .5em; + left: 60%; + transform: translate(-50%, 25%) rotate(-45deg); + width: .5em; + } +} + +// Icon link +.icon-link { + &::before, + &::after { + border: $icon-border-width solid currentColor; + border-radius: 5em 0 0 5em; + border-right: 0; + height: .5em; + width: .75em; + } + &::before { + transform: translate(-70%, -45%) rotate(-45deg); + } + &::after { + transform: translate(-30%, -55%) rotate(135deg); + } +} + +// Icon location +.icon-location { + &::before { + border: $icon-border-width solid currentColor; + border-radius: 50% 50% 50% 0; + height: .8em; + transform: translate(-50%, -60%) rotate(-45deg); + width: .8em; + } + &::after { + border: $icon-border-width solid currentColor; + border-radius: 50%; + height: .2em; + transform: translate(-50%, -80%); + width: .2em; + } +} + +// Icon emoji +.icon-emoji { + border: $icon-border-width solid currentColor; + border-radius: 50%; + &::before { + border-radius: 50%; + box-shadow: -.17em -.1em, .17em -.1em; + height: .15em; + width: .15em; + } + &::after { + border: $icon-border-width solid currentColor; + border-bottom-color: transparent; + border-radius: 50%; + border-right-color: transparent; + height: .5em; + transform: translate(-50%, -40%) rotate(-135deg); + width: .5em; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/mixins/_avatar.scss b/yopa-web/resources/src/style/spectre/src/mixins/_avatar.scss new file mode 100644 index 0000000..14617ad --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/mixins/_avatar.scss @@ -0,0 +1,6 @@ +// Avatar mixin +@mixin avatar-base($size: $unit-8) { + font-size: $size / 2; + height: $size; + width: $size; +} diff --git a/yopa-web/resources/src/style/spectre/src/mixins/_button.scss b/yopa-web/resources/src/style/spectre/src/mixins/_button.scss new file mode 100644 index 0000000..c90a94b --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/mixins/_button.scss @@ -0,0 +1,54 @@ +// Button variant mixin +@mixin button-variant($color: $primary-color) { + background: $color; + border-color: darken($color, 3%); + color: $light-color; + &:focus { + @include control-shadow($color); + } + &:focus, + &:hover { + background: darken($color, 2%); + border-color: darken($color, 5%); + color: $light-color; + } + &:active, + &.active { + background: darken($color, 7%); + border-color: darken($color, 10%); + color: $light-color; + } + &.loading { + &::after { + border-bottom-color: $light-color; + border-left-color: $light-color; + } + } +} + +@mixin button-outline-variant($color: $primary-color) { + background: $light-color; + border-color: $color; + color: $color; + &:focus { + @include control-shadow($color); + } + &:focus, + &:hover { + background: lighten($color, 50%); + border-color: darken($color, 2%); + color: $color; + } + &:active, + &.active { + background: $color; + border-color: darken($color, 5%); + color: $light-color; + } + &.loading { + &::after { + border-bottom-color: $color; + border-left-color: $color; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/mixins/_clearfix.scss b/yopa-web/resources/src/style/spectre/src/mixins/_clearfix.scss new file mode 100644 index 0000000..db6895f --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/mixins/_clearfix.scss @@ -0,0 +1,8 @@ +// Clearfix mixin +@mixin clearfix() { + &::after { + clear: both; + content: ""; + display: table; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/mixins/_color.scss b/yopa-web/resources/src/style/spectre/src/mixins/_color.scss new file mode 100644 index 0000000..697d0c3 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/mixins/_color.scss @@ -0,0 +1,27 @@ +// Background color utility mixin +@mixin bg-color-variant($name: ".bg-primary", $color: $primary-color) { + #{$name} { + background: $color !important; + + @if (lightness($color) < 60) { + color: $light-color; + } + } +} + +// Text color utility mixin +@mixin text-color-variant($name: ".text-primary", $color: $primary-color) { + #{$name} { + color: $color !important; + } + + a#{$name} { + &:focus, + &:hover { + color: darken($color, 5%); + } + &:visited { + color: lighten($color, 5%); + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/mixins/_label.scss b/yopa-web/resources/src/style/spectre/src/mixins/_label.scss new file mode 100644 index 0000000..1574f02 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/mixins/_label.scss @@ -0,0 +1,11 @@ +// Label base style +@mixin label-base() { + border-radius: $border-radius; + line-height: 1.25; + padding: .1rem .2rem; +} + +@mixin label-variant($color: $light-color, $bg-color: $primary-color) { + background: $bg-color; + color: $color; +} diff --git a/yopa-web/resources/src/style/spectre/src/mixins/_position.scss b/yopa-web/resources/src/style/spectre/src/mixins/_position.scss new file mode 100644 index 0000000..98b5cfc --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/mixins/_position.scss @@ -0,0 +1,65 @@ +// Margin utility mixin +@mixin margin-variant($id: 1, $size: $unit-1) { + .m-#{$id} { + margin: $size !important; + } + + .mb-#{$id} { + margin-bottom: $size !important; + } + + .ml-#{$id} { + margin-left: $size !important; + } + + .mr-#{$id} { + margin-right: $size !important; + } + + .mt-#{$id} { + margin-top: $size !important; + } + + .mx-#{$id} { + margin-left: $size !important; + margin-right: $size !important; + } + + .my-#{$id} { + margin-bottom: $size !important; + margin-top: $size !important; + } +} + +// Padding utility mixin +@mixin padding-variant($id: 1, $size: $unit-1) { + .p-#{$id} { + padding: $size !important; + } + + .pb-#{$id} { + padding-bottom: $size !important; + } + + .pl-#{$id} { + padding-left: $size !important; + } + + .pr-#{$id} { + padding-right: $size !important; + } + + .pt-#{$id} { + padding-top: $size !important; + } + + .px-#{$id} { + padding-left: $size !important; + padding-right: $size !important; + } + + .py-#{$id} { + padding-bottom: $size !important; + padding-top: $size !important; + } +} diff --git a/yopa-web/resources/src/style/spectre/src/mixins/_shadow.scss b/yopa-web/resources/src/style/spectre/src/mixins/_shadow.scss new file mode 100644 index 0000000..7984449 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/mixins/_shadow.scss @@ -0,0 +1,9 @@ +// Component focus shadow +@mixin control-shadow($color: $primary-color) { + box-shadow: 0 0 0 .1rem rgba($color, .2); +} + +// Shadow mixin +@mixin shadow-variant($offset) { + box-shadow: 0 $offset ($offset + .05rem) * 2 rgba($dark-color, .3); +} diff --git a/yopa-web/resources/src/style/spectre/src/mixins/_text.scss b/yopa-web/resources/src/style/spectre/src/mixins/_text.scss new file mode 100644 index 0000000..97dc99d --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/mixins/_text.scss @@ -0,0 +1,6 @@ +// Text Ellipsis +@mixin text-ellipsis() { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/yopa-web/resources/src/style/spectre/src/mixins/_toast.scss b/yopa-web/resources/src/style/spectre/src/mixins/_toast.scss new file mode 100644 index 0000000..fa2bb13 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/mixins/_toast.scss @@ -0,0 +1,5 @@ +// Toast variant mixin +@mixin toast-variant($color: $dark-color) { + background: rgba($color, .95); + border-color: $color; +} diff --git a/yopa-web/resources/src/style/spectre/src/spectre-exp.scss b/yopa-web/resources/src/style/spectre/src/spectre-exp.scss new file mode 100644 index 0000000..33ed3fe --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/spectre-exp.scss @@ -0,0 +1,18 @@ +// Variables and mixins +@import "variables"; +@import "mixins"; + +/*! Spectre.css Experimentals v#{$version} | MIT License | github.com/picturepan2/spectre */ +// Experimentals +@import "autocomplete"; +@import "calendars"; +@import "carousels"; +@import "comparison-sliders"; +@import "filters"; +@import "meters"; +@import "off-canvas"; +@import "parallax"; +@import "progress"; +@import "sliders"; +@import "timelines"; +@import "viewer-360"; diff --git a/yopa-web/resources/src/style/spectre/src/spectre-icons.scss b/yopa-web/resources/src/style/spectre/src/spectre-icons.scss new file mode 100644 index 0000000..383624e --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/spectre-icons.scss @@ -0,0 +1,10 @@ +// Variables and mixins +@import "variables"; +@import "mixins"; + +/*! Spectre.css Icons v#{$version} | MIT License | github.com/picturepan2/spectre */ +// Icons +@import "icons/icons-core"; +@import "icons/icons-navigation"; +@import "icons/icons-action"; +@import "icons/icons-object"; diff --git a/yopa-web/resources/src/style/spectre/src/spectre.scss b/yopa-web/resources/src/style/spectre/src/spectre.scss new file mode 100644 index 0000000..cff1fde --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/spectre.scss @@ -0,0 +1,49 @@ +// Variables and mixins +@import "variables"; +@import "mixins"; + +/*! Spectre.css v#{$version} | MIT License | github.com/picturepan2/spectre */ +// Reset and dependencies +@import "normalize"; +@import "base"; + +// Elements +@import "typography"; +@import "asian"; +@import "tables"; +@import "buttons"; +@import "forms"; +@import "labels"; +@import "codes"; +@import "media"; + +// Layout +@import "layout"; +@import "hero"; +@import "navbar"; + +// Components +@import "accordions"; +@import "avatars"; +@import "badges"; +@import "breadcrumbs"; +@import "bars"; +@import "cards"; +@import "chips"; +@import "dropdowns"; +@import "empty"; +@import "menus"; +@import "modals"; +@import "navs"; +@import "pagination"; +@import "panels"; +@import "popovers"; +@import "steps"; +@import "tabs"; +@import "tiles"; +@import "toasts"; +@import "tooltips"; + +// Utility classes +@import "animations"; +@import "utilities"; diff --git a/yopa-web/resources/src/style/spectre/src/utilities/_colors.scss b/yopa-web/resources/src/style/spectre/src/utilities/_colors.scss new file mode 100644 index 0000000..28dd221 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/utilities/_colors.scss @@ -0,0 +1,31 @@ +// Text colors +@include text-color-variant(".text-primary", $primary-color); + +@include text-color-variant(".text-secondary", $secondary-color-dark); + +@include text-color-variant(".text-gray", $gray-color); + +@include text-color-variant(".text-light", $light-color); + +@include text-color-variant(".text-dark", $body-font-color); + +@include text-color-variant(".text-success", $success-color); + +@include text-color-variant(".text-warning", $warning-color); + +@include text-color-variant(".text-error", $error-color); + +// Background colors +@include bg-color-variant(".bg-primary", $primary-color); + +@include bg-color-variant(".bg-secondary", $secondary-color); + +@include bg-color-variant(".bg-dark", $dark-color); + +@include bg-color-variant(".bg-gray", $bg-color); + +@include bg-color-variant(".bg-success", $success-color); + +@include bg-color-variant(".bg-warning", $warning-color); + +@include bg-color-variant(".bg-error", $error-color); diff --git a/yopa-web/resources/src/style/spectre/src/utilities/_cursors.scss b/yopa-web/resources/src/style/spectre/src/utilities/_cursors.scss new file mode 100644 index 0000000..bd755c8 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/utilities/_cursors.scss @@ -0,0 +1,24 @@ +// Cursors +.c-hand { + cursor: pointer; +} + +.c-move { + cursor: move; +} + +.c-zoom-in { + cursor: zoom-in; +} + +.c-zoom-out { + cursor: zoom-out; +} + +.c-not-allowed { + cursor: not-allowed; +} + +.c-auto { + cursor: auto; +} \ No newline at end of file diff --git a/yopa-web/resources/src/style/spectre/src/utilities/_display.scss b/yopa-web/resources/src/style/spectre/src/utilities/_display.scss new file mode 100644 index 0000000..c6248e0 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/utilities/_display.scss @@ -0,0 +1,44 @@ +// Display +.d-block { + display: block; +} +.d-inline { + display: inline; +} +.d-inline-block { + display: inline-block; +} +.d-flex { + display: flex; +} +.d-inline-flex { + display: inline-flex; +} +.d-none, +.d-hide { + display: none !important; +} +.d-visible { + visibility: visible; +} +.d-invisible { + visibility: hidden; +} +.text-hide { + background: transparent; + border: 0; + color: transparent; + font-size: 0; + line-height: 0; + text-shadow: none; +} +.text-assistive { + border: 0; + clip: rect(0,0,0,0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} diff --git a/yopa-web/resources/src/style/spectre/src/utilities/_divider.scss b/yopa-web/resources/src/style/spectre/src/utilities/_divider.scss new file mode 100644 index 0000000..e6c09d2 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/utilities/_divider.scss @@ -0,0 +1,50 @@ +// Divider +.divider, +.divider-vert { + display: block; + position: relative; + + &[data-content]::after { + background: $bg-color-light; + color: $gray-color; + content: attr(data-content); + display: inline-block; + font-size: $font-size-sm; + padding: 0 $unit-2; + transform: translateY(-$font-size-sm + $border-width); + } +} + +.divider { + border-top: $border-width solid $border-color-light; + height: $border-width; + margin: $unit-2 0; + + &[data-content] { + margin: $unit-4 0; + } +} + +.divider-vert { + display: block; + padding: $unit-4; + + &::before { + border-left: $border-width solid $border-color; + bottom: $unit-2; + content: ""; + display: block; + left: 50%; + position: absolute; + top: $unit-2; + transform: translateX(-50%); + } + + &[data-content]::after { + left: 50%; + padding: $unit-1 0; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + } +} diff --git a/yopa-web/resources/src/style/spectre/src/utilities/_loading.scss b/yopa-web/resources/src/style/spectre/src/utilities/_loading.scss new file mode 100644 index 0000000..2cfdefa --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/utilities/_loading.scss @@ -0,0 +1,37 @@ +// Loading +.loading { + color: transparent !important; + min-height: $unit-4; + pointer-events: none; + position: relative; + &::after { + animation: loading 500ms infinite linear; + background: transparent; + border: $border-width-lg solid $primary-color; + border-radius: 50%; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + height: $unit-4; + left: 50%; + margin-left: -$unit-2; + margin-top: -$unit-2; + opacity: 1; + padding: 0; + position: absolute; + top: 50%; + width: $unit-4; + z-index: $zindex-0; + } + + &.loading-lg { + min-height: $unit-10; + &::after { + height: $unit-8; + margin-left: -$unit-4; + margin-top: -$unit-4; + width: $unit-8; + } + } +} diff --git a/yopa-web/resources/src/style/spectre/src/utilities/_position.scss b/yopa-web/resources/src/style/spectre/src/utilities/_position.scss new file mode 100644 index 0000000..c1a7f75 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/utilities/_position.scss @@ -0,0 +1,54 @@ +// Position +.clearfix { + @include clearfix(); +} + +.float-left { + float: left !important; +} + +.float-right { + float: right !important; +} + +.p-relative { + position: relative !important; +} + +.p-absolute { + position: absolute !important; +} + +.p-fixed { + position: fixed !important; +} + +.p-sticky { + position: sticky !important; +} + +.p-centered { + display: block; + float: none; + margin-left: auto; + margin-right: auto; +} + +.flex-centered { + align-items: center; + display: flex; + justify-content: center; +} + +// Spacing +@include margin-variant(0, 0); + +@include margin-variant(1, $unit-1); + +@include margin-variant(2, $unit-2); + +@include padding-variant(0, 0); + +@include padding-variant(1, $unit-1); + +@include padding-variant(2, $unit-2); diff --git a/yopa-web/resources/src/style/spectre/src/utilities/_shapes.scss b/yopa-web/resources/src/style/spectre/src/utilities/_shapes.scss new file mode 100644 index 0000000..23e131e --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/utilities/_shapes.scss @@ -0,0 +1,8 @@ +// Shapes +.s-rounded { + border-radius: $border-radius; +} + +.s-circle { + border-radius: 50%; +} \ No newline at end of file diff --git a/yopa-web/resources/src/style/spectre/src/utilities/_text.scss b/yopa-web/resources/src/style/spectre/src/utilities/_text.scss new file mode 100644 index 0000000..5796905 --- /dev/null +++ b/yopa-web/resources/src/style/spectre/src/utilities/_text.scss @@ -0,0 +1,76 @@ +// Text +// Text alignment utilities +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-center { + text-align: center; +} + +.text-justify { + text-align: justify; +} + +// Text transform utilities +.text-lowercase { + text-transform: lowercase; +} + +.text-uppercase { + text-transform: uppercase; +} + +.text-capitalize { + text-transform: capitalize; +} + +// Text style utilities +.text-normal { + font-weight: normal; +} + +.text-bold { + font-weight: bold; +} + +.text-italic { + font-style: italic; +} + +.text-large { + font-size: 1.2em; +} + +.text-small { + font-size: .9em; +} + +.text-tiny { + font-size: .8em; +} + +.text-muted { + opacity: .8; +} + +// Text overflow utilities +.text-ellipsis { + @include text-ellipsis(); +} + +.text-clip { + overflow: hidden; + text-overflow: clip; + white-space: nowrap; +} + +.text-break { + hyphens: auto; + word-break: break-word; + word-wrap: break-word; +} diff --git a/yopa-web/resources/static/bundle.js b/yopa-web/resources/static/bundle.js index a13f142..2cc28c1 100644 --- a/yopa-web/resources/static/bundle.js +++ b/yopa-web/resources/static/bundle.js @@ -1,2 +1,10412 @@ -!function(){"use strict";function e(e,t){const n=Object.create(null),o=e.split(",");for(let e=0;e!!n[e.toLowerCase()]:e=>!!n[e]}const t=e("Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl"),n=e("itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly");function o(e){if(O(e)){const t={};for(let n=0;n{if(e){const n=e.split(i);n.length>1&&(t[n[0].trim()]=n[1].trim())}})),t}function a(e){let t="";if($(e))t=e;else if(O(e))for(let n=0;nu(e,t)))}const f=e=>null==e?"":F(e)?JSON.stringify(e,p,2):String(e),p=(e,t)=>S(t)?{[`Map(${t.size})`]:[...t.entries()].reduce(((e,[t,n])=>(e[`${t} =>`]=n,e)),{})}:C(t)?{[`Set(${t.size})`]:[...t.values()]}:!F(t)||O(t)||I(t)?t:String(t),h=Object.freeze({}),m=Object.freeze([]),v=()=>{},y=()=>!1,g=/^on[^a-z]/,b=e=>g.test(e),_=e=>e.startsWith("onUpdate:"),w=Object.assign,j=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},x=Object.prototype.hasOwnProperty,k=(e,t)=>x.call(e,t),O=Array.isArray,S=e=>"[object Map]"===V(e),C=e=>"[object Set]"===V(e),R=e=>e instanceof Date,E=e=>"function"==typeof e,$=e=>"string"==typeof e,A=e=>"symbol"==typeof e,F=e=>null!==e&&"object"==typeof e,P=e=>F(e)&&E(e.then)&&E(e.catch),T=Object.prototype.toString,V=e=>T.call(e),N=e=>V(e).slice(8,-1),I=e=>"[object Object]"===V(e),U=e=>$(e)&&"NaN"!==e&&"-"!==e[0]&&""+parseInt(e,10)===e,M=e(",key,ref,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),L=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},D=/-(\w)/g,B=L((e=>e.replace(D,((e,t)=>t?t.toUpperCase():"")))),z=/\B([A-Z])/g,q=L((e=>e.replace(z,"-$1").toLowerCase())),H=L((e=>e.charAt(0).toUpperCase()+e.slice(1))),J=L((e=>e?`on${H(e)}`:"")),W=(e,t)=>e!==t&&(e==e||t==t),K=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},G=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let Y;const Z=new WeakMap,Q=[];let ee;const te=Symbol("iterate"),ne=Symbol("Map key iterate");function oe(e,t=h){(function(e){return e&&!0===e._isEffect})(e)&&(e=e.raw);const n=function(e,t){const n=function(){if(!n.active)return t.scheduler?void 0:e();if(!Q.includes(n)){se(n);try{return le.push(ae),ae=!0,Q.push(n),ee=n,e()}finally{Q.pop(),ue(),ee=Q[Q.length-1]}}};return n.id=ie++,n.allowRecurse=!!t.allowRecurse,n._isEffect=!0,n.active=!0,n.raw=e,n.deps=[],n.options=t,n}(e,t);return t.lazy||n(),n}function re(e){e.active&&(se(e),e.options.onStop&&e.options.onStop(),e.active=!1)}let ie=0;function se(e){const{deps:t}=e;if(t.length){for(let n=0;n{e&&e.forEach((e=>{(e!==ee||e.allowRecurse)&&a.add(e)}))};if("clear"===t)s.forEach(l);else if("length"===n&&O(e))s.forEach(((e,t)=>{("length"===t||t>=o)&&l(e)}));else switch(void 0!==n&&l(s.get(n)),t){case"add":O(e)?U(n)&&l(s.get("length")):(l(s.get(te)),S(e)&&l(s.get(ne)));break;case"delete":O(e)||(l(s.get(te)),S(e)&&l(s.get(ne)));break;case"set":S(e)&&l(s.get(te))}a.forEach((s=>{s.options.onTrigger&&s.options.onTrigger({effect:s,target:e,key:n,type:t,newValue:o,oldValue:r,oldTarget:i}),s.options.scheduler?s.options.scheduler(s):s()}))}const pe=new Set(Object.getOwnPropertyNames(Symbol).map((e=>Symbol[e])).filter(A)),he=be(),me=be(!1,!0),ve=be(!0),ye=be(!0,!0),ge={};function be(e=!1,t=!1){return function(n,o,r){if("__v_isReactive"===o)return!e;if("__v_isReadonly"===o)return e;if("__v_raw"===o&&r===(e?Ke:We).get(n))return n;const i=O(n);if(!e&&i&&k(ge,o))return Reflect.get(ge,o,r);const s=Reflect.get(n,o,r);if(A(o)?pe.has(o):"__proto__"===o||"__v_isRef"===o)return s;if(e||de(n,"get",o),t)return s;if(ot(s)){return!i||!U(o)?s.value:s}return F(s)?e?Ge(s):Xe(s):s}}["includes","indexOf","lastIndexOf"].forEach((e=>{const t=Array.prototype[e];ge[e]=function(...e){const n=nt(this);for(let e=0,t=this.length;e{const t=Array.prototype[e];ge[e]=function(...e){ce();const n=t.apply(this,e);return ue(),n}}));function _e(e=!1){return function(t,n,o,r){const i=t[n];if(!e&&(o=nt(o),!O(t)&&ot(i)&&!ot(o)))return i.value=o,!0;const s=O(t)&&U(n)?Number(n)(console.warn(`Set operation on key "${String(t)}" failed: target is readonly.`,e),!0),deleteProperty:(e,t)=>(console.warn(`Delete operation on key "${String(t)}" failed: target is readonly.`,e),!0)},xe=w({},we,{get:me,set:_e(!0)}),ke=w({},je,{get:ye}),Oe=e=>F(e)?Xe(e):e,Se=e=>F(e)?Ge(e):e,Ce=e=>e,Re=e=>Reflect.getPrototypeOf(e);function Ee(e,t,n=!1,o=!1){const r=nt(e=e.__v_raw),i=nt(t);t!==i&&!n&&de(r,"get",t),!n&&de(r,"get",i);const{has:s}=Re(r),a=n?Se:o?Ce:Oe;return s.call(r,t)?a(e.get(t)):s.call(r,i)?a(e.get(i)):void 0}function $e(e,t=!1){const n=this.__v_raw,o=nt(n),r=nt(e);return e!==r&&!t&&de(o,"has",e),!t&&de(o,"has",r),e===r?n.has(e):n.has(e)||n.has(r)}function Ae(e,t=!1){return e=e.__v_raw,!t&&de(nt(e),"iterate",te),Reflect.get(e,"size",e)}function Fe(e){e=nt(e);const t=nt(this),n=Re(t).has.call(t,e);return t.add(e),n||fe(t,"add",e,e),this}function Pe(e,t){t=nt(t);const n=nt(this),{has:o,get:r}=Re(n);let i=o.call(n,e);i?Je(n,o,e):(e=nt(e),i=o.call(n,e));const s=r.call(n,e);return n.set(e,t),i?W(t,s)&&fe(n,"set",e,t,s):fe(n,"add",e,t),this}function Te(e){const t=nt(this),{has:n,get:o}=Re(t);let r=n.call(t,e);r?Je(t,n,e):(e=nt(e),r=n.call(t,e));const i=o?o.call(t,e):void 0,s=t.delete(e);return r&&fe(t,"delete",e,void 0,i),s}function Ve(){const e=nt(this),t=0!==e.size,n=S(e)?new Map(e):new Set(e),o=e.clear();return t&&fe(e,"clear",void 0,void 0,n),o}function Ne(e,t){return function(n,o){const r=this,i=r.__v_raw,s=nt(i),a=e?Se:t?Ce:Oe;return!e&&de(s,"iterate",te),i.forEach(((e,t)=>n.call(o,a(e),a(t),r)))}}function Ie(e,t,n){return function(...o){const r=this.__v_raw,i=nt(r),s=S(i),a="entries"===e||e===Symbol.iterator&&s,l="keys"===e&&s,c=r[e](...o),u=t?Se:n?Ce:Oe;return!t&&de(i,"iterate",l?ne:te),{next(){const{value:e,done:t}=c.next();return t?{value:e,done:t}:{value:a?[u(e[0]),u(e[1])]:u(e),done:t}},[Symbol.iterator](){return this}}}}function Ue(e){return function(...t){{const n=t[0]?`on key "${t[0]}" `:"";console.warn(`${H(e)} operation ${n}failed: target is readonly.`,nt(this))}return"delete"!==e&&this}}const Me={get(e){return Ee(this,e)},get size(){return Ae(this)},has:$e,add:Fe,set:Pe,delete:Te,clear:Ve,forEach:Ne(!1,!1)},Le={get(e){return Ee(this,e,!1,!0)},get size(){return Ae(this)},has:$e,add:Fe,set:Pe,delete:Te,clear:Ve,forEach:Ne(!1,!0)},De={get(e){return Ee(this,e,!0)},get size(){return Ae(this,!0)},has(e){return $e.call(this,e,!0)},add:Ue("add"),set:Ue("set"),delete:Ue("delete"),clear:Ue("clear"),forEach:Ne(!0,!1)};function Be(e,t){const n=t?Le:e?De:Me;return(t,o,r)=>"__v_isReactive"===o?!e:"__v_isReadonly"===o?e:"__v_raw"===o?t:Reflect.get(k(n,o)&&o in t?n:t,o,r)}["keys","values","entries",Symbol.iterator].forEach((e=>{Me[e]=Ie(e,!1,!1),De[e]=Ie(e,!0,!1),Le[e]=Ie(e,!1,!0)}));const ze={get:Be(!1,!1)},qe={get:Be(!1,!0)},He={get:Be(!0,!1)};function Je(e,t,n){const o=nt(n);if(o!==n&&t.call(e,o)){const t=N(e);console.warn(`Reactive ${t} contains both the raw and reactive versions of the same object${"Map"===t?" as keys":""}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.`)}}const We=new WeakMap,Ke=new WeakMap;function Xe(e){return e&&e.__v_isReadonly?e:Ze(e,!1,we,ze)}function Ge(e){return Ze(e,!0,je,He)}function Ye(e){return Ze(e,!0,ke,He)}function Ze(e,t,n,o){if(!F(e))return console.warn(`value cannot be made reactive: ${String(e)}`),e;if(e.__v_raw&&(!t||!e.__v_isReactive))return e;const r=t?Ke:We,i=r.get(e);if(i)return i;const s=(a=e).__v_skip||!Object.isExtensible(a)?0:function(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}(N(a));var a;if(0===s)return e;const l=new Proxy(e,2===s?o:n);return r.set(e,l),l}function Qe(e){return et(e)?Qe(e.__v_raw):!(!e||!e.__v_isReactive)}function et(e){return!(!e||!e.__v_isReadonly)}function tt(e){return Qe(e)||et(e)}function nt(e){return e&&nt(e.__v_raw)||e}function ot(e){return Boolean(e&&!0===e.__v_isRef)}const rt={get:(e,t,n)=>{return ot(o=Reflect.get(e,t,n))?o.value:o;var o},set:(e,t,n,o)=>{const r=e[t];return ot(r)&&!ot(n)?(r.value=n,!0):Reflect.set(e,t,n,o)}};function it(e){return Qe(e)?e:new Proxy(e,rt)}class st{constructor(e,t){this._object=e,this._key=t,this.__v_isRef=!0}get value(){return this._object[this._key]}set value(e){this._object[this._key]=e}}class at{constructor(e,t,n){this._setter=t,this._dirty=!0,this.__v_isRef=!0,this.effect=oe(e,{lazy:!0,scheduler:()=>{this._dirty||(this._dirty=!0,fe(nt(this),"set","value"))}}),this.__v_isReadonly=n}get value(){return this._dirty&&(this._value=this.effect(),this._dirty=!1),de(nt(this),"get","value"),this._value}set value(e){this._setter(e)}}const lt=[];function ct(e){lt.push(e)}function ut(){lt.pop()}function dt(e,...t){ce();const n=lt.length?lt[lt.length-1].component:null,o=n&&n.appContext.config.warnHandler,r=function(){let e=lt[lt.length-1];if(!e)return[];const t=[];for(;e;){const n=t[0];n&&n.vnode===e?n.recurseCount++:t.push({vnode:e,recurseCount:0});const o=e.component&&e.component.parent;e=o&&o.vnode}return t}();if(o)mt(o,n,11,[e+t.join(""),n&&n.proxy,r.map((({vnode:e})=>`at <${wr(n,e.type)}>`)).join("\n"),r]);else{const n=[`[Vue warn]: ${e}`,...t];r.length&&n.push("\n",...function(e){const t=[];return e.forEach(((e,n)=>{t.push(...0===n?[]:["\n"],...function({vnode:e,recurseCount:t}){const n=t>0?`... (${t} recursive calls)`:"",o=!!e.component&&null==e.component.parent,r=` at <${wr(e.component,e.type,o)}`,i=">"+n;return e.props?[r,...ft(e.props),i]:[r+i]}(e))})),t}(r)),console.warn(...n)}ue()}function ft(e){const t=[],n=Object.keys(e);return n.slice(0,3).forEach((n=>{t.push(...pt(n,e[n]))})),n.length>3&&t.push(" ..."),t}function pt(e,t,n){return $(t)?(t=JSON.stringify(t),n?t:[`${e}=${t}`]):"number"==typeof t||"boolean"==typeof t||null==t?n?t:[`${e}=${t}`]:ot(t)?(t=pt(e,nt(t.value),!0),n?t:[`${e}=Ref<`,t,">"]):E(t)?[`${e}=fn${t.name?`<${t.name}>`:""}`]:(t=nt(t),n?t:[`${e}=`,t])}const ht={bc:"beforeCreate hook",c:"created hook",bm:"beforeMount hook",m:"mounted hook",bu:"beforeUpdate hook",u:"updated",bum:"beforeUnmount hook",um:"unmounted hook",a:"activated hook",da:"deactivated hook",ec:"errorCaptured hook",rtc:"renderTracked hook",rtg:"renderTriggered hook",0:"setup function",1:"render function",2:"watcher getter",3:"watcher callback",4:"watcher cleanup function",5:"native event handler",6:"component event handler",7:"vnode hook",8:"directive hook",9:"transition hook",10:"app errorHandler",11:"app warnHandler",12:"ref function",13:"async component loader",14:"scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next"};function mt(e,t,n,o){let r;try{r=o?e(...o):e()}catch(e){yt(e,t,n)}return r}function vt(e,t,n,o){if(E(e)){const r=mt(e,t,n,o);return r&&P(r)&&r.catch((e=>{yt(e,t,n)})),r}const r=[];for(let i=0;iUt(e)-Ut(t))),Ct=0;Ctnull==e.id?1/0:e.id;function Mt(e){bt=!1,gt=!0,Nt(e=e||new Map),_t.sort(((e,t)=>Ut(e)-Ut(t)));try{for(wt=0;wt<_t.length;wt++){const t=_t[wt];t&&(Lt(e,t),mt(t,null,14))}}finally{wt=0,_t.length=0,It(e),gt=!1,Et=null,(_t.length||Ot.length)&&Mt(e)}}function Lt(e,t){if(e.has(t)){const n=e.get(t);if(n>100)throw new Error("Maximum recursive updates exceeded. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.");e.set(t,n+1)}else e.set(t,1)}let Dt=!1;const Bt=new Set;("undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{}).__VUE_HMR_RUNTIME__={createRecord:Ht(qt),rerender:Ht((function(e,t){const n=zt.get(e);if(!n)return;t&&(n.component.render=t);Array.from(n.instances).forEach((e=>{t&&(e.render=t),e.renderCache=[],Dt=!0,e.update(),Dt=!1}))})),reload:Ht((function(e,t){const n=zt.get(e);if(!n)return;const{component:o,instances:r}=n;if(!Bt.has(o)){t=jr(t)?t.__vccOpts:t,w(o,t);for(const e in o)e in t||delete o[e];Bt.add(o),Vt((()=>{Bt.delete(o)}))}Array.from(r).forEach((e=>{e.parent?Ft(e.parent.update):e.appContext.reload?e.appContext.reload():"undefined"!=typeof window?window.location.reload():console.warn("[HMR] Root or manually mounted instance modified. Full reload required.")}))}))};const zt=new Map;function qt(e,t){return t||(dt("HMR API usage is out of date.\nPlease upgrade vue-loader/vite/rollup-plugin-vue or other relevant depdendency that handles Vue SFC compilation."),t={}),!zt.has(e)&&(zt.set(e,{component:jr(t)?t.__vccOpts:t,instances:new Set}),!0)}function Ht(e){return(t,n)=>{try{return e(t,n)}catch(e){console.error(e),console.warn("[HMR] Something went wrong during Vue component hot-reload. Full reload required.")}}}let Jt;const Wt=Gt("component:added"),Kt=Gt("component:updated"),Xt=Gt("component:removed");function Gt(e){return t=>{Jt&&Jt.emit(e,t.appContext.app,t.uid,t.parent?t.parent.uid:void 0,t)}}function Yt(e,t,...n){const o=e.vnode.props||h;{const{emitsOptions:o,propsOptions:[r]}=e;if(o)if(t in o){const e=o[t];if(E(e)){e(...n)||dt(`Invalid event arguments: event validation failed for event "${t}".`)}}else r&&J(t)in r||dt(`Component emitted event "${t}" but it is neither declared in the emits option nor as an "${J(t)}" prop.`)}let r=n;const i=t.startsWith("update:"),s=i&&t.slice(7);if(s&&s in o){const e=`${"modelValue"===s?"model":s}Modifiers`,{number:t,trim:i}=o[e]||h;i?r=n.map((e=>e.trim())):t&&(r=n.map(G))}!function(e,t,n){Jt&&Jt.emit("component:emit",e.appContext.app,e,t,n)}(e,t,r);{const n=t.toLowerCase();n!==t&&o[J(n)]&&dt(`Event "${n}" is emitted in component ${wr(e,e.type)} but the handler is registered for "${t}". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "${q(t)}" instead of "${t}".`)}let a=J(B(t)),l=o[a];!l&&i&&(a=J(q(t)),l=o[a]),l&&vt(l,e,6,r);const c=o[a+"Once"];if(c){if(e.emitted){if(e.emitted[a])return}else(e.emitted={})[a]=!0;vt(c,e,6,r)}}function Zt(e,t,n=!1){if(!t.deopt&&void 0!==e.__emits)return e.__emits;const o=e.emits;let r={},i=!1;if(!E(e)){const o=e=>{i=!0,w(r,Zt(e,t,!0))};!n&&t.mixins.length&&t.mixins.forEach(o),e.extends&&o(e.extends),e.mixins&&e.mixins.forEach(o)}return o||i?(O(o)?o.forEach((e=>r[e]=null)):w(r,o),e.__emits=r):e.__emits=null}function Qt(e,t){return!(!e||!b(t))&&(t=t.slice(2).replace(/Once$/,""),k(e,t[0].toLowerCase()+t.slice(1))||k(e,q(t))||k(e,t))}let en=null;function tn(e){en=e}let nn=!1;function on(){nn=!0}function rn(e){const{type:t,vnode:n,proxy:o,withProxy:r,props:i,propsOptions:[s],slots:a,attrs:l,emit:c,render:u,renderCache:d,data:f,setupState:p,ctx:h}=e;let m;en=e,nn=!1;try{let e;if(4&n.shapeFlag){const t=r||o;m=Ho(u.call(t,t,d,i,p,f,h)),e=l}else{const n=t;l===i&&on(),m=Ho(n.length>1?n(i,{get attrs(){return on(),l},slots:a,emit:c}):n(i,null)),e=t.props?l:ln(l)}let v,y=m;if(2048&m.patchFlag&&([y,v]=sn(m)),!1!==t.inheritAttrs&&e){const t=Object.keys(e),{shapeFlag:n}=y;if(t.length)if(1&n||6&n)s&&t.some(_)&&(e=cn(e,s)),y=Bo(y,e);else if(!nn&&y.type!==Eo){const e=Object.keys(l),t=[],n=[];for(let o=0,r=e.length;o renders non-element root node that cannot be animated."),y.transition=n.transition),v?v(y):m=y}catch(t){yt(t,e,1),m=Do(Eo)}return en=null,m}const sn=e=>{const t=e.children,n=e.dynamicChildren,o=an(t);if(!o)return[e,void 0];const r=t.indexOf(o),i=n?n.indexOf(o):-1;return[Ho(o),o=>{t[r]=o,n&&(i>-1?n[i]=o:o.patchFlag>0&&(e.dynamicChildren=[...n,o]))}]};function an(e){let t;for(let n=0;n{let t;for(const n in e)("class"===n||"style"===n||b(n))&&((t||(t={}))[n]=e[n]);return t},cn=(e,t)=>{const n={};for(const o in e)_(o)&&o.slice(9)in t||(n[o]=e[o]);return n},un=e=>6&e.shapeFlag||1&e.shapeFlag||e.type===Eo;function dn(e,t,n){const o=Object.keys(t);if(o.length!==Object.keys(e).length)return!0;for(let r=0;r slots expect a single root node."),e=t}return Ho(e)}let pn=0;const hn=e=>pn+=e;function mn(e,t=en){if(!t)return e;const n=(...n)=>{pn||Po(!0);const o=en;tn(t);const r=e(...n);return tn(o),pn||To(),r};return n._c=!0,n}let vn=null;const yn=[];function gn(e){yn.push(vn=e)}function bn(){yn.pop(),vn=yn[yn.length-1]||null}function _n(e){return t=>mn((function(){gn(e);const n=t.apply(this,arguments);return bn(),n}))}function wn(e,t,n,o=!1){const r={},i={};X(i,Uo,1),jn(e,t,r,i),En(r,e),n?e.props=o?r:Ze(r,!1,xe,qe):e.type.props?e.props=r:e.props=i,e.attrs=i}function jn(e,t,n,o){const[r,i]=e.propsOptions;if(t)for(const i in t){const s=t[i];if(M(i))continue;let a;r&&k(r,a=B(i))?n[a]=s:Qt(e.emitsOptions,i)||(o[i]=s)}if(i){const t=nt(n);for(let o=0;o{s=!0;const[n,o]=kn(e,t,!0);w(r,n),o&&i.push(...o)};!n&&t.mixins.length&&t.mixins.forEach(o),e.extends&&o(e.extends),e.mixins&&e.mixins.forEach(o)}if(!o&&!s)return e.__props=m;if(O(o))for(let e=0;e-1,s[1]=n<0||e-1||k(s,"default"))&&i.push(t)}}}}return e.__props=[r,i]}function On(e){return"$"!==e[0]||(dt(`Invalid prop name: "${e}" is a reserved property.`),!1)}function Sn(e){const t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:""}function Cn(e,t){return Sn(e)===Sn(t)}function Rn(e,t){if(O(t)){for(let n=0,o=t.length;n"boolean"===e.toLowerCase()))}(r,i)&&(o+=` with value ${s}`);o+=`, got ${i} `,Tn(i)&&(o+=`with value ${a}.`);return o}(e,t,i))}s&&!s(t)&&dt('Invalid prop: custom validator check failed for prop "'+e+'".')}}const An=e("String,Number,Boolean,Function,Symbol");function Fn(e,t){let n;const o=Sn(t);if(An(o)){const r=typeof e;n=r===o.toLowerCase(),n||"object"!==r||(n=e instanceof t)}else n="Object"===o?F(e):"Array"===o?O(e):e instanceof t;return{valid:n,expectedType:o}}function Pn(e,t){return"String"===t?`"${e}"`:"Number"===t?`${Number(e)}`:`${e}`}function Tn(e){return["string","number","boolean"].some((t=>e.toLowerCase()===t))}function Vn(e,t,n=ur,o=!1){if(n){const r=n[e]||(n[e]=[]),i=t.__weh||(t.__weh=(...o)=>{if(n.isUnmounted)return;ce(),dr(n);const r=vt(t,n,e,o);return dr(null),ue(),r});return o?r.unshift(i):r.push(i),i}dt(`${J(ht[e].replace(/ hook$/,""))} is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup(). If you are using async setup(), make sure to register lifecycle hooks before the first await statement.`)}const Nn=e=>(t,n=ur)=>!hr&&Vn(e,t,n),In=Nn("bm"),Un=Nn("m"),Mn=Nn("bu"),Ln=Nn("u"),Dn=Nn("bum"),Bn=Nn("um"),zn=Nn("rtg"),qn=Nn("rtc"),Hn={};function Jn(e,t,n){return E(t)||dt("`watch(fn, options?)` signature has been moved to a separate API. Use `watchEffect(fn, options?)` instead. `watch` now only supports `watch(source, cb, options?) signature."),Wn(e,t,n)}function Wn(e,t,{immediate:n,deep:o,flush:r,onTrack:i,onTrigger:s}=h,a=ur){t||(void 0!==n&&dt('watch() "immediate" option is only respected when using the watch(source, callback, options?) signature.'),void 0!==o&&dt('watch() "deep" option is only respected when using the watch(source, callback, options?) signature.'));const l=e=>{dt("Invalid watch source: ",e,"A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.")};let c,u,d=!1;if(ot(e)?(c=()=>e.value,d=!!e._shallow):Qe(e)?(c=()=>e,o=!0):O(e)?c=()=>e.map((e=>ot(e)?e.value:Qe(e)?Xn(e):E(e)?mt(e,a,2):void l(e))):E(e)?c=t?()=>mt(e,a,2):()=>{if(!a||!a.isUnmounted)return u&&u(),mt(e,a,3,[f])}:(c=v,l(e)),t&&o){const e=c;c=()=>Xn(e())}const f=e=>{u=g.options.onStop=()=>{mt(e,a,4)}};let p=O(e)?[]:Hn;const m=()=>{if(g.active)if(t){const e=g();(o||d||W(e,p))&&(u&&u(),vt(t,a,3,[e,p===Hn?void 0:p,f]),p=e)}else g()};let y;m.allowRecurse=!!t,y="sync"===r?m:"post"===r?()=>yo(m,a&&a.suspense):()=>{!a||a.isMounted?function(e){Tt(e,xt,jt,kt)}(m):m()};const g=oe(c,{lazy:!0,onTrack:i,onTrigger:s,scheduler:y});return gr(g,a),t?n?m():p=g():"post"===r?yo(g,a&&a.suspense):g(),()=>{re(g),a&&j(a.effects,g)}}function Kn(e,t,n){const o=this.proxy;return Wn($(e)?()=>o[e]:e.bind(o),t.bind(o),n,this)}function Xn(e,t=new Set){if(!F(e)||t.has(e))return e;if(t.add(e),ot(e))Xn(e.value,t);else if(O(e))for(let n=0;n{Xn(e,t)}));else for(const n in e)Xn(e[n],t);return e}const Gn=e=>e.type.__isKeepAlive;function Yn(e,t,n=ur){const o=e.__wdc||(e.__wdc=()=>{let t=n;for(;t;){if(t.isDeactivated)return;t=t.parent}e()});if(Vn(t,o,n),n){let e=n.parent;for(;e&&e.parent;)Gn(e.parent.vnode)&&Zn(o,t,n,e),e=e.parent}}function Zn(e,t,n,o){const r=Vn(t,e,o,!0);Bn((()=>{j(o[t],r)}),n)}const Qn=e=>"_"===e[0]||"$stable"===e,eo=e=>O(e)?e.map(Ho):[Ho(e)],to=(e,t,n)=>mn((n=>(ur&&dt(`Slot "${e}" invoked outside of the render function: this will not track dependencies used in the slot. Invoke the slot function inside the render function instead.`),eo(t(n)))),n),no=(e,t)=>{const n=e._ctx;for(const o in e){if(Qn(o))continue;const r=e[o];if(E(r))t[o]=to(o,r,n);else if(null!=r){dt(`Non-function value encountered for slot "${o}". Prefer function slots for better performance.`);const e=eo(r);t[o]=()=>e}}},oo=(e,t)=>{Gn(e.vnode)||dt("Non-function value encountered for default slot. Prefer function slots for better performance.");const n=eo(t);e.slots.default=()=>n},ro=e("bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text");function io(e){ro(e)&&dt("Do not use built-in directive ids as custom directive id: "+e)}function so(e,t){const n=en;if(null===n)return dt("withDirectives can only be used inside render functions."),e;const o=n.proxy,r=e.dirs||(e.dirs=[]);for(let e=0;e(i.has(e)?dt("Plugin has already been applied to target app."):e&&E(e.install)?(i.add(e),e.install(a,...t)):E(e)?(i.add(e),e(a,...t)):dt('A plugin must either be a function or an object with an "install" function.'),a),mixin:e=>(r.mixins.includes(e)?dt("Mixin has already been applied to target app"+(e.name?`: ${e.name}`:"")):(r.mixins.push(e),(e.props||e.emits)&&(r.deopt=!0)),a),component:(e,t)=>(pr(e,r.config),t?(r.components[e]&&dt(`Component "${e}" has already been registered in target app.`),r.components[e]=t,a):r.components[e]),directive:(e,t)=>(io(e),t?(r.directives[e]&&dt(`Directive "${e}" has already been registered in target app.`),r.directives[e]=t,a):r.directives[e]),mount(i,l){if(!s){const c=Do(n,o);return c.appContext=r,r.reload=()=>{e(Bo(c),i)},l&&t?t(c,i):e(c,i),s=!0,a._container=i,i.__vue_app__=a,function(e,t){Jt&&Jt.emit("app:init",e,t,{Fragment:Co,Text:Ro,Comment:Eo,Static:$o})}(a,Or),c.component.proxy}dt("App has already been mounted.\nIf you want to remount the same app, move your app creation logic into a factory function and create fresh app instances for each mount - e.g. `const createMyApp = () => createApp(App)`")},unmount(){s?(e(null,a._container),function(e){Jt&&Jt.emit("app:unmount",e)}(a)):dt("Cannot unmount an app that is not mounted.")},provide:(e,t)=>(e in r.provides&&dt(`App already provides property with key "${String(e)}". It will be overwritten with the new value.`),r.provides[e]=t,a)};return a}}function ho(e,t){e.appContext.config.performance&&vo()&&uo.mark(`vue-${t}-${e.uid}`)}function mo(e,t){if(e.appContext.config.performance&&vo()){const n=`vue-${t}-${e.uid}`,o=n+":end";uo.mark(o),uo.measure(`<${wr(e,e.type)}> ${t}`,n,o),uo.clearMarks(n),uo.clearMarks(o)}}function vo(){return void 0!==co||("undefined"!=typeof window&&window.performance?(co=!0,uo=window.performance):co=!1),co}const yo=function(e,t){t&&t.pendingBranch?O(e)?t.effects.push(...e):t.effects.push(e):Vt(e)},go=(e,t,n,o)=>{if(O(e))return void e.forEach(((e,r)=>go(e,t&&(O(t)?t[r]:t),n,o)));let r;r=!o||o.type.__asyncLoader?null:4&o.shapeFlag?o.component.exposed||o.component.proxy:o.el;const{i:i,r:s}=e;if(!i)return void dt("Missing ref owner context. ref cannot be used on hoisted vnodes. A vnode with ref must be created inside the render function.");const a=t&&t.r,l=i.refs===h?i.refs={}:i.refs,c=i.setupState;if(null!=a&&a!==s&&($(a)?(l[a]=null,k(c,a)&&(c[a]=null)):ot(a)&&(a.value=null)),$(s)){const e=()=>{l[s]=r,k(c,s)&&(c[s]=r)};r?(e.id=-1,yo(e,n)):e()}else if(ot(s)){const e=()=>{s.value=r};r?(e.id=-1,yo(e,n)):e()}else E(s)?mt(s,i,12,[r,l]):dt("Invalid template ref type:",r,`(${typeof r})`)};function bo(e){return function(e,t){const{insert:n,remove:o,patchProp:r,forcePatchProp:i,createElement:s,createText:a,createComment:l,setText:c,setElementText:u,parentNode:d,nextSibling:f,setScopeId:p=v,cloneNode:y,insertStaticContent:g}=e,b=(e,t,n,o=null,r=null,i=null,s=!1,a=!1)=>{e&&!Io(e,t)&&(o=ne(e),Y(e,r,i,!0),e=null),-2===t.patchFlag&&(a=!1,t.dynamicChildren=null);const{type:l,ref:c,shapeFlag:u}=t;switch(l){case Ro:_(e,t,n,o);break;case Eo:j(e,t,n,o);break;case $o:null==e?x(t,n,o,s):O(e,t,n,s);break;case Co:N(e,t,n,o,r,i,s,a);break;default:1&u?R(e,t,n,o,r,i,s,a):6&u?I(e,t,n,o,r,i,s,a):64&u||128&u?l.process(e,t,n,o,r,i,s,a,se):dt("Invalid VNode type:",l,`(${typeof l})`)}null!=c&&r&&go(c,e&&e.ref,i,t)},_=(e,t,o,r)=>{if(null==e)n(t.el=a(t.children),o,r);else{const n=t.el=e.el;t.children!==e.children&&c(n,t.children)}},j=(e,t,o,r)=>{null==e?n(t.el=l(t.children||""),o,r):t.el=e.el},x=(e,t,n,o)=>{[e.el,e.anchor]=g(e.children,t,n,o)},O=(e,t,n,o)=>{if(t.children!==e.children){const r=f(e.anchor);C(e),[t.el,t.anchor]=g(t.children,n,r,o)}else t.el=e.el,t.anchor=e.anchor},S=({el:e,anchor:t},o,r)=>{let i;for(;e&&e!==t;)i=f(e),n(e,o,r),e=i;n(t,o,r)},C=({el:e,anchor:t})=>{let n;for(;e&&e!==t;)n=f(e),o(e),e=n;o(t)},R=(e,t,n,o,r,i,s,a)=>{s=s||"svg"===t.type,null==e?E(t,n,o,r,i,s,a):F(e,t,r,i,s,a)},E=(e,t,o,i,a,l,c)=>{let d,f;const{type:p,props:h,shapeFlag:m,transition:v,scopeId:y,patchFlag:g,dirs:b}=e;if(d=e.el=s(e.type,l,h&&h.is),8&m?u(d,e.children):16&m&&A(e.children,d,null,i,a,l&&"foreignObject"!==p,c||!!e.dynamicChildren),b&&ao(e,null,i,"created"),h){for(const t in h)M(t)||r(d,t,null,h[t],l,e.children,i,a,te);(f=h.onVnodeBeforeMount)&&_o(f,i,e)}$(d,y,e,i),Object.defineProperty(d,"__vnode",{value:e,enumerable:!1}),Object.defineProperty(d,"__vueParentComponent",{value:i,enumerable:!1}),b&&ao(e,null,i,"beforeMount");const _=(!a||a&&!a.pendingBranch)&&v&&!v.persisted;_&&v.beforeEnter(d),n(d,t,o),((f=h&&h.onVnodeMounted)||_||b)&&yo((()=>{f&&_o(f,i,e),_&&v.enter(d),b&&ao(e,null,i,"mounted")}),a)},$=(e,t,n,o)=>{if(t&&p(e,t),o){const r=o.type.__scopeId;r&&r!==t&&p(e,r+"-s");let i=o.subTree;i.type===Co&&(i=an(i.children)||i),n===i&&$(e,o.vnode.scopeId,o.vnode,o.parent)}},A=(e,t,n,o,r,i,s,a=0)=>{for(let l=a;l{const l=t.el=e.el;let{patchFlag:c,dynamicChildren:d,dirs:f}=t;c|=16&e.patchFlag;const p=e.props||h,m=t.props||h;let v;if((v=m.onVnodeBeforeUpdate)&&_o(v,n,t,e),f&&ao(t,e,n,"beforeUpdate"),Dt&&(c=0,a=!1,d=null),c>0){if(16&c)V(l,t,p,m,n,o,s);else if(2&c&&p.class!==m.class&&r(l,"class",null,m.class,s),4&c&&r(l,"style",p.style,m.style,s),8&c){const a=t.dynamicProps;for(let t=0;t{v&&_o(v,n,t,e),f&&ao(t,e,n,"updated")}),o)},T=(e,t,n,o,r,i)=>{for(let s=0;s{if(n!==o){for(const c in o){if(M(c))continue;const u=o[c],d=n[c];(u!==d||i&&i(e,c))&&r(e,c,d,u,l,t.children,s,a,te)}if(n!==h)for(const i in n)M(i)||i in o||r(e,i,n[i],null,l,t.children,s,a,te)}},N=(e,t,o,r,i,s,l,c)=>{const u=t.el=e?e.el:a(""),d=t.anchor=e?e.anchor:a("");let{patchFlag:f,dynamicChildren:p}=t;f>0&&(c=!0),Dt&&(f=0,c=!1,p=null),null==e?(n(u,o,r),n(d,o,r),A(t.children,o,d,i,s,l,c)):f>0&&64&f&&p&&e.dynamicChildren?(T(e.dynamicChildren,p,o,i,s,l),i&&i.type.__hmrId?wo(e,t):(null!=t.key||i&&t===i.subTree)&&wo(e,t,!0)):H(e,t,o,d,i,s,l,c)},I=(e,t,n,o,r,i,s,a)=>{null==e?512&t.shapeFlag?r.ctx.activate(t,n,o,s,a):U(t,n,o,r,i,s,a):L(e,t,a)},U=(e,t,n,o,r,i,s)=>{const a=e.component=function(e,t,n){const o=e.type,r=(t?t.appContext:e.appContext)||lr,i={uid:cr++,vnode:e,type:o,parent:t,appContext:r,root:null,next:null,subTree:null,update:null,render:null,proxy:null,exposed:null,withProxy:null,effects:null,provides:t?t.provides:Object.create(r.provides),accessCache:null,renderCache:[],components:null,directives:null,propsOptions:kn(o,r),emitsOptions:Zt(o,r),emit:null,emitted:null,ctx:h,data:h,props:h,attrs:h,slots:h,refs:h,setupState:h,setupContext:null,suspense:n,suspenseId:n?n.pendingId:0,asyncDep:null,asyncResolved:!1,isMounted:!1,isUnmounted:!1,isDeactivated:!1,bc:null,c:null,bm:null,m:null,bu:null,u:null,um:null,bum:null,da:null,a:null,rtg:null,rtc:null,ec:null};return i.ctx=function(e){const t={};Object.defineProperty(t,"_",{configurable:!0,enumerable:!1,get:()=>e}),Object.keys(ir).forEach((n=>{Object.defineProperty(t,n,{configurable:!0,enumerable:!1,get:()=>ir[n](e),set:v})}));const{globalProperties:n}=e.appContext.config;return Object.keys(n).forEach((e=>{Object.defineProperty(t,e,{configurable:!0,enumerable:!1,get:()=>n[e],set:v})})),t}(i),i.root=t?t.root:i,i.emit=Yt.bind(null,i),Wt(i),i}(e,o,r);if(a.type.__hmrId&&function(e){const t=e.type.__hmrId;let n=zt.get(t);n||(qt(t,e.type),n=zt.get(t)),n.instances.add(e)}(a),ct(e),ho(a,"mount"),Gn(e)&&(a.ctx.renderer=se),ho(a,"init"),function(e,t=!1){hr=t;const{props:n,children:o,shapeFlag:r}=e.vnode,i=4&r;wn(e,n,i,t),((e,t)=>{if(32&e.vnode.shapeFlag){const n=t._;n?(e.slots=t,X(t,"_",n)):no(t,e.slots={})}else e.slots={},t&&oo(e,t);X(e.slots,Uo,1)})(e,o);const s=i?function(e,t){const n=e.type;n.name&&pr(n.name,e.appContext.config);if(n.components){const t=Object.keys(n.components);for(let n=0;n{Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>e.props[n],set:v})}))}(e);const{setup:o}=n;if(o){const n=e.setupContext=o.length>1?function(e){const t=t=>{e.exposed&&dt("expose() should be called only once per setup()."),e.exposed=it(t)};return Object.freeze({get props(){return e.props},get attrs(){return new Proxy(e.attrs,yr)},get slots(){return Ye(e.slots)},get emit(){return(t,...n)=>e.emit(t,...n)},expose:t})}(e):null;ur=e,ce();const r=mt(o,e,0,[Ye(e.props),n]);if(ue(),ur=null,P(r)){if(t)return r.then((t=>{mr(e,t)}));e.asyncDep=r}else mr(e,r)}else vr(e)}(e,t):void 0;hr=!1}(a),mo(a,"init"),a.asyncDep){if(r&&r.registerDep(a,D),!e.el){const e=a.subTree=Do(Eo);j(null,e,t,n)}}else D(a,e,t,n,r,i,s),ut(),mo(a,"mount")},L=(e,t,n)=>{const o=t.component=e.component;if(function(e,t,n){const{props:o,children:r,component:i}=e,{props:s,children:a,patchFlag:l}=t,c=i.emitsOptions;if((r||a)&&Dt)return!0;if(t.dirs||t.transition)return!0;if(!(n&&l>=0))return!(!r&&!a||a&&a.$stable)||o!==s&&(o?!s||dn(o,s,c):!!s);if(1024&l)return!0;if(16&l)return o?dn(o,s,c):!!s;if(8&l){const e=t.dynamicProps;for(let t=0;t-1&&_t.splice(t,1)}(o.update),o.update()}else t.component=e.component,t.el=e.el,o.vnode=t},D=(e,t,n,o,r,i,s)=>{e.update=oe((function(){if(e.isMounted){let t,{next:n,bu:o,u:a,parent:l,vnode:c}=e,u=n;ct(n||e.vnode),n?(n.el=c.el,z(e,n,s)):n=c,o&&K(o),(t=n.props&&n.props.onVnodeBeforeUpdate)&&_o(t,l,n,c),ho(e,"render");const f=rn(e);mo(e,"render");const p=e.subTree;e.subTree=f,ho(e,"patch"),b(p,f,d(p.el),ne(p),e,r,i),mo(e,"patch"),n.el=f.el,null===u&&function({vnode:e,parent:t},n){for(;t&&t.subTree===e;)(e=t.vnode).el=n,t=t.parent}(e,f.el),a&&yo(a,r),(t=n.props&&n.props.onVnodeUpdated)&&yo((()=>{_o(t,l,n,c)}),r),Kt(e),ut()}else{let s;const{el:a,props:l}=t,{bm:c,m:u,parent:d}=e;c&&K(c),(s=l&&l.onVnodeBeforeMount)&&_o(s,d,t),ho(e,"render");const f=e.subTree=rn(e);if(mo(e,"render"),a&&le?(ho(e,"hydrate"),le(t.el,f,e,r),mo(e,"hydrate")):(ho(e,"patch"),b(null,f,n,o,e,r,i),mo(e,"patch"),t.el=f.el),u&&yo(u,r),s=l&&l.onVnodeMounted){const e=t;yo((()=>{_o(s,d,e)}),r)}const{a:p}=e;p&&256&t.shapeFlag&&yo(p,r),e.isMounted=!0,t=n=o=null}}),function(e){return{scheduler:Ft,allowRecurse:!0,onTrack:e.rtc?t=>K(e.rtc,t):void 0,onTrigger:e.rtg?t=>K(e.rtg,t):void 0}}(e))},z=(e,t,n)=>{t.component=e;const o=e.vnode.props;e.vnode=t,e.next=null,function(e,t,n,o){const{props:r,attrs:i,vnode:{patchFlag:s}}=e,a=nt(r),[l]=e.propsOptions;if(e.type.__hmrId||e.parent&&e.parent.type.__hmrId||!(o||s>0)||16&s){let o;jn(e,t,r,i);for(const i in a)t&&(k(t,i)||(o=q(i))!==i&&k(t,o))||(l?!n||void 0===n[i]&&void 0===n[o]||(r[i]=xn(l,t||h,i,void 0,e)):delete r[i]);if(i!==a)for(const e in i)t&&k(t,e)||delete i[e]}else if(8&s){const n=e.vnode.dynamicProps;for(let o=0;o{const{vnode:n,slots:o}=e;let r=!0,i=h;if(32&n.shapeFlag){const e=t._;e?Dt?w(o,t):1===e?r=!1:w(o,t):(r=!t.$stable,no(t,o)),i=t}else t&&(oo(e,t),i={default:1});if(r)for(const e in o)Qn(e)||e in i||delete o[e]})(e,t.children),Nt(void 0,e.update)},H=(e,t,n,o,r,i,s,a=!1)=>{const l=e&&e.children,c=e?e.shapeFlag:0,d=t.children,{patchFlag:f,shapeFlag:p}=t;if(f>0){if(128&f)return void W(l,d,n,o,r,i,s,a);if(256&f)return void J(l,d,n,o,r,i,s,a)}8&p?(16&c&&te(l,r,i),d!==l&&u(n,d)):16&c?16&p?W(l,d,n,o,r,i,s,a):te(l,r,i,!0):(8&c&&u(n,""),16&p&&A(d,n,o,r,i,s,a))},J=(e,t,n,o,r,i,s,a)=>{t=t||m;const l=(e=e||m).length,c=t.length,u=Math.min(l,c);let d;for(d=0;dc?te(e,r,i,!0,!1,u):A(t,n,o,r,i,s,a,u)},W=(e,t,n,o,r,i,s,a)=>{let l=0;const c=t.length;let u=e.length-1,d=c-1;for(;l<=u&&l<=d;){const o=e[l],c=t[l]=a?Jo(t[l]):Ho(t[l]);if(!Io(o,c))break;b(o,c,n,null,r,i,s,a),l++}for(;l<=u&&l<=d;){const o=e[u],l=t[d]=a?Jo(t[d]):Ho(t[d]);if(!Io(o,l))break;b(o,l,n,null,r,i,s,a),u--,d--}if(l>u){if(l<=d){const e=d+1,u=ed)for(;l<=u;)Y(e[l],r,i,!0),l++;else{const f=l,p=l,h=new Map;for(l=p;l<=d;l++){const e=t[l]=a?Jo(t[l]):Ho(t[l]);null!=e.key&&(h.has(e.key)&&dt("Duplicate keys found during update:",JSON.stringify(e.key),"Make sure keys are unique."),h.set(e.key,l))}let v,y=0;const g=d-p+1;let _=!1,w=0;const j=new Array(g);for(l=0;l=g){Y(o,r,i,!0);continue}let c;if(null!=o.key)c=h.get(o.key);else for(v=p;v<=d;v++)if(0===j[v-p]&&Io(o,t[v])){c=v;break}void 0===c?Y(o,r,i,!0):(j[c-p]=l+1,c>=w?w=c:_=!0,b(o,t[c],n,null,r,i,s,a),y++)}const x=_?function(e){const t=e.slice(),n=[0];let o,r,i,s,a;const l=e.length;for(o=0;o0&&(t[o]=n[i-1]),n[i]=o)}}i=n.length,s=n[i-1];for(;i-- >0;)n[i]=s,s=t[s];return n}(j):m;for(v=x.length-1,l=g-1;l>=0;l--){const e=p+l,a=t[e],u=e+1{const{el:s,type:a,transition:l,children:c,shapeFlag:u}=e;if(6&u)return void G(e.component.subTree,t,o,r);if(128&u)return void e.suspense.move(t,o,r);if(64&u)return void a.move(e,t,o,se);if(a===Co){n(s,t,o);for(let e=0;el.enter(s)),i);else{const{leave:e,delayLeave:r,afterLeave:i}=l,a=()=>n(s,t,o),c=()=>{e(s,(()=>{a(),i&&i()}))};r?r(s,a,c):c()}else n(s,t,o)},Y=(e,t,n,o=!1,r=!1)=>{const{type:i,props:s,ref:a,children:l,dynamicChildren:c,shapeFlag:u,patchFlag:d,dirs:f}=e;if(null!=a&&go(a,null,n,null),256&u)return void t.ctx.deactivate(e);const p=1&u&&f;let h;if((h=s&&s.onVnodeBeforeUnmount)&&_o(h,t,e),6&u)ee(e.component,n,o);else{if(128&u)return void e.suspense.unmount(n,o);p&&ao(e,null,t,"beforeUnmount"),c&&(i!==Co||d>0&&64&d)?te(c,t,n,!1,!0):(i===Co&&(128&d||256&d)||!r&&16&u)&&te(l,t,n),64&u&&(o||!jo(e.props))&&e.type.remove(e,se),o&&Z(e)}((h=s&&s.onVnodeUnmounted)||p)&&yo((()=>{h&&_o(h,t,e),p&&ao(e,null,t,"unmounted")}),n)},Z=e=>{const{type:t,el:n,anchor:r,transition:i}=e;if(t===Co)return void Q(n,r);if(t===$o)return void C(e);const s=()=>{o(n),i&&!i.persisted&&i.afterLeave&&i.afterLeave()};if(1&e.shapeFlag&&i&&!i.persisted){const{leave:t,delayLeave:o}=i,r=()=>t(n,s);o?o(e.el,s,r):r()}else s()},Q=(e,t)=>{let n;for(;e!==t;)n=f(e),o(e),e=n;o(t)},ee=(e,t,n)=>{e.type.__hmrId&&function(e){zt.get(e.type.__hmrId).instances.delete(e)}(e);const{bum:o,effects:r,update:i,subTree:s,um:a}=e;if(o&&K(o),r)for(let e=0;e{e.isUnmounted=!0}),t),t&&t.pendingBranch&&!t.isUnmounted&&e.asyncDep&&!e.asyncResolved&&e.suspenseId===t.pendingId&&(t.deps--,0===t.deps&&t.resolve()),Xt(e)},te=(e,t,n,o=!1,r=!1,i=0)=>{for(let s=i;s6&e.shapeFlag?ne(e.component.subTree):128&e.shapeFlag?e.suspense.next():f(e.anchor||e.el),ie=(e,t)=>{null==e?t._vnode&&Y(t._vnode,null,null,!0):b(t._vnode||null,e,t),It(),t._vnode=e},se={p:b,um:Y,m:G,r:Z,mt:U,mc:A,pc:H,pbc:T,n:ne,o:e};let ae,le;t&&([ae,le]=t(se));return{render:ie,hydrate:ae,createApp:po(ie,ae)}}(e)}function _o(e,t,n,o=null){vt(e,t,7,[n,o])}function wo(e,t,n=!1){const o=e.children,r=t.children;if(O(o)&&O(r))for(let e=0;ee&&(e.disabled||""===e.disabled),xo="components";function ko(e){return function(e,t,n=!0){const o=en||ur;if(o){const r=o.type;if(e===xo){if("_self"===t)return r;const e=_r(r);if(e&&(e===t||e===B(t)||e===H(B(t))))return r}const i=So(o[e]||r[e],t)||So(o.appContext[e],t);return n&&!i&&dt(`Failed to resolve ${e.slice(0,-1)}: ${t}`),i}dt(`resolve${H(e.slice(0,-1))} can only be used in render() or setup().`)}(xo,e)||e}const Oo=Symbol();function So(e,t){return e&&(e[t]||e[B(t)]||e[H(B(t))])}const Co=Symbol("Fragment"),Ro=Symbol("Text"),Eo=Symbol("Comment"),$o=Symbol("Static"),Ao=[];let Fo=null;function Po(e=!1){Ao.push(Fo=e?null:[])}function To(){Ao.pop(),Fo=Ao[Ao.length-1]||null}function Vo(e,t,n,o,r){const i=Do(e,t,n,o,r,!0);return i.dynamicChildren=Fo||m,To(),Fo&&Fo.push(i),i}function No(e){return!!e&&!0===e.__v_isVNode}function Io(e,t){return!(6&t.shapeFlag&&Bt.has(t.type))&&(e.type===t.type&&e.key===t.key)}const Uo="__vInternal",Mo=({key:e})=>null!=e?e:null,Lo=({ref:e})=>null!=e?$(e)||ot(e)||E(e)?{i:en,r:e}:e:null,Do=(...e)=>function(e,t=null,n=null,r=0,i=null,s=!1){e&&e!==Oo||(e||dt(`Invalid vnode type when creating vnode: ${e}.`),e=Eo);if(No(e)){const o=Bo(e,t,!0);return n&&Wo(o,n),o}jr(e)&&(e=e.__vccOpts);if(t){(tt(t)||Uo in t)&&(t=w({},t));let{class:e,style:n}=t;e&&!$(e)&&(t.class=a(e)),F(n)&&(tt(n)&&!O(n)&&(n=w({},n)),t.style=o(n))}const l=$(e)?1:(e=>e.__isSuspense)(e)?128:(e=>e.__isTeleport)(e)?64:F(e)?4:E(e)?2:0;4&l&&tt(e)&&dt("Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with `markRaw` or using `shallowRef` instead of `ref`.","\nComponent that was made reactive: ",e=nt(e));const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Mo(t),ref:t&&Lo(t),scopeId:vn,children:null,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:l,patchFlag:r,dynamicProps:i,dynamicChildren:null,appContext:null};c.key!=c.key&&dt("VNode created with invalid key (NaN). VNode type:",c.type);if(Wo(c,n),128&l){const{content:e,fallback:t}=function(e){const{shapeFlag:t,children:n}=e;let o,r;return 32&t?(o=fn(n.default),r=fn(n.fallback)):(o=fn(n),r=Ho(null)),{content:o,fallback:r}}(c);c.ssContent=e,c.ssFallback=t}!s&&Fo&&(r>0||6&l)&&32!==r&&Fo.push(c);return c}(...e);function Bo(e,t,n=!1){const{props:r,ref:i,patchFlag:s}=e,l=t?function(...e){const t=w({},e[0]);for(let n=1;n1)return n&&E(t)?t():t;dt(`injection "${String(e)}" not found.`)}else dt("inject() can only be used inside setup() or functional components.")}let Xo=!1;function Go(e,t,n=[],o=[],r=[],i=!1){const{mixins:s,extends:a,data:l,computed:c,methods:u,watch:d,provide:f,inject:p,components:m,directives:y,beforeMount:g,mounted:b,beforeUpdate:_,updated:j,activated:x,deactivated:k,beforeDestroy:S,beforeUnmount:C,destroyed:R,unmounted:$,render:A,renderTracked:P,renderTriggered:T,errorCaptured:V,expose:N}=t,I=e.proxy,U=e.ctx,M=e.appContext.mixins;i&&A&&e.render===v&&(e.render=A),i||(Xo=!0,Yo("beforeCreate","bc",t,e,M),Xo=!1,er(e,M,n,o,r)),a&&Go(e,a,n,o,r,!0),s&&er(e,s,n,o,r);const L=function(){const e=Object.create(null);return(t,n)=>{e[n]?dt(`${t} property "${n}" is already defined in ${e[n]}.`):e[n]=t}}();{const[t]=e.propsOptions;if(t)for(const e in t)L("Props",e)}if(p)if(O(p))for(let e=0;etr(e,t,I))),l&&tr(e,l,I);{const t=nt(e.data);for(const e in t)L("Data",e),"$"!==e[0]&&"_"!==e[0]&&Object.defineProperty(U,e,{configurable:!0,enumerable:!0,get:()=>t[e],set:v})}}if(c)for(const e in c){const t=c[e],n=E(t)?t.bind(I,I):E(t.get)?t.get.bind(I,I):v;n===v&&dt(`Computed property "${e}" has no getter.`);const o=xr({get:n,set:!E(t)&&E(t.set)?t.set.bind(I):()=>{dt(`Write operation failed: computed property "${e}" is readonly.`)}});Object.defineProperty(U,e,{enumerable:!0,configurable:!0,get:()=>o.value,set:e=>o.value=e}),L("Computed",e)}var D;if(d&&o.push(d),!i&&o.length&&o.forEach((e=>{for(const t in e)nr(e[t],U,I,t)})),f&&r.push(f),!i&&r.length&&r.forEach((e=>{const t=E(e)?e.call(I):e;Reflect.ownKeys(t).forEach((e=>{!function(e,t){if(ur){let n=ur.provides;const o=ur.parent&&ur.parent.provides;o===n&&(n=ur.provides=Object.create(o)),n[e]=t}else dt("provide() can only be used inside setup().")}(e,t[e])}))})),i&&(m&&w(e.components||(e.components=w({},e.type.components)),m),y&&w(e.directives||(e.directives=w({},e.type.directives)),y)),i||Yo("created","c",t,e,M),g&&In(g.bind(I)),b&&Un(b.bind(I)),_&&Mn(_.bind(I)),j&&Ln(j.bind(I)),x&&Yn(x.bind(I),"a",D),k&&function(e,t){Yn(e,"da",t)}(k.bind(I)),V&&((e,t=ur)=>{Vn("ec",e,t)})(V.bind(I)),P&&qn(P.bind(I)),T&&zn(T.bind(I)),S&&dt("`beforeDestroy` has been renamed to `beforeUnmount`."),C&&Dn(C.bind(I)),R&&dt("`destroyed` has been renamed to `unmounted`."),$&&Bn($.bind(I)),O(N))if(i)dt("The `expose` option is ignored when used in mixins.");else if(N.length){const t=e.exposed||(e.exposed=it({}));N.forEach((e=>{t[e]=function(e,t){return ot(e[t])?e[t]:new st(e,t)}(I,e)}))}else e.exposed||(e.exposed=h)}function Yo(e,t,n,o,r){Qo(e,t,r,o);const{extends:i,mixins:s}=n;i&&Zo(e,t,i,o),s&&Qo(e,t,s,o);const a=n[e];a&&vt(a.bind(o.proxy),o,t)}function Zo(e,t,n,o){n.extends&&Zo(e,t,n.extends,o);const r=n[e];r&&vt(r.bind(o.proxy),o,t)}function Qo(e,t,n,o){for(let r=0;r."),F(o)?e.data===h?e.data=Xe(o):w(e.data,o):dt("data() should return an object.")}function nr(e,t,n,o){const r=o.includes(".")?function(e,t){const n=t.split(".");return()=>{let t=e;for(let e=0;en[o];if($(e)){const n=t[e];E(n)?Jn(r,n):dt(`Invalid watch handler specified by key "${e}"`,n)}else if(E(e))Jn(r,e.bind(n));else if(F(e))if(O(e))e.forEach((e=>nr(e,t,n,o)));else{const o=E(e.handler)?e.handler.bind(n):t[e.handler];E(o)?Jn(r,o,e):dt(`Invalid watch handler specified by key "${e.handler}"`,o)}else dt(`Invalid watch option: "${o}"`,e)}function or(e,t,n){const o=n.appContext.config.optionMergeStrategies,{mixins:r,extends:i}=t;i&&or(e,i,n),r&&r.forEach((t=>or(e,t,n)));for(const r in t)o&&k(o,r)?e[r]=o[r](e[r],t[r],n.proxy,r):e[r]=t[r]}const rr=e=>e&&(e.proxy?e.proxy:rr(e.parent)),ir=w(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>Ye(e.props),$attrs:e=>Ye(e.attrs),$slots:e=>Ye(e.slots),$refs:e=>Ye(e.refs),$parent:e=>rr(e.parent),$root:e=>e.root&&e.root.proxy,$emit:e=>e.emit,$options:e=>function(e){const t=e.type,{__merged:n,mixins:o,extends:r}=t;if(n)return n;const i=e.appContext.mixins;if(!i.length&&!o&&!r)return t;const s={};return i.forEach((t=>or(s,t,e))),or(s,t,e),t.__merged=s}(e),$forceUpdate:e=>()=>Ft(e.update),$nextTick:e=>At.bind(e.proxy),$watch:e=>Kn.bind(e)}),sr={get({_:e},t){const{ctx:n,setupState:o,data:r,props:i,accessCache:s,type:a,appContext:l}=e;if("__v_skip"===t)return!0;if("__isVue"===t)return!0;let c;if("$"!==t[0]){const a=s[t];if(void 0!==a)switch(a){case 0:return o[t];case 1:return r[t];case 3:return n[t];case 2:return i[t]}else{if(o!==h&&k(o,t))return s[t]=0,o[t];if(r!==h&&k(r,t))return s[t]=1,r[t];if((c=e.propsOptions[0])&&k(c,t))return s[t]=2,i[t];if(n!==h&&k(n,t))return s[t]=3,n[t];Xo||(s[t]=4)}}const u=ir[t];let d,f;return u?("$attrs"===t&&(de(e,"get",t),on()),u(e)):(d=a.__cssModules)&&(d=d[t])?d:n!==h&&k(n,t)?(s[t]=3,n[t]):(f=l.config.globalProperties,k(f,t)?f[t]:void(!en||$(t)&&0===t.indexOf("__v")||(r===h||"$"!==t[0]&&"_"!==t[0]||!k(r,t)?dt(`Property ${JSON.stringify(t)} was accessed during render but is not defined on instance.`):dt(`Property ${JSON.stringify(t)} must be accessed via $data because it starts with a reserved character ("$" or "_") and is not proxied on the render context.`))))},set({_:e},t,n){const{data:o,setupState:r,ctx:i}=e;if(r!==h&&k(r,t))r[t]=n;else if(o!==h&&k(o,t))o[t]=n;else if(t in e.props)return dt(`Attempting to mutate prop "${t}". Props are readonly.`,e),!1;return"$"===t[0]&&t.slice(1)in e?(dt(`Attempting to mutate public property "${t}". Properties starting with $ are reserved and readonly.`,e),!1):(t in e.appContext.config.globalProperties?Object.defineProperty(i,t,{enumerable:!0,configurable:!0,value:n}):i[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:o,appContext:r,propsOptions:i}},s){let a;return void 0!==n[s]||e!==h&&k(e,s)||t!==h&&k(t,s)||(a=i[0])&&k(a,s)||k(o,s)||k(ir,s)||k(r.config.globalProperties,s)},ownKeys:e=>(dt("Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead."),Reflect.ownKeys(e))},ar=w({},sr,{get(e,t){if(t!==Symbol.unscopables)return sr.get(e,t,e)},has(e,n){const o="_"!==n[0]&&!t(n);return!o&&sr.has(e,n)&&dt(`Property ${JSON.stringify(n)} should not start with _ which is a reserved prefix for Vue internals.`),o}});const lr=lo();let cr=0;let ur=null;const dr=e=>{ur=e},fr=e("slot,component");function pr(e,t){const n=t.isNativeTag||y;(fr(e)||n(e))&&dt("Do not use built-in or reserved HTML elements as component id: "+e)}let hr=!1;function mr(e,t,n){E(t)?e.render=t:F(t)?(No(t)&&dt("setup() should not return VNodes directly - return a render function instead."),e.devtoolsRawSetupState=t,e.setupState=it(t),function(e){const{ctx:t,setupState:n}=e;Object.keys(nt(n)).forEach((e=>{"$"!==e[0]&&"_"!==e[0]?Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:()=>n[e],set:v}):dt(`setup() return property ${JSON.stringify(e)} should not start with "$" or "_" which are reserved prefixes for Vue internals.`)}))}(e)):void 0!==t&&dt("setup() should return an object. Received: "+(null===t?"null":typeof t)),vr(e)}function vr(e,t){const n=e.type;e.render||(e.render=n.render||v,e.render._rc&&(e.withProxy=new Proxy(e.ctx,ar))),ur=e,ce(),Go(e,n),ue(),ur=null,n.render||e.render!==v||(n.template?dt('Component provided template option but runtime compilation is not supported in this build of Vue. Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".'):dt("Component is missing template or render function."))}const yr={get:(e,t)=>(on(),e[t]),set:()=>(dt("setupContext.attrs is readonly."),!1),deleteProperty:()=>(dt("setupContext.attrs is readonly."),!1)};function gr(e,t=ur){t&&(t.effects||(t.effects=[])).push(e)}const br=/(?:^|[-_])(\w)/g;function _r(e){return E(e)&&e.displayName||e.name}function wr(e,t,n=!1){let o=_r(t);if(!o&&t.__file){const e=t.__file.match(/([^/\\]+)\.\w+$/);e&&(o=e[1])}if(!o&&e&&e.parent){const n=e=>{for(const n in e)if(e[n]===t)return n};o=n(e.components||e.parent.type.components)||n(e.appContext.components)}return o?o.replace(br,(e=>e.toUpperCase())).replace(/[-_]/g,""):n?"App":"Anonymous"}function jr(e){return E(e)&&"__vccOpts"in e}function xr(e){const t=function(e){let t,n;return E(e)?(t=e,n=()=>{console.warn("Write operation failed: computed value is readonly")}):(t=e.get,n=e.set),new at(t,n,E(e)||!e.set)}(e);return gr(t.effect),t}function kr(e,t){let n;if(O(e)||$(e)){n=new Array(e.length);for(let o=0,r=e.length;o{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n)=>t?Cr.createElementNS(Sr,e):Cr.createElement(e,n?{is:n}:void 0),createText:e=>Cr.createTextNode(e),createComment:e=>Cr.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Cr.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},cloneNode:e=>e.cloneNode(!0),insertStaticContent(e,t,n,o){const r=o?Er||(Er=Cr.createElementNS(Sr,"svg")):Rr||(Rr=Cr.createElement("div"));r.innerHTML=e;const i=r.firstChild;let s=i,a=s;for(;s;)a=s,$r.insert(s,t,n),s=r.firstChild;return[i,a]}};const Ar=/\s*!important$/;function Fr(e,t,n){if(O(n))n.forEach((n=>Fr(e,t,n)));else if(t.startsWith("--"))e.setProperty(t,n);else{const o=function(e,t){const n=Tr[t];if(n)return n;let o=B(t);if("filter"!==o&&o in e)return Tr[t]=o;o=H(o);for(let n=0;ndocument.createEvent("Event").timeStamp&&(Nr=()=>performance.now());let Ir=0;const Ur=Promise.resolve(),Mr=()=>{Ir=0};function Lr(e,t,n,o){e.addEventListener(t,n,o)}function Dr(e,t,n,o,r=null){const i=e._vei||(e._vei={}),s=i[t];if(o&&s)s.value=o;else{const[n,a]=function(e){let t;if(Br.test(e)){let n;for(t={};n=e.match(Br);)e=e.slice(0,e.length-n[0].length),t[n[0].toLowerCase()]=!0}return[e.slice(2).toLowerCase(),t]}(t);if(o){Lr(e,n,i[t]=function(e,t){const n=e=>{(e.timeStamp||Nr())>=n.attached-1&&vt(function(e,t){if(O(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map((e=>t=>!t._stopped&&e(t)))}return t}(e,n.value),t,5,[e])};return n.value=e,n.attached=(()=>Ir||(Ur.then(Mr),Ir=Nr()))(),n}(o,r),a)}else s&&(!function(e,t,n,o){e.removeEventListener(t,n,o)}(e,n,s,a),i[t]=void 0)}}const Br=/(?:Once|Passive|Capture)$/;const zr=/^on[a-z]/;const qr=e=>{const t=e.props["onUpdate:modelValue"];return O(t)?e=>K(t,e):t};function Hr(e){e.target.composing=!0}function Jr(e){const t=e.target;t.composing&&(t.composing=!1,function(e,t){const n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}(t,"input"))}const Wr={created(e,{modifiers:{lazy:t,trim:n,number:o}},r){e._assign=qr(r);const i=o||"number"===e.type;Lr(e,t?"change":"input",(t=>{if(t.target.composing)return;let o=e.value;n?o=o.trim():i&&(o=G(o)),e._assign(o)})),n&&Lr(e,"change",(()=>{e.value=e.value.trim()})),t||(Lr(e,"compositionstart",Hr),Lr(e,"compositionend",Jr),Lr(e,"change",Jr))},mounted(e,{value:t}){e.value=null==t?"":t},beforeUpdate(e,{value:t,modifiers:{trim:n,number:o}},r){if(e._assign=qr(r),e.composing)return;if(document.activeElement===e){if(n&&e.value.trim()===t)return;if((o||"number"===e.type)&&G(e.value)===t)return}const i=null==t?"":t;e.value!==i&&(e.value=i)}},Kr={created(e,t,n){e._assign=qr(n),Lr(e,"change",(()=>{const t=e._modelValue,n=Zr(e),o=e.checked,r=e._assign;if(O(t)){const e=d(t,n),i=-1!==e;if(o&&!i)r(t.concat(n));else if(!o&&i){const n=[...t];n.splice(e,1),r(n)}}else if(C(t)){const e=new Set(t);o?e.add(n):e.delete(n),r(e)}else r(Qr(e,o))}))},mounted:Xr,beforeUpdate(e,t,n){e._assign=qr(n),Xr(e,t,n)}};function Xr(e,{value:t,oldValue:n},o){e._modelValue=t,O(t)?e.checked=d(t,o.props.value)>-1:C(t)?e.checked=t.has(o.props.value):t!==n&&(e.checked=u(t,Qr(e,!0)))}const Gr={created(e,{value:t,modifiers:{number:n}},o){const r=C(t);Lr(e,"change",(()=>{const t=Array.prototype.filter.call(e.options,(e=>e.selected)).map((e=>n?G(Zr(e)):Zr(e)));e._assign(e.multiple?r?new Set(t):t:t[0])})),e._assign=qr(o)},mounted(e,{value:t}){Yr(e,t)},beforeUpdate(e,t,n){e._assign=qr(n)},updated(e,{value:t}){Yr(e,t)}};function Yr(e,t){const n=e.multiple;if(!n||O(t)||C(t)){for(let o=0,r=e.options.length;o-1:r.selected=t.has(i);else if(u(Zr(r),t))return void(e.selectedIndex=o)}n||(e.selectedIndex=-1)}else dt(`` + if (shapeFlag & 8 /* TEXT_CHILDREN */) { + hostSetElementText(el, vnode.children); + } + else if (shapeFlag & 16 /* ARRAY_CHILDREN */) { + mountChildren(vnode.children, el, null, parentComponent, parentSuspense, isSVG && type !== 'foreignObject', optimized || !!vnode.dynamicChildren); + } + if (dirs) { + invokeDirectiveHook(vnode, null, parentComponent, 'created'); + } + // props + if (props) { + for (const key in props) { + if (!isReservedProp(key)) { + hostPatchProp(el, key, null, props[key], isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren); + } + } + if ((vnodeHook = props.onVnodeBeforeMount)) { + invokeVNodeHook(vnodeHook, parentComponent, vnode); + } + } + // scopeId + setScopeId(el, scopeId, vnode, parentComponent); + } + if (dirs) { + invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount'); + } + // #1583 For inside suspense + suspense not resolved case, enter hook should call when suspense resolved + // #1689 For inside suspense + suspense resolved case, just call it + const needCallTransitionHooks = (!parentSuspense || (parentSuspense && !parentSuspense.pendingBranch)) && + transition && + !transition.persisted; + if (needCallTransitionHooks) { + transition.beforeEnter(el); + } + hostInsert(el, container, anchor); + if ((vnodeHook = props && props.onVnodeMounted) || + needCallTransitionHooks || + dirs) { + queuePostRenderEffect(() => { + vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode); + needCallTransitionHooks && transition.enter(el); + dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted'); + }, parentSuspense); + } + }; + const setScopeId = (el, scopeId, vnode, parentComponent) => { + if (scopeId) { + hostSetScopeId(el, scopeId); + } + if (parentComponent) { + const treeOwnerId = parentComponent.type.__scopeId; + // vnode's own scopeId and the current patched component's scopeId is + // different - this is a slot content node. + if (treeOwnerId && treeOwnerId !== scopeId) { + hostSetScopeId(el, treeOwnerId + '-s'); + } + let subTree = parentComponent.subTree; + if (vnode === subTree) { + setScopeId(el, parentComponent.vnode.scopeId, parentComponent.vnode, parentComponent.parent); + } + } + }; + const mountChildren = (children, container, anchor, parentComponent, parentSuspense, isSVG, optimized, start = 0) => { + for (let i = start; i < children.length; i++) { + const child = (children[i] = optimized + ? cloneIfMounted(children[i]) + : normalizeVNode(children[i])); + patch(null, child, container, anchor, parentComponent, parentSuspense, isSVG, optimized); + } + }; + const patchElement = (n1, n2, parentComponent, parentSuspense, isSVG, optimized) => { + const el = (n2.el = n1.el); + let { patchFlag, dynamicChildren, dirs } = n2; + // #1426 take the old vnode's patch flag into account since user may clone a + // compiler-generated vnode, which de-opts to FULL_PROPS + patchFlag |= n1.patchFlag & 16 /* FULL_PROPS */; + const oldProps = n1.props || EMPTY_OBJ; + const newProps = n2.props || EMPTY_OBJ; + let vnodeHook; + if ((vnodeHook = newProps.onVnodeBeforeUpdate)) { + invokeVNodeHook(vnodeHook, parentComponent, n2, n1); + } + if (dirs) { + invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate'); + } + if (patchFlag > 0) { + // the presence of a patchFlag means this element's render code was + // generated by the compiler and can take the fast path. + // in this path old node and new node are guaranteed to have the same shape + // (i.e. at the exact same position in the source template) + if (patchFlag & 16 /* FULL_PROPS */) { + // element props contain dynamic keys, full diff needed + patchProps(el, n2, oldProps, newProps, parentComponent, parentSuspense, isSVG); + } + else { + // class + // this flag is matched when the element has dynamic class bindings. + if (patchFlag & 2 /* CLASS */) { + if (oldProps.class !== newProps.class) { + hostPatchProp(el, 'class', null, newProps.class, isSVG); + } + } + // style + // this flag is matched when the element has dynamic style bindings + if (patchFlag & 4 /* STYLE */) { + hostPatchProp(el, 'style', oldProps.style, newProps.style, isSVG); + } + // props + // This flag is matched when the element has dynamic prop/attr bindings + // other than class and style. The keys of dynamic prop/attrs are saved for + // faster iteration. + // Note dynamic keys like :[foo]="bar" will cause this optimization to + // bail out and go through a full diff because we need to unset the old key + if (patchFlag & 8 /* PROPS */) { + // if the flag is present then dynamicProps must be non-null + const propsToUpdate = n2.dynamicProps; + for (let i = 0; i < propsToUpdate.length; i++) { + const key = propsToUpdate[i]; + const prev = oldProps[key]; + const next = newProps[key]; + if (next !== prev || + (hostForcePatchProp && hostForcePatchProp(el, key))) { + hostPatchProp(el, key, prev, next, isSVG, n1.children, parentComponent, parentSuspense, unmountChildren); + } + } + } + } + // text + // This flag is matched when the element has only dynamic text children. + if (patchFlag & 1 /* TEXT */) { + if (n1.children !== n2.children) { + hostSetElementText(el, n2.children); + } + } + } + else if (!optimized && dynamicChildren == null) { + // unoptimized, full diff + patchProps(el, n2, oldProps, newProps, parentComponent, parentSuspense, isSVG); + } + const areChildrenSVG = isSVG && n2.type !== 'foreignObject'; + if (dynamicChildren) { + patchBlockChildren(n1.dynamicChildren, dynamicChildren, el, parentComponent, parentSuspense, areChildrenSVG); + } + else if (!optimized) { + // full diff + patchChildren(n1, n2, el, null, parentComponent, parentSuspense, areChildrenSVG); + } + if ((vnodeHook = newProps.onVnodeUpdated) || dirs) { + queuePostRenderEffect(() => { + vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1); + dirs && invokeDirectiveHook(n2, n1, parentComponent, 'updated'); + }, parentSuspense); + } + }; + // The fast path for blocks. + const patchBlockChildren = (oldChildren, newChildren, fallbackContainer, parentComponent, parentSuspense, isSVG) => { + for (let i = 0; i < newChildren.length; i++) { + const oldVNode = oldChildren[i]; + const newVNode = newChildren[i]; + // Determine the container (parent element) for the patch. + const container = + // - In the case of a Fragment, we need to provide the actual parent + // of the Fragment itself so it can move its children. + oldVNode.type === Fragment || + // - In the case of different nodes, there is going to be a replacement + // which also requires the correct parent container + !isSameVNodeType(oldVNode, newVNode) || + // - In the case of a component, it could contain anything. + oldVNode.shapeFlag & 6 /* COMPONENT */ || + oldVNode.shapeFlag & 64 /* TELEPORT */ + ? hostParentNode(oldVNode.el) + : // In other cases, the parent container is not actually used so we + // just pass the block element here to avoid a DOM parentNode call. + fallbackContainer; + patch(oldVNode, newVNode, container, null, parentComponent, parentSuspense, isSVG, true); + } + }; + const patchProps = (el, vnode, oldProps, newProps, parentComponent, parentSuspense, isSVG) => { + if (oldProps !== newProps) { + for (const key in newProps) { + // empty string is not valid prop + if (isReservedProp(key)) + continue; + const next = newProps[key]; + const prev = oldProps[key]; + if (next !== prev || + (hostForcePatchProp && hostForcePatchProp(el, key))) { + hostPatchProp(el, key, prev, next, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren); + } + } + if (oldProps !== EMPTY_OBJ) { + for (const key in oldProps) { + if (!isReservedProp(key) && !(key in newProps)) { + hostPatchProp(el, key, oldProps[key], null, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren); + } + } + } + } + }; + const processFragment = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => { + const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateText('')); + const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateText('')); + let { patchFlag, dynamicChildren } = n2; + if (patchFlag > 0) { + optimized = true; + } + if (n1 == null) { + hostInsert(fragmentStartAnchor, container, anchor); + hostInsert(fragmentEndAnchor, container, anchor); + // a fragment can only have array children + // since they are either generated by the compiler, or implicitly created + // from arrays. + mountChildren(n2.children, container, fragmentEndAnchor, parentComponent, parentSuspense, isSVG, optimized); + } + else { + if (patchFlag > 0 && + patchFlag & 64 /* STABLE_FRAGMENT */ && + dynamicChildren && + // #2715 the previous fragment could've been a BAILed one as a result + // of renderSlot() with no valid children + n1.dynamicChildren) { + // a stable fragment (template root or