react-md icon indicating copy to clipboard operation
react-md copied to clipboard

v5.0.0 and v6.0.0 Release Target

Open mlaursen opened this issue 4 years ago • 0 comments

Due to the already large size of the v4.0.0 release, I'm going to move some of the targeted changes for the v5.0.0 release target instead.

v5.0.0 Release Target

Improved @react-md/menu API

A new and improved @react-md/menu API. Instead of passing a list of items, directly use the <MenuItem> components within the children.

 function Example() {
   return (
-    <DropdownMenu
-      id="dropdown-menu-1"
-      items={[
-        'Item 1',
-        null,
-        0,
-        1,
-        'separator',
-        { children: 'Item 2' },
-        { role: 'separator', inset: true },
-        { rightAddon: <HomeSVGIcon />, children: 'Home' },
-        {
-          leftAddon: <InfoOutlineSVGIcon />,
-          children: <span>Custom content</span>,
-        },
-        { href: '#', children: 'Link' },
-        <MenuItem>Custom item</MenuItem>,
-        <MenuItemLink href="#">Link</MenuItemLink>,
-      ]}
-    >
-      Options...
+    <DropdownMenu id="dropdown-menu-1" buttonChildren="Options...">
+      <MenuItem>Item 1</MenuItem>
+      {someBoolean && <MenuItem>Conditional Item</MenuItem>}
+      <MenuItem>0</MenuItem>
+      <MenuItem>1</MenuItem>
+      <Divider />
+      <MenuItem>Item 2</MenuItem>
+      <Divider inset />
+      <MenuItem rightAddon={<HomeSVGIcon />}>Home</MenuItem>
+      <MenuItem leftAddon={<InfoOutlineSVGIcon />}>
+        <span>Custom content</span>
+      </MenuItem>
+      <MenuItemLink href="#">Link</MenuItemLink>
+      <MenuItem>Custom item</MenuItem>
+      <MenuItemLink href="#">Link</MenuItemLink>
     </DropdownMenu>
   );
 }

These changes make it so that you can now create resuable components to handle specific actions. i.e.

function CreateNewFolder(): ReactElement {
  return <MenuItem onClick={() => /* some click behavior */}>Folder</MenuItem>;
}

function Example(): ReactElement {
  return (
    <DropdownMenu id="some-menu-id" buttonChildren="New">
      <CreateNewFolder />
      <AnotherAction />
      <AndAnotherAction />
    </DropdownMenu>
  );
}

Additional changes:

  • There will be some new hooks to handle creating custom menus
  • Support hover mode by default

v6.0.0 Release Target

Improved Select API and Behavior

What I'm mostly trying to accomplish with these changes are:

  • fix the terrible typescript types so that you don't need to do: onChange={(value) => setValue(value as SomeTypeUnion)}
  • Allow the Select to handle validation like the TextField and TextArea components
    • mostly want the required behavior to start working

The Select component will now render an invisible <select> element and all the <option>s instead of using an <input type="hidden" value={CURRENT_VALUE} />. This change makes it so that the Select component can now correctly handle form validation.

I'm still working out the API, but I think it'll mimic the new Menu API since it follows how you'd use a native <select> and allows for elements to be rendered without first creating a list of options:

<Select {...props}>
  {options.map(({ label, value }) => <Option value={value}>{label}</Option>
</Select>
const [value, setValue] = useState("");

<Select {...props}>
  <OptGroup label="A">
   // the `label` might be optional since I can automatically pull the text content via refs
    <Option label="Alabama" value="AL" />
    <Option value="AK"><strong>Alaska</strong></Option>
  </OptGroup>
  <Divider />
  <OptGroup label={<span>C</span>}>
    <Option label="California" value="CA" disabled />
  </OptGroup>
  <Option label="Delaware" value="DE" />
</Select>

Another alternative is something like this (less preferred now):

interface SearchableListboxOption {
  label: string;
  value: string;
  // this would be rendered instead of the `label` if exists. The `label` string is required since this is how the "type to focus" behavior is implemented to mimic the native `<select>` element
  children?: ReactNode;
  disabled?: boolean;

  // Like other places if any of these keys are part of the `ListItemProps`, they will be passed correctly and rendered (like addons, secondaryText, etc)
  [key: string]: unknown;
}
type IgnoredListboxOption = Record<string, unknown> | ReactElement;
type ListboxOption = SearchableListboxOption  | IgnoredListboxOption;

const options: readonly ListboxOption[] = [
  { label: "Some label", value: "a" },
  { label: "Another Label', value: "b" },
  <Divider key="divider-1" />,
  { label: "Final Label", value: "c", disabled: true },
];

const [value, setValue] = useState("");

<Select
  id="some-select-id"
  options={options}
  label="Some label"
  value={value}
  setValue={setValue}
  required
  // this is optional (ha) if the default behavior doesn't work for your use-case. I
  renderOptions={(option) => {
    // this will pretty much be the default implementation
    return <Option {...option}>{option.children ?? option.label}</Option>
  }} 
/>

Quick video example of changes:

https://user-images.githubusercontent.com/3920850/143302148-9a37f442-7e3d-4ebc-a9b1-1f91632047fb.mov

Remove @react-md/autocomplete package

With all the new Select changes listed above, I think it would be better just to integrate the AutoComplete component into the @react-md/form package with a new useAutoComplete hook and AutoComplete component.

mlaursen avatar Nov 24 '21 19:11 mlaursen