gradient
This commit is contained in:
@@ -1,26 +1,37 @@
|
|||||||
use super::icons::Icon;
|
use super::icons::Icon;
|
||||||
|
use crate::components::outlined_box::OutlinedBox;
|
||||||
|
use crate::gradient::Gradient;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use web_sys::MouseEvent;
|
use web_sys::MouseEvent;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Button(
|
pub fn Button(
|
||||||
#[prop(optional)]
|
#[prop(into, optional)] icon: MaybeProp<Icon>,
|
||||||
#[prop(into)]
|
|
||||||
icon: MaybeProp<Icon>,
|
|
||||||
|
|
||||||
#[prop(into)] text: Signal<String>,
|
#[prop(into)] text: Signal<String>,
|
||||||
|
|
||||||
|
#[prop(optional)] color: Gradient,
|
||||||
|
|
||||||
onclick: impl FnMut(MouseEvent) + 'static,
|
onclick: impl FnMut(MouseEvent) + 'static,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let icon_view = icon.get().map(|i| {
|
let icon_view = icon.get().map(|i| {
|
||||||
let icon_view = i.into_view();
|
let icon_view = i.into_view();
|
||||||
view! { <div class="self-center mx-5 my-2.5 text-pink-500 rounded-sm">{icon_view}</div> }
|
let mut classes = "self-center mx-5 my-2.5 text-pink-500 rounded-sm".to_string();
|
||||||
|
classes.push(' ');
|
||||||
|
classes.push_str(color.class_text());
|
||||||
|
|
||||||
|
view! { <div class=classes>{icon_view}</div> }
|
||||||
});
|
});
|
||||||
|
|
||||||
let separator = icon
|
let separator = icon.get().is_some().then(|| {
|
||||||
.get()
|
let mut classes = "w-0.5 bg-gradient-to-br min-w-0.5".to_string();
|
||||||
.is_some()
|
classes.push(' ');
|
||||||
.then(|| view! { <div class="w-0.5 bg-gradient-to-br from-pink-500 to-orange-400" /> });
|
classes.push_str(color.class_from());
|
||||||
|
classes.push(' ');
|
||||||
|
classes.push_str(color.class_to());
|
||||||
|
|
||||||
|
view! { <div class=classes /> }
|
||||||
|
});
|
||||||
|
|
||||||
let text_view = view! { <div class="self-center mx-5 my-2.5 uppercase w-full text-lg font-thin">{text.get()}</div> };
|
let text_view = view! { <div class="self-center mx-5 my-2.5 uppercase w-full text-lg font-thin">{text.get()}</div> };
|
||||||
|
|
||||||
@@ -28,12 +39,11 @@ pub fn Button(
|
|||||||
<button
|
<button
|
||||||
on:click=onclick
|
on:click=onclick
|
||||||
type="button"
|
type="button"
|
||||||
class="p-0.5 mb-2 bg-gradient-to-br from-pink-500 to-orange-400 rounded-lg me-2 hover:brightness-125 active:brightness-90"
|
class="mb-2 me-2 hover:brightness-125 active:brightness-90"
|
||||||
>
|
>
|
||||||
<div class="flex items-stretch py-1.5 bg-gray-900 rounded-md">
|
<OutlinedBox color>
|
||||||
{icon_view} {separator} {text_view}
|
<div class="flex items-stretch">{icon_view} {separator} {text_view}</div>
|
||||||
|
</OutlinedBox>
|
||||||
</div>
|
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
crates/ascend/src/components/checkbox.rs
Normal file
50
crates/ascend/src/components/checkbox.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use crate::components::icons;
|
||||||
|
use crate::components::outlined_box::OutlinedBox;
|
||||||
|
use crate::gradient::Gradient;
|
||||||
|
use leptos::prelude::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn Checkbox(checked: RwSignal<bool>, #[prop(into)] text: Signal<String>, #[prop(optional)] color: Gradient) -> impl IntoView {
|
||||||
|
let unique_id = Oco::from(format!("checkbox-{}", uuid::Uuid::new_v4()));
|
||||||
|
|
||||||
|
let checkbox_view = view! {
|
||||||
|
<div class="self-center text-white bg-white rounded-sm aspect-square mx-5 my-2.5">
|
||||||
|
<span class=("text-gray-950", move || checked.get())>
|
||||||
|
<icons::Check />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
};
|
||||||
|
|
||||||
|
let separator = {
|
||||||
|
let mut classes = "w-0.5 bg-gradient-to-br min-w-0.5".to_string();
|
||||||
|
classes.push(' ');
|
||||||
|
classes.push_str(color.class_from());
|
||||||
|
classes.push(' ');
|
||||||
|
classes.push_str(color.class_to());
|
||||||
|
view! { <div class=classes /> }
|
||||||
|
};
|
||||||
|
|
||||||
|
let text_view = view! {
|
||||||
|
<div class="self-center mx-5 my-2.5 uppercase w-full text-lg font-thin">
|
||||||
|
{move || text.get()}
|
||||||
|
</div>
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="inline-block mb-2 me-2 hover:brightness-125 active:brightness-90">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id=unique_id.clone()
|
||||||
|
value=""
|
||||||
|
class="hidden peer"
|
||||||
|
required=""
|
||||||
|
bind:checked=checked
|
||||||
|
/>
|
||||||
|
<label for=unique_id class="cursor-pointer">
|
||||||
|
<OutlinedBox color>
|
||||||
|
<div class="flex">{checkbox_view} {separator} {text_view}</div>
|
||||||
|
</OutlinedBox>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -104,6 +104,8 @@ pub fn Check() -> impl IntoView {
|
|||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z"
|
d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
/>
|
/>
|
||||||
|
|||||||
17
crates/ascend/src/components/outlined_box.rs
Normal file
17
crates/ascend/src/components/outlined_box.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use crate::gradient::Gradient;
|
||||||
|
use leptos::prelude::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn OutlinedBox(children: Children, color: Gradient) -> impl IntoView {
|
||||||
|
let mut classes = "p-0.5 bg-gradient-to-br rounded-lg".to_string();
|
||||||
|
classes.push(' ');
|
||||||
|
classes.push_str(color.class_from());
|
||||||
|
classes.push(' ');
|
||||||
|
classes.push_str(color.class_to());
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class=classes>
|
||||||
|
<div class="py-1.5 bg-gray-900 rounded-md">{children()}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
32
crates/ascend/src/gradient.rs
Normal file
32
crates/ascend/src/gradient.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
|
pub enum Gradient {
|
||||||
|
#[default]
|
||||||
|
PinkOrange,
|
||||||
|
CyanBlue,
|
||||||
|
TealLime,
|
||||||
|
}
|
||||||
|
impl Gradient {
|
||||||
|
pub fn class_from(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Gradient::PinkOrange => "from-pink-500",
|
||||||
|
Gradient::CyanBlue => "from-cyan-500",
|
||||||
|
Gradient::TealLime => "from-teal-300",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn class_to(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Gradient::PinkOrange => "to-orange-400",
|
||||||
|
Gradient::CyanBlue => "to-blue-500",
|
||||||
|
Gradient::TealLime => "to-lime-300",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn class_text(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Gradient::PinkOrange => "text-pink-500",
|
||||||
|
Gradient::CyanBlue => "text-cyan-500",
|
||||||
|
Gradient::TealLime => "text-teal-300",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,12 +14,16 @@ pub mod components {
|
|||||||
|
|
||||||
pub mod attempt;
|
pub mod attempt;
|
||||||
pub mod button;
|
pub mod button;
|
||||||
|
pub mod checkbox;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod icons;
|
pub mod icons;
|
||||||
|
pub mod outlined_box;
|
||||||
pub mod problem;
|
pub mod problem;
|
||||||
pub mod problem_info;
|
pub mod problem_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod gradient;
|
||||||
|
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
|
|
||||||
pub mod codec;
|
pub mod codec;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ pub use v3::UserInteraction;
|
|||||||
|
|
||||||
pub mod v3 {
|
pub mod v3 {
|
||||||
use super::v2;
|
use super::v2;
|
||||||
use chrono::NaiveDate;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@@ -31,7 +30,7 @@ pub mod v3 {
|
|||||||
pub problem_uid: v2::ProblemUid,
|
pub problem_uid: v2::ProblemUid,
|
||||||
|
|
||||||
/// Dates on which this problem was attempted, and how it went
|
/// Dates on which this problem was attempted, and how it went
|
||||||
pub attempted_on: BTreeMap<NaiveDate, Attempt>,
|
pub attempted_on: BTreeMap<chrono::DateTime<chrono::Utc>, Attempt>,
|
||||||
|
|
||||||
/// Is among favorite problems
|
/// Is among favorite problems
|
||||||
pub is_favorite: bool,
|
pub is_favorite: bool,
|
||||||
@@ -50,7 +49,7 @@ pub mod v3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn best_attempt(&self) -> Option<(NaiveDate, Attempt)> {
|
pub fn best_attempt(&self) -> Option<(chrono::DateTime<chrono::Utc>, Attempt)> {
|
||||||
self.attempted_on
|
self.attempted_on
|
||||||
.iter()
|
.iter()
|
||||||
.max_by_key(|(_date, attempt)| *attempt)
|
.max_by_key(|(_date, attempt)| *attempt)
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
// | |
|
// | |
|
||||||
// | |
|
// | |
|
||||||
// +--------------------------------------+
|
// +--------------------------------------+
|
||||||
//
|
|
||||||
// +---------------------------+
|
// +---------------------------+
|
||||||
// | Next Problem |
|
// | Next Problem |
|
||||||
// +---------------------------+
|
// +---------------------------+
|
||||||
//
|
|
||||||
// +--------------- Problem --------------+
|
// +--------------- Problem --------------+
|
||||||
// | Name: ... |
|
// | Name: ... |
|
||||||
// | Method: ... |
|
// | Method: ... |
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
// + ------- + + ------ -+ +---------+
|
// + ------- + + ------ -+ +---------+
|
||||||
// | Flash | | Top | | Attempt |
|
// | Flash | | Top | | Attempt |
|
||||||
// + ------- + + ------ -+ +---------+
|
// + ------- + + ------ -+ +---------+
|
||||||
//
|
|
||||||
// +---------- <Latest attempt> ----------+
|
// +---------- <Latest attempt> ----------+
|
||||||
// | Today: <Attempt> |
|
// | Today: <Attempt> |
|
||||||
// | 14 days ago: <Attempt> |
|
// | 14 days ago: <Attempt> |
|
||||||
@@ -33,11 +33,12 @@
|
|||||||
use crate::components::ProblemInfo;
|
use crate::components::ProblemInfo;
|
||||||
use crate::components::attempt::Attempt;
|
use crate::components::attempt::Attempt;
|
||||||
use crate::components::button::Button;
|
use crate::components::button::Button;
|
||||||
|
use crate::components::checkbox::Checkbox;
|
||||||
use crate::components::header::HeaderItem;
|
use crate::components::header::HeaderItem;
|
||||||
use crate::components::header::HeaderItems;
|
use crate::components::header::HeaderItems;
|
||||||
use crate::components::header::StyledHeader;
|
use crate::components::header::StyledHeader;
|
||||||
use crate::components::icons;
|
|
||||||
use crate::components::icons::Icon;
|
use crate::components::icons::Icon;
|
||||||
|
use crate::gradient::Gradient;
|
||||||
use crate::models;
|
use crate::models;
|
||||||
use crate::models::HoldRole;
|
use crate::models::HoldRole;
|
||||||
use leptos::Params;
|
use leptos::Params;
|
||||||
@@ -161,6 +162,20 @@ pub fn Wall() -> impl IntoView {
|
|||||||
<div>{grid}</div>
|
<div>{grid}</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
<div class="flex">
|
||||||
|
<Checkbox
|
||||||
|
checked=ui_is_flash
|
||||||
|
text="Flash"
|
||||||
|
color=Gradient::CyanBlue
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
checked=ui_is_climbed
|
||||||
|
text="Top"
|
||||||
|
color=Gradient::TealLime
|
||||||
|
/>
|
||||||
|
<Checkbox checked=ui_is_favorite text="Attempt" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
icon=Icon::ArrowPath
|
icon=Icon::ArrowPath
|
||||||
text="Next problem"
|
text="Next problem"
|
||||||
@@ -182,84 +197,6 @@ pub fn Wall() -> impl IntoView {
|
|||||||
|
|
||||||
<div class="m-4" />
|
<div class="m-4" />
|
||||||
|
|
||||||
<div class="inline-flex overflow-hidden relative justify-center items-center p-0.5 mb-2 text-sm font-medium text-white bg-gradient-to-br from-cyan-500 to-blue-500 rounded-lg me-2 group hover:brightness-125">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="flash-option"
|
|
||||||
value=""
|
|
||||||
class="hidden peer"
|
|
||||||
required=""
|
|
||||||
bind:checked=ui_is_flash
|
|
||||||
/>
|
|
||||||
<label for="flash-option" class="cursor-pointer">
|
|
||||||
<div
|
|
||||||
class="flex relative gap-2 items-center py-3.5 px-5 bg-gray-900 rounded-md transition-all duration-75 ease-in"
|
|
||||||
class:bg-transparent=move || ui_is_flash.get()
|
|
||||||
>
|
|
||||||
<div class="text-white bg-white rounded-sm border-gray-500 ring-offset-gray-700 aspect-square">
|
|
||||||
<span class=("text-cyan-500", move || ui_is_flash.get())>
|
|
||||||
<icons::Check />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
// <icons::Bolt />
|
|
||||||
<div class="w-full text-lg font-semibold">Flash</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inline-flex overflow-hidden relative justify-center items-center p-0.5 mb-2 text-sm font-medium text-white bg-gradient-to-br from-teal-300 to-lime-300 rounded-lg me-2 group hover:brightness-125">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="climbed-option"
|
|
||||||
value=""
|
|
||||||
class="hidden peer"
|
|
||||||
required=""
|
|
||||||
bind:checked=ui_is_climbed
|
|
||||||
/>
|
|
||||||
<label for="climbed-option" class="cursor-pointer">
|
|
||||||
<div
|
|
||||||
class="flex relative gap-2 items-center py-3.5 px-5 text-white bg-gray-900 rounded-md transition-all duration-75 ease-in"
|
|
||||||
class:bg-transparent=move || ui_is_climbed.get()
|
|
||||||
>
|
|
||||||
<div class="bg-white rounded-sm border-gray-500 ring-offset-gray-700 aspect-square">
|
|
||||||
<span class=("text-black", move || ui_is_climbed.get())>
|
|
||||||
<icons::Check />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="w-full text-lg font-semibold"
|
|
||||||
class=("text-black", move || ui_is_climbed.get())
|
|
||||||
>
|
|
||||||
Climbed
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inline-flex overflow-hidden relative justify-center items-center p-0.5 mb-2 text-sm font-medium text-white bg-gradient-to-br from-pink-500 to-orange-400 rounded-lg me-2 group hover:brightness-125">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="favorite-option"
|
|
||||||
value=""
|
|
||||||
class="hidden peer"
|
|
||||||
required=""
|
|
||||||
bind:checked=ui_is_favorite
|
|
||||||
/>
|
|
||||||
<label for="favorite-option" class="cursor-pointer">
|
|
||||||
<div
|
|
||||||
class="flex relative gap-2 items-center py-3.5 px-5 bg-gray-900 rounded-md transition-all duration-75 ease-in"
|
|
||||||
class:bg-transparent=move || ui_is_favorite.get()
|
|
||||||
>
|
|
||||||
<div class="text-pink-500 rounded-sm border-gray-500 ring-offset-gray-700 aspect-square">
|
|
||||||
<span class=("text-white", move || ui_is_favorite.get())>
|
|
||||||
<icons::HeartOutline />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="w-full text-lg font-semibold">Favorite</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Suspense fallback=move || {
|
<Suspense fallback=move || {
|
||||||
view! {}
|
view! {}
|
||||||
}>
|
}>
|
||||||
|
|||||||
@@ -132,6 +132,7 @@
|
|||||||
rust.toolchain.targets = [ "wasm32-unknown-unknown" ];
|
rust.toolchain.targets = [ "wasm32-unknown-unknown" ];
|
||||||
|
|
||||||
packages = [
|
packages = [
|
||||||
|
pkgs.bacon
|
||||||
pkgs.cargo-leptos
|
pkgs.cargo-leptos
|
||||||
pkgs.leptosfmt
|
pkgs.leptosfmt
|
||||||
pkgs.dart-sass
|
pkgs.dart-sass
|
||||||
|
|||||||
1
todo.md
1
todo.md
@@ -11,3 +11,4 @@
|
|||||||
- hotkeys (enter =next problem, arrow = shift left/right up/down)
|
- hotkeys (enter =next problem, arrow = shift left/right up/down)
|
||||||
- impl `sizes` hint next to `srcset`
|
- impl `sizes` hint next to `srcset`
|
||||||
- add refresh wall button for when a hold is changed
|
- add refresh wall button for when a hold is changed
|
||||||
|
- fix a font, or why does font-thin not do anything?
|
||||||
|
|||||||
Reference in New Issue
Block a user