refactor: param sections are collapsible

This commit is contained in:
Carlos Valente
2025-01-24 21:16:44 +01:00
committed by Carlos Valente
parent b4caec0644
commit 4503d1e411
5 changed files with 133 additions and 78 deletions
@@ -1,6 +1,6 @@
.drawerFooter {
display: flex;
justify-content: end;
justify-content: end;
gap: $section-spacing;
button {
@@ -25,39 +25,11 @@
}
}
.label {
font-size: $inner-section-text-size;
color: $label-gray;
.sectionList {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.section {
color: $ui-white;
font-size: 1rem;
&:not(:first-child) {
margin-top: 2rem;
}
}
.fieldSet {
display: flex;
padding: $section-spacing 0;
flex-direction: column;
gap: $element-spacing;
margin-left: 0.5rem;
}
.title {
font-size: $inner-section-text-size;
display: block;
width: 100%;
}
.description {
font-size: $inner-section-text-size;
display: block;
color: $modal-note-color;
min-height: 100%;
gap: 2rem;
overflow-y: scroll;
padding-right: 0.5rem;
}
@@ -15,8 +15,8 @@ import { IoAlertCircle } from '@react-icons/all-files/io5/IoAlertCircle';
import useViewSettings from '../../../common/hooks-query/useViewSettings';
import ParamInput from './ParamInput';
import { isSection, ViewOption } from './types';
import { ViewOption } from './types';
import ViewParamsSection from './ViewParamsSection';
import style from './ViewParamsEditor.module.scss';
@@ -40,15 +40,15 @@ const getURLSearchParamsFromObj = (paramsObj: ViewParamsObj, paramFields: ViewOp
// Convert paramFields to an object that contains default values
const defaultValues: Record<string, string> = {};
paramFields.forEach((option) => {
if (!isSection(option)) {
paramFields.forEach((section) => {
section.options.forEach((option) => {
defaultValues[option.id] = String(option.defaultValue);
}
// extract persisted values
if ('type' in option && option.type === 'persist') {
newSearchParams.set(option.id, option.value);
}
// extract persisted values
if ('type' in option && option.type === 'persist') {
newSearchParams.set(option.id, option.value);
}
});
});
// compare which values are different from the default values
@@ -138,30 +138,15 @@ export default function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
This view style is being modified by a custom CSS file. <br />
</div>
)}
<form id='edit-params-form' onSubmit={onParamsFormSubmit}>
{viewOptions.map((option) => {
if (isSection(option)) {
return (
<div key={option.section} className={style.section}>
{option.section}
</div>
);
}
if (option.type === 'persist') {
return null;
}
return (
<div key={option.title} className={style.fieldSet}>
<label className={style.label}>
<span className={style.title}>{option.title}</span>
<span className={style.description}>{option.description}</span>
<ParamInput key={option.title} paramField={option} />
</label>
</div>
);
})}
<form id='edit-params-form' onSubmit={onParamsFormSubmit} className={style.sectionList}>
{viewOptions.map((section) => (
<ViewParamsSection
key={section.title}
title={section.title}
collapsible={section.collapsible}
options={section.options}
/>
))}
</form>
</DrawerBody>
@@ -0,0 +1,50 @@
.section {
color: $ui-white;
font-size: 1rem;
}
.sectionHeader {
display: flex;
justify-content: space-between;
align-items: center;
height: 2rem;
&.collapsible {
cursor: pointer;
&:hover {
border-bottom: 1px solid $white-10;
}
}
}
.label {
margin-top: $section-spacing;
font-size: $inner-section-text-size;
color: $label-gray;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.title {
font-size: $inner-section-text-size;
display: block;
width: 100%;
}
.description {
font-size: $inner-section-text-size;
display: block;
color: $modal-note-color;
}
.closed {
transition: rotate $transition-time-feedback;
rotate: 0deg;
}
.open {
transition: rotate $transition-time-feedback;
rotate: 180deg;
}
@@ -0,0 +1,54 @@
import { useLocalStorage } from '@mantine/hooks';
import { IoChevronDown } from '@react-icons/all-files/io5/IoChevronDown';
import { cx } from '../../utils/styleUtils';
import ParamInput from './ParamInput';
import { type ParamField } from './types';
import style from './ViewParamsSection.module.scss';
interface ViewParamsSectionProps {
title: string;
collapsible?: boolean;
options: ParamField[];
}
export default function ViewParamsSection(props: ViewParamsSectionProps) {
const { title, collapsible, options } = props;
const [collapsed, setCollapsed] = useLocalStorage({ key: `params-${title}`, defaultValue: false });
const handleCollapse = () => {
if (collapsible) {
setCollapsed((prev) => !prev);
}
};
return (
<section className={style.section}>
<div className={cx([style.sectionHeader, collapsible && style.collapsible])} onClick={handleCollapse}>
{title}
{collapsible && <IoChevronDown className={cx([collapsed ? style.closed : style.open])} />}
</div>
{!collapsed && (
<>
{options.map((option) => {
if (option.type === 'persist') {
return null;
}
return (
<label key={option.title} className={style.label}>
<span className={style.title}>{option.title}</span>
<span className={style.description}>{option.description}</span>
<ParamInput paramField={option} />
</label>
);
})}
</>
)}
</section>
);
}
@@ -1,7 +1,3 @@
type ParamSection = {
section: string;
};
type BaseField = {
id: string;
title: string;
@@ -28,11 +24,9 @@ type PersistedField = { type: 'persist'; defaultValue?: string; value: string };
export type ParamField = BaseField &
(OptionsField | MultiOptionsField | StringField | NumberField | BooleanField | ColourField | PersistedField);
export type ViewOption = ParamSection | ParamField;
/**
* Type assertion utility checks whether an entry is a section separator
*/
export function isSection(entry: ViewOption): entry is ParamSection {
return 'section' in entry;
}
export type ViewOption = {
title: string;
options: ParamField[];
collapsible?: boolean;
};