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

categorization support #100

Open
LuoShiXi opened this issue Sep 25, 2024 · 6 comments
Open

categorization support #100

LuoShiXi opened this issue Sep 25, 2024 · 6 comments

Comments

@LuoShiXi
Copy link

hi, master
please, any time support the categorization ?

https://jsonforms.io/docs/uischema/layouts#categorization

thanks

@Typiqally
Copy link

I have some code for this, just haven't had the time to make a proper PR for it. If you want it I can send you a gist.

@DrewHoo
Copy link
Contributor

DrewHoo commented Sep 26, 2024

@LuoShiXi Thanks for reaching out! Our original implementation included Categorization Layout elements, but we found that use case to be really thin (our original use case for Categorization elements was more easily solved outside of jsonforms) and it comes with a fairly complex implementation (ie validation at different steps of Categorization is an unsolved problem in the official renderer libraries AFAICT).
That said, could you tell us more about your use case? Maybe share some proposed validation and ui schemas? @Typiqally we'd love to hear about your use case as well!

@LuoShiXi
Copy link
Author

I have some code for this, just haven't had the time to make a proper PR for it. If you want it I can send you a gist.

Not for now, thanks

@LuoShiXi
Copy link
Author

@LuoShiXi Thanks for reaching out! Our original implementation included Categorization Layout elements, but we found that use case to be really thin (our original use case for Categorization elements was more easily solved outside of jsonforms) and it comes with a fairly complex implementation (ie validation at different steps of Categorization is an unsolved problem in the official renderer libraries AFAICT). That said, could you tell us more about your use case? Maybe share some proposed validation and ui schemas? @Typiqally we'd love to hear about your use case as well!

@Typiqally @DrewHoo Thanks for your reply. My usage scenario is that I have a k8s application deployment platform, and then there is a set of configurations for each application (for example: nginx, zookeeper, etc.). Because there are many applications, I hope to implement Tabs switching by grouping instead of full-screen sliding display, which can provide a better user experience. The following is a similar screenshot (it has not yet implemented the form mode)

image

And now I also tried to use Group to achieve my purpose. I hope that Group can be folded, but I found that it is not possible because it is a Card type instead of a folding panel.

@Typiqally
Copy link

Hi, our use case is quite similar. We have quite a large configuration form for configuring one of our systems in the cloud. To improve UX, it is better to separate the form into groups using tabs.

I used the following code to add this, it is based on the official Material UI renderers from JSONForms. Feel free to use it. I'm also happy to make a PR, but that'll have to wait until I have some more free time.

import { useState, useMemo } from 'react';
import {
  and,
  Categorization,
  Category,
  deriveLabelForUISchemaElement,
  getAjv,
  isVisible,
  rankWith,
  StatePropsOfLayout,
  Tester,
  UISchemaElement,
  uiTypeIs
} from '@jsonforms/core';
import {
  TranslateProps,
  useJsonForms,
  withJsonFormsLayoutProps,
  withTranslateProps
} from '@jsonforms/react';
import { Tabs } from 'antd';
import { AntDLayout, AntDLayoutProps } from './AntdLayoutRenderer.tsx';

export const isSingleLevelCategorization: Tester = and(
  uiTypeIs('Categorization'),
  (uischema: UISchemaElement): boolean => {
    const categorization = uischema as Categorization;

    return (
      categorization.elements &&
      categorization.elements.reduce(
        (acc, e) => acc && e.type === 'Category',
        true
      )
    );
  }
);

export const antdCategorizationLayoutTester = rankWith(
  2,
  and(uiTypeIs('Categorization'), isSingleLevelCategorization)
);

export interface AntdCategorizationLayoutRendererProps
  extends StatePropsOfLayout,
    TranslateProps {
  tabDirection?: 'ltr' | 'rtl';
  tabPosition?: 'left' | 'right' | 'top' | 'bottom';
  selected?: number;

  onChange?(selected: number, prevSelected: number): void;
}

const AntdCategorizationLayout = (
  {
    data,
    path,
    renderers,
    cells,
    schema,
    uischema,
    visible,
    enabled,
    selected,
    tabDirection = 'ltr',
    tabPosition = 'top',
    t,
    onChange
  }: AntdCategorizationLayoutRendererProps) => {
  const ctx = useJsonForms();
  const ajv = getAjv({ jsonforms: { ...ctx } });

  const categorization = uischema as Categorization;
  const [previousCategorization, setPreviousCategorization] =
    useState<Categorization>(uischema as Categorization);
  const [activeCategoryIndex, setActiveCategoryIndex] = useState<number>(
    selected ?? 0
  );
  const categories = useMemo(() => {
    return categorization.elements.filter(
      (category: Category | Categorization) => {
        return isVisible(category, data, path, ajv);
      }
    );
  }, [ajv, categorization.elements, data, path]);

  if (categorization !== previousCategorization) {
    setActiveCategoryIndex(0);
    setPreviousCategorization(categorization);
  }

  const childProps: AntDLayoutProps = {
    elements: [],
    schema,
    path,
    enabled,
    visible,
    renderers,
    cells
  };

  const onTabChange = (key: string) => {
    const keyIndex = parseInt(key);

    if (onChange) {
      onChange(keyIndex, activeCategoryIndex);
    }

    setActiveCategoryIndex(keyIndex);
  };

  const tabItems = useMemo(
    () =>
      categories.map((e: Category | Categorization, index: number) => ({
        key: index.toString(),
        label: deriveLabelForUISchemaElement(e, t),
        children: (
          <AntDLayout
            {...childProps}
            key={index}
            elements={categories[index] ? categories[index].elements : []}
          />
        )
      })),
    [categories, childProps, t]
  );

  if (!visible) {
    return null;
  }

  return (
    <Tabs
      items={tabItems}
      onTabClick={onTabChange}
      activeKey={activeCategoryIndex.toString()}
      direction={tabDirection}
      tabPosition={tabPosition}
    />
  );
};

export default withTranslateProps(
  withJsonFormsLayoutProps(AntdCategorizationLayout)
);

Please note that I used my own version of AntDLayout. This is simply a copy of the original one, as it isn't exported by the package.

@DrewHoo
Copy link
Contributor

DrewHoo commented Oct 1, 2024

Thank you @LuoShiXi and @Typiqally for the use case descriptions! I'm taking this to our team to review, and will get back to you by end of week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants