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