From 26ffb461a650159aa6ed937c98433d312dbb25ac Mon Sep 17 00:00:00 2001 From: Juanra GM Date: Fri, 25 Mar 2022 16:45:23 +0100 Subject: [PATCH] feat(material): add `Fab` component --- .changeset/honest-lemons-hammer.md | 5 + packages/material/src/Fab/Fab.tsx | 177 ++++++++++++++++++ packages/material/src/Fab/FabProps.tsx | 92 +++++++++ packages/material/src/Fab/fabClasses.ts | 50 +++++ packages/material/src/Fab/index.tsx | 6 + .../material/src/styles/components-types.ts | 1 + 6 files changed, 331 insertions(+) create mode 100644 .changeset/honest-lemons-hammer.md create mode 100644 packages/material/src/Fab/Fab.tsx create mode 100644 packages/material/src/Fab/FabProps.tsx create mode 100644 packages/material/src/Fab/fabClasses.ts create mode 100644 packages/material/src/Fab/index.tsx diff --git a/.changeset/honest-lemons-hammer.md b/.changeset/honest-lemons-hammer.md new file mode 100644 index 000000000..adfd4ffbc --- /dev/null +++ b/.changeset/honest-lemons-hammer.md @@ -0,0 +1,5 @@ +--- +"@suid/material": patch +--- + +Add `Fab` component diff --git a/packages/material/src/Fab/Fab.tsx b/packages/material/src/Fab/Fab.tsx new file mode 100644 index 000000000..a55802821 --- /dev/null +++ b/packages/material/src/Fab/Fab.tsx @@ -0,0 +1,177 @@ +import ButtonBase from "../ButtonBase"; +import styled from "../styles/styled"; +import capitalize from "../utils/capitalize"; +import { FabTypeMap } from "./FabProps"; +import fabClasses, { getFabUtilityClass } from "./fabClasses"; +import createComponentFactory from "@suid/base/createComponentFactory"; +import { ComponentInProps } from "@suid/types"; +import clsx from "clsx"; + +const $ = createComponentFactory()({ + name: "MuiFab", + propDefaults: ({ set }) => + set({ + color: "default", + component: "button", + disabled: false, + disableFocusRipple: false, + size: "large", + variant: "circular", + }), + selfPropNames: [ + "children", + "classes", + "color", + "disableFocusRipple", + "disableRipple", + "disabled", + "href", + "size", + "variant", + ], + utilityClass: getFabUtilityClass, + slotClasses: (ownerState) => ({ + root: [ + "root", + ownerState.variant, + `size${capitalize(ownerState.size)}`, + ownerState.color === "inherit" ? "colorInherit" : ownerState.color, + ], + }), +}); + +const FabRoot = styled(ButtonBase, { + name: "MuiFab", + slot: "Root", + overridesResolver: (props, styles) => { + const { ownerState } = props; + + return [ + styles.root, + styles[ownerState.variant], + styles[`size${capitalize(ownerState.size)}`], + ownerState.color === "inherit" && styles.colorInherit, + styles[capitalize(ownerState.size)], + styles[ownerState.color], + ]; + }, +})>( + ({ theme, ownerState }) => ({ + ...theme.typography.button, + minHeight: 36, + transition: theme.transitions.create( + ["background-color", "box-shadow", "border-color"], + { + duration: theme.transitions.duration.short, + } + ), + borderRadius: "50%", + padding: 0, + minWidth: 0, + width: 56, + height: 56, + boxShadow: theme.shadows[6], + "&:active": { + boxShadow: theme.shadows[12], + }, + color: theme.palette.getContrastText(theme.palette.grey[300]), + backgroundColor: theme.palette.grey[300], + "&:hover": { + backgroundColor: theme.palette.grey.A100, + // Reset on touch devices, it doesn't add specificity + "@media (hover: none)": { + backgroundColor: theme.palette.grey[300], + }, + textDecoration: "none", + }, + [`&.${fabClasses.focusVisible}`]: { + boxShadow: theme.shadows[6], + }, + [`&.${fabClasses.disabled}`]: { + color: theme.palette.action.disabled, + boxShadow: theme.shadows[0], + backgroundColor: theme.palette.action.disabledBackground, + }, + ...(ownerState.size === "small" && { + width: 40, + height: 40, + }), + ...(ownerState.size === "medium" && { + width: 48, + height: 48, + }), + ...(ownerState.variant === "extended" && { + borderRadius: 48 / 2, + padding: "0 16px", + width: "auto", + minHeight: "auto", + minWidth: 48, + height: 48, + }), + ...(ownerState.variant === "extended" && + ownerState.size === "small" && { + width: "auto", + padding: "0 8px", + borderRadius: 34 / 2, + minWidth: 34, + height: 34, + }), + ...(ownerState.variant === "extended" && + ownerState.size === "medium" && { + width: "auto", + padding: "0 16px", + borderRadius: 40 / 2, + minWidth: 40, + height: 40, + }), + ...(ownerState.color === "inherit" && { + color: "inherit", + }), + }), + ({ theme, ownerState }) => ({ + ...(ownerState.color !== "inherit" && + ownerState.color !== "default" && + theme.palette[ownerState.color] != null && { + color: theme.palette[ownerState.color].contrastText, + backgroundColor: theme.palette[ownerState.color].main, + "&:hover": { + backgroundColor: theme.palette[ownerState.color].dark, + // Reset on touch devices, it doesn't add specificity + "@media (hover: none)": { + backgroundColor: theme.palette[ownerState.color].main, + }, + }, + }), + }) +); + +/** + * + * Demos: + * + * - [Floating Action Button](https://mui.com/components/floating-action-button/) + * + * API: + * + * - [Fab API](https://mui.com/api/fab/) + * - inherits [ButtonBase API](https://mui.com/api/button-base/) + */ +const Fab = $.component(function Fab({ allProps, classes, otherProps, props }) { + return ( + + {props.children} + + ); +}); + +export default Fab; diff --git a/packages/material/src/Fab/FabProps.tsx b/packages/material/src/Fab/FabProps.tsx new file mode 100644 index 000000000..1df8fa309 --- /dev/null +++ b/packages/material/src/Fab/FabProps.tsx @@ -0,0 +1,92 @@ +import { PropTypes, Theme } from ".."; +import { ButtonBaseTypeMap } from "../ButtonBase"; +import { OverrideProps } from "../OverridableComponent"; +import { FabClasses } from "./fabClasses"; +import { SxProps } from "@suid/system"; +import { OverridableStringUnion, ElementType } from "@suid/types"; +import { JSXElement } from "solid-js"; + +export interface FabPropsVariantOverrides {} + +export interface FabPropsSizeOverrides {} + +export interface FabPropsColorOverrides {} + +export type FabTypeMap

= { + name: "MuiFab"; + defaultPropNames: + | "color" + | "disabled" + | "disableFocusRipple" + | "size" + | "variant"; + selfProps: { + /** + * The content of the component. + */ + children?: JSXElement; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial; + /** + * The color of the component. It supports those theme colors that make sense for this component. + * @default 'default' + */ + color?: OverridableStringUnion< + PropTypes.Color | "success" | "error" | "info" | "warning", + FabPropsColorOverrides + >; + /** + * If `true`, the component is disabled. + * @default false + */ + disabled?: boolean; + /** + * If `true`, the keyboard focus ripple is disabled. + * @default false + */ + disableFocusRipple?: boolean; + /** + * If `true`, the ripple effect is disabled. + */ + disableRipple?: boolean; + /** + * The URL to link to when the button is clicked. + * If defined, an `a` element will be used as the root node. + */ + href?: string; + /** + * The size of the component. + * `small` is equivalent to the dense button styling. + * @default 'large' + */ + size?: OverridableStringUnion< + "small" | "medium" | "large", + FabPropsSizeOverrides + >; + /** + * The variant to use. + * @default 'circular' + */ + variant?: OverridableStringUnion< + "circular" | "extended", + FabPropsVariantOverrides + >; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; + }; + props: P & + FabTypeMap["selfProps"] & + Omit; + defaultComponent: D; +}; + +export type FabProps< + D extends ElementType = FabTypeMap["defaultComponent"], + P = {} +> = OverrideProps, D>; + +export default FabProps; diff --git a/packages/material/src/Fab/fabClasses.ts b/packages/material/src/Fab/fabClasses.ts new file mode 100644 index 000000000..352d70fb1 --- /dev/null +++ b/packages/material/src/Fab/fabClasses.ts @@ -0,0 +1,50 @@ +import { generateUtilityClass, generateUtilityClasses } from "@suid/base"; + +export interface FabClasses { + /** Styles applied to the root element. */ + root: string; + /** Styles applied to the root element if `color="primary"`. */ + primary: string; + /** Styles applied to the root element if `color="secondary"`. */ + secondary: string; + /** Styles applied to the root element if `variant="extended"`. */ + extended: string; + /** Styles applied to the root element if `variant="circular"`. */ + circular: string; + /** State class applied to the ButtonBase root element if the button is keyboard focused. */ + focusVisible: string; + /** State class applied to the root element if `disabled={true}`. */ + disabled: string; + /** Styles applied to the root element if `color="inherit"`. */ + colorInherit: string; + /** Styles applied to the root element if `size="small"``. */ + sizeSmall: string; + /** Styles applied to the root element if `size="medium"``. */ + sizeMedium: string; +} + +export type FabClassKey = keyof FabClasses; + +export function getFabUtilityClass(slot: string): string { + return generateUtilityClass("MuiFab", slot); +} + +const fabClasses: FabClasses = generateUtilityClasses("MuiFab", [ + "root", + "primary", + "secondary", + "extended", + "circular", + "focusVisible", + "disabled", + "colorInherit", + "sizeSmall", + "sizeMedium", + "sizeLarge", + "info", + "error", + "warning", + "success", +]); + +export default fabClasses; diff --git a/packages/material/src/Fab/index.tsx b/packages/material/src/Fab/index.tsx new file mode 100644 index 000000000..9dd744967 --- /dev/null +++ b/packages/material/src/Fab/index.tsx @@ -0,0 +1,6 @@ +export { default } from "./Fab"; +export * from "./Fab"; +export * from "./FabProps"; + +export { default as fabClasses } from "./fabClasses"; +export * from "./fabClasses"; diff --git a/packages/material/src/styles/components-types.ts b/packages/material/src/styles/components-types.ts index 7c2a3dd32..fe4e4177b 100644 --- a/packages/material/src/styles/components-types.ts +++ b/packages/material/src/styles/components-types.ts @@ -16,6 +16,7 @@ export { default as MuiContainer } from "../Container"; export { default as MuiCssBaseline } from "../CssBaseline"; 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 MuiGrid } from "../Grid"; export { default as MuiIcon } from "../Icon";