diff --git a/src/client/components/ToggleTip.tsx b/src/client/components/ToggleTip.tsx new file mode 100644 index 00000000..004ed5b8 --- /dev/null +++ b/src/client/components/ToggleTip.tsx @@ -0,0 +1,82 @@ +import { + Popover as ChakraPopover, + IconButton, + type IconButtonProps, + Portal, +} from "@chakra-ui/react" +import * as React from "react" +import { HiOutlineInformationCircle } from "react-icons/hi" + +export interface ToggleTipProps extends ChakraPopover.RootProps { + showArrow?: boolean + portalled?: boolean + portalRef?: React.RefObject + content?: React.ReactNode + contentProps?: ChakraPopover.ContentProps +} + +export const ToggleTip = React.forwardRef( + function ToggleTip(props, ref) { + const { + showArrow, + children, + portalled = true, + content, + contentProps, + portalRef, + ...rest + } = props + + return ( + + {children} + + + + {showArrow && ( + + + + )} + {content} + + + + + ) + }, +) + +export interface InfoTipProps extends Partial { + buttonProps?: IconButtonProps | undefined +} + +export const InfoTip = React.forwardRef( + function InfoTip(props, ref) { + const { children, buttonProps, ...rest } = props + return ( + + + + + + ) + }, +) diff --git a/src/client/components/msComponent/MSComponentList.tsx b/src/client/components/msComponent/MSComponentList.tsx new file mode 100644 index 00000000..706a5ad7 --- /dev/null +++ b/src/client/components/msComponent/MSComponentList.tsx @@ -0,0 +1,34 @@ +import React, { ComponentProps, useMemo, useState, forwardRef, Fragment } from "react" +import { CheckboxCard, Span, Stack, SegmentGroup, Text, Box, Center, Heading, Button, Separator, HStack, Flex, Badge } from '@chakra-ui/react'; +import { ComponentsApiJson } from "../../../core/Api"; +import { components } from "storybook/internal/components"; +import { MSComponentSummary } from "./MSComponentSummary"; + +export interface ComponentListProps { + components: ComponentsApiJson[] +} + +export const MSComponentList = (props: ComponentListProps) => { + const [shownType, setShownType] = useState("All"); + return ( + +
+ setShownType(val.value)}> + + + +
+ + {props.components.filter(x => { + if (shownType === 'All') { + return true; + } + if (shownType === 'Sources') { + return x.mode === 'source'; + } + return x.mode === 'client'; + }).map(x => )} + +
+ ) +} \ No newline at end of file diff --git a/src/client/components/msComponent/MSComponentQuickDisplay.tsx b/src/client/components/msComponent/MSComponentSummary.tsx similarity index 81% rename from src/client/components/msComponent/MSComponentQuickDisplay.tsx rename to src/client/components/msComponent/MSComponentSummary.tsx index d7d0397b..a9d25168 100644 --- a/src/client/components/msComponent/MSComponentQuickDisplay.tsx +++ b/src/client/components/msComponent/MSComponentSummary.tsx @@ -1,12 +1,13 @@ import React, { ComponentProps, useMemo, forwardRef, Fragment } from "react" import { Accordion, For, Span, Stack, Text, Box, Heading, AbsoluteCenter, Button, Separator, HStack, Flex, Badge, IconButton, Container, Collapsible, Card, LinkOverlay, LinkBox } from '@chakra-ui/react'; -import { ComponentCommonApi, ComponentCommonApiJson, isComponentClientApiJson, isComponentSourceApiJson } from "../../../core/Api"; -import { TextMuted } from "../TextMuted"; -import { isClientType } from "../../../backend/common/infrastructure/Atomic"; -import { capitalize } from "../../../core/StringUtils"; -import { ShortDateDisplay } from "../DateDisplay"; -import { ChevronRightButton } from "../icons/ChakraIcons"; -import { ChakraPlayer } from "../chakraPlayer/Player"; +import { ComponentCommonApi, ComponentCommonApiJson, isComponentClientApiJson, isComponentSourceApiJson } 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 { ChevronRightButton } from "../icons/ChakraIcons.js"; +import { ChakraPlayer } from "../chakraPlayer/Player.js"; +import { InfoTip } from "../ToggleTip.js"; export const MSComponentSummary = (props: { data: ComponentCommonApiJson }) => { const { @@ -26,7 +27,7 @@ export const MSComponentSummary = (props: { data: ComponentCommonApiJson }) => { body = ( { - Object.values(players).map((x) => ) + Object.values(players).map((x) => ) } ); @@ -50,7 +51,7 @@ export const MSComponentSummary = (props: { data: ComponentCommonApiJson }) => { - {isClient ? `(${data.mode}) ` : ''}{capitalize(data.type)} + {capitalize(data.mode)} {capitalize(data.type)} {body} @@ -83,10 +84,12 @@ const QuickStatsSource = (props: { data: ComponentCommonApiJson }) => { {queued} Queued - {deadLetterScrobbles} ({deadLetterScrobblesTotal}) Dead (Total) + {deadLetterScrobbles} ({deadLetterScrobblesTotal}) Dead + {countLive} Scrobbled + ) } diff --git a/src/core/Api.ts b/src/core/Api.ts index fab5bd59..bb64fce8 100644 --- a/src/core/Api.ts +++ b/src/core/Api.ts @@ -81,6 +81,8 @@ export type ComponentSourceApiBase = { export type ComponentSourceApi = ComponentCommonApi & ComponentSourceApiBase; export type ComponentSourceApiJson = Replace, string>; +export type ComponentsApiJson = ComponentSourceApiJson | ComponentClientApiJson; + export const isComponentSourceApiJson = (data: ComponentCommonApiJson): data is ComponentSourceApiJson => { return data.mode === 'source'; } diff --git a/src/stories/ComponentList.stories.tsx b/src/stories/ComponentList.stories.tsx new file mode 100644 index 00000000..e4cee57c --- /dev/null +++ b/src/stories/ComponentList.stories.tsx @@ -0,0 +1,62 @@ +import preview from "../../.storybook/preview.js"; +import React from 'react'; + +import { Container } from '@chakra-ui/react'; +import { MSComponentList } from "../client/components/msComponent/MSComponentList.js"; +import {Provider} from "../client/components/Provider"; +import { generateClientApiJson, generateSourceApiJson, generateSourcePlayerJson } from "../core/tests/utils/apiFixtures.js"; +import { generateArray } from "../core/DataUtils.js"; +import { faker } from "@faker-js/faker"; + +// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export +const meta = preview.meta({ + title: 'Examples/ComponentList', + component: MSComponentList, + parameters: { + // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout + layout: 'padded', + }, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs + tags: ['autodocs'], + // More on argTypes: https://storybook.js.org/docs/api/argtypes + // args: { + // data: generatePlayApiCommonDetailed(), + // }, + // argTypes: { + // componentType: { + // control: { type: 'select' }, + // options: ['source', 'client'], + // } + // }, + render: function Render(args) { + return () + }, +decorators: [ + (Story) => (), + ] + // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#story-args +}); + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Simple = meta.story({ + args: { + components: [generateSourceApiJson(), generateClientApiJson()] + } +}); + +export const Many = meta.story({ + args: { + components: [...generateArray(7, () => generateSourceApiJson()),...generateArray(4, () => generateClientApiJson())] + } +}); + +export const WithSourcePlayers = meta.story({ + args: { + components: [...generateArray(3, () => { + if(faker.datatype.boolean()) { + return generateSourceApiJson({players: {test: generateSourcePlayerJson(undefined, {art: true})}}); + } + return generateSourceApiJson(); + }),...generateArray(3, () => generateClientApiJson())] + } +}); \ No newline at end of file diff --git a/src/stories/ComponentSummary.stories.tsx b/src/stories/ComponentSummary.stories.tsx index 15e77673..a4586863 100644 --- a/src/stories/ComponentSummary.stories.tsx +++ b/src/stories/ComponentSummary.stories.tsx @@ -2,7 +2,7 @@ import preview from "../../.storybook/preview.js"; import React from 'react'; import { Container } from '@chakra-ui/react'; -import { MSComponentSummary } from "../client/components/msComponent/MSComponentQuickDisplay"; +import { MSComponentSummary } from "../client/components/msComponent/MSComponentSummary.js"; import {Provider} from "../client/components/Provider"; import { generateClientApiJson, generateSourceApiJson, generateSourcePlayerJson } from "../core/tests/utils/apiFixtures.js";