mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-17 05:12:34 +02:00
web ui playlist browser
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
:root {
|
||||
--media-size-mobile: 600px;
|
||||
--media-size-mobile: 720px;
|
||||
--text-color: #ffffff;
|
||||
--hint-color: #888888;
|
||||
--label-color: #73d6fe;
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
@@ -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": [
|
||||
|
||||
+14
-4
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
overflow: hidden;
|
||||
background-color: transparent;
|
||||
box-sizing: border-box;
|
||||
|
||||
gap: var(--gap-default);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,10 @@ body {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: var(--margin-mini) 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#root {
|
||||
|
||||
@@ -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::<HtmlElement>() {
|
||||
// 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! {
|
||||
<div class={classes!("tp__collapse-panel", if *expanded {""} else {"tp__collapsed"}, props.class.to_string())}>
|
||||
<div class="tp__collapse-panel__header" onclick={toggle}>
|
||||
@@ -31,7 +44,7 @@ pub fn CollapsePanel(props: &CollapsePanelProps) -> Html {
|
||||
</span>
|
||||
<AppIcon name={ if *expanded { "ChevronUp" } else {"ChevronDown"} }/>
|
||||
</div>
|
||||
<div class="tp__collapse-panel__body">
|
||||
<div /*ref={panel_ref} */class="tp__collapse-panel__body">
|
||||
{ for props.children.iter() }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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<String>,
|
||||
#[prop_or_default]
|
||||
pub style: String,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
@@ -22,7 +24,7 @@ pub fn IconButton(props: &IconButtonProps) -> Html {
|
||||
};
|
||||
|
||||
html! {
|
||||
<button class="tp__icon-button" onclick={handle_click}>
|
||||
<button class={classes!("tp__icon-button", props.style.clone())} onclick={handle_click}>
|
||||
<AppIcon name={props.icon.clone()}></AppIcon>
|
||||
</button>
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn MenuItem(props: &MenuItemProps) -> Html {
|
||||
};
|
||||
|
||||
html! {
|
||||
<div class={classes!("tp__menu-item", props.style.to_string())} onclick={ handle_click }>
|
||||
<div class={classes!("tp__menu-item", props.style.clone())} onclick={ handle_click }>
|
||||
<AppIcon name={props.icon.clone()}></AppIcon>
|
||||
<label>{props.label.clone()}</label>
|
||||
</div>
|
||||
|
||||
@@ -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<PlaylistResponseGroup>),
|
||||
Group(Rc<UiPlaylistGroup>),
|
||||
}
|
||||
|
||||
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::<PlaylistExplorerContext>().expect("PlaylistExlorer context not found");
|
||||
let current_item = use_state(|| ExplorerLevel::Categories);
|
||||
//
|
||||
// let popup_anchor_ref = use_state(|| None::<web_sys::Element>);
|
||||
// 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<ConfigTargetDto>, MouseEvent)| {
|
||||
// if let Some(target) = event.target_dyn_into::<web_sys::Element>() {
|
||||
// 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::<usize, Html>::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<ConfigTargetDto>), Html>::from(
|
||||
// move |(row, col, dto): (usize, usize, Rc<ConfigTargetDto>)| {
|
||||
// match col {
|
||||
// 0 => {
|
||||
// let popup_onclick = popup_onclick.clone();
|
||||
// html! {
|
||||
// <button class="tp__icon-button"
|
||||
// onclick={Callback::from(move |event: MouseEvent| popup_onclick.emit((dto.clone(), event)))}
|
||||
// data-row={row.to_string()}>
|
||||
// <AppIcon name="Popup"></AppIcon>
|
||||
// </button>
|
||||
// }
|
||||
// }
|
||||
// 1 => html! { <Chip class={ convert_bool_to_chip_style(dto.enabled) }
|
||||
// label={if dto.enabled {translator.t("LABEL.ACTIVE")} else { translator.t("LABEL.DISABLED")} }
|
||||
// /> },
|
||||
// 2 => html! { dto.name.as_str() },
|
||||
// 3 => html! { <TargetOutput target={Rc::clone(&dto)} /> },
|
||||
// 4 => html! { <TargetOptions target={Rc::clone(&dto)} /> },
|
||||
// 5 => dto.sort.as_ref().map_or_else(|| html! {}, |_s| html! { <RevealContent><TargetSort target={Rc::clone(&dto)} /></RevealContent> }),
|
||||
// 6 => dto.t_filter.as_ref().map_or_else(|| html! {}, |f| html! { <RevealContent preview={Some(html!{<FilterView inline={true} filter={f.clone()} />})}><FilterView pretty={true} filter={f.clone()} /></RevealContent> }),
|
||||
// 7 => dto.rename.as_ref().map_or_else(|| html! {}, |_r| html! { <RevealContent><TargetRename target={Rc::clone(&dto)} /></RevealContent> }),
|
||||
// 8 => html! { <PlaylistMappings mappings={dto.mapping.clone()} /> },
|
||||
// 9 => html! { <PlaylistProcessing order={dto.processing_order} /> },
|
||||
// 10 => html! { <TargetWatch target={Rc::clone(&dto)} /> },
|
||||
// _ => 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::<ConfigTargetDto> {
|
||||
// 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<Rc<UiPlaylistGroup>>| {
|
||||
cluster.iter()
|
||||
.map(|group| html! {
|
||||
<div class="tp__playlist-explorer__category">{ group.title.clone() }</div>
|
||||
})
|
||||
let handle_category_select = {
|
||||
let set_current_item = current_item.clone();
|
||||
Callback::from(move |(group, event): (Rc<UiPlaylistGroup>, MouseEvent)| {
|
||||
set_current_item.set(ExplorerLevel::Group(group));
|
||||
})
|
||||
};
|
||||
|
||||
let render_cluster = |cluster: XtreamCluster, list: &Vec<Rc<UiPlaylistGroup>>| {
|
||||
|
||||
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! {
|
||||
<span class="tp__playlist-explorer__category" onclick={on_click}>
|
||||
{
|
||||
match cluster {
|
||||
XtreamCluster::Live => html! {<span class="tp__playlist-explorer__category-live"></span>},
|
||||
XtreamCluster::Video => html! {<span class="tp__playlist-explorer__category-video"></span>},
|
||||
XtreamCluster::Series => html! {<span class="tp__playlist-explorer__category-series"></span>},
|
||||
}
|
||||
}
|
||||
{ group.title.clone() }</span>
|
||||
}})
|
||||
.collect::<Html>()
|
||||
};
|
||||
|
||||
@@ -30,21 +154,17 @@ pub fn PlaylistExplorer() -> Html {
|
||||
<div class="tp__playlist-explorer__categories-list">
|
||||
{ context.playlist.as_ref()
|
||||
.and_then(|response| response.live.as_ref())
|
||||
.map(render_cluster)
|
||||
.map(|list| render_cluster(XtreamCluster::Live, list))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
</div>
|
||||
<div class="tp__playlist-explorer__categories-list">
|
||||
{ context.playlist.as_ref()
|
||||
.and_then(|response| response.vod.as_ref())
|
||||
.map(render_cluster)
|
||||
.map(|list| render_cluster(XtreamCluster::Video, list))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
</div>
|
||||
<div class="tp__playlist-explorer__categories-list">
|
||||
{ context.playlist.as_ref()
|
||||
.and_then(|response| response.series.as_ref())
|
||||
.map(render_cluster)
|
||||
.map(|list| render_cluster(XtreamCluster::Series, list))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
</div>
|
||||
@@ -52,14 +172,25 @@ pub fn PlaylistExplorer() -> Html {
|
||||
}
|
||||
};
|
||||
|
||||
let render_group = |group: &Rc<UiPlaylistGroup>| {
|
||||
html! {
|
||||
<div class="tp__playlist-explorer__group">
|
||||
{
|
||||
group.channels.iter().map(|c| {
|
||||
html! { c.title.clone() }
|
||||
}).collect::<Html>()
|
||||
}
|
||||
</div>
|
||||
}
|
||||
};
|
||||
|
||||
html! {
|
||||
<div class="tp__playlist-explorer">
|
||||
<div class="tp__playlist-explorer__body">
|
||||
{
|
||||
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) },
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -105,9 +105,6 @@ pub fn PlaylistSourceSelector() -> Html {
|
||||
</div>
|
||||
</Card>
|
||||
</CollapsePanel>
|
||||
<div class="tp__playlist-source-selector__body tp__list-list__selector">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -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! {
|
||||
<div class="tp__app-sidebar__content">
|
||||
<MenuItem icon="DashboardOutline" name={ViewType::Dashboard.to_string()} label={translate.t("LABEL.DASHBOARD")} onclick={&handle_menu_click}></MenuItem>
|
||||
<MenuItem icon="Stats" name={ViewType::Stats.to_string()} label={translate.t("LABEL.STATS")} onclick={&handle_menu_click}></MenuItem>
|
||||
<MenuItem style={if *active_menu == ViewType::Dashboard { "active" } else {""}} icon="DashboardOutline" name={ViewType::Dashboard.to_string()} label={translate.t("LABEL.DASHBOARD")} onclick={&handle_menu_click}></MenuItem>
|
||||
<MenuItem style={if *active_menu == ViewType::Stats { "active" } else {""}} icon="Stats" name={ViewType::Stats.to_string()} label={translate.t("LABEL.STATS")} onclick={&handle_menu_click}></MenuItem>
|
||||
<CollapsePanel title={translate.t("LABEL.SETTINGS")}>
|
||||
<MenuItem icon="UserOutline" name={ViewType::Users.to_string()} label={translate.t("LABEL.USER")} onclick={&handle_menu_click}></MenuItem>
|
||||
<MenuItem style={if *active_menu == ViewType::Users { "active" } else {""}} icon="UserOutline" name={ViewType::Users.to_string()} label={translate.t("LABEL.USER")} onclick={&handle_menu_click}></MenuItem>
|
||||
</CollapsePanel>
|
||||
<CollapsePanel title={translate.t("LABEL.PLAYLIST")}>
|
||||
<MenuItem icon="PlayArrowOutline" name={ViewType::PlaylistEditor.to_string()} label={translate.t("LABEL.PLAYLIST")} onclick={&handle_menu_click}></MenuItem>
|
||||
<MenuItem icon="Live" name={ViewType::PlaylistExplorer.to_string()} label={translate.t("LABEL.PLAYLIST_VIEWER")} onclick={&handle_menu_click}></MenuItem>
|
||||
<MenuItem style={if *active_menu == ViewType::PlaylistEditor { "active" } else {""}} icon="PlayArrowOutline" name={ViewType::PlaylistEditor.to_string()} label={translate.t("LABEL.PLAYLIST")} onclick={&handle_menu_click}></MenuItem>
|
||||
<MenuItem style={if *active_menu == ViewType::PlaylistExplorer { "active" } else {""}} icon="Live" name={ViewType::PlaylistExplorer.to_string()} label={translate.t("LABEL.PLAYLIST_VIEWER")} onclick={&handle_menu_click}></MenuItem>
|
||||
</CollapsePanel>
|
||||
</div>
|
||||
}
|
||||
@@ -146,13 +149,13 @@ pub fn Sidebar(props: &SidebarProps) -> Html {
|
||||
let render_collapsed = || {
|
||||
html! {
|
||||
<div class="tp__app-sidebar__content">
|
||||
<IconButton icon="DashboardOutline" name={ViewType::Dashboard.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
<IconButton icon="Stats" name={ViewType::Stats.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
<IconButton style={if *active_menu == ViewType::Dashboard { "active" } else {""}} icon="DashboardOutline" name={ViewType::Dashboard.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
<IconButton style={if *active_menu == ViewType::Stats { "active" } else {""}} icon="Stats" name={ViewType::Stats.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
<span class="tp__app-sidebar__content-space"></span>
|
||||
<IconButton icon="UserOutline" name={ViewType::Users.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
<IconButton style={if *active_menu == ViewType::Users { "active" } else {""}} icon="UserOutline" name={ViewType::Users.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
<span class="tp__app-sidebar__content-space"></span>
|
||||
<IconButton icon="PlayArrowOutline" name={ViewType::PlaylistEditor.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
<IconButton icon="Live" name={ViewType::PlaylistExplorer.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
<IconButton style={if *active_menu == ViewType::PlaylistEditor { "active" } else {""}} icon="PlayArrowOutline" name={ViewType::PlaylistEditor.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
<IconButton style={if *active_menu == ViewType::PlaylistExplorer { "active" } else {""}} icon="Live" name={ViewType::PlaylistExplorer.to_string()} onclick={&handle_menu_click}></IconButton>
|
||||
</div>
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user