mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-31 03:02:24 +01:00
refactored web ui video player
refactored responsiveness
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -44,6 +44,9 @@ button {
|
||||
|
||||
border-radius:var(--border-radius);
|
||||
|
||||
@media (min-width: common.$media-size-mobile) {
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.vert-group {
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
--ok-color: #21e821;
|
||||
|
||||
--card-background-color: #202733; //#282828;
|
||||
--card-background-color-darker: #1c2026;
|
||||
--card-color: #ffffff;
|
||||
--card-border-color: #47495d;
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user