Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Merge #515 first) initia interaction: function panel and selected function info accordion #521

Merged
merged 6 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#521](https://github.com/alleslabs/celatone-frontend/pull/521) Initia module interaction function panel and selected function info accordion
- [#515](https://github.com/alleslabs/celatone-frontend/pull/515) Initia select module drawer wireup
- [#494](https://github.com/alleslabs/celatone-frontend/pull/494) Initia select module drawer UI
- [#490](https://github.com/alleslabs/celatone-frontend/pull/490) Add initia module interaction page
Expand Down
8 changes: 6 additions & 2 deletions src/lib/components/LabelText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TooltipInfo } from "./Tooltip";

interface LabelTextProps extends FlexProps {
label: string;
labelWeight?: number;
tooltipText?: string;
children: string | JSX.Element;
helperText1?: string;
Expand All @@ -13,6 +14,7 @@ interface LabelTextProps extends FlexProps {

export const LabelText = ({
label,
labelWeight = 500,
tooltipText,
children,
helperText1,
Expand All @@ -21,13 +23,15 @@ export const LabelText = ({
}: LabelTextProps) => (
<Flex direction="column" gap={1} {...flexProps}>
<Flex align="center" gap={1}>
<Text variant="body2" color="text.dark" fontWeight={500}>
<Text variant="body2" color="text.dark" fontWeight={labelWeight}>
{label}
</Text>
{tooltipText && <TooltipInfo label={tooltipText} />}
</Flex>
{typeof children === "string" ? (
<Text variant="body2">{children}</Text>
<Text variant="body2" overflowWrap="anywhere">
{children}
</Text>
) : (
children
)}
Expand Down
8 changes: 5 additions & 3 deletions src/lib/components/PageContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import type { BoxProps } from "@chakra-ui/react";
import { Box } from "@chakra-ui/react";
import type { ReactNode } from "react";

type PageContainerProps = {
interface PageContainerProps extends BoxProps {
children: ReactNode;
};
}

const PageContainer = ({ children }: PageContainerProps) => (
const PageContainer = ({ children, ...containerProps }: PageContainerProps) => (
<Box
as="main"
p={{ base: "16px", md: "48px" }}
overflowX="hidden"
minH="inherit"
{...containerProps}
>
{children}
</Box>
Expand Down
61 changes: 35 additions & 26 deletions src/lib/components/module/FunctionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,48 @@ import { Flex, Text } from "@chakra-ui/react";
import { DotSeparator } from "../DotSeparator";
import { CustomIcon } from "../icon";
import { Tooltip } from "lib/components/Tooltip";
import type { ExposedFunction, Visibility } from "lib/types";
import type { ExposedFunction } from "lib/types";
import { getVisibilityIcon } from "lib/utils";
import { checkAvailability } from "lib/utils/abi";

type CardVariant = "common" | "disabled" | "selected";

interface FunctionCardProps {
variant?: CardVariant;
isSelected?: boolean;
exposedFn: ExposedFunction;
onFunctionSelect: (fn: ExposedFunction) => void;
}

const getIcon = (visibility: Visibility) => {
switch (visibility) {
case "friend":
return <CustomIcon name="friends" color="gray.600" boxSize={3} />;
case "script":
return <CustomIcon name="code" color="gray.600" boxSize={3} />;
case "public":
default:
return <CustomIcon name="website" color="gray.600" boxSize={3} />;
}
};

const disabledStyle: { [key in `${boolean}`]: FlexProps } = {
true: {
const cardStyles: { [key in CardVariant]: FlexProps } = {
common: {
bgColor: "gray.800",
_hover: { bg: "gray.700" },
cursor: "pointer",
borderColor: "transparent",
},
disabled: {
bgColor: "gray.900",
_hover: { bg: "gray.900" },
cursor: "not-allowed",
pointerEvents: "none",
borderColor: "gray.700",
},
false: {
bgColor: "gray.800",
selected: {
bgColor: "gray.700",
_hover: { bg: "gray.700" },
cursor: "pointer",
borderColor: "transparent",
borderColor: "gray.600",
},
};

export const FunctionCard = ({
variant = "common",
exposedFn,
onFunctionSelect,
}: FunctionCardProps) => {
const { is_entry: isEntry, is_view: isView, visibility, name } = exposedFn;
const disabled = !isEntry || visibility !== "public";
const { is_view: isView, visibility, name } = exposedFn;
const disabled = !checkAvailability(exposedFn);

return (
<Flex
Expand All @@ -56,7 +57,7 @@ export const FunctionCard = ({
gap={1}
border="1px solid"
onClick={() => onFunctionSelect(exposedFn)}
{...disabledStyle[String(disabled) as `${boolean}`]}
{...cardStyles[disabled ? "disabled" : variant]}
>
<Flex gap={1} justifyContent="space-between" w="full">
<Flex gap={1} alignItems="center">
Expand All @@ -70,21 +71,29 @@ export const FunctionCard = ({
</Text>
</Flex>
<Flex alignItems="center" gap={2}>
{!disabled && (
{!isView && (
<>
<Tooltip
bg="primary.dark"
label="Only functions with “is_entry: true” and “visibility: public” are interactable through Celatone’s module interactions."
label="Only execute functions with “is_entry: true” and “visibility: public” are interactable through Celatone’s module interactions."
>
<Flex>
<CustomIcon name="check" color="success.main" boxSize={3} />
<Flex pointerEvents="auto" onClick={(e) => e.stopPropagation()}>
{disabled ? (
<CustomIcon name="close" color="gray.600" boxSize={3} />
) : (
<CustomIcon name="check" color="success.main" boxSize={3} />
)}
</Flex>
</Tooltip>
<DotSeparator bg="gray.600" />
</>
)}
<Flex alignItems="center" gap={1}>
{getIcon(visibility)}
<CustomIcon
name={getVisibilityIcon(visibility)}
color="gray.600"
boxSize={3}
/>
<Text variant="body3" color="text.dark" textTransform="capitalize">
{visibility}
</Text>
Expand Down
3 changes: 3 additions & 0 deletions src/lib/components/module/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./CountBadge";
export * from "./FunctionCard";
export * from "./ModuleCard";
8 changes: 5 additions & 3 deletions src/lib/components/state/EmptyState.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ImageProps, TextProps } from "@chakra-ui/react";
import type { FlexProps, ImageProps, TextProps } from "@chakra-ui/react";
import { Flex, Heading, Text } from "@chakra-ui/react";
import type { ReactElement } from "react";

Expand All @@ -11,7 +11,8 @@ export interface EmptyStateProps {
message: string | ReactElement;
heading?: string;
withBorder?: boolean;
my?: number;
my?: FlexProps["my"];
py?: FlexProps["py"];
textVariant?: TextProps["variant"];
}

Expand All @@ -22,10 +23,11 @@ export const EmptyState = ({
heading,
withBorder = false,
my = 12,
py = 8,
textVariant = "body1",
}: EmptyStateProps) => (
<Flex
py={8}
py={py}
my={my}
direction="column"
borderY={withBorder ? "1px solid" : undefined}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface ModuleEmptyStateProps {
h?: FlexProps["h"];
p?: FlexProps["p"];
}

export const ModuleEmptyState = ({
description = "Available functions for selected modules will display here",
imageWidth = "160px",
Expand Down Expand Up @@ -41,3 +42,7 @@ export const ModuleEmptyState = ({
</Flex>
);
};

export const NoImageEmptyState = ({ desc }: { desc: string }) => (
<ModuleEmptyState h="fit-content" p={4} hasImage={false} description={desc} />
);
63 changes: 63 additions & 0 deletions src/lib/pages/interaction/component/common/FunctionAccordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Flex,
Text,
} from "@chakra-ui/react";
import type { Dispatch, SetStateAction } from "react";

import { CountBadge, FunctionCard } from "lib/components/module";
import type { ExposedFunction, Option } from "lib/types";

import { NoImageEmptyState } from "./EmptyState";

interface FunctionAccordionProps {
filteredFns: ExposedFunction[];
isEmpty: boolean;
triggerText: string;
selectedFn: Option<ExposedFunction>;
setSelectedFn: Dispatch<SetStateAction<Option<ExposedFunction>>>;
}

export const FunctionAccordion = ({
filteredFns,
isEmpty,
triggerText,
selectedFn,
setSelectedFn,
}: FunctionAccordionProps) => {
return (
<AccordionItem bg="background.main" py={1}>
<h6>
<AccordionButton>
<Flex align="center" justify="space-between" gap={2}>
<Text variant="body2" color="text.dark">
{triggerText}
</Text>
<CountBadge count={filteredFns.length} variant="common" />
</Flex>
<AccordionIcon color="gray.600" ml="auto" />
</AccordionButton>
</h6>
<AccordionPanel py={3} px={0}>
<Flex flexDirection="column" gap={1}>
{filteredFns.length ? (
filteredFns.map((fn) => (
<FunctionCard
variant={selectedFn?.name === fn.name ? "selected" : "common"}
exposedFn={fn}
onFunctionSelect={setSelectedFn}
/>
))
) : (
<NoImageEmptyState
desc={isEmpty ? "No function" : "No matching function found"}
/>
)}
</Flex>
</AccordionPanel>
</AccordionItem>
);
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { FlexProps } from "@chakra-ui/react";
import { Flex, Heading } from "@chakra-ui/react";
import type { Dispatch, SetStateAction } from "react";
import { useRef } from "react";

import { MotionBox } from "lib/components/MotionBox";
import type { Option } from "lib/types";
Expand All @@ -14,6 +13,7 @@ export enum InteractionTabs {
interface InteractionTypeSwitchProps extends FlexProps {
currentTab: Option<InteractionTabs>;
disabled?: boolean;
counts: [Option<number>, Option<number>];
onTabChange: Dispatch<SetStateAction<Option<InteractionTabs>>>;
}

Expand All @@ -22,17 +22,18 @@ const tabs = Object.values(InteractionTabs);
export const InteractionTypeSwitch = ({
currentTab,
disabled = false,
counts,
onTabChange,
...flexProps
}: InteractionTypeSwitchProps) => {
const tabRefs = useRef<(HTMLDivElement | null)[]>([]);
const activeIndex = currentTab ? tabs.indexOf(currentTab) : 0;

return (
<Flex
border="1px solid var(--chakra-colors-gray-700)"
borderRadius="4px"
p={1}
h="32px"
direction="row"
align="center"
position="relative"
Expand All @@ -43,9 +44,6 @@ export const InteractionTypeSwitch = ({
<MotionBox
key={tab}
w="full"
ref={(el) => {
tabRefs.current[idx] = el;
}}
cursor="pointer"
p="2px 10px"
variants={{
Expand All @@ -61,17 +59,17 @@ export const InteractionTypeSwitch = ({
textAlign="center"
>
<Heading as="h6" variant="h6" fontSize="14px">
{tab}
{tab} {counts[idx] && `(${counts[idx]})`}
</Heading>
</MotionBox>
))}
<MotionBox
h={tabRefs.current[activeIndex]?.clientHeight}
w={tabRefs.current[activeIndex]?.clientWidth}
h="calc(100% - 8px)"
w="calc(50% - 4px)"
position="absolute"
borderRadius="2px"
backgroundColor="primary.dark"
animate={{ left: `${tabRefs.current[activeIndex]?.offsetLeft ?? 0}px` }}
animate={{ left: activeIndex === 0 ? "4px" : "50%" }}
transition={{
type: "spring",
stiffness: "250",
Expand Down
12 changes: 12 additions & 0 deletions src/lib/pages/interaction/component/common/ModuleContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Flex, chakra } from "@chakra-ui/react";

export const ModuleContainer = chakra(Flex, {
baseStyle: {
flexDirection: "column",
bgColor: "gray.900",
alignItems: "center",
justifyContent: "center",
borderRadius: 8,
gap: 4,
},
});
3 changes: 3 additions & 0 deletions src/lib/pages/interaction/component/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./EmptyState";
export * from "./InteractionTypeSwitch";
export * from "./ModuleContainer";
Loading