From c59bc52f8ba47888ff22f86bb6577ecbff06bf13 Mon Sep 17 00:00:00 2001 From: Philippe Fanaro Date: Fri, 30 Jun 2023 20:13:40 -0300 Subject: [PATCH 1/2] first steps towards autocomplete --- .../src/Autocomplete/Autocomplete.tsx | 67 +++++++++++++ .../src/Autocomplete/autocompleteClasses.ts | 97 +++++++++++++++++++ .../src/Autocomplete/useAutocomplete.ts | 0 3 files changed, 164 insertions(+) create mode 100644 packages/material/src/Autocomplete/Autocomplete.tsx create mode 100644 packages/material/src/Autocomplete/autocompleteClasses.ts create mode 100644 packages/material/src/Autocomplete/useAutocomplete.ts diff --git a/packages/material/src/Autocomplete/Autocomplete.tsx b/packages/material/src/Autocomplete/Autocomplete.tsx new file mode 100644 index 000000000..ffb000e23 --- /dev/null +++ b/packages/material/src/Autocomplete/Autocomplete.tsx @@ -0,0 +1,67 @@ +import composeClasses from "../../../base/src/composeClasses"; +import IconButton from "../IconButton/IconButton"; +import styled from "../styles/styled"; +import capitalize from "../utils/capitalize"; + +// const useUtilityClasses = (ownerState) => { +// const { +// classes, +// disablePortal, +// expanded, +// focused, +// fullWidth, +// hasClearIcon, +// hasPopupIcon, +// inputFocused, +// popupOpen, +// size, +// } = ownerState; + +// const slots = { +// root: [ +// 'root', +// expanded && 'expanded', +// focused && 'focused', +// fullWidth && 'fullWidth', +// hasClearIcon && 'hasClearIcon', +// hasPopupIcon && 'hasPopupIcon', +// ], +// inputRoot: ['inputRoot'], +// input: ['input', inputFocused && 'inputFocused'], +// tag: ['tag', `tagSize${capitalize(size)}`], +// endAdornment: ['endAdornment'], +// clearIndicator: ['clearIndicator'], +// popupIndicator: ['popupIndicator', popupOpen && 'popupIndicatorOpen'], +// popper: ['popper', disablePortal && 'popperDisablePortal'], +// paper: ['paper'], +// listbox: ['listbox'], +// loading: ['loading'], +// noOptions: ['noOptions'], +// option: ['option'], +// groupLabel: ['groupLabel'], +// groupUl: ['groupUl'], +// }; + +// return composeClasses(slots, getAutocompleteUtilityClass, classes); +// }; + +const AutocompleteEndAdornment = styled("div", { + name: "MuiAutocomplete", + slot: "EndAdornment", + overridesResolver: (props, styles) => styles.endAdornment, +})({ + // We use a position absolute to support wrapping tags. + position: "absolute", + right: 0, + top: "calc(50% - 14px)", // Center vertically +}); + +const AutocompleteClearIndicator = styled(IconButton, { + name: "MuiAutocomplete", + slot: "ClearIndicator", + overridesResolver: (props, styles) => styles.clearIndicator, +})({ + marginRight: -2, + padding: 4, + visibility: "hidden", +}); diff --git a/packages/material/src/Autocomplete/autocompleteClasses.ts b/packages/material/src/Autocomplete/autocompleteClasses.ts new file mode 100644 index 000000000..d4bfbb54e --- /dev/null +++ b/packages/material/src/Autocomplete/autocompleteClasses.ts @@ -0,0 +1,97 @@ +import { generateUtilityClass } from "@suid/base"; +import { generateUtilityClasses } from "@suid/base"; + +export interface AutocompleteClasses { + /** Styles applied to the root element. */ + root: string; + /** Styles applied to the root element if `fullWidth={true}`. */ + fullWidth: string; + /** State class applied to the root element if the listbox is displayed. */ + expanded: string; + /** State class applied to the root element if focused. */ + focused: string; + /** Styles applied to the option elements if they are keyboard focused. */ + focusVisible: string; + /** Styles applied to the tag elements, e.g. the chips. */ + tag: string; + /** Styles applied to the tag elements, e.g. the chips if `size="small"`. */ + tagSizeSmall: string; + /** Styles applied to the tag elements, e.g. the chips if `size="medium"`. */ + tagSizeMedium: string; + /** Styles applied when the popup icon is rendered. */ + hasPopupIcon: string; + /** Styles applied when the clear icon is rendered. */ + hasClearIcon: string; + /** Styles applied to the Input element. */ + inputRoot: string; + /** Styles applied to the input element. */ + input: string; + /** Styles applied to the input element if the input is focused. */ + inputFocused: string; + /** Styles applied to the endAdornment element. */ + endAdornment: string; + /** Styles applied to the clear indicator. */ + clearIndicator: string; + /** Styles applied to the popup indicator. */ + popupIndicator: string; + /** Styles applied to the popup indicator if the popup is open. */ + popupIndicatorOpen: string; + /** Styles applied to the popper element. */ + popper: string; + /** Styles applied to the popper element if `disablePortal={true}`. */ + popperDisablePortal: string; + /** Styles applied to the Paper component. */ + paper: string; + /** Styles applied to the listbox component. */ + listbox: string; + /** Styles applied to the loading wrapper. */ + loading: string; + /** Styles applied to the no option wrapper. */ + noOptions: string; + /** Styles applied to the option elements. */ + option: string; + /** Styles applied to the group's label elements. */ + groupLabel: string; + /** Styles applied to the group's ul elements. */ + groupUl: string; +} + +export type AutocompleteClassKey = keyof AutocompleteClasses; + +export function getAutocompleteUtilityClass(slot: string): string { + return generateUtilityClass("MuiAutocomplete", slot); +} + +const autocompleteClasses: AutocompleteClasses = generateUtilityClasses( + "MuiAutocomplete", + [ + "root", + "expanded", + "fullWidth", + "focused", + "focusVisible", + "tag", + "tagSizeSmall", + "tagSizeMedium", + "hasPopupIcon", + "hasClearIcon", + "inputRoot", + "input", + "inputFocused", + "endAdornment", + "clearIndicator", + "popupIndicator", + "popupIndicatorOpen", + "popper", + "popperDisablePortal", + "paper", + "listbox", + "loading", + "noOptions", + "option", + "groupLabel", + "groupUl", + ] +); + +export default autocompleteClasses; diff --git a/packages/material/src/Autocomplete/useAutocomplete.ts b/packages/material/src/Autocomplete/useAutocomplete.ts new file mode 100644 index 000000000..e69de29bb From 494516b5a4f2955f8e911cd183f9b21419951dc8 Mon Sep 17 00:00:00 2001 From: Philippe Fanaro Date: Sat, 1 Jul 2023 09:19:09 -0300 Subject: [PATCH 2/2] trying to add the utils functions... failed --- .../src/useAutocomplete/useAutocomplete.ts | 9 +++++++ packages/base/src/utils/setRef.ts | 24 +++++++++++++++++++ packages/base/src/utils/useEnhancedEffect.ts | 7 ++++++ packages/base/src/utils/useEventCallback.ts | 21 ++++++++++++++++ .../src/Autocomplete/useAutocomplete.ts | 0 5 files changed, 61 insertions(+) create mode 100644 packages/base/src/useAutocomplete/useAutocomplete.ts create mode 100644 packages/base/src/utils/setRef.ts create mode 100644 packages/base/src/utils/useEnhancedEffect.ts create mode 100644 packages/base/src/utils/useEventCallback.ts delete mode 100644 packages/material/src/Autocomplete/useAutocomplete.ts diff --git a/packages/base/src/useAutocomplete/useAutocomplete.ts b/packages/base/src/useAutocomplete/useAutocomplete.ts new file mode 100644 index 000000000..b197cc8b1 --- /dev/null +++ b/packages/base/src/useAutocomplete/useAutocomplete.ts @@ -0,0 +1,9 @@ +/* eslint-disable no-constant-condition */ +import { + unstable_setRef as setRef, + unstable_useEventCallback as useEventCallback, + unstable_useControlled as useControlled, + unstable_useId as useId, + usePreviousProps, +} from "@mui/utils"; +import * as React from "react"; diff --git a/packages/base/src/utils/setRef.ts b/packages/base/src/utils/setRef.ts new file mode 100644 index 000000000..9a69e0093 --- /dev/null +++ b/packages/base/src/utils/setRef.ts @@ -0,0 +1,24 @@ +/** + * TODO v5: consider making it private + * + * passes {value} to {ref} + * + * WARNING: Be sure to only call this inside a callback that is passed as a ref. + * Otherwise, make sure to cleanup the previous {ref} if it changes. See + * https://github.com/mui/material-ui/issues/13539 + * + * Useful if you want to expose the ref of an inner component to the public API + * while still using it inside the component. + * @param ref A ref callback or ref object. If anything falsy, this is a no-op. + */ +export default function setRef( + ref: // TODO: What ref type to use here? + any | ((instance: T | null) => void) | null | undefined, + value: T | null +): void { + if (typeof ref === "function") { + ref(value); + } else if (ref) { + ref.current = value; + } +} diff --git a/packages/base/src/utils/useEnhancedEffect.ts b/packages/base/src/utils/useEnhancedEffect.ts new file mode 100644 index 000000000..9f3113db2 --- /dev/null +++ b/packages/base/src/utils/useEnhancedEffect.ts @@ -0,0 +1,7 @@ +import { createEffect } from "solid-js"; + +// TODO: `useLayoutEffect` +const useEnhancedEffect = + typeof window !== "undefined" ? createEffect : createEffect; + +export default useEnhancedEffect; diff --git a/packages/base/src/utils/useEventCallback.ts b/packages/base/src/utils/useEventCallback.ts new file mode 100644 index 000000000..25712b6a2 --- /dev/null +++ b/packages/base/src/utils/useEventCallback.ts @@ -0,0 +1,21 @@ +import createRef from "../../../system/src/createRef"; +import useEnhancedEffect from "./useEnhancedEffect"; + +/** + * https://github.com/facebook/react/issues/14099#issuecomment-440013892 + */ +export default function useEventCallback( + fn: (...args: Args) => Return +): (...args: Args) => Return { + const ref = createRef(fn); + useEnhancedEffect(() => { + ref.current = fn; + }); + return React.useCallback( + (...args: Args) => + // @ts-expect-error hide `this` + // tslint:disable-next-line:ban-comma-operator + (0, ref.current!)(...args), + [] + ); +} diff --git a/packages/material/src/Autocomplete/useAutocomplete.ts b/packages/material/src/Autocomplete/useAutocomplete.ts deleted file mode 100644 index e69de29bb..000000000