Skip to content

Commit

Permalink
feat(material): add FilledInput component
Browse files Browse the repository at this point in the history
  • Loading branch information
juanrgm committed Apr 9, 2022
1 parent 2ab060b commit 593ffd8
Show file tree
Hide file tree
Showing 8 changed files with 345 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/kind-experts-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@suid/material": patch
---

Add `FilledInput` component
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
| Drawer ||
| Fab ||
| Fade ||
| FilledInput | |
| FilledInput | |
| FormControl ||
| FormControlLabel ||
| FormGroup ||
Expand Down
226 changes: 226 additions & 0 deletions packages/material/src/FilledInput/FilledInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import InputBase from "../InputBase";
import {
rootOverridesResolver as inputBaseRootOverridesResolver,
inputOverridesResolver as inputBaseInputOverridesResolver,
InputBaseRoot,
InputBaseComponent as InputBaseInput,
} from "../InputBase/InputBase";
import { PaletteColorName } from "../styles/createPalette";
import styled from "../styles/styled";
import { FilledInputTypeMap } from "./FilledInputProps";
import filledInputClasses, {
getFilledInputUtilityClass,
} from "./filledInputClasses";
import createComponentFactory from "@suid/base/createComponentFactory";
import { ComponentProps } from "@suid/types";
import deepmerge from "@suid/utils/deepmerge";
import { createMemo, mergeProps } from "solid-js";

const $ = createComponentFactory<FilledInputTypeMap>()({
name: "MuiFilledInput",
propDefaults: ({ set }) =>
set({
components: {},
fullWidth: false,
inputComponent: "input",
multiline: false,
type: "text",
hiddenLabel: false,
}),
selfPropNames: ["classes", "disableUnderline", "hiddenLabel"],
utilityClass: getFilledInputUtilityClass,
slotClasses: (ownerState) => ({
root: ["root", !ownerState.disableUnderline && "underline"],
input: ["input"],
}),
});

const FilledInputRoot = styled(InputBaseRoot, {
/*shouldForwardProp: (prop) =>
rootShouldForwardProp(prop) || prop === "classes",*/
name: "MuiFilledInput",
slot: "Root",
overridesResolver: (props, styles) => {
const { ownerState } = props;
return [
...inputBaseRootOverridesResolver(props as any, styles),
!ownerState.disableUnderline && styles.underline,
];
},
})<ComponentProps<FilledInputTypeMap>>(({ theme, ownerState }) => {
const light = theme.palette.mode === "light";
const bottomLineColor = light
? "rgba(0, 0, 0, 0.42)"
: "rgba(255, 255, 255, 0.7)";
const backgroundColor = light
? "rgba(0, 0, 0, 0.06)"
: "rgba(255, 255, 255, 0.09)";
return {
position: "relative",
backgroundColor,
// [review] unitless
borderTopLeftRadius: `${theme.shape.borderRadius}px`,
borderTopRightRadius: `${theme.shape.borderRadius}px`,
transition: theme.transitions.create("background-color", {
duration: theme.transitions.duration.shorter,
easing: theme.transitions.easing.easeOut,
}),
"&:hover": {
backgroundColor: light
? "rgba(0, 0, 0, 0.09)"
: "rgba(255, 255, 255, 0.13)",
// Reset on touch devices, it doesn't add specificity
"@media (hover: none)": {
backgroundColor,
},
},
[`&.${filledInputClasses.focused}`]: {
backgroundColor,
},
[`&.${filledInputClasses.disabled}`]: {
backgroundColor: light
? "rgba(0, 0, 0, 0.12)"
: "rgba(255, 255, 255, 0.12)",
},
...(!ownerState.disableUnderline && {
"&:after": {
borderBottom: `2px solid ${
theme.palette[ownerState.color as PaletteColorName].main
}`,
left: 0,
bottom: 0,
// Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242
content: '""',
position: "absolute",
right: 0,
transform: "scaleX(0)",
transition: theme.transitions.create("transform", {
duration: theme.transitions.duration.shorter,
easing: theme.transitions.easing.easeOut,
}),
pointerEvents: "none", // Transparent to the hover style.
},
[`&.${filledInputClasses.focused}:after`]: {
transform: "scaleX(1)",
},
[`&.${filledInputClasses.error}:after`]: {
borderBottomColor: theme.palette.error.main,
transform: "scaleX(1)", // error is always underlined in red
},
"&:before": {
borderBottom: `1px solid ${bottomLineColor}`,
left: 0,
bottom: 0,
// Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242
content: '"\\00a0"',
position: "absolute",
right: 0,
transition: theme.transitions.create("border-bottom-color", {
duration: theme.transitions.duration.shorter,
}),
pointerEvents: "none", // Transparent to the hover style.
},
[`&:hover:not(.${filledInputClasses.disabled}):before`]: {
borderBottom: `1px solid ${theme.palette.text.primary}`,
},
[`&.${filledInputClasses.disabled}:before`]: {
borderBottomStyle: "dotted",
},
}),
...(ownerState.startAdornment && {
paddingLeft: 12,
}),
...(ownerState.endAdornment && {
paddingRight: 12,
}),
...(ownerState.multiline && {
padding: "25px 12px 8px",
...(ownerState.size === "small" && {
paddingTop: 21,
paddingBottom: 4,
}),
...(ownerState.hiddenLabel && {
paddingTop: 16,
paddingBottom: 17,
}),
}),
};
});

const FilledInputInput = styled(InputBaseInput, {
name: "MuiFilledInput",
slot: "Input",
overridesResolver: inputBaseInputOverridesResolver as any,
})<ComponentProps<FilledInputTypeMap>>(({ theme, ownerState }) => ({
paddingTop: 25,
paddingRight: 12,
paddingBottom: 8,
paddingLeft: 12,
"&:-webkit-autofill": {
WebkitBoxShadow:
theme.palette.mode === "light" ? null : "0 0 0 100px #266798 inset",
WebkitTextFillColor: theme.palette.mode === "light" ? null : "#fff",
caretColor: theme.palette.mode === "light" ? null : "#fff",
borderTopLeftRadius: "inherit",
borderTopRightRadius: "inherit",
},
...(ownerState.size === "small" && {
paddingTop: 21,
paddingBottom: 4,
}),
...(ownerState.hiddenLabel && {
paddingTop: 16,
paddingBottom: 17,
}),
...(ownerState.multiline && {
paddingTop: 0,
paddingBottom: 0,
paddingLeft: 0,
paddingRight: 0,
}),
...(ownerState.startAdornment && {
paddingLeft: 0,
}),
...(ownerState.endAdornment && {
paddingRight: 0,
}),
...(ownerState.hiddenLabel &&
ownerState.size === "small" && {
paddingTop: 8,
paddingBottom: 9,
}),
}));

const FilledInput = $.component(function FilledInput({
allProps,
classes,
otherProps,
props,
}) {
const componentProps = createMemo(() => {
const filledInputComponentsProps = {
root: { ownerState: allProps },
input: { ownerState: allProps },
};
return otherProps.componentsProps
? deepmerge(otherProps.componentsProps, filledInputComponentsProps)
: filledInputComponentsProps;
});

const allClasses = mergeProps(() => props.classes || {}, classes);

return (
<InputBase
{...otherProps}
components={{
Root: FilledInputRoot,
Input: FilledInputInput,
...(otherProps.components || {}),
}}
componentsProps={componentProps()}
classes={allClasses}
/>
);
});

export default FilledInput;
36 changes: 36 additions & 0 deletions packages/material/src/FilledInput/FilledInputProps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { InputBaseProps } from "../InputBase";
import { FilledInputClasses } from "./filledInputClasses";
import SxProps from "@suid/system/sxProps";
import { ElementType } from "@suid/types";

export interface FilledInputTypeMap<P = {}, D extends ElementType = "input"> {
name: "MuiFilledInput";
defaultPropNames: "hiddenLabel";
selfProps: {
/**
* Override or extend the styles applied to the component.
*/
classes?: Partial<FilledInputClasses>;
/**
* If `true`, the label is hidden.
* This is used to increase density for a `FilledInput`.
* Be sure to add `aria-label` to the `input` element.
* @default false
*/
hiddenLabel?: boolean;
/**
* If `true`, the input will not have an underline.
*/
disableUnderline?: boolean;
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps;
};
props: P & InputBaseProps & FilledInputTypeMap["selfProps"];
defaultComponent: D;
}

export type FilledInputProps = FilledInputTypeMap["props"];

export default FilledInputProps;
69 changes: 69 additions & 0 deletions packages/material/src/FilledInput/filledInputClasses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { generateUtilityClasses, generateUtilityClass } from "@suid/base";

export interface FilledInputClasses {
/** Styles applied to the root element. */
root: string;
/** Styles applied to the root element if color secondary. */
colorSecondary: string;
/** Styles applied to the root element unless `disableUnderline={true}`. */
underline: string;
/** State class applied to the root element if the component is focused. */
focused: string;
/** State class applied to the root element if `disabled={true}`. */
disabled: string;
/** Styles applied to the root element if `startAdornment` is provided. */
adornedStart: string;
/** Styles applied to the root element if `endAdornment` is provided. */
adornedEnd: string;
/** State class applied to the root element if `error={true}`. */
error: string;
/** Styles applied to the input element if `size="small"`. */
sizeSmall: string;
/** Styles applied to the root element if `multiline={true}`. */
multiline: string;
/** Styles applied to the root element if `hiddenLabel={true}`. */
hiddenLabel: string;
/** Styles applied to the input element. */
input: string;
/** Styles applied to the input element if `size="small"`. */
inputSizeSmall: string;
/** Styles applied to the `input` if in `<FormControl hiddenLabel />`. */
inputHiddenLabel: string;
/** Styles applied to the input element if `multiline={true}`. */
inputMultiline: string;
/** Styles applied to the input element if `startAdornment` is provided. */
inputAdornedStart: string;
/** Styles applied to the input element if `endAdornment` is provided. */
inputAdornedEnd: string;
}

export type FilledInputClassKey = keyof FilledInputClasses;

export function getFilledInputUtilityClass(slot: string): string {
return generateUtilityClass("MuiFilledInput", slot);
}

const filledInputClasses: FilledInputClasses = generateUtilityClasses(
"MuiFilledInput",
[
"root",
"colorSecondary",
"underline",
"focused",
"disabled",
"adornedStart",
"adornedEnd",
"error",
"sizeSmall",
"multiline",
"hiddenLabel",
"input",
"inputSizeSmall",
"inputHiddenLabel",
"inputMultiline",
"inputAdornedStart",
"inputAdornedEnd",
]
);

export default filledInputClasses;
6 changes: 6 additions & 0 deletions packages/material/src/FilledInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { default } from "./FilledInput";
export * from "./FilledInput";
export * from "./FilledInputProps";

export { default as FilledInputClasses } from "./filledInputClasses";
export * from "./filledInputClasses";
1 change: 1 addition & 0 deletions packages/material/src/styles/components-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export { default as MuiDivider } from "../Divider";
export { default as MuiDrawer } from "../Drawer";
export { default as MuiFab } from "../Fab";
export { default as MuiFade } from "../Fade";
export { default as MuiFilledInput } from "../FilledInput";
export { default as MuiFormControl } from "../FormControl";
export { default as MuiFormControlLabel } from "../FormControlLabel";
export { default as MuiFormGroup } from "../FormGroup";
Expand Down
7 changes: 1 addition & 6 deletions scripts/actions/genRoadmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,7 @@ const codemodTransformers: Record<string, boolean | "pending"> = {
"`React.Fragment`": true,
};

const pendingComponents = [
"TextField",
"FilledInput",
"OutlinedInput",
"Select",
];
const pendingComponents = ["TextField", "OutlinedInput", "Select"];

function stateIcon(state: boolean | "pending") {
if (state === true) return "✅";
Expand Down

0 comments on commit 593ffd8

Please sign in to comment.