Skip to content

Commit

Permalink
perf(material): reduce mergeProps calls and transform to non-reacti…
Browse files Browse the repository at this point in the history
…ve the root level of nested objects
  • Loading branch information
juanrgm committed May 13, 2023
1 parent bfd31f1 commit c2226ae
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 250 deletions.
6 changes: 6 additions & 0 deletions .changeset/small-ears-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@suid/material": patch
"@suid/system": patch
---

Reduce `mergeProps` calls and transform to non-reactive the root level of nested objects
141 changes: 77 additions & 64 deletions packages/material/src/ButtonBase/ButtonBase.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTheme } from "../styles";
import styled from "../styles/styled";
import { ButtonBaseTypeMap } from "./ButtonBaseProps";
import TouchRipple from "./TouchRipple";
Expand All @@ -7,7 +8,7 @@ import buttonBaseClasses, {
} from "./buttonBaseClasses";
import createComponentFactory from "@suid/base/createComponentFactory";
import createRef from "@suid/system/createRef";
import { InPropsOf, EventParam } from "@suid/types";
import { InPropsOf, EventParam, PropsOf } from "@suid/types";
import useIsFocusVisible from "@suid/utils/useIsFocusVisible";
import clsx from "clsx";
import {
Expand All @@ -18,14 +19,14 @@ import {
mergeProps,
onMount,
Show,
splitProps,
} from "solid-js";

const $ = createComponentFactory<
ButtonBaseTypeMap,
InPropsOf<ButtonBaseTypeMap> & {
focusVisible: boolean;
}
>()({
type OwnerState = Pick<InPropsOf<ButtonBaseTypeMap>, "disabled"> & {
focusVisible: boolean;
};

const $ = createComponentFactory<ButtonBaseTypeMap, OwnerState>()({
name: "MuiButtonBase",
selfPropNames: [
"LinkComponent",
Expand All @@ -44,17 +45,6 @@ const $ = createComponentFactory<
"tabIndex",
"touchRippleRef",
],
propDefaults: ({ set }) =>
set({
component: "button",
disabled: false,
disableRipple: false,
disableTouchRipple: false,
focusRipple: false,
LinkComponent: "a",
centerRipple: false,
tabIndex: 0,
}),
autoCallUseClasses: false,
utilityClass: getButtonBaseUtilityClass,
slotClasses: (ownerState) => ({
Expand All @@ -70,7 +60,7 @@ export const ButtonBaseRoot = styled("button", {
name: "MuiButtonBase",
slot: "Root",
overridesResolver: (props, styles) => styles.root,
})({
})<OwnerState>({
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
Expand Down Expand Up @@ -117,22 +107,48 @@ export const ButtonBaseRoot = styled("button", {
*
* - [ButtonBase API](https://mui.com/api/button-base/)
*/
const ButtonBase = $.component(function ButtonBase({
allProps,
props,
otherProps,
}) {
const button = createRef<HTMLButtonElement>(otherProps);
const ButtonBase = $.defineComponent(function ButtonBase(inProps) {
const theme = useTheme();
const props = mergeProps(
{
component: "button",
LinkComponent: "a",
tabIndex: 0,
},
theme.components?.[$.name]?.defaultProps,
inProps
) as PropsOf<ButtonBaseTypeMap>;
const button = createRef<HTMLButtonElement>(inProps);
const ripple = createRef<TouchRippleActions>(() => props.touchRippleRef);
const focus = useIsFocusVisible();
let keydown = false;
const [focusVisible, setFocusVisible] = createSignal(false);
const [mountedState, setMountedState] = createSignal(false);
const ownerState = mergeProps(allProps, {
const [, otherProps] = splitProps(props, [
"LinkComponent",
"TouchRippleProps",
"action",
"centerRipple",
"children",
"classes",
"disableRipple",
"disableRipple",
"disableTouchRipple",
"disabled",
"focusRipple",
"focusVisibleClassName",
"onFocusVisible",
"tabIndex",
"touchRippleRef",
]);
const ownerState = {
get disabled() {
return props.disabled || false;
},
get focusVisible() {
return focusVisible();
},
});
};
const classes = $.useClasses(ownerState);

onMount(() => {
Expand All @@ -154,7 +170,7 @@ const ButtonBase = $.component(function ButtonBase({
function useRippleHandler(
rippleAction: "start" | "stop",
eventCallback: JSX.EventHandlerUnion<HTMLButtonElement, any> | undefined,
skipRippleAction: boolean = props.disableTouchRipple
skipRippleAction: boolean | undefined = props.disableTouchRipple
) {
return (event: Event) => {
if (typeof eventCallback === "function") {
Expand All @@ -170,21 +186,21 @@ const ButtonBase = $.component(function ButtonBase({
};
}

const handleMouseDown = useRippleHandler("start", otherProps.onMouseDown);
const handleContextMenu = useRippleHandler("stop", otherProps.onContextMenu);
const handleDragLeave = useRippleHandler("stop", otherProps.onDragLeave);
const handleMouseUp = useRippleHandler("stop", otherProps.onMouseUp);
const handleMouseDown = useRippleHandler("start", props.onMouseDown);
const handleContextMenu = useRippleHandler("stop", props.onContextMenu);
const handleDragLeave = useRippleHandler("stop", props.onDragLeave);
const handleMouseUp = useRippleHandler("stop", props.onMouseUp);
const handleMouseLeave = useRippleHandler("stop", (event) => {
if (focusVisible()) {
event.preventDefault();
}
if (typeof otherProps.onMouseLeave === "function") {
otherProps.onMouseLeave(event);
if (typeof props.onMouseLeave === "function") {
props.onMouseLeave(event);
}
});
const handleTouchStart = useRippleHandler("start", otherProps.onTouchStart);
const handleTouchEnd = useRippleHandler("stop", otherProps.onTouchEnd);
const handleTouchMove = useRippleHandler("stop", otherProps.onTouchMove);
const handleTouchStart = useRippleHandler("start", props.onTouchStart);
const handleTouchEnd = useRippleHandler("stop", props.onTouchEnd);
const handleTouchMove = useRippleHandler("stop", props.onTouchMove);

const handleBlur = useRippleHandler(
"stop",
Expand All @@ -193,8 +209,8 @@ const ButtonBase = $.component(function ButtonBase({
if (focus.isFocusVisibleRef.current === false) {
setFocusVisible(false);
}
if (typeof otherProps.onFocusOut === "function") {
otherProps.onFocusOut(event);
if (typeof props.onFocusOut === "function") {
props.onFocusOut(event);
}
},
false
Expand All @@ -215,15 +231,15 @@ const ButtonBase = $.component(function ButtonBase({
}
}

if (typeof otherProps.onFocusIn === "function") {
otherProps.onFocusIn(event);
if (typeof props.onFocusIn === "function") {
props.onFocusIn(event);
}
};

const isNonNativeButton = () => {
return (
otherProps.component &&
otherProps.component !== "button" &&
props.component &&
props.component !== "button" &&
!(button.ref.tagName === "A" && button.ref.hasAttribute("href"))
);
};
Expand Down Expand Up @@ -257,8 +273,8 @@ const ButtonBase = $.component(function ButtonBase({
event.preventDefault();
}

if (typeof otherProps.onKeyDown === "function") {
otherProps.onKeyDown(event);
if (typeof props.onKeyDown === "function") {
props.onKeyDown(event);
}

// Keyboard accessibility for non interactive elements
Expand All @@ -269,8 +285,8 @@ const ButtonBase = $.component(function ButtonBase({
!props.disabled
) {
event.preventDefault();
if (typeof otherProps.onClick === "function") {
otherProps.onClick(event as any);
if (typeof props.onClick === "function") {
props.onClick(event as any);
}
}
};
Expand All @@ -290,28 +306,25 @@ const ButtonBase = $.component(function ButtonBase({
ripple.ref.pulsate(event);
});
}
if (typeof otherProps.onKeyUp === "function") {
otherProps.onKeyUp(event);
if (typeof props.onKeyUp === "function") {
props.onKeyUp(event);
}

// Keyboard accessibility for non interactive elements
if (
typeof otherProps.onClick === "function" &&
typeof props.onClick === "function" &&
event.target === event.currentTarget &&
isNonNativeButton() &&
event.key === " " &&
!event.defaultPrevented
) {
otherProps.onClick(event as any);
props.onClick(event as any);
}
};

const ComponentProp = createMemo(() => {
let result = otherProps.component;
if (
result === "button" &&
((otherProps as any).href || (otherProps as any).to)
) {
let result = props.component;
if (result === "button" && ((props as any).href || (props as any).to)) {
result = props.LinkComponent;
}
return result;
Expand All @@ -321,30 +334,30 @@ const ButtonBase = $.component(function ButtonBase({

const enableTouchRipple = () =>
mountedState() && !props.disableRipple && !props.disabled;
// [non-reactive root]
const touchRipple = props.TouchRippleProps;

return (
<ButtonBaseRoot
type={
isButtonComponent()
? otherProps.type === undefined
? props.type === undefined
? "button"
: otherProps.type
: props.type
: undefined
}
disabled={isButtonComponent() ? props.disabled : undefined}
role={
!isButtonComponent() &&
(otherProps as any).href &&
!(otherProps as any).to
!isButtonComponent() && (props as any).href && !(props as any).to
? "button"
: undefined
}
aria-disabled={!isButtonComponent() && props.disabled ? true : undefined}
{...otherProps}
class={clsx(classes.root, otherProps.class)}
class={clsx(classes.root, props.class)}
ownerState={ownerState}
onFocusOut={handleBlur}
onClick={otherProps.onClick}
onClick={props.onClick}
onContextMenu={handleContextMenu}
onFocusIn={handleFocus}
onKeyDown={handleKeyDown}
Expand Down Expand Up @@ -377,7 +390,7 @@ const ButtonBase = $.component(function ButtonBase({
}
}}
center={props.centerRipple}
{...props.TouchRippleProps}
{...touchRipple}
/>
</Show>
</ButtonBaseRoot>
Expand Down
1 change: 1 addition & 0 deletions packages/material/src/ButtonBase/ButtonBaseProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export interface ButtonBaseTypeMap<P = {}, D extends ElementType = "button"> {
tabIndex?: number | string;
/**
* Props applied to the `TouchRipple` element.
* (non-reactive root)
*/
TouchRippleProps?: Partial<TouchRippleProps>;
/**
Expand Down
7 changes: 1 addition & 6 deletions packages/material/src/ButtonBase/Ripple.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Box from "../Box";
import { RipplePropsTypeMap } from "./RippleProps";
import createComponentFactory from "@suid/base/createComponentFactory";
import clsx from "clsx";
import { createEffect, createMemo, createSignal, onCleanup } from "solid-js";
import { createEffect, createSignal, onCleanup } from "solid-js";

const $ = createComponentFactory<RipplePropsTypeMap>()({
name: "MuiRipple",
Expand All @@ -17,11 +17,6 @@ const $ = createComponentFactory<RipplePropsTypeMap>()({
"onExited",
"timeout",
],
propDefaults: ({ set }) =>
set({
pulsate: false,
classes: {},
}),
});

/**
Expand Down
Loading

0 comments on commit c2226ae

Please sign in to comment.