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

Transform CategorizationRenderer from class to functional #2120

Merged
merged 14 commits into from
Jan 22, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -24,55 +24,65 @@
*/
import React, { useMemo } from 'react';
import {
Categorization,
Category,
Categorization,
deriveLabelForUISchemaElement,
Translator,
isVisible,
} from '@jsonforms/core';
import { isCategorization } from './tester';
import { AjvProps } from '../../util';

const getCategoryClassName = (
category: Category,
selectedCategory: Category
): string => (selectedCategory === category ? 'selected' : '');

export interface CategorizationProps {
categorization: Categorization;
elements: (Category | Categorization)[];
selectedCategory: Category;
depth: number;
data: any;
onSelect: any;
subcategoriesClassName: string;
groupClassName: string;
t: Translator;
}

export const CategorizationList = ({
categorization,
selectedCategory,
elements,
data,
depth,
onSelect,
subcategoriesClassName,
groupClassName,
t,
}: CategorizationProps) => {
ajv,
}: CategorizationProps & AjvProps) => {
const filteredElements = useMemo(() => {
return elements.filter((category: Category | Categorization) =>
isVisible(category, data, undefined, ajv)
);
}, [elements, data, ajv]);

const categoryLabels = useMemo(
() =>
categorization.elements.map((cat) =>
deriveLabelForUISchemaElement(cat, t)
),
[categorization, t]
() => filteredElements.map((cat) => deriveLabelForUISchemaElement(cat, t)),
[filteredElements, t]
);

return (
<ul className={subcategoriesClassName}>
{categorization.elements.map((category, idx) => {
{filteredElements.map((category, idx) => {
if (isCategorization(category)) {
return (
<li key={categoryLabels[idx]} className={groupClassName}>
<span>{categoryLabels[idx]}</span>
<CategorizationList
categorization={category}
selectedCategory={selectedCategory}
elements={category.elements}
data={data}
ajv={ajv}
depth={depth + 1}
onSelect={onSelect}
subcategoriesClassName={subcategoriesClassName}
Expand All @@ -85,7 +95,7 @@ export const CategorizationList = ({
return (
<li
key={categoryLabels[idx]}
onClick={onSelect(category)}
onClick={onSelect(idx)}
className={getCategoryClassName(category, selectedCategory)}
>
<span>{categoryLabels[idx]}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,89 +22,102 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
import React from 'react';
import React, { useState } from 'react';
import type { Categorization, Category, LayoutProps } from '@jsonforms/core';
import {
RendererComponent,
TranslateProps,
withJsonFormsLayoutProps,
withTranslateProps,
} from '@jsonforms/react';
import { CategorizationList } from './CategorizationList';
import { SingleCategory } from './SingleCategory';
import { isCategorization } from './tester';
import { withVanillaControlProps } from '../../util';
import type { VanillaRendererProps } from '../../index';
import { withAjvProps, withVanillaControlProps } from '../../util';
import type { AjvProps, VanillaRendererProps } from '../../util';

export interface CategorizationState {
selectedCategory: Category;
}

class CategorizationRenderer extends RendererComponent<
LayoutProps & VanillaRendererProps & TranslateProps,
CategorizationState
> {
onCategorySelected = (category: Category) => () => {
return this.setState({ selectedCategory: category });
};
interface CategorizationProps {
selected?: number;
onChange?(selected: number, prevSelected: number): void;
}

/**
* @inheritDoc
*/
render() {
const { uischema, visible, getStyleAsClassName, t } = this.props;
const categorization = uischema as Categorization;
const classNames = getStyleAsClassName('categorization');
const masterClassNames = getStyleAsClassName('categorization.master');
const detailClassNames = getStyleAsClassName('categorization.detail');
const selectedCategory = this.findCategory(categorization);
const subcategoriesClassName = getStyleAsClassName(
'category.subcategories'
);
const groupClassName = getStyleAsClassName('category.group');
export const CategorizationRenderer = ({
data,
uischema,
schema,
path,
selected,
t,
visible,
getStyleAsClassName,
onChange,
ajv,
}: LayoutProps &
VanillaRendererProps &
TranslateProps &
CategorizationProps &
AjvProps) => {
const categorization = uischema as Categorization;
const elements = categorization.elements as (Category | Categorization)[];
const classNames = getStyleAsClassName('categorization');
const masterClassNames = getStyleAsClassName('categorization.master');
const detailClassNames = getStyleAsClassName('categorization.detail');
const subcategoriesClassName = getStyleAsClassName('category.subcategories');
const groupClassName = getStyleAsClassName('category.group');

return (
<div
className={classNames}
hidden={visible === null || visible === undefined ? false : !visible}
>
<div className={masterClassNames}>
<CategorizationList
categorization={categorization}
selectedCategory={selectedCategory}
depth={0}
onSelect={this.onCategorySelected}
subcategoriesClassName={subcategoriesClassName}
groupClassName={groupClassName}
t={t}
/>
</div>
<div className={detailClassNames}>
<SingleCategory
category={selectedCategory}
schema={this.props.schema}
path={this.props.path}
/>
</div>
</div>
);
}
const [previousCategorization, setPreviousCategorization] =
useState<Categorization>(uischema as Categorization);
const [activeCategory, setActiveCategory] = useState<number>(selected ?? 0);

private findCategory(categorization: Categorization): Category {
const category = categorization.elements[0];
const safeCategory =
activeCategory >= categorization.elements.length ? 0 : activeCategory;

if (this.state && this.state.selectedCategory) {
return this.state.selectedCategory;
}
if (categorization !== previousCategorization) {
setActiveCategory(0);
setPreviousCategorization(categorization);
}

if (isCategorization(category)) {
return this.findCategory(category);
const onCategorySelected = (categoryIndex: number) => () => {
if (onChange) {
return onChange(categoryIndex, safeCategory);
}
return setActiveCategory(categoryIndex);
};

return category;
}
}
return (
<div
className={classNames}
hidden={visible === null || visible === undefined ? false : !visible}
>
<div className={masterClassNames}>
<CategorizationList
elements={elements}
selectedCategory={elements[safeCategory] as Category}
data={data}
ajv={ajv}
depth={0}
onSelect={onCategorySelected}
subcategoriesClassName={subcategoriesClassName}
groupClassName={groupClassName}
t={t}
/>
</div>
<div className={detailClassNames}>
<SingleCategory
category={elements[safeCategory] as Category}
schema={schema}
path={path}
key={safeCategory}
/>
</div>
</div>
);
};

export default withVanillaControlProps(
withTranslateProps(withJsonFormsLayoutProps(CategorizationRenderer))
export default withAjvProps(
withVanillaControlProps(
withTranslateProps(withJsonFormsLayoutProps(CategorizationRenderer))
)
);
110 changes: 1 addition & 109 deletions packages/vanilla-renderers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,89 +22,6 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
import { RankedTester } from '@jsonforms/core';

import {
BooleanCell,
booleanCellTester,
DateCell,
dateCellTester,
dateTimeCellTester,
EnumCell,
enumCellTester,
IntegerCell,
integerCellTester,
NumberCell,
numberCellTester,
SliderCell,
sliderCellTester,
TextAreaCell,
textAreaCellTester,
TextCell,
textCellTester,
TimeCell,
timeCellTester,
} from './cells';

import {
InputControl,
inputControlTester,
RadioGroupControl,
radioGroupControlTester,
OneOfRadioGroupControl,
oneOfRadioGroupControlTester,
} from './controls';

import {
ArrayControl,
arrayControlTester,
Categorization,
categorizationTester,
LabelRenderer,
labelRendererTester,
TableArrayControl,
tableArrayControlTester,
} from './complex';

import {
GroupLayout,
groupTester,
HorizontalLayout,
horizontalLayoutTester,
VerticalLayout,
verticalLayoutTester,
} from './layouts';
import DateTimeCell from './cells/DateTimeCell';

export interface WithClassname {
className?: string;
}

/**
* Additional renderer props specific to vanilla renderers.
*/
export interface VanillaRendererProps extends WithClassname {
classNames?: { [className: string]: string };
/**
* Returns all classes associated with the given style.
* @param {string} string the style name
* @param args any additional args necessary to calculate the classes
* @returns {string[]} array of class names
*/
getStyle?(string: string, ...args: any[]): string[];

/**
* Returns all classes associated with the given style as a single class name.
* @param {string} string the style name
* @param args any additional args necessary to calculate the classes
* @returns {string[]} array of class names
*/
getStyleAsClassName?(string: string, ...args: any[]): string;
}

export interface WithChildren {
children: any;
}

export * from './actions';
export * from './controls';
Expand All @@ -114,29 +31,4 @@ export * from './layouts';
export * from './reducers';
export * from './util';
export * from './styles';

export const vanillaRenderers: { tester: RankedTester; renderer: any }[] = [
{ tester: inputControlTester, renderer: InputControl },
{ tester: radioGroupControlTester, renderer: RadioGroupControl },
{ tester: oneOfRadioGroupControlTester, renderer: OneOfRadioGroupControl },
{ tester: arrayControlTester, renderer: ArrayControl },
{ tester: labelRendererTester, renderer: LabelRenderer },
{ tester: categorizationTester, renderer: Categorization },
{ tester: tableArrayControlTester, renderer: TableArrayControl },
{ tester: groupTester, renderer: GroupLayout },
{ tester: verticalLayoutTester, renderer: VerticalLayout },
{ tester: horizontalLayoutTester, renderer: HorizontalLayout },
];

export const vanillaCells: { tester: RankedTester; cell: any }[] = [
{ tester: booleanCellTester, cell: BooleanCell },
{ tester: dateCellTester, cell: DateCell },
{ tester: dateTimeCellTester, cell: DateTimeCell },
{ tester: enumCellTester, cell: EnumCell },
{ tester: integerCellTester, cell: IntegerCell },
{ tester: numberCellTester, cell: NumberCell },
{ tester: sliderCellTester, cell: SliderCell },
{ tester: textAreaCellTester, cell: TextAreaCell },
{ tester: textCellTester, cell: TextCell },
{ tester: timeCellTester, cell: TimeCell },
];
export * from './renderers';
Loading