edc_web_components/components/
asset_selector.rs1use 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}