transformation buttons

This commit is contained in:
Asger Juul Brunshøj 2025-03-31 13:20:47 +02:00
parent e403be8090
commit 221e15d7ac
4 changed files with 129 additions and 58 deletions

View File

@ -7,11 +7,15 @@ use leptos::prelude::*;
pub fn Button( pub fn Button(
#[prop(into, optional)] icon: MaybeProp<Icon>, #[prop(into, optional)] icon: MaybeProp<Icon>,
#[prop(into)] text: Signal<String>, #[prop(into, optional)] text: MaybeProp<String>,
#[prop(optional)] color: Gradient, #[prop(optional)] color: Gradient,
#[prop(into, optional)] highlight: MaybeProp<bool>, #[prop(into, optional)] highlight: MaybeProp<bool>,
#[prop(into, optional)] disabled: MaybeProp<bool>,
#[prop(into, optional)] on_click: MaybeProp<Callback<()>>,
) -> impl IntoView { ) -> impl IntoView {
let margin = "mx-2 my-1 sm:mx-5 sm:my-2.5"; let margin = "mx-2 my-1 sm:mx-5 sm:my-2.5";
@ -26,7 +30,7 @@ pub fn Button(
view! { <div class=classes>{icon_view}</div> } view! { <div class=classes>{icon_view}</div> }
}); });
let separator = icon.get().is_some().then(|| { let separator = (icon.read().is_some() && text.read().is_some()).then(|| {
let mut classes = "w-0.5 bg-linear-to-br min-w-0.5".to_string(); let mut classes = "w-0.5 bg-linear-to-br min-w-0.5".to_string();
classes.push(' '); classes.push(' ');
classes.push_str(color.class_from()); classes.push_str(color.class_from());
@ -36,16 +40,34 @@ pub fn Button(
view! { <div class=classes /> } view! { <div class=classes /> }
}); });
let text_view = { let text_view = text.get().map(|text| {
let mut classes = "self-center uppercase w-full text-sm sm:text-base md:text-lg font-thin".to_string(); let mut classes = "self-center uppercase w-full text-sm sm:text-base md:text-lg font-thin".to_string();
classes.push(' '); classes.push(' ');
classes.push_str(margin); classes.push_str(margin);
view! { <div class=classes>{text.get()}</div> } view! { <div class=classes>{text}</div> }
});
let class = move || {
let mut classes = vec![];
if disabled.get().unwrap_or_default() {
classes.extend(["brightness-50"]);
} else {
classes.extend(["cursor-pointer", "hover:brightness-125", "active:brightness-90"]);
}
classes.join(" ")
}; };
let on_click = move |_| {
if let Some(cb) = on_click.get() {
cb.run(());
}
};
let prop_disabled = move || disabled.get();
view! { view! {
<button type="button" class="cursor-pointer hover:brightness-125 active:brightness-90"> <button type="button" class=class prop:disabled=prop_disabled on:click=on_click>
<OutlinedBox color highlight> <OutlinedBox color highlight>
<div class="flex items-stretch">{icon_view} {separator} {text_view}</div> <div class="flex items-stretch">{icon_view} {separator} {text_view}</div>
</OutlinedBox> </OutlinedBox>

View File

@ -13,6 +13,9 @@ pub enum Icon {
NoSymbol, NoSymbol,
Trophy, Trophy,
ArrowTrendingUp, ArrowTrendingUp,
ChevronLeft,
ChevronRight,
CodeBracketSquare,
} }
impl Icon { impl Icon {
// TODO: Actually impl IntoView for Icon instead // TODO: Actually impl IntoView for Icon instead
@ -29,6 +32,9 @@ impl Icon {
Icon::NoSymbol => view! { <NoSymbol /> }.into_any(), Icon::NoSymbol => view! { <NoSymbol /> }.into_any(),
Icon::Trophy => view! { <Trophy /> }.into_any(), Icon::Trophy => view! { <Trophy /> }.into_any(),
Icon::ArrowTrendingUp => view! { <ArrowTrendingUp /> }.into_any(), Icon::ArrowTrendingUp => view! { <ArrowTrendingUp /> }.into_any(),
Icon::ChevronLeft => view! { <ChevronLeft /> }.into_any(),
Icon::ChevronRight => view! { <ChevronRight /> }.into_any(),
Icon::CodeBracketSquare => view! { <CodeBracketSquare /> }.into_any(),
} }
} }
} }
@ -226,3 +232,55 @@ pub fn ArrowTrendingUp() -> impl IntoView {
</svg> </svg>
} }
} }
#[component]
pub fn ChevronLeft() -> impl IntoView {
view! {
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
</svg>
}
}
#[component]
pub fn ChevronRight() -> impl IntoView {
view! {
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
}
}
#[component]
pub fn CodeBracketSquare() -> impl IntoView {
view! {
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M14.25 9.75 16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0 0 20.25 18V6A2.25 2.25 0 0 0 18 3.75H6A2.25 2.25 0 0 0 3.75 6v12A2.25 2.25 0 0 0 6 20.25Z"
/>
</svg>
}
}

View File

@ -177,37 +177,59 @@ fn View() -> impl IntoView {
<Wall /> <Wall />
</div> </div>
<div class="flex flex-col" style="width:38rem"> <div class="flex flex-col px-2 pt-3" style="width:38rem">
<Section title="Filter"> <Section title="Problems">
<Filter /> <Filter />
<Separator />
<div class="flex flex-row justify-around">
<Transformations />
<NextProblemButton />
</div>
</Section> </Section>
<Separator /> <Separator />
<NextProblemButton /> <Section title="Current problem">
<Separator />
<Section title="Problem">
{move || ctx.problem.get().map(|problem| view! { <ProblemInfo problem /> })} {move || ctx.problem.get().map(|problem| view! { <ProblemInfo problem /> })}
<Separator /> <AttemptRadioGroup /> <Separator /> <History />
</Section> </Section>
<Separator />
<AttemptRadioGroup />
<Separator />
<History />
</div> </div>
<div class="flex-auto flex flex-row justify-end items-start px-5 pt-3"> <div class="flex-auto flex flex-row justify-end items-start px-2 pt-3">
<HoldsButton /> <HoldsButton />
</div> </div>
</div> </div>
} }
} }
#[component]
#[tracing::instrument(skip_all)]
fn Transformations() -> impl IntoView {
crate::tracing::on_enter!();
let ctx = use_context::<Context>().unwrap();
let on_left = Callback::new(move |()| {
tracing::info!("left");
});
let on_mirror = Callback::new(move |()| {
tracing::info!("mirror");
});
let on_right = Callback::new(move |()| {
tracing::info!("right");
});
view! {
<div class="flex flex-row justify-center gap-2">
<Button icon=Icon::ChevronLeft disabled=true on_click=on_left />
<Button icon=Icon::CodeBracketSquare on_click=on_mirror />
<Button icon=Icon::ChevronRight on_click=on_right />
</div>
}
}
#[component] #[component]
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn HoldsButton() -> impl IntoView { fn HoldsButton() -> impl IntoView {
@ -231,19 +253,8 @@ fn NextProblemButton() -> impl IntoView {
let ctx = use_context::<Context>().unwrap(); let ctx = use_context::<Context>().unwrap();
let on_click = move |_| ctx.cb_next_problem.run(()); let on_click = Callback::new(move |_| ctx.cb_next_problem.run(()));
view! { view! { <Button icon=Icon::ArrowPath text="Next problem" on_click color=Gradient::PurpleBlue /> }
<div class="flex flex-col">
<div class="self-center">
<Button
icon=Icon::ArrowPath
text="Next problem"
on:click=on_click
color=Gradient::PurpleBlue
/>
</div>
</div>
}
} }
#[component] #[component]
@ -425,7 +436,10 @@ fn History() -> impl IntoView {
}) })
}; };
view! { <Section title="History">{placeholder} {attempts}</Section> } view! {
{placeholder}
{attempts}
}
} }
#[component] #[component]

View File

@ -10,29 +10,6 @@ pub async fn run_migrations(db: &Database) -> Result<(), Box<dyn std::error::Err
migrate_to_v3(db).await?; migrate_to_v3(db).await?;
} }
migrate_wall(db).await?;
Ok(())
}
#[tracing::instrument(skip_all, err)]
pub async fn migrate_wall(db: &Database) -> Result<(), Box<dyn std::error::Error>> {
tracing::warn!("MIGRATING WALL");
db.write(|txn| {
let mut table = txn.open_table(db::current::TABLE_WALLS)?;
let wall = table
.get(models::WallUid(uuid::Uuid::parse_str("8a00ab39-89f5-4fc5-b9c6-f86b4c040f68").unwrap()))?
.map(|x| x.value());
if let Some(mut wall) = wall {
wall.rows = 11;
wall.holds.retain(|k, _| k.row < 11);
table.insert(wall.uid, wall)?;
}
Ok(())
})
.await?;
Ok(()) Ok(())
} }