mirror of
https://github.com/FoxxMD/multi-scrobbler.git
synced 2026-07-09 17:28:28 +00:00
feat(ui): Implement placeholder component state actions
This commit is contained in:
parent
91ba24e165
commit
145960ca04
4 changed files with 51 additions and 17 deletions
|
|
@ -112,11 +112,11 @@ export const ComponentStateBadge = (props: ComponentProps<typeof Badge> & {
|
|||
let sep: React.JSX.Element | undefined;
|
||||
if(suffix !== undefined) {
|
||||
if(separator === true) {
|
||||
sep = <Separator orientation="vertical" borderColor="var(--chakra-colors-color-palette-muted)" height="5"/>;
|
||||
sep = <Separator orientation="vertical" borderColor="var(--chakra-colors-color-palette-muted)" ml="2" height="5"/>;
|
||||
} else if(separator !== false) {
|
||||
sep = separator;
|
||||
}
|
||||
}
|
||||
|
||||
return <Badge variant="surface" colorPalette={badgeColor} {...rest}><HStack>{componentStateToFriendly(componentState)}{sep}{suffix}</HStack></Badge>
|
||||
return <Badge variant="surface" colorPalette={badgeColor} {...rest}><HStack gap="0">{componentStateToFriendly(componentState)}{sep}{suffix}</HStack></Badge>
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
LuArrowUp,
|
||||
LuArrowDown,
|
||||
LuEllipsis,
|
||||
LuEllipsisVertical,
|
||||
LuArrowBigRight,
|
||||
LuBug,
|
||||
LuPower,
|
||||
|
|
@ -100,6 +101,8 @@ export const IdleIcon = (props: {animated?: boolean} & ComponentProps<typeof RiZ
|
|||
|
||||
export const EllipsisIcon = LuEllipsis;
|
||||
export const EllipsisButton = makeIconButton(EllipsisIcon);
|
||||
export const EllipsisVerticalIcon = LuEllipsisVertical;
|
||||
export const EllipsisVerticalButton = makeIconButton(EllipsisVerticalIcon);
|
||||
|
||||
export const FatArrowRight = LuArrowBigRight;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import React, { ComponentProps, useState } from "react"
|
||||
import { SegmentGroup, Switch, Span, ButtonGroup, Button, Box, Heading, Skeleton, Wrap, HStack, Stack, Flex, Card, LinkOverlay, SkeletonText, BadgeProps, Badge } from '@chakra-ui/react';
|
||||
import { SegmentGroup, Switch, Portal, Group, Span, Menu, ButtonGroup, Button, Box, Heading, Skeleton, Wrap, HStack, Stack, Flex, Card, LinkOverlay, SkeletonText, BadgeProps, Badge, MenuItemProps } from '@chakra-ui/react';
|
||||
import { COMPONENT_STATE, ComponentClientApiJson, ComponentCommonApiJson, ComponentsApiJson, ComponentSourceApiJson, ComponentState, componentStateToFriendly, isComponentClientApiJson, isComponentSourceApiJson, MsSseEvent, MsSseEventPayload } from "../../../core/Api.js";
|
||||
import { TextMuted } from "../TextMuted.js";
|
||||
import { isClientType } from "../../../backend/common/infrastructure/Atomic.js";
|
||||
import { capitalize } from "../../../core/StringUtils.js";
|
||||
import { ShortDateDisplay } from "../DateDisplay.js";
|
||||
import { CheckIcon, ChevronLeftButton, ChevronRightButton, EyeButton, IdleIcon, PowerButton, PowerOffButton, PowerOffIcon, XIcon } from "../icons/ChakraIcons.js";
|
||||
import { CheckIcon, ChevronLeftButton, ChevronRightButton, EllipsisButton, EyeButton, EyeClosedIcon, EyeIcon, IdleIcon, PowerButton, PowerIcon, PowerOffButton, PowerOffIcon, RetryIcon, XIcon } from "../icons/ChakraIcons.js";
|
||||
import { ChakraPlayer, ChakraPlayerFetchable, PlayersContainer, PlayersContainerFetchable } from "../chakraPlayer/Player.js";
|
||||
import { InfoTip, ToggleTip, Tooltip } from "../ToggleTip.js";
|
||||
import { QueryFunctionContext, queryOptions, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
|
@ -27,14 +27,13 @@ import { shortTodayAwareFormat } from "../../../core/TimeUtils.js";
|
|||
import { durationToHuman } from "../../../backend/utils.js";
|
||||
import { tanQueries } from "../../queries/index.js";
|
||||
import { MSErrorBoundary } from "../ErrorBoundary.js";
|
||||
import { IconType } from "react-icons/lib";
|
||||
|
||||
export const ComponentBackButton = (props: ComponentProps<typeof ChevronLeftButton> = {}) => {
|
||||
return (
|
||||
<LinkOverlay asChild>
|
||||
<Link to={`/next`}>
|
||||
<ChevronLeftButton variant="ghost" iconProps={{style: {width: 'unset', height: 'unset', fontSize: "2em"}}} {...props} />
|
||||
</Link>
|
||||
</LinkOverlay>
|
||||
<Link to={`/next`}>
|
||||
<ChevronLeftButton variant="ghost" iconProps={{style: {width: 'unset', height: 'unset', fontSize: "2em"}}} {...props} />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +94,15 @@ const ComponentSettings = (props: { data: ComponentCommonApiJson }) => {
|
|||
)
|
||||
}
|
||||
|
||||
const componentStateMenuItem = (Icon: IconType, value: string, name?: string) => (props: Pick<MenuItemProps, 'disabled'> = {}) => {
|
||||
return (<Menu.Item key={value} value={value} {...props}><Icon/><Box flex="1">{name ?? capitalize(value)}</Box></Menu.Item>);
|
||||
}
|
||||
const MenuItemRestart = componentStateMenuItem(RetryIcon, 'restart');
|
||||
const MenuItemStop = componentStateMenuItem(PowerOffIcon, 'stop');
|
||||
const MenuItemStart = componentStateMenuItem(PowerIcon, 'start');
|
||||
const MenuItemMute = componentStateMenuItem(EyeClosedIcon, 'mute', 'Ignore')
|
||||
const MenuItemUnmute = componentStateMenuItem(EyeIcon, 'unmute', 'Monitor');
|
||||
|
||||
const primaryActionProps: ComponentProps<typeof PowerOffButton> = {
|
||||
margin: "1px",
|
||||
variant: "subtle",
|
||||
|
|
@ -107,28 +115,52 @@ export const ComponentStateBadgeActionable = (props: Omit<ComponentProps<typeof
|
|||
live,
|
||||
...rest
|
||||
} = props;
|
||||
let suffix: React.JSX.Element | null;
|
||||
let primaryAction: React.JSX.Element | null;
|
||||
let suffix: React.JSX.Element | undefined;
|
||||
let primaryAction: React.JSX.Element | undefined;
|
||||
let menuElm: React.JSX.Element | undefined;
|
||||
let menuItems: React.JSX.Element[] = [];
|
||||
let badgeProps: BadgeProps = {};
|
||||
switch(props.data.state) {
|
||||
case COMPONENT_STATE.RUNNING:
|
||||
primaryAction = <PowerOffButton {...primaryActionProps}/>
|
||||
menuItems = [<MenuItemStop/>,<MenuItemRestart/>,<MenuItemMute/>];
|
||||
break;
|
||||
case COMPONENT_STATE.MUTED:
|
||||
primaryAction = <EyeButton {...primaryActionProps}/>
|
||||
primaryAction = <EyeButton {...primaryActionProps}/>;
|
||||
menuItems = [<MenuItemStop/>,<MenuItemRestart/>,<MenuItemUnmute/>];
|
||||
break;
|
||||
case COMPONENT_STATE.INITIALIZING:
|
||||
// no actions while init is occurring
|
||||
break;
|
||||
default:
|
||||
// otherwise generic start action for all non-running states
|
||||
primaryAction = <PowerButton {...primaryActionProps}/>
|
||||
primaryAction = <PowerButton {...primaryActionProps}/>;
|
||||
menuItems = [<MenuItemStart/>];
|
||||
}
|
||||
if(primaryAction !== undefined) {
|
||||
if(menuItems.length > 0) {
|
||||
menuElm = (
|
||||
<Menu.Root positioning={{ placement: "bottom-end" }}>
|
||||
<Group attached>
|
||||
{primaryAction}
|
||||
<Menu.Trigger asChild>
|
||||
<EllipsisButton {...primaryActionProps}/>
|
||||
</Menu.Trigger>
|
||||
</Group>
|
||||
<Portal>
|
||||
<Menu.Positioner>
|
||||
<Menu.Content>
|
||||
{menuItems}
|
||||
</Menu.Content>
|
||||
</Menu.Positioner>
|
||||
</Portal>
|
||||
</Menu.Root>
|
||||
);
|
||||
suffix = menuElm;
|
||||
} else if(primaryAction !== undefined) {
|
||||
suffix = primaryAction;
|
||||
}
|
||||
if(suffix !== undefined || primaryAction !== undefined) {
|
||||
//badgeProps.paddingRight = 0;
|
||||
badgeProps.paddingRight = 0;
|
||||
}
|
||||
|
||||
return <ComponentStateBadge size="lg" maxWidth="fit-content" {...badgeProps} separator suffix={suffix} {...rest}/>;
|
||||
|
|
@ -170,7 +202,6 @@ export const ComponentDetailedDesktop = (props: {data?: ComponentCommonApiJson,
|
|||
<HStack truncate>{sleepingRender}{props.data.status}</HStack>
|
||||
</Wrap>
|
||||
<Flex justifyContent="flex-end" rowGap="6" flexDirection="row-reverse" wrap="wrap">
|
||||
<ComponentSettings data={props.data}/>
|
||||
<Box marginEnd="auto"><MSComponentStats {...props}/></Box>
|
||||
</Flex>
|
||||
{props.live ? <PlayersContainerFetchable data={props.data}/> : <PlayersContainer data={props.data} live={props.live}/>}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export const componentStateToFriendly = (state: ComponentState) => {
|
|||
case 1:
|
||||
return 'Running';
|
||||
case 2:
|
||||
return 'Muted';
|
||||
return 'Ignored';
|
||||
case 3:
|
||||
return 'Idle';
|
||||
case 4:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue