119 lines
4.0 KiB
Rust
119 lines
4.0 KiB
Rust
use chara::CharacterDefinition;
|
|
use maud::{Markup, html};
|
|
|
|
pub fn render_character(name: &str, def: &CharacterDefinition, relative_path: &str) -> Markup {
|
|
html! {
|
|
style { (CHARACTER_CSS) }
|
|
|
|
div.customizer-container {
|
|
div.character-container {
|
|
@for layer in &def.layers {
|
|
img.character-layer
|
|
id=(format!("{name}-{}-layer", layer.internal_name))
|
|
src=(layer.entries.first().map(|e| format!("/{relative_path}/{}", e.path)).unwrap_or_default())
|
|
style=(format!("display: {};",
|
|
if layer.entries.first().is_some_and(|e| !e.path.is_empty()) {
|
|
"block"
|
|
} else {
|
|
"none"
|
|
}
|
|
));
|
|
}
|
|
}
|
|
|
|
div.controls-column {
|
|
@for layer in &def.layers {
|
|
@if let Some(display_name) = &layer.display_name {
|
|
div.layer-group {
|
|
div.layer-title { (display_name) }
|
|
div.layer-options {
|
|
@for (i, entry) in layer.entries.iter().enumerate() {
|
|
label.option {
|
|
input type="radio"
|
|
name=(format!("{name}-{}", layer.internal_name))
|
|
value=(if entry.path.is_empty() {
|
|
String::new()
|
|
} else {
|
|
format!("/{relative_path}/{}", entry.path)
|
|
})
|
|
checked[i==0]
|
|
onchange=(format!(
|
|
"var img=document.getElementById('{name}-{}-layer');{}",
|
|
layer.internal_name,
|
|
if entry.path.is_empty() {
|
|
"img.style.display='none';"
|
|
} else {
|
|
"img.src=this.value;img.style.display='block';"
|
|
}
|
|
));
|
|
(entry.name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const CHARACTER_CSS: &str = r"
|
|
.customizer-container { display: flex; gap: 20px; margin-top: 20px; }
|
|
.character-container {
|
|
position: relative;
|
|
width: 300px;
|
|
height: 400px;
|
|
border: 2px solid #ddd;
|
|
border-radius: 10px;
|
|
background-color: white;
|
|
flex-shrink: 0;
|
|
}
|
|
.character-layer {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: contain;
|
|
}
|
|
.controls-column {
|
|
flex-grow: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
}
|
|
.layer-group {
|
|
background-color: white;
|
|
padding: 15px;
|
|
border-radius: 5px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.layer-title {
|
|
font-weight: bold;
|
|
margin-bottom: 10px;
|
|
color: #333;
|
|
}
|
|
.layer-options {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
gap: 8px;
|
|
}
|
|
.option {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.option input {
|
|
margin-right: 8px;
|
|
}
|
|
.back-link {
|
|
display: inline-block;
|
|
margin-bottom: 15px;
|
|
color: #333;
|
|
text-decoration: none;
|
|
}
|
|
.back-link:hover {
|
|
text-decoration: underline;
|
|
}
|
|
";
|