Files
lldap/app/src/components/form/attribute_input.rs
Austin Alvarado dcba3d17dc app: Add support for user-created attributes
Note: This PR doesn't handle errors around Jpeg files very well.

Co-authored-by: Bojidar Marinov <bojidar.marinov.bg@gmail.com>
Co-authored-by: Austin Alvarado <pixelrazor@gmail.com>
2024-09-30 23:53:14 +02:00

191 lines
5.6 KiB
Rust

use crate::{
components::form::{date_input::DateTimeInput, file_input::JpegFileInput},
infra::{schema::AttributeType, tooltip::Tooltip},
};
use web_sys::Element;
use yew::{
function_component, html, use_effect_with_deps, use_node_ref, virtual_dom::AttrValue,
Component, Context, Html, Properties,
};
#[derive(Properties, PartialEq)]
struct AttributeInputProps {
name: AttrValue,
attribute_type: AttributeType,
#[prop_or(None)]
value: Option<String>,
}
#[function_component(AttributeInput)]
fn attribute_input(props: &AttributeInputProps) -> Html {
let input_type = match props.attribute_type {
AttributeType::String => "text",
AttributeType::Integer => "number",
AttributeType::DateTime => {
return html! {
<DateTimeInput name={props.name.clone()} value={props.value.clone()} />
}
}
AttributeType::Jpeg => {
return html! {
<JpegFileInput name={props.name.clone()} value={props.value.clone()} />
}
}
};
html! {
<input
type={input_type}
name={props.name.clone()}
class="form-control"
value={props.value.clone()} />
}
}
#[derive(Properties, PartialEq)]
struct AttributeLabelProps {
pub name: String,
}
#[function_component(AttributeLabel)]
fn attribute_label(props: &AttributeLabelProps) -> Html {
let tooltip_ref = use_node_ref();
use_effect_with_deps(
move |tooltip_ref| {
Tooltip::new(
tooltip_ref
.cast::<Element>()
.expect("Tooltip element should exist"),
);
|| {}
},
tooltip_ref.clone(),
);
html! {
<label for={props.name.clone()}
class="form-label col-4 col-form-label"
>
{props.name[0..1].to_uppercase() + &props.name[1..].replace('_', " ")}{":"}
<button
class="btn btn-sm btn-link"
type="button"
data-bs-placement="right"
title={props.name.clone()}
ref={tooltip_ref}>
<i class="bi bi-info-circle" aria-label="Info" />
</button>
</label>
}
}
#[derive(Properties, PartialEq)]
pub struct SingleAttributeInputProps {
pub name: String,
pub attribute_type: AttributeType,
#[prop_or(None)]
pub value: Option<String>,
}
#[function_component(SingleAttributeInput)]
pub fn single_attribute_input(props: &SingleAttributeInputProps) -> Html {
html! {
<div class="row mb-3">
<AttributeLabel name={props.name.clone()} />
<div class="col-8">
<AttributeInput
attribute_type={props.attribute_type.clone()}
name={props.name.clone()}
value={props.value.clone()} />
</div>
</div>
}
}
#[derive(Properties, PartialEq)]
pub struct ListAttributeInputProps {
pub name: String,
pub attribute_type: AttributeType,
#[prop_or(vec!())]
pub values: Vec<String>,
}
pub enum ListAttributeInputMsg {
Remove(usize),
Append,
}
pub struct ListAttributeInput {
indices: Vec<usize>,
next_index: usize,
values: Vec<String>,
}
impl Component for ListAttributeInput {
type Message = ListAttributeInputMsg;
type Properties = ListAttributeInputProps;
fn create(ctx: &Context<Self>) -> Self {
let values = ctx.props().values.clone();
Self {
indices: (0..values.len()).collect(),
next_index: values.len(),
values,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
ListAttributeInputMsg::Remove(removed) => {
self.indices.retain_mut(|x| *x != removed);
}
ListAttributeInputMsg::Append => {
self.indices.push(self.next_index);
self.next_index += 1;
}
};
true
}
fn changed(&mut self, ctx: &Context<Self>) -> bool {
if ctx.props().values != self.values {
self.values.clone_from(&ctx.props().values);
self.indices = (0..self.values.len()).collect();
self.next_index = self.values.len();
}
true
}
fn view(&self, ctx: &Context<Self>) -> Html {
let props = &ctx.props();
let link = &ctx.link();
html! {
<div class="row mb-3">
<AttributeLabel name={props.name.clone()} />
<div class="col-8">
{self.indices.iter().map(|&i| html! {
<div class="input-group mb-2" key={i}>
<AttributeInput
attribute_type={props.attribute_type.clone()}
name={props.name.clone()}
value={props.values.get(i).cloned().unwrap_or_default()} />
<button
class="btn btn-danger"
type="button"
onclick={link.callback(move |_| ListAttributeInputMsg::Remove(i))}>
<i class="bi-x-circle-fill" aria-label="Remove value" />
</button>
</div>
}).collect::<Html>()}
<button
class="btn btn-secondary"
type="button"
onclick={link.callback(|_| ListAttributeInputMsg::Append)}>
<i class="bi-plus-circle me-2"></i>
{"Add value"}
</button>
</div>
</div>
}
}
}