Skip to content

Commit

Permalink
Merge pull request #8 from Rhymlore/parameter-chips
Browse files Browse the repository at this point in the history
Created Parameter Chips for Parameters Page
  • Loading branch information
CannonLock authored Mar 22, 2024
2 parents 90d0ac7 + 3fca9f2 commit 1126d44
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 140 deletions.
71 changes: 71 additions & 0 deletions components/ParameterBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useState } from "react";
import { ParameterDetail } from "../utils/types";
import { Box, Paper, Typography, IconButton } from "@mui/material";
import { Link } from "@mui/icons-material";
import { useTheme } from "@mui/material/styles";

export const ParameterBox: React.FC<{ parameter: ParameterDetail }> = ({ parameter }) => {

const [hover, setHover] = useState(false);
const parts = parameter.name.split('.');
const groupName = parts.length > 1 ? parts.slice(0, -1).join('.') : null;
const parameterName = parts.length > 1 ? parts[parts.length - 1] : parameter.name;
const parameterId = parameter.name.replace(".", "-")
const theme = useTheme();

return (
<Box
id={parameterId}
sx={{ marginTop: ".8em" }}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
onClick={async () => {
// Copy link to clipboard
const url = new URL(window.location.href);
url.hash = parameterId;
await navigator.clipboard.writeText(url.toString());
}}
>
<Paper elevation={hover ? 3 : 1} sx={{ padding: "0.7em" }}>
<Box mb={1}>
<Box display={"flex"} justifyContent={"space-between"}>
<Typography variant="h5" >
{parameterName}
</Typography>
{hover && (
<IconButton size={"small"}>
<Link fontSize={"small"}/>
</IconButton>
)}
</Box>
{/* If groupName exists, display it on a separate line */}
{groupName && (
<Typography variant="subtitle2" component="div">
{groupName}
</Typography>
)}
</Box>
<Typography variant="body1" mb={1}>
{parameter.description}
</Typography>
<Box p={1} sx={{
borderRadius: "0.5em",
marginTop: "0.5em",
backgroundColor: theme.palette.mode === "light" ? "#e7e7e7" : "#666666"
}}>
{parameter.components && (
<Typography variant="body2">
Components: [{parameter.components.join(", ")}]
</Typography>
)}
<Typography variant="body2">
Type: {parameter.type}
</Typography>
<Typography variant="body2">
Default: {parameter.default === "" ? '""' : parameter.default.toString()}
</Typography>
</Box>
</Paper>
</Box>
);
};
38 changes: 38 additions & 0 deletions components/ParameterChips.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Chip, Stack, Divider, Typography, Box } from "@mui/material";
import { parameterGroups } from "../utils/types";
import ClearIcon from '@mui/icons-material/Clear';

interface ParameterChipsProps {
handleClick: (group: string) => void;
}

export const ParameterChips: React.FC<ParameterChipsProps> = ({handleClick}) => {
return (
<Box sx={{ marginBottom: 2, display:"flex", alignContent:"center", flexDirection:"column" }}>
<Typography variant="overline" sx={{ marginBottom: 1 }} align="center">Filter by component</Typography>
<Stack
direction="row"
spacing={2}
flexWrap="wrap"
useFlexGap
divider={<Divider orientation="vertical" flexItem />}
sx={{ marginBottom: 2 }}
justifyContent={"center"}
>
{parameterGroups.map((component, index) => (
<Chip
key={index}
label={component}
onClick={() => handleClick(component)}
/>
))}
<Chip
variant="outlined"
label="Clear Filter"
onClick={() => handleClick("")}
icon={<ClearIcon />}
/>
</Stack>
</Box>
);
};
210 changes: 88 additions & 122 deletions components/Parameters.tsx
Original file line number Diff line number Diff line change
@@ -1,142 +1,108 @@
import { Box, Paper, Typography, Autocomplete, TextField, Divider, IconButton} from '@mui/material';
import {Link} from "@mui/icons-material";
import { Box, Typography, Autocomplete, TextField, Divider, IconButton} from '@mui/material';
import React, {useState, useMemo} from "react";
import { ParametersArray, ParameterDetail } from "../utils/types";
import { ParameterBox } from "./ParameterBox";
import { ParameterChips } from './ParameterChips';
import { Link } from "@mui/icons-material";
import { DarkLightContainer } from '@/utils/darkLightContainer';

interface ParameterDetail {
components: string[];
default: boolean | string;
description: string;
name: string;
type: string;
}

interface Parameter {
[key: string]: ParameterDetail;
}

type ParametersArray = Parameter[];

const ParameterBox: React.FC<{ parameter: ParameterDetail }> = ({ parameter }) => {

const Parameters: React.FC<{ parameters: ParametersArray }> = ({ parameters }) => {
const [searchValue, setSearchValue] = useState('');
const [selectedComponent, setSelectedComponent] = useState<string | null>(null);
const [hover, setHover] = useState(false);

const parts = parameter.name.split('.');
const groupName = parts.length > 1 ? parts.slice(0, -1).join('.') : null;
const parameterName = parts.length > 1 ? parts[parts.length - 1] : parameter.name;
const parameterId = parameter.name.replace(".", "-")
const filteredParameters = useMemo(() => {
const searchLower = searchValue.toLowerCase();
return parameters.filter((parameter) => {
const parameterName = Object.keys(parameter)[0].toLowerCase();
const detail = Object.values(parameter)[0];

// Ensure detail.components is defined and is an array before calling includes.
const isComponentMatch = selectedComponent ? (Array.isArray(detail.components) && detail.components.includes(selectedComponent)) : true;
return parameterName.includes(searchLower) && isComponentMatch;
});
}, [searchValue, parameters, selectedComponent]);

const groupedParameters = useMemo(() => {
const groups: { [key: string]: ParameterDetail[] } = {};
filteredParameters.forEach((param) => {
const detail = Object.values(param)[0];
const parent = detail.name.split('.').slice(0, -1).join('.');
const group = parent || '';

if (!groups[group]) {
groups[group] = [];
}
groups[group].push(detail);
});
return groups;
}, [filteredParameters]);
return (
<Box
id={parameterId}
sx={{ marginTop: ".8em" }}
<DarkLightContainer>
<Box>
<Autocomplete
disablePortal
options={parameters.map((param) => Object.keys(param)[0])}
onInputChange={(_, value) => setSearchValue(value)}
sx={{ marginBottom: 2 }}
fullWidth={true}
freeSolo
renderInput={(params) => (
<TextField
{...params}
label="Search..."
value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}
/>
)}
/>
<ParameterChips handleClick={(component) => {
setSelectedComponent(component);
setSearchValue('');
}} />
{Object.entries(groupedParameters).map(([group, groupParams]) => (
<Box
key={group}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
onClick={async () => {
// Copy link to clipboard
const url = new URL(window.location.href);
url.hash = parameterId;
await navigator.clipboard.writeText(url);
url.hash = group
await navigator.clipboard.writeText(url.toString());
}}
>
<Paper elevation={hover ? 3 : 1} sx={{ padding: "0.7em" }}>
<Box mb={1}>
<Box display={"flex"} justifyContent={"space-between"}>
<Typography variant="h5" >
{parameterName}
</Typography>
{hover && (
<IconButton size={"small"}>
<Link fontSize={"small"}/>
</IconButton>
)}
</Box>
{/* If groupName exists, display it on a separate line */}
{groupName && (
<Typography variant="subtitle2" component="div">
{groupName}
</Typography>
>
<Typography
sx={{
marginTop: ".5em"

}}
variant="h4"
gutterBottom
id={group}
>
{group}
{hover && group && group !== "" && (
<IconButton size={"small"}>
<Link fontSize={"small"}/>
</IconButton>
)}
</Box>
<Typography variant="body1" mb={1}>
{parameter.description}
</Typography>
<Box p={1} bgcolor={"#e7e7e7"}>
{parameter.components && (
<Typography variant="body2">
Components: [{parameter.components.join(", ")}]
</Typography>
)}
<Typography variant="body2">
Type: {parameter.type}
</Typography>
<Typography variant="body2">
Default: {parameter.default === "" ? '""' : parameter.default.toString()}
</Typography>
</Box>
</Paper>
</Box>
);
};

const Parameters: React.FC<{ parameters: ParametersArray }> = ({ parameters }) => {
const [searchValue, setSearchValue] = useState('');

const filteredParameters = useMemo(() => {
const searchLower = searchValue.toLowerCase();
return parameters.filter((parameter) => {
const parameterName = Object.keys(parameter)[0].toLowerCase();
return parameterName.includes(searchLower);
});
}, [searchValue, parameters]);

const groupedParameters = useMemo(() => {
const groups: { [key: string]: ParameterDetail[] } = {};
filteredParameters.forEach((param) => {
const detail = Object.values(param)[0];
const parent = detail.name.split('.').slice(0, -1).join('.');
const group = parent || '';

if (!groups[group]) {
groups[group] = [];
}
groups[group].push(detail);
});
return groups;
}, [filteredParameters]);

return (
<Box>
<Autocomplete
disablePortal
options={parameters.map((param) => Object.keys(param)[0])}
onInputChange={(_, value) => setSearchValue(value)}
sx={{marginBottom: 2}}
fullWidth={true}
renderInput={(params) => (
<TextField
{...params}
label="Search..."
value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}
/>
)}
/>
{Object.entries(groupedParameters).map(([group, groupParams]) => (
<React.Fragment key={group}>
<Typography sx={{ marginTop: ".5em" }} variant="h4" gutterBottom>{group}</Typography>

{group && group !== "" && (
<Divider sx={{ height: "0.5em", backgroundColor: "#0885ff", width: "100%", borderRadius: "0.5em" }} />
<Divider sx={{ height: "0.5em", backgroundColor: "#0885ff", width: "100%", borderRadius: "0.5em" }} />
)}
{groupParams.map((param, index) => (
<ParameterBox key={index} parameter={param} />
<ParameterBox key={index} parameter={param} />
))}
</React.Fragment>
</Box>
))}
{filteredParameters.length === 0 && searchValue ? (
<Typography variant="h5">No results found</Typography>
{filteredParameters.length === 0 && (searchValue || selectedComponent) ? (
<Typography variant="h5">No results found</Typography>
) : null}
</Box>
);
};

export default Parameters;
</Box>
</DarkLightContainer>
);
};

export default Parameters;
Loading

0 comments on commit 1126d44

Please sign in to comment.