refactored web ui video player

refactored responsiveness
This commit is contained in:
euzu
2025-05-07 13:03:08 +02:00
parent 297d4421a7
commit b6fa87b53c
14 changed files with 122 additions and 57 deletions
+1 -1
View File
@@ -116,7 +116,7 @@
"IP": "IP",
"TARGET": "Target",
"TARGETS": "Targets",
"COMMENT": "Comment"
"NOTES": "Notes"
},
"TITLE": {
"USER_BOUQUET_EDITOR": "User group editor"
@@ -26,6 +26,13 @@
&__row {
flex-flow: row nowrap;
@include common.container_common(flex-start, 0);
@media (max-width: common.$media-size-mobile) {
flex-flow: column;
&:nth-child(even) {
background-color: var(--card-background-color-darker);
border-radius: var(--border-radius);
}
}
}
&__col {
@@ -35,9 +42,13 @@
&-label {
display: flex;
width: 7em;
justify-content: flex-end;
min-width: 10em;
width: 10em;
justify-content: flex-start;
align-items: center;
@media (max-width: common.$media-size-mobile) {
justify-content: flex-start;
}
}
&-value {
@@ -35,14 +35,6 @@
padding-bottom: 4px;
margin: 8px 0;
}
.form-view__col-label {
width: 120px;
min-width: 120px;
max-width: 120px;
@media (max-width: common.$media-size-mobile) {
justify-content: flex-start;
}
}
}
&-help {
@@ -72,7 +72,7 @@ const CONFIG_WEB_AUTH_FIELDS = [
name: 'secret',
label: 'LABEL.SECRET',
fieldType: FormFieldType.TEXT,
action: {icon: 'Clear', handler: getRandomHex}
action: {icon: 'Refresh', handler: getRandomHex}
},
{name: 'userfile', label: 'LABEL.USERFILE', fieldType: FormFieldType.TEXT},
];
@@ -16,11 +16,12 @@
&__toolbar {
display: flex;
flex-flow: row;
flex-flow: row wrap;
flex: 0 0 auto;
box-sizing: border-box;
overflow: hidden;
background-color: inherit;
gap: 12px;
.source-selector {
flex: 1 1 auto;
@@ -1,6 +1,7 @@
@use '../../scss/common';
$menu-width: 300px;
$menu-width-closed: -288px;
$item-height: 48px;
.playlist-video {
@@ -16,7 +17,6 @@ $item-height: 48px;
.channel-view {
display: flex;
flex-flow: column;
border: 1px transparent;
box-sizing: border-box;
z-index: 2;
font-size: 1rem;
@@ -26,28 +26,41 @@ $item-height: 48px;
bottom: 0;
overflow: hidden;
gap: 4px;
transition: width 0.4s ease;
width: 12px;
height: 90%;
transition: left 0.4s ease;
width: $menu-width;
height: 100%;
background-color: transparent;
border: 1px solid var(--border-color);
&:hover {
background-color: #00000088;
border: 1px solid var(--border-color);
width: $menu-width;
.channel-view__content,
.channel-view__header {
display: flex;
&__closed {
left: $menu-width-closed;
border: none;
cursor:pointer;
&:hover {
background-color: var(--card-background-color);
}
.channel-view__header,
.channel-view__content {
display: none;
}
}
//&:hover {
// background-color: #00000088;
// border: 1px solid var(--border-color);
// width: $menu-width;
// .channel-view__content,
// .channel-view__header {
// display: flex;
// }
//}
&__header {
display: none;
display: flex;
flex-flow: row;
align-items: center;
box-sizing: border-box;
height: $item-height;
border-bottom:1px solid var(--border-color)
}
&__menu {
@@ -59,13 +72,23 @@ $item-height: 48px;
overflow: hidden;
cursor: pointer;
&-disabled {
pointer-events: none;
&-title {
overflow: hidden;
white-space: nowrap;
}
&:hover {
color: var(--icon-button-hover-color);
background-color: var(--icon-button-hover-background-color);
&-back,
&-title {
&:hover {
cursor: pointer;
color: var(--icon-button-hover-color);
background-color: var(--icon-button-hover-background-color);
}
}
&-disabled {
pointer-events: none;
}
&-back {
@@ -98,7 +121,7 @@ $item-height: 48px;
}
&__content {
display: none;
display: flex;
flex-flow: column;
box-sizing: border-box;
overflow-x: hidden;
@@ -3,6 +3,7 @@ import './channel-view.scss';
import React, {ReactNode, useCallback, useState} from "react";
import {getIconByName} from "../../icons/icons";
import useTranslator from "../../hook/use-translator";
import {set} from "react-datepicker/dist/date_utils";
const getLogo = (item: PlaylistItem): string | undefined => {
if (item.logo_small && item.logo_small.length > 0) {
@@ -26,6 +27,7 @@ export default function ChannelView(props: ChannelViewProps) {
const [selectedItem, setSelectedItem] = useState<PlaylistItem>();
const [selectedGroup, setSelectedGroup] = useState<PlaylistGroup>();
const [selectedCategory, setSelectedCategory] = useState<PlaylistCategory>(PlaylistCategory.LIVE);
const [openChannelList, setOpenChannelList] = useState<boolean>(true);
const handleCategorySelect = useCallback((evt: any) => {
let category = evt.target.dataset.category;
@@ -52,6 +54,17 @@ export default function ChannelView(props: ChannelViewProps) {
}
}, [onPlay]);
const handleCloseChannelViewState = useCallback(() => {
setOpenChannelList(false);
}, []);
const handleOpenChannelViewState = useCallback(() => {
if (!openChannelList) {
setOpenChannelList(true);
}
}, [openChannelList]);
const handleBack = useCallback(() => {
if (selectedGroup) {
setSelectedGroup(undefined);
@@ -122,15 +135,16 @@ export default function ChannelView(props: ChannelViewProps) {
}, [data, selectedCategory, selectedGroup, selectedItem, renderSelectionContent, translate]);
const renderMenu = useCallback((): ReactNode => {
return <div className={"channel-view__menu" + (selectedGroup ? '' : ' channel-view__menu-disabled')}
onClick={handleBack}>
<div
className={"channel-view__menu-back" + (selectedGroup ? '' : ' channel-view__menu-back-disabled')}>{getIconByName('Back')}</div>
{selectedGroup?.name ?? ''}
return <div className={"channel-view__menu"}>
<div className={"channel-view__menu-back"} onClick={handleCloseChannelViewState}>{getIconByName('Clear')}</div>
{selectedGroup &&
<div className={"channel-view__menu-back"} onClick={handleBack}>{getIconByName('Back')}</div>
}
<div className={"channel-view__menu-title"} onClick={handleBack}>{selectedGroup?.name ?? ''}</div>
</div>
}, [selectedGroup, handleBack])
}, [selectedGroup, handleBack, handleCloseChannelViewState])
return <div className="channel-view">
return <div className={"channel-view" + (openChannelList ? '' : ' channel-view__closed') } onClick={handleOpenChannelViewState}>
<div className="channel-view__header">
{renderMenu()}
{renderCategories()}
@@ -23,7 +23,7 @@ const COLUMNS = [
{name: 'status', label: 'LABEL.STATUS', fieldType: FormFieldType.SINGLE_SELECT, options: STATUS_OPTIONS},
{name: 'exp_date', label: 'LABEL.EXP_DATE', fieldType: FormFieldType.DATE},
{name: 'ui_enabled', label: 'LABEL.UI_ENABLED', fieldType: FormFieldType.CHECK},
{name: 'comment', label: 'LABEL.COMMENT', fieldType: FormFieldType.TEXT}
{name: 'comment', label: 'LABEL.NOTES', fieldType: FormFieldType.TEXT}
];
interface UserViewProps {
@@ -157,6 +157,7 @@ dialog:not([open]) {
&-row {
display: table-row;
width: auto;
height: 3rem;
clear: both;
border: 1px solid var(--table-border-color);
}
@@ -186,6 +187,7 @@ dialog:not([open]) {
align-items: center;
gap: 4px;
}
&-action {
cursor: pointer;
span {
@@ -196,6 +198,12 @@ dialog:not([open]) {
}
}
&-comment {
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
}
.visibility {
opacity: 0.5;
cursor: pointer;
@@ -209,12 +217,12 @@ dialog:not([open]) {
display: flex;
flex-flow: row;
align-items: flex-end;
justify-content: flex-end;
justify-content: flex-start;
padding-left: 4px;
}
span {
padding: 4px;
transform: scale(0.7) translateY(5px);
transform: scale(0.9) translateY(5px);
@include common.iconButton();
}
}
@@ -240,6 +248,7 @@ dialog:not([open]) {
display: flex;
flex-flow: column;
gap: 4px;
height: auto;
}
&-col-label {
+13 -11
View File
@@ -26,7 +26,7 @@ const renderBool = (value: any, hidden?: boolean) => {
const renderComment = (value: any, hidden?: boolean) => {
if (value?.length) {
return <span className={'visibility'}>{getIconByName('Visibility')}</span>;
return <span className={'user__target-user-col-comment'}>{value}</span>;
}
return undefined;
};
@@ -62,7 +62,7 @@ const COLUMNS = [
{field: 'status', label: 'LABEL.STATUS', render: renderStatus},
{field: 'exp_date', label: 'LABEL.EXP_DATE', render: renderExpDate},
{field: 'ui_enabled', label: 'LABEL.UI_ENABLED', render: renderBool},
{field: 'comment', label: 'LABEL.COMMENT', render: renderComment, action: true},
{field: 'comment', label: 'LABEL.NOTES', render: renderComment, action: true},
]
COLUMNS.forEach(col => {
@@ -456,15 +456,17 @@ export default function UserView(props: UserViewProps) {
{(filteredUser[target.target]?.user ?? target.credentials).map((usr, idx) =>
<div key={'credential' + idx} className={'user__target-user-row'}>
<div className={'user__target-user-col-toolbar'}>
<span data-target={target.target} data-user={usr.username}
onClick={handleUserRemove}>
{getIconByName('PersonRemove')}
</span>
<span data-target={target.target} data-user={usr.username}
onClick={handleUserEdit}>
{getIconByName('Edit')}
</span>
<div className={'user__target-user-col'}>
<div className={'user__target-user-col-toolbar'}>
<span data-target={target.target} data-user={usr.username}
onClick={handleUserRemove}>
{getIconByName('PersonRemove')}
</span>
<span data-target={target.target} data-user={usr.username}
onClick={handleUserEdit}>
{getIconByName('Edit')}
</span>
</div>
</div>
{COLUMNS.map(c => <div
key={'target_' + target.target + '_' + c.field + '_' + usr.username}
+6 -1
View File
@@ -5,7 +5,12 @@ $media-size-mobile: 600px;
justify-content: $justify-content;
gap: 8px;
box-sizing: border-box;
padding: $padding;
@media (max-width: $media-size-mobile) {
padding: $padding;
}
@media (min-width: $media-size-mobile) {
padding: 2px;
}
}
@mixin container_vert() {
+3
View File
@@ -44,6 +44,9 @@ button {
border-radius:var(--border-radius);
@media (min-width: common.$media-size-mobile) {
padding: 2px;
}
}
.vert-group {
+1
View File
@@ -38,6 +38,7 @@
--ok-color: #21e821;
--card-background-color: #202733; //#282828;
--card-background-color-darker: #1c2026;
--card-color: #ffffff;
--card-border-color: #47495d;
+7 -3
View File
@@ -420,6 +420,7 @@ mod tests {
max_connections: 1,
status: Some(ProxyUserStatus::Active),
ui_enabled: true,
comment: None,
},
ProxyUserCredentials {
username: "Test2".to_string(),
@@ -432,7 +433,8 @@ mod tests {
exp_date: Some(1672705545),
max_connections: 1,
status: Some(ProxyUserStatus::Expired),
ui_enabled: true
ui_enabled: true,
comment: None,
},
ProxyUserCredentials {
username: "Test3".to_string(),
@@ -445,7 +447,8 @@ mod tests {
exp_date: Some(1672705545),
max_connections: 1,
status: Some(ProxyUserStatus::Expired),
ui_enabled: true
ui_enabled: true,
comment: None,
},
ProxyUserCredentials {
username: "Test4".to_string(),
@@ -458,7 +461,8 @@ mod tests {
exp_date: Some(1672705545),
max_connections: 1,
status: Some(ProxyUserStatus::Expired),
ui_enabled: true
ui_enabled: true,
comment: None,
}
],
};