Skip to content

Commit

Permalink
feat: customized page enhancement (#890)
Browse files Browse the repository at this point in the history
* feat: support navigation list

* feat: replace checkbox with switch

* feat: support collapsible for object value
  • Loading branch information
embbnux authored Sep 27, 2024
1 parent 57cf51a commit aa7f215
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 84 deletions.
90 changes: 65 additions & 25 deletions src/components/CustomizedPanel/CustomizedForm/Fields/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import {
RcListItemAvatar,
RcListItemSecondaryAction,
RcAvatar,
RcIcon,
styled,
palette2,
css
} from '@ringcentral/juno';

import { ArrowRight } from '@ringcentral/juno-icon';

const StyledList = styled(RcList)`
margin: 0 -16px;
`;
Expand All @@ -32,6 +35,59 @@ const StyledAvatar = styled(RcAvatar)`
}
`;

const NavigationIcon = styled(RcIcon)`
margin: 8px 0;
`;

function Item({
item,
disabled,
selected,
onClick,
showIconAsAvatar,
showAsNavigation,
}) {
return (
<StyledItem
key={item.const}
disabled={disabled}
selected={selected}
onClick={onClick}
>
{
item.icon ? (
<RcListItemAvatar>
<StyledAvatar
size="xsmall"
src={item.icon}
$round={showIconAsAvatar}
/>
</RcListItemAvatar>
) : null
}
<RcListItemText
primary={item.title}
secondary={item.description}
/>
{
(item.meta || showAsNavigation) ? (
<RcListItemSecondaryAction>
{item.meta}
{
showAsNavigation ? (
<NavigationIcon
symbol={ArrowRight}
size="large"
/>
) : null
}
</RcListItemSecondaryAction>
) : null
}
</StyledItem>
);
}

export function List({
schema,
uiSchema,
Expand All @@ -43,40 +99,24 @@ export function List({
typeof uiSchema['ui:showIconAsAvatar'] === 'undefined' ?
true :
uiSchema['ui:showIconAsAvatar'];
const showAsNavigation =
typeof uiSchema['ui:navigation'] === 'undefined' ?
false :
uiSchema['ui:navigation'];
return (
<StyledList>
{schema.oneOf.map((item) => (
<StyledItem
<Item
key={item.const}
item={item}
disabled={disabled}
selected={formData === item.const}
onClick={() => {
onChange(item.const);
}}
>
{
item.icon ? (
<RcListItemAvatar>
<StyledAvatar
size="xsmall"
src={item.icon}
$round={showIconAsAvatar}
/>
</RcListItemAvatar>
) : null
}
<RcListItemText
primary={item.title}
secondary={item.description}
/>
{
item.meta ? (
<RcListItemSecondaryAction>
{item.meta}
</RcListItemSecondaryAction>
) : null
}
</StyledItem>
showIconAsAvatar={showIconAsAvatar}
showAsNavigation={showAsNavigation}
/>
))}
</StyledList>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { RcGrid as Grid } from '@ringcentral/juno';
import React, { useState } from 'react';
import { RcGrid as Grid, RcDivider as Divider } from '@ringcentral/juno';
import {
FormContextType,
ObjectFieldTemplateProps,
Expand All @@ -24,7 +24,7 @@ export default function ObjectFieldTemplate<
required,
disabled,
readonly,
uiSchema,
uiSchema = {},
idSchema,
schema,
formData,
Expand All @@ -42,6 +42,15 @@ export default function ObjectFieldTemplate<
const {
ButtonTemplates: { AddButton },
} = registry.templates;
const gridStyle: {
paddingLeft?: string;
marginTop: string;
} = { marginTop: '10px' };
if (title) {
gridStyle.paddingLeft = '16px';
}
const collapsible = uiOptions.collapsible || false;
const [extended, setExtended] = useState<boolean>(collapsible ? false : true);
return (
<>
{title && (
Expand All @@ -52,6 +61,13 @@ export default function ObjectFieldTemplate<
schema={schema}
uiSchema={uiSchema}
registry={registry}
extended={extended}
onClick={() => {
if (!collapsible) {
return;
}
setExtended(!extended);
}}
/>
)}
{description && (
Expand All @@ -63,39 +79,46 @@ export default function ObjectFieldTemplate<
registry={registry}
/>
)}
<Grid container={true} spacing={2} style={{ marginTop: '10px' }}>
{properties.map((element, index) => {
// Remove the <Grid> if the inner element is hidden as the <Grid>
// itself would otherwise still take up space.
if (element.hidden) {
return element.content;
}
const uiSchemaProperty = uiSchema?.[element.name] || {};
const style = uiSchemaProperty?.['ui:bulletedList'] ? {
marginLeft: '16px',
} : {
marginBottom: '10px'
};
return (
<Grid item={true} xs={12} key={index} style={style}>
{element.content}
</Grid>
);
})}
{canExpand<T, S, F>(schema, uiSchema, formData) && (
<Grid container justifyContent='flex-end'>
<Grid item={true}>
<AddButton
className='object-property-expand'
onClick={onAddClick(schema)}
disabled={disabled || readonly}
uiSchema={uiSchema}
registry={registry}
/>
</Grid>
{
extended && (
<Grid container={true} spacing={2} style={gridStyle}>
{properties.map((element, index) => {
// Remove the <Grid> if the inner element is hidden as the <Grid>
// itself would otherwise still take up space.
if (element.hidden) {
return element.content;
}
const uiSchemaProperty = uiSchema?.[element.name] || {};
const style = uiSchemaProperty?.['ui:bulletedList'] ? {
marginLeft: '16px',
} : {
marginBottom: '10px'
};
return (
<Grid item={true} xs={12} key={index} style={style}>
{element.content}
</Grid>
);
})}
{canExpand<T, S, F>(schema, uiSchema, formData) && (
<Grid container justifyContent='flex-end'>
<Grid item={true}>
<AddButton
className='object-property-expand'
onClick={onAddClick(schema)}
disabled={disabled || readonly}
uiSchema={uiSchema}
registry={registry}
/>
</Grid>
</Grid>
)}
</Grid>
)}
</Grid>
)
}
{
collapsible && (<Divider />)
}
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,58 @@
import React from 'react';
import {
RcBox as Box,
RcDivider as Divider,
RcTypography as Typography,
RcIcon,
RcDivider as Divider,
styled,
css,
} from '@ringcentral/juno';
import { ArrowDown2, ArrowUp2 } from '@ringcentral/juno-icon';

import { FormContextType, TitleFieldProps, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';

const StyledTitle = styled(Typography)`
flex: 1;
`;

const StyledBox = styled(Box)<{ $clickable: boolean }>`
display: flex;
align-items: center;
flex-direction: row;
${({ $clickable }) => $clickable && css`
cursor: pointer;
`};
`;

const StyledIcon = styled(RcIcon)`
margin: 4px 0;
`;

export default function TitleField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>({
id,
title,
uiSchema = {},
extended = false,
onClick = undefined
}: TitleFieldProps<T, S, F>) {
const collapsible = uiSchema['ui:collapsible'] || false;
return (
<Box id={id} mb={1} mt={1}>
<Typography variant="title1">{title}</Typography>
<Divider />
</Box>
<StyledBox
id={id} mb={1} mt={1}
$clickable={collapsible}
onClick={onClick}
>
<StyledTitle variant="subheading1">{title}</StyledTitle>
{
collapsible ? (
<StyledIcon
symbol={extended ? ArrowUp2 : ArrowDown2}
/>
) : null
}
{
collapsible ? null : (<Divider />)
}
</StyledBox>
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { FocusEvent } from 'react';
import {
RcCheckbox as Checkbox,
RcFormControlLabel as FormControlLabel,
RcSwitch,
styled,
css,
}from '@ringcentral/juno';
import {
ariaDescribedByIds,
Expand All @@ -15,6 +16,29 @@ import {
WidgetProps,
} from '@rjsf/utils';

const SwitchContainer = styled.div`
width: 100%;
.MuiFormControlLabel-label {
flex: 1;
}
.MuiFormControlLabel-root {
margin-left: 0;
width: 100%;
}
.RcSwitch-root {
margin: 2px;
}
`;

const StyledSwitch = styled(RcSwitch)`
${(props) => props.readOnly && css`
opacity: 0.5;
`}
`;

export default function CheckboxWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
Expand Down Expand Up @@ -62,23 +86,25 @@ export default function CheckboxWidget<
registry={registry}
/>
)}
<FormControlLabel
control={
<Checkbox
id={id}
name={id}
checked={typeof value === 'undefined' ? false : Boolean(value)}
required={required}
disabled={disabled || readonly}
autoFocus={autofocus}
onChange={_onChange}
onBlur={_onBlur}
onFocus={_onFocus}
aria-describedby={ariaDescribedByIds<T>(id)}
/>
}
label={labelValue(label, hideLabel, false)}
/>
<SwitchContainer>
<StyledSwitch
id={id}
name={id}
checked={typeof value === 'undefined' ? false : Boolean(value)}
required={required}
disabled={disabled || readonly}
autoFocus={autofocus}
onChange={_onChange}
onBlur={_onBlur}
onFocus={_onFocus}
aria-describedby={ariaDescribedByIds<T>(id)}
formControlLabelProps={{
labelPlacement: 'start',
}}
label={labelValue(label, hideLabel, false)}
readOnly={readonly}
/>
</SwitchContainer>
</>
);
}

0 comments on commit aa7f215

Please sign in to comment.