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

Autocomplete First Attempt (Not Working Yet) #235

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/base/src/useAutocomplete/useAutocomplete.ts
Original file line number Diff line number Diff line change
@@ -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";
24 changes: 24 additions & 0 deletions packages/base/src/utils/setRef.ts
Original file line number Diff line number Diff line change
@@ -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<T>(
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;
}
}
7 changes: 7 additions & 0 deletions packages/base/src/utils/useEnhancedEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createEffect } from "solid-js";

// TODO: `useLayoutEffect`
const useEnhancedEffect =
typeof window !== "undefined" ? createEffect : createEffect;

export default useEnhancedEffect;
21 changes: 21 additions & 0 deletions packages/base/src/utils/useEventCallback.ts
Original file line number Diff line number Diff line change
@@ -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<Args extends unknown[], Return>(
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),
[]
);
}
67 changes: 67 additions & 0 deletions packages/material/src/Autocomplete/Autocomplete.tsx
Original file line number Diff line number Diff line change
@@ -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",
});
97 changes: 97 additions & 0 deletions packages/material/src/Autocomplete/autocompleteClasses.ts
Original file line number Diff line number Diff line change
@@ -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;