Skip to main content

edc_web_components/components/
dataset_card.rs

1use crate::components::LandingPage;
2use crate::models::DataspaceDataset;
3use patternfly_yew::prelude::*;
4use stylist::yew::styled_component;
5use yew::prelude::*;
6
7#[derive(Clone, PartialEq, Properties)]
8pub struct DatasetCardProps {
9  pub dataset: DataspaceDataset,
10  #[prop_or_default]
11  pub on_offer_click: Option<Callback<()>>,
12  #[prop_or_default]
13  pub selected: bool,
14  #[prop_or_default]
15  pub on_selected: Option<Callback<bool>>,
16  #[prop_or(AttrValue::Static("Select"))]
17  pub button_label: AttrValue,
18}
19
20#[styled_component]
21pub fn DatasetCard(props: &DatasetCardProps) -> Html {
22  let card_class = css!(
23    width: 250px;
24  );
25
26  let thumbnails_class = css!(
27    height: 180px;
28    object-fit: cover;
29  );
30
31  let provider_logo_class = css!(
32    width: 50px;
33    height: 50px;
34    object-fit: contain;
35  );
36
37  let version_class = css!(
38    position: absolute;
39    top: 5px;
40    right: 5px;
41  );
42
43  let keyword_class = css!(
44    .pf-v6-l-split {
45      overflow: hidden;
46    }
47  );
48
49  let description_class = css!(
50    display: block;
51    display: -webkit-box;
52    -webkit-line-clamp: 5;
53    -webkit-box-orient: vertical;
54    text-overflow: ellipsis;
55    overflow: hidden;
56  );
57
58  let landing_page = if let Some(landing_page) = props.dataset.landing_page.clone() {
59    html!(<LandingPage {landing_page} />)
60  } else {
61    html!()
62  };
63
64  let offer_button = if let Some(offer) = props.on_offer_click.clone() {
65    html!(
66      <Button
67        variant={ButtonVariant::Primary}
68        icon={Icon::AngleRight}
69        onclick={offer.reform(|_| ())}
70      >
71        { props.button_label.clone() }
72      </Button>
73    )
74  } else {
75    html!()
76  };
77
78  let title = props.dataset.title.to_string();
79
80  let version = props
81    .dataset
82    .version
83    .clone()
84    .map(|version| version.to_string())
85    .unwrap_or("0.0.0".to_string());
86
87  let comment = props.dataset.comment.clone().unwrap_or_default();
88
89  let thumbnail = props
90    .dataset
91    .thumbnail
92    .clone()
93    .and_then(|thumbnail| thumbnail.resource)
94    .map(|thumbnail| html! { <img src={thumbnail} class={thumbnails_class.clone()} /> })
95    .unwrap_or(html!(<Skeleton height="180px" />));
96
97  let provider_logo = props
98    .dataset
99    .creator
100    .clone()
101    .and_then(|creator| creator.thumbnail)
102    .and_then(|thumbnail| thumbnail.resource)
103    .map(|thumbnail| html! { <img src={thumbnail} class={provider_logo_class.clone()} /> });
104
105  let keywords = props.dataset.keywords.iter().map(|keyword| {
106    html_nested! {
107      <SplitItem>
108        <Label label={keyword.clone()} />
109      </SplitItem>
110    }
111  });
112
113  let selectable_actions = if let Some(on_selected) = props.on_selected.clone() {
114    let checked = if props.selected {
115      CheckboxState::Checked
116    } else {
117      CheckboxState::Unchecked
118    };
119
120    Some(yew::props!(CardSelectableActionsObjectProperties {
121      action: CardSelectableActionsVariant::MultiSelect {
122        checked,
123        onchange: on_selected.reform(move |checked: CheckboxState| {
124          match checked {
125            CheckboxState::Checked => true,
126            CheckboxState::Unchecked | CheckboxState::Indeterminate => false,
127          }
128        }),
129      }
130    }))
131  } else {
132    None
133  };
134
135  html!(
136    <Card full_height=true class={card_class.clone()} selectable={props.on_selected.is_some()}>
137      { thumbnail }
138      <div class={version_class.clone()}>
139        <Badge read=true>{ "v" }{ version }</Badge>
140      </div>
141      <CardHeader {selectable_actions}>
142        <div>
143          <div>{ provider_logo }</div>
144          <Truncate content={title} />
145        </div>
146      </CardHeader>
147      <CardBody>
148        <Stack gutter=true>
149          <StackItem fill=true>
150            <div class={description_class.clone()}>{ comment }</div>
151          </StackItem>
152          <StackItem>
153            <Split gutter=true>
154              <SplitItem fill=true>{ landing_page.clone() }</SplitItem>
155              <SplitItem>{ offer_button.clone() }</SplitItem>
156            </Split>
157          </StackItem>
158        </Stack>
159      </CardBody>
160      <CardFooter>
161        <slot class={keyword_class.clone()}>
162          <Split gutter=true>{ for keywords }</Split>
163        </slot>
164      </CardFooter>
165    </Card>
166  )
167}