Skip to main content

edc_web_components/components/
asset_selector.rs

1use crate::contexts::use_edc_connector_context;
2use crate::models::AssetItem;
3use edc_connector_client::EdcConnectorApiVersion;
4use edc_connector_client::types::query::Query;
5use patternfly_yew::prelude::*;
6use yew::prelude::*;
7use yew::suspense::use_future_with;
8
9#[derive(Clone, Debug, PartialEq, Properties)]
10pub struct AssetSelectorProps {
11  pub selected_assets: Vec<AssetItem>,
12  pub onselect: Callback<Vec<AssetItem>>,
13  #[prop_or("selectable-asset".to_string())]
14  pub select_id: String,
15}
16
17#[component]
18pub fn AssetSelector(props: &AssetSelectorProps) -> Html {
19  html!(
20    <Suspense>
21      <AssetSelectorInner
22        onselect={props.onselect.clone()}
23        selected_assets={props.selected_assets.clone()}
24        select_id={props.select_id.clone()}
25      />
26    </Suspense>
27  )
28}
29
30#[derive(Clone, Debug, PartialEq, Properties)]
31pub struct AssetSelectorInnerProps {
32  pub selected_assets: Vec<AssetItem>,
33  pub onselect: Callback<Vec<AssetItem>>,
34  pub select_id: String,
35}
36
37#[component]
38fn AssetSelectorInner(props: &AssetSelectorInnerProps) -> HtmlResult {
39  let limit = use_state(|| 10usize);
40  let offset = use_state(|| 0usize);
41  let force_refresh = use_state(|| 0usize);
42  let edc_connector_context = use_edc_connector_context();
43
44  let assets = use_future_with(
45    (edc_connector_context, *limit, *offset, *force_refresh),
46    |parameters| async move {
47      let (edc_connector_context, limit, offset, _) = (*parameters).clone();
48
49      let query = Query::builder()
50        .limit(limit as u32)
51        .offset(offset as u32)
52        .build();
53
54      if let Some(client) = edc_connector_context.get_client() {
55        client
56          .assets(EdcConnectorApiVersion::V4)
57          .query(query)
58          .await
59          .unwrap_or_default()
60          .into_iter()
61          .map(AssetItem::from)
62          .collect::<Vec<_>>()
63      } else {
64        vec![]
65      }
66    },
67  )?;
68
69  let assets = (*assets).clone();
70
71  let items = assets.iter().map(|asset_item| {
72    let asset_item_name = asset_item.name.to_string();
73    let onselect = props.onselect.clone();
74
75    let selectable_actions = {
76      let asset_item = asset_item.clone();
77      let selected_assets = props.selected_assets.clone();
78      let checked = if props.selected_assets.contains(&asset_item) {
79        CheckboxState::Checked
80      } else {
81        CheckboxState::Unchecked
82      };
83
84      yew::props!(CardSelectableActionsObjectProperties {
85        action: CardSelectableActionsVariant::MultiSelect {
86          checked,
87          onchange: onselect.reform(move |checked: CheckboxState| {
88            let mut selected_assets = selected_assets.clone();
89
90            match checked {
91              CheckboxState::Checked => {
92                selected_assets.push(asset_item.clone());
93              }
94              CheckboxState::Unchecked => {
95                selected_assets.retain(|item| item.id != asset_item.id);
96              }
97              CheckboxState::Indeterminate => {}
98            }
99
100            selected_assets
101          }),
102        }
103      })
104    };
105
106    html!(
107      <Card selectable=true selected={props.selected_assets.contains(asset_item)}>
108        <CardHeader {selectable_actions}>{ asset_item_name }</CardHeader>
109      </Card>
110    )
111  });
112
113  let onlimit = use_callback(limit.setter(), |limit, limit_setter| {
114    limit_setter.set(limit)
115  });
116
117  let total_entries = Option::<usize>::None;
118
119  let nav_callback = use_callback(
120    (offset.clone(), *limit, total_entries),
121    |page: Navigation, (offset, limit, total_entries)| {
122      let new_offset = match page {
123        Navigation::First => 0,
124        Navigation::Last => (total_entries.unwrap_or_default().saturating_sub(1) / limit) * limit,
125        Navigation::Previous => **offset - limit,
126        Navigation::Next => **offset + limit,
127        Navigation::Page(n) => n * limit,
128      };
129      offset.set(new_offset);
130    },
131  );
132
133  Ok(html!(
134    <Stack gutter=true>
135      <StackItem>
136        <Pagination
137          offset={*offset}
138          entries_per_page_choices={vec![5, 10, 25, 50, 100]}
139          selected_choice={*limit}
140          onlimit={&onlimit}
141          onnavigation={&nav_callback}
142        />
143      </StackItem>
144      <StackItem>
145        <Gallery gutter=true>{ for items }</Gallery>
146      </StackItem>
147    </Stack>
148  ))
149}