diff --git a/crates/frontend/src/components/condition_pills.rs b/crates/frontend/src/components/condition_pills.rs index 1757a69d..28dc99b9 100644 --- a/crates/frontend/src/components/condition_pills.rs +++ b/crates/frontend/src/components/condition_pills.rs @@ -4,53 +4,60 @@ pub mod utils; use crate::components::condition_pills::types::ConditionOperator; use self::types::Condition; -use leptos::*; +use leptos::{leptos_dom::helpers::WindowListenerHandle, *}; use wasm_bindgen::JsCast; use web_sys::Element; +use derive_more::{Deref, DerefMut}; + +#[derive(Debug, Clone, Deref, DerefMut, Default)] +pub struct ConditionId(pub Option); + +pub fn use_condition_collapser() -> WindowListenerHandle { + let condition_id_ws = use_context::>().expect( + "use_condition_collapser must be used inside condition_collapse_provider", + ); + + window_event_listener(ev::click, move |ev| { + if let Some(t) = ev.target() { + let target_element = t.dyn_into::(); + if let Ok(te) = target_element { + let parent_id = te.parent_element().map(|e| e.id()); + condition_id_ws.set(ConditionId(parent_id)); + } + } + }) +} + #[component] pub fn condition_expression( #[prop(into)] id: String, #[prop(into)] list_id: String, condition: Condition, ) -> impl IntoView { + let id = store_value(id); + let condition = store_value(condition); + let (expand_rs, expand_ws) = create_signal(false); + let condition_id_rs = use_context::>().expect( + "condition_expression component must be used inside condition_collapse_provider", + ); let classes = Signal::derive(move || { if expand_rs.get() { - ( - "pointer flex items-center w-max max-w-full rounded-md bg-gray-50 px-2 py-1 text-xs ring-1 ring-inset ring-purple-700/10 shadow-md gap-x-2 overflow-hidden", - "font-mono font-semibold context_condition w-full text-wrap word-break-break" - ) + ("condition-item", "condition-value") } else { - ( - "pointer flex items-center w-max max-w-[300px] rounded-md bg-gray-50 px-2 py-1 text-xs ring-1 ring-inset ring-purple-700/10 shadow-md gap-x-2 overflow-hidden whitespace-nowrap", - - "font-mono font-semibold context_condition w-full text-ellipsis overflow-hidden whitespace-nowrap" - ) + ("condition-item-collapsed", "condition-value-collapsed") } }); - let condition = store_value(condition); - let id = store_value(id); - - let click_event_handler = window_event_listener(ev::click, move |ev| { - if let Some(t) = ev.target() { - let target_element = t.dyn_into::(); - if let Ok(te) = target_element { - let parent_id = te.parent_element().map(|e| e.id()); - - if let Some(p_id) = parent_id { - if !p_id.contains(&list_id) { - expand_ws.set(false); - } - } + create_effect(move |_| { + if let ConditionId(Some(c_id)) = condition_id_rs.get() { + if !c_id.contains(&list_id) { + expand_ws.set(false); } } }); - on_cleanup(|| { - click_event_handler.remove(); - }); view! { {move || { @@ -116,12 +123,21 @@ pub fn condition( #[prop(into)] id: String, #[prop(into)] conditions: Vec, #[prop(into, default=String::new())] class: String, + #[prop(default = true)] grouped_view: bool, ) -> impl IntoView { - let outer_div_class = format!("{} pt-3 relative flex flex-col w-full py-4 pl-6 border-l-2 border-gray-300 rounded-lg", class); + let conditions = store_value(conditions); + + let outer_div_class = if grouped_view { + format!("{} condition grouped", class) + } else { + format!("{} condition", class) + }; + view! {
-
    +
      {conditions + .get_value() .into_iter() .enumerate() .map(|(idx, condition)| { @@ -130,9 +146,11 @@ pub fn condition( }) .collect::>()}
    - - "and" - + + + "and" + +
} } diff --git a/crates/frontend/src/components/context_card.rs b/crates/frontend/src/components/context_card.rs index cd7cb03a..77d01b8a 100644 --- a/crates/frontend/src/components/context_card.rs +++ b/crates/frontend/src/components/context_card.rs @@ -45,7 +45,7 @@ pub fn context_card( ]; view! { -
+

"Condition" @@ -80,14 +80,13 @@ pub fn context_card( id=context_id.get_value() class="xl:w-[400px] h-fit" /> -
- - +
} diff --git a/crates/frontend/src/components/table.rs b/crates/frontend/src/components/table.rs index eb5b61c8..4f180ed7 100644 --- a/crates/frontend/src/components/table.rs +++ b/crates/frontend/src/components/table.rs @@ -24,16 +24,19 @@ fn generate_table_row_str(row: &Value) -> String { #[component] pub fn table( key_column: String, - cell_style: String, columns: Vec, rows: Vec>, + #[prop(into, default = String::new())] cell_style: String, + #[prop(into, default = String::new())] style: String, + #[prop(into, default = String::new())] head_style: String, #[prop(default = TablePaginationProps::default())] pagination: TablePaginationProps, ) -> impl IntoView { let pagination_props = StoredValue::new(pagination); + let container_style = format!("{} overflow-x-auto", style); view! { -
+
- + @@ -94,26 +97,26 @@ pub fn table(
- +
+ - {move || { - let TablePaginationProps { current_page, total_pages, on_prev, on_next, .. } = pagination_props - .get_value(); - view! { -
- -
- } - }} + {move || { + let TablePaginationProps { current_page, total_pages, on_prev, on_next, .. } = pagination_props + .get_value(); + view! { +
+ +
+ } + }} -
-

+ } } diff --git a/crates/frontend/src/pages/context_override.rs b/crates/frontend/src/pages/context_override.rs index 13b50512..0fdd14b2 100644 --- a/crates/frontend/src/pages/context_override.rs +++ b/crates/frontend/src/pages/context_override.rs @@ -10,6 +10,7 @@ use crate::components::drawer::{close_drawer, open_drawer, Drawer, DrawerBtn}; use crate::components::override_form::OverrideForm; use crate::components::skeleton::{Skeleton, SkeletonVariant}; use crate::providers::alert_provider::enqueue_alert; +use crate::providers::condition_collapse_provider::ConditionCollapseProvider; use crate::types::{Config, Context, DefaultConfig, Dimension}; use crate::utils::extract_conditions; use futures::join; @@ -335,8 +336,11 @@ pub fn context_override() -> impl IntoView { (context.clone(), overrides) }) .collect::)>>(); - if ctx_n_overrides.is_empty() { - view! { + let is_empty = ctx_n_overrides.is_empty(); + + + view! { +
@@ -345,25 +349,28 @@ pub fn context_override() -> impl IntoView { "Start with creating an override"
+
+ + + { + ctx_n_overrides + .into_iter() + .map(|(context, overrides)| { + view! { + + } + }) + .collect_view() + } - }.into_view() - } else { - ctx_n_overrides - .into_iter() - .map(|(context, overrides)| { - view! { - - } - }) - .collect_view() + } - }}
diff --git a/crates/frontend/src/pages/home.rs b/crates/frontend/src/pages/home.rs index a24488f0..663b609d 100644 --- a/crates/frontend/src/pages/home.rs +++ b/crates/frontend/src/pages/home.rs @@ -3,6 +3,7 @@ use std::time::Duration; use crate::components::condition_pills::types::Condition; use crate::components::condition_pills::Condition as ConditionComponent; use crate::components::skeleton::{Skeleton, SkeletonVariant}; +use crate::providers::condition_collapse_provider::ConditionCollapseProvider; use crate::types::Config; use crate::{ api::{fetch_config, fetch_dimensions}, @@ -99,43 +100,48 @@ fn all_context_view(config: Config) -> impl IntoView { view! {
- {contexts - .iter() - .map(|context| { - let rows: Vec<_> = context - .override_with_keys + + { + contexts .iter() - .filter_map(|key| overrides.get(key).map(|o| rows(key, o, true))) - .collect(); - let conditions: Vec = context.try_into().unwrap_or_default(); - view! { -
-

- "Condition" -

-
- -
- - - - - - - - {rows} -
KeyValue
+ .map(|context| { + let rows: Vec<_> = context + .override_with_keys + .iter() + .filter_map(|key| overrides.get(key).map(|o| rows(key, o, true))) + .collect(); + let conditions: Vec = context.try_into().unwrap_or_default(); + view! { +
+

+ "Condition" +

+
+ +
+ + + + + + + + {rows} +
KeyValue
+
+
-
-
- } - }) - .rev() - .collect::>()}
+ } + }) + .rev() + .collect::>() + } + +

Default Configuration

diff --git a/crates/frontend/src/providers.rs b/crates/frontend/src/providers.rs index 6fcddf6c..1517e8d8 100644 --- a/crates/frontend/src/providers.rs +++ b/crates/frontend/src/providers.rs @@ -1 +1,2 @@ pub mod alert_provider; +pub mod condition_collapse_provider; diff --git a/crates/frontend/src/providers/condition_collapse_provider.rs b/crates/frontend/src/providers/condition_collapse_provider.rs new file mode 100644 index 00000000..2c5810e5 --- /dev/null +++ b/crates/frontend/src/providers/condition_collapse_provider.rs @@ -0,0 +1,20 @@ +use leptos::*; + +use crate::components::condition_pills::{use_condition_collapser, ConditionId}; + +#[component] +pub fn condition_collapse_provider(children: Children) -> impl IntoView { + let (condition_id_rs, condition_id_ws) = + create_signal::(ConditionId(None)); + + provide_context(condition_id_rs); + provide_context(condition_id_ws); + + let collapse_event_handle = use_condition_collapser(); + on_cleanup(move || { + collapse_event_handle.remove(); + logging::debug_warn!("removing event handle"); + }); + + children() +} diff --git a/crates/frontend/styles/tailwind.css b/crates/frontend/styles/tailwind.css index 617365d2..e463f0b7 100644 --- a/crates/frontend/styles/tailwind.css +++ b/crates/frontend/styles/tailwind.css @@ -88,11 +88,90 @@ min-width: 12rem; } -span.and-badge { - margin: 0; +.text-wrap { + text-wrap: wrap; } -/* .table-zebra tbody tr:nth-child(2n-1) { - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); -}*/ +.condition { + @apply relative; + @apply flex; + @apply flex-col; + @apply w-full; +} + +.condition.grouped { + @apply py-4; + @apply pl-6; + @apply border-l-2; + @apply border-gray-300; + @apply rounded-lg; +} + +.condition > ol { + @apply flex; + @apply flex-col; + @apply gap-2; + @apply w-full; + @apply list-none; +} + +.condition.grouped > ol { + @apply pl-3; + @apply gap-4; +} + +.condition.grouped > .and { + @apply absolute; + @apply badge; + @apply badge-ghost; + @apply capitalize; + @apply top-1/2; + @apply left-0; + @apply -translate-x-1/2; + @apply -translate-y-1/2; + @apply m-0; +} + +.condition-item { + @apply cursor-pointer; + @apply flex; + @apply items-center; + @apply w-max; + @apply max-w-full; + @apply rounded-md; + @apply bg-gray-50; + @apply px-2; + @apply py-1; + @apply text-xs; + @apply ring-1; + @apply ring-inset; + @apply ring-purple-700/10; + @apply shadow-md; + @apply gap-x-2; + @apply overflow-hidden; +} + +.condition-item-collapsed { + @apply condition-item; + @apply whitespace-nowrap; +} + +.condition-value { + @apply font-mono; + @apply font-semibold; + @apply context_condition; + @apply w-full; + @apply text-wrap; + @apply word-break-break; +} + + +.condition-value-collapsed { + @apply font-mono; + @apply font-semibold; + @apply context_condition; + @apply w-full; + @apply text-ellipsis; + @apply overflow-hidden; + @apply whitespace-nowrap; +}