diff --git a/frontend/src/scss/_theme.scss b/frontend/src/scss/_theme.scss index c4073f420..9089edeb6 100644 --- a/frontend/src/scss/_theme.scss +++ b/frontend/src/scss/_theme.scss @@ -1,5 +1,5 @@ :root { - --media-size-mobile: 600px; + --media-size-mobile: 720px; --text-color: #ffffff; --hint-color: #888888; --label-color: #73d6fe; diff --git a/webui/Cargo.toml b/webui/Cargo.toml index 0973959e5..caa1a878a 100644 --- a/webui/Cargo.toml +++ b/webui/Cargo.toml @@ -29,7 +29,7 @@ bytes = "1" [dependencies.web-sys] version = "0.3" -features = ["Document", "Element", "Node", "Window", "HtmlCollection", "HtmlInputElement", "DomRect", "HtmlDialogElement"] +features = ["Document", "Element", "Node", "Window", "HtmlCollection", "HtmlInputElement", "DomRect", "HtmlDialogElement", "CssStyleDeclaration"] [dev-dependencies] js-sys = "0.3" diff --git a/webui/public/assets/icons.json b/webui/public/assets/icons.json index 1c13dae95..e0aa6870a 100644 --- a/webui/public/assets/icons.json +++ b/webui/public/assets/icons.json @@ -87,13 +87,13 @@ "keys": [ "PlayArrow" ], - "path": "M8 5v14l11-7z" + "path": "M 2,3.5077234 C 2,2.3622575 3.3299194,1.6369741 4.4176398,2.1882965 L 21.15403,10.681605 a 1.6315805,1.5054313 0 0 1 0,2.638854 L 4.4176398,21.813768 A 1.6315805,1.5054313 0 0 1 2,20.494341 Z" }, { "keys": [ "PlayArrowOutline" ], - "path": "m 6.9091055,7.1999977 7.6654235,4.7999983 -7.6654235,4.8 z M 4,2 V 22 L 20,11.999996 Z" + "path": "M 20.805539,10.144378 5.5830037,2.2804593 c -0.7254261,-0.3744562 -1.6646379,-0.3734349 -2.3874011,0 C 2.4583841,2.6603621 2,3.3701266 2,4.1323151 V 19.861175 c 0,0.762529 0.4580037,1.472293 1.1910377,1.850153 C 3.5535606,21.900259 3.967818,22 4.3889227,22 4.8088862,22 5.2227632,21.9006 5.5837645,21.71303 L 20.806299,13.84843 C 21.542757,13.467506 22,12.758082 22,11.996915 c -3.8e-4,-0.760146 -0.457243,-1.46957 -1.194461,-1.852537 z m -1.141205,1.935939 -15.2236767,7.86494 c -0.031193,0.016 -0.068472,0.0177 -0.106132,-0.0017 -0.031954,-0.01668 -0.052115,-0.04834 -0.052115,-0.08238 V 4.1319747 c 0,-0.034041 0.019781,-0.06536 0.053256,-0.082721 0.015977,-0.00851 0.034236,-0.012936 0.052876,-0.012936 0.0194,0 0.03766,0.00443 0.054017,0.012936 L 19.662432,11.912152 c 0.03386,0.0177 0.05516,0.05004 0.05516,0.08476 -3.81e-4,0.03506 -0.02016,0.06604 -0.05326,0.0834 z" }, { "keys": [ diff --git a/webui/scss/_theme.scss b/webui/scss/_theme.scss index 2f4e37cb4..056081e2d 100644 --- a/webui/scss/_theme.scss +++ b/webui/scss/_theme.scss @@ -1,4 +1,6 @@ :root { + --mobile-size: 720px; + --dashboard-mobile-size: 1280px; --sidebar-width-expaned: 270px; --sidebar-width-collapsed: 42px; --max-view-width: 1280px; @@ -8,6 +10,7 @@ --app-header-height: 56px; --scrollbar-width: 8px; --scrollbar-border-radius: 4px; + --gap-small: 0.3rem; --gap-default: 0.5rem; --gap-large: 1rem; @@ -20,6 +23,9 @@ --padding-large: 1.5rem; --padding-larger: 2rem; + --margin-micro: 0.2rem; + --margin-mini: 0.5rem; + --margin-small: 0.875rem; --margin-default: 1rem; --primary-color: #124a9b; @@ -50,7 +56,8 @@ --text-button-background-color: var(--action-item-background-color); --text-button-color: #ffffff; --text-button-border-color: #2F2F31; - --text-button-hover-color: #303032; + --text-button-hover-color: #ffffff; + --text-button-hover-background-color: #303032; --text-button-primary-background-color: var(--primary-color); --text-button-primary-color: #ffffff; --text-button-primary-hover-background-color: #2f68be; @@ -132,6 +139,12 @@ --output-hdhomerun-color: #6366f1; --output-strm-color: #f43f5e; + --tag-live-background-color: #075b41; + --tag-live-border-color: #10b981; + --tag-video-background-color: #035b6e; + --tag-video-border-color: #06b6d4; + --tag-series-background-color: #3c3f9f; + --tag-series-border-color: #6366f1; --hint-color: #888888; --label-color: #73d6fe; @@ -180,9 +193,6 @@ --menu-button-warn-hover-color: #dd7573; --menu-button-warn-hover-background-color: #4c3434; - --text-button-border-radius: var(--border-radius); - --text-button-hover-background-color: #0d6efd; - --scrollbar-track-color: #838383; --scrollbar-handle-color: #505050; diff --git a/webui/scss/app/components/_collapse_panel.scss b/webui/scss/app/components/_collapse_panel.scss index a2356925f..30aefdcce 100644 --- a/webui/scss/app/components/_collapse_panel.scss +++ b/webui/scss/app/components/_collapse_panel.scss @@ -2,13 +2,14 @@ display: flex; flex-flow: column; overflow: hidden; + flex-shrink: 0; &__header { display: flex; flex-flow: row nowrap; color: var(--modest-text-color); fill: var(--modest-text-color); - max-height: 1.2rem; + //max-height: 1.2rem; cursor: pointer; box-sizing: border-box; width: 100%; @@ -34,6 +35,7 @@ &__body { display: flex; flex-flow: column; + flex-shrink: 0; width: 100%; box-sizing: border-box; height: fit-content; diff --git a/webui/scss/app/components/_icon_button.scss b/webui/scss/app/components/_icon_button.scss index 08682f9a4..295afea60 100644 --- a/webui/scss/app/components/_icon_button.scss +++ b/webui/scss/app/components/_icon_button.scss @@ -5,6 +5,7 @@ fill: var(--menu-icon-color); background-color: transparent; border: none; + border-radius: var(--border-radius); aspect-ratio: 1 / 1; cursor: pointer; box-sizing: border-box; diff --git a/webui/scss/app/components/_list_view.scss b/webui/scss/app/components/_list_view.scss index 95a997762..eb2106517 100644 --- a/webui/scss/app/components/_list_view.scss +++ b/webui/scss/app/components/_list_view.scss @@ -2,7 +2,6 @@ display: flex; flex-flow: column; box-sizing: border-box; - flex: 1 1 auto; width: 100%; //max-width: var(--max-view-width); padding: var(--padding-default) 0; @@ -13,7 +12,6 @@ display: flex; flex-flow: column; box-sizing: border-box; - flex: 1 1 auto; width: 100%; overflow: hidden; } @@ -25,9 +23,8 @@ flex-flow: column; box-sizing: border-box; overflow: hidden; - flex: 1 1 auto; width: 100%; - height: 100%; + flex-shrink: 0; //max-width: var(--max-view-width); gap: var(--gap-default); @@ -35,7 +32,6 @@ display: flex; flex-flow: row nowrap; box-sizing: border-box; - flex: 1 1 auto; width: 100%; justify-content: space-between; align-items: center; @@ -45,7 +41,6 @@ display: flex; flex-flow: column; box-sizing: border-box; - flex: 1 1 auto; width: 100%; overflow: auto; gap: var(--gap-default); diff --git a/webui/scss/app/components/_panel.scss b/webui/scss/app/components/_panel.scss index 7470a60ef..9d22a97f3 100644 --- a/webui/scss/app/components/_panel.scss +++ b/webui/scss/app/components/_panel.scss @@ -4,5 +4,5 @@ overflow: hidden; background-color: transparent; box-sizing: border-box; - + gap: var(--gap-default); } \ No newline at end of file diff --git a/webui/scss/app/components/_sidebar.scss b/webui/scss/app/components/_sidebar.scss index 46cfb6c9b..d3f1c91e7 100644 --- a/webui/scss/app/components/_sidebar.scss +++ b/webui/scss/app/components/_sidebar.scss @@ -56,6 +56,12 @@ } } + .tp__menu-item.active, + .tp__icon-button.active { + background-color: var(--menu-item-hover-background-color); + color: var(--menu-item-hover-color) + } + &__content { display: flex; flex-flow: column; diff --git a/webui/scss/app/components/_text_button.scss b/webui/scss/app/components/_text_button.scss index dc903b6ff..5b37f2f4d 100644 --- a/webui/scss/app/components/_text_button.scss +++ b/webui/scss/app/components/_text_button.scss @@ -26,7 +26,8 @@ &:focus, &:hover { - background-color: var(--text-button-hover-color); + background-color: var(--text-button-hover-background-color); + color: var(--text-button-hover-color); outline: none; } } diff --git a/webui/scss/app/components/playlist/_playlist_explorer.scss b/webui/scss/app/components/playlist/_playlist_explorer.scss index 0df60ecc4..62c7a926f 100644 --- a/webui/scss/app/components/playlist/_playlist_explorer.scss +++ b/webui/scss/app/components/playlist/_playlist_explorer.scss @@ -1,3 +1,5 @@ +$mobile-size: 720px; + .tp__playlist-explorer { display: flex; flex-flow: column; @@ -20,18 +22,18 @@ overflow: hidden; &-list { - display: flex; - flex-flow: column; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--gap-default); box-sizing: border-box; - overflow: auto; - padding: var(--padding-default); + overflow-y: auto; + width: 100%; } } &__category { display: flex; - justify-content: center; + justify-content: flex-start; align-items: center; fill: var(--menu-icon-color); background-color: var(--text-button-background-color); @@ -46,6 +48,39 @@ padding: 0 var(--padding-default); font-weight: bold; font-size: 1rem; + overflow: hidden; + text-overflow: ellipsis; + + &:hover { + background-color: var(--text-button-hover-background-color); + color: var(--text-button-hover-color); + } + + &-live:before { + content: "L"; + background-color: var(--tag-live-background-color); + border: 1px solid var(--tag-live-border-color); + } + &-video:before { + content: "V"; + background-color: var(--tag-video-background-color); + border: 1px solid var(--tag-video-border-color); + } + &-series:before { + content: "S"; + background-color: var(--tag-series-background-color); + border: 1px solid var(--tag-series-border-color); + } + &-live:before, + &-video:before, + &-series:before { + display: flex; + justify-content: center; + align-items: center; + width: 1.2rem; + height: 1.2rem; + border-radius: 50%; + } } } \ No newline at end of file diff --git a/webui/scss/main.scss b/webui/scss/main.scss index b6a662f4a..910e3eb50 100644 --- a/webui/scss/main.scss +++ b/webui/scss/main.scss @@ -23,6 +23,10 @@ body { font-family: inherit; } + h1 { + margin: var(--margin-mini) 0; + } + } #root { diff --git a/webui/src/app/components/collapse_panel.rs b/webui/src/app/components/collapse_panel.rs index 92d25d2be..f487528bb 100644 --- a/webui/src/app/components/collapse_panel.rs +++ b/webui/src/app/components/collapse_panel.rs @@ -17,12 +17,25 @@ pub struct CollapsePanelProps { #[function_component] pub fn CollapsePanel(props: &CollapsePanelProps) -> Html { let expanded = use_state(|| props.expanded); + // let panel_ref = use_node_ref(); let toggle = { let expanded = expanded.clone(); Callback::from(move |_| expanded.set(!*expanded)) }; + // use_effect_with((expanded.clone(), panel_ref.clone()),move |(expanded, panel_ref)| { + // if let Some(element) = panel_ref.cast::() { + // if **expanded { + // let scroll_height = element.scroll_height(); + // element.style().set_property("height", &format!("{scroll_height}px")).unwrap(); + // } else { + // element.style().set_property("height", "0px").unwrap(); + // } + // } + // || () + // }); + html! {
@@ -31,7 +44,7 @@ pub fn CollapsePanel(props: &CollapsePanelProps) -> Html {
-
+
{ for props.children.iter() }
diff --git a/webui/src/app/components/icon_button.rs b/webui/src/app/components/icon_button.rs index 4b23b32b8..d76df8db7 100644 --- a/webui/src/app/components/icon_button.rs +++ b/webui/src/app/components/icon_button.rs @@ -1,5 +1,5 @@ use web_sys::MouseEvent; -use yew::{function_component, html, Callback, Html, Properties}; +use yew::{classes, function_component, html, Callback, Html, Properties}; use crate::app::components::AppIcon; #[derive(Properties, Clone, PartialEq, Debug)] @@ -7,6 +7,8 @@ pub struct IconButtonProps { pub name: String, pub icon: String, pub onclick: Callback, + #[prop_or_default] + pub style: String, } #[function_component] @@ -22,7 +24,7 @@ pub fn IconButton(props: &IconButtonProps) -> Html { }; html! { - } diff --git a/webui/src/app/components/menu_item.rs b/webui/src/app/components/menu_item.rs index 81026ca8c..5a9a462d7 100644 --- a/webui/src/app/components/menu_item.rs +++ b/webui/src/app/components/menu_item.rs @@ -26,7 +26,7 @@ pub fn MenuItem(props: &MenuItemProps) -> Html { }; html! { -
+
diff --git a/webui/src/app/components/playlist/playlist_explorer.rs b/webui/src/app/components/playlist/playlist_explorer.rs index 3ef4fa19a..fe7682882 100644 --- a/webui/src/app/components/playlist/playlist_explorer.rs +++ b/webui/src/app/components/playlist/playlist_explorer.rs @@ -2,25 +2,149 @@ use std::rc::Rc; use crate::app::context::PlaylistExplorerContext; use yew::prelude::*; use yew_i18n::use_translation; -use shared::model::{PlaylistResponseGroup, UiPlaylistGroup, XtreamCluster}; +use shared::model::{UiPlaylistGroup, XtreamCluster}; enum ExplorerLevel { Categories, - Groups(XtreamCluster), - Group(Rc), + Group(Rc), } +const HEADERS: [&str; 11] = [ + "TABLE.EMPTY", + "TABLE.ENABLED", + "TABLE.NAME", + "TABLE.OUTPUT", + "TABLE.OPTIONS", + "TABLE.SORT", + "TABLE.FILTER", + "TABLE.RENAME", + "TABLE.MAPPING", + "TABLE.PROCESSING_ORDER", + "TABLE.WATCH", +]; + #[function_component] pub fn PlaylistExplorer() -> Html { let translate = use_translation(); let context = use_context::().expect("PlaylistExlorer context not found"); let current_item = use_state(|| ExplorerLevel::Categories); + // + // let popup_anchor_ref = use_state(|| None::); + // let popup_is_open = use_state(|| false); + // + // let handle_popup_close = { + // let set_is_open = popup_is_open.clone(); + // Callback::from(move |()| { + // set_is_open.set(false); + // }) + // }; + // + // let handle_popup_onclick = { + // let set_selected_dto = selected_dto.clone(); + // let set_anchor_ref = popup_anchor_ref.clone(); + // let set_is_open = popup_is_open.clone(); + // Callback::from(move |(dto, event): (Rc, MouseEvent)| { + // if let Some(target) = event.target_dyn_into::() { + // set_selected_dto.set(Some(dto.clone())); + // set_anchor_ref.set(Some(target)); + // set_is_open.set(true); + // } + // }) + // }; + // + // let render_header_cell = { + // let translator = translate.clone(); + // Callback::::from(move |col| { + // html! { + // { + // if col < HEADERS.len() { + // translator.t(HEADERS[col]) + // } else { + // String::new() + // } + // } + // } + // }) + // }; + // + // let render_data_cell = { + // let translator = translate.clone(); + // let popup_onclick = handle_popup_onclick.clone(); + // Callback::<(usize, usize, Rc), Html>::from( + // move |(row, col, dto): (usize, usize, Rc)| { + // match col { + // 0 => { + // let popup_onclick = popup_onclick.clone(); + // html! { + // + // } + // } + // 1 => html! { }, + // 2 => html! { dto.name.as_str() }, + // 3 => html! { }, + // 4 => html! { }, + // 5 => dto.sort.as_ref().map_or_else(|| html! {}, |_s| html! { }), + // 6 => dto.t_filter.as_ref().map_or_else(|| html! {}, |f| html! { })}> }), + // 7 => dto.rename.as_ref().map_or_else(|| html! {}, |_r| html! { }), + // 8 => html! { }, + // 9 => html! { }, + // 10 => html! { }, + // _ => html! {""}, + // } + // }) + // }; + // + // let table_definition = { + // // first register for config update + // let render_header_cell_cb = render_header_cell.clone(); + // let render_data_cell_cb = render_data_cell.clone(); + // let num_cols = crate::app::components::playlist::target_table::HEADERS.len(); + // use_memo(props.targets.clone(), move |targets| { + // targets.as_ref().map(|list| + // Rc::new(TableDefinition:: { + // items: Rc::new(list.clone()), + // num_cols, + // render_header_cell: render_header_cell_cb, + // render_data_cell: render_data_cell_cb, + // })) + // }) + // }; - let render_cluster = |cluster: &Vec>| { - cluster.iter() - .map(|group| html! { -
{ group.title.clone() }
- }) + let handle_category_select = { + let set_current_item = current_item.clone(); + Callback::from(move |(group, event): (Rc, MouseEvent)| { + set_current_item.set(ExplorerLevel::Group(group)); + }) + }; + + let render_cluster = |cluster: XtreamCluster, list: &Vec>| { + + list.iter() + .map(|group| { + let group_clone = group.clone(); + let on_click = { + let category_select = handle_category_select.clone(); + Callback::from(move |event: MouseEvent| { + category_select.emit((group_clone.clone(), event)); + }) + }; + html! { + + { + match cluster { + XtreamCluster::Live => html! {}, + XtreamCluster::Video => html! {}, + XtreamCluster::Series => html! {}, + } + } + { group.title.clone() } + }}) .collect::() }; @@ -30,21 +154,17 @@ pub fn PlaylistExplorer() -> Html {
{ context.playlist.as_ref() .and_then(|response| response.live.as_ref()) - .map(render_cluster) + .map(|list| render_cluster(XtreamCluster::Live, list)) .unwrap_or_default() } -
-
{ context.playlist.as_ref() .and_then(|response| response.vod.as_ref()) - .map(render_cluster) + .map(|list| render_cluster(XtreamCluster::Video, list)) .unwrap_or_default() } -
-
{ context.playlist.as_ref() .and_then(|response| response.series.as_ref()) - .map(render_cluster) + .map(|list| render_cluster(XtreamCluster::Series, list)) .unwrap_or_default() }
@@ -52,14 +172,25 @@ pub fn PlaylistExplorer() -> Html { } }; + let render_group = |group: &Rc| { + html! { +
+ { + group.channels.iter().map(|c| { + html! { c.title.clone() } + }).collect::() + } +
+ } + }; + html! {
{ match *current_item { ExplorerLevel::Categories => html!{render_categories()} , - ExplorerLevel::Groups(ref cluster) => html!{}, - ExplorerLevel::Group(ref groups) => html!{}, + ExplorerLevel::Group(ref group) => html!{ render_group(group) }, } }
diff --git a/webui/src/app/components/playlist/source_selector.rs b/webui/src/app/components/playlist/source_selector.rs index e73728476..15b4e8152 100644 --- a/webui/src/app/components/playlist/source_selector.rs +++ b/webui/src/app/components/playlist/source_selector.rs @@ -105,9 +105,6 @@ pub fn PlaylistSourceSelector() -> Html {
-
- -
} diff --git a/webui/src/app/components/sidebar.rs b/webui/src/app/components/sidebar.rs index ab8fc5cc6..f7a1a7580 100644 --- a/webui/src/app/components/sidebar.rs +++ b/webui/src/app/components/sidebar.rs @@ -33,11 +33,14 @@ pub fn Sidebar(props: &SidebarProps) -> Html { let translate = use_translation(); let collapsed = use_state(|| CollapseState::AutoExpanded); let block_sidebar_toggle = use_state(|| false); + let active_menu = use_state(|| ViewType::Dashboard); let handle_menu_click = { let viewchange = props.onview.clone(); + let active_menu = active_menu.clone(); Callback::from(move |name: String| { if let Ok(view_type) = ViewType::from_str(&name) { + active_menu.set(view_type); viewchange.emit(view_type); } }) @@ -130,14 +133,14 @@ pub fn Sidebar(props: &SidebarProps) -> Html { let render_expanded = || { html! {
- - + + - + - - + +
} @@ -146,13 +149,13 @@ pub fn Sidebar(props: &SidebarProps) -> Html { let render_collapsed = || { html! {
- - + + - + - - + +
} };