Skip to content

Commit

Permalink
[core] Remove @mui/utils and @mui/types dependencies (#827)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaldudak authored Nov 27, 2024
1 parent bdbe328 commit 09c0946
Show file tree
Hide file tree
Showing 35 changed files with 778 additions and 94 deletions.
1 change: 0 additions & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
"@mdx-js/mdx": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@mui/system": "6.1.8",
"@mui/utils": "6.1.8",
"@next/mdx": "^15.0.2",
"@react-spring/web": "^9.7.5",
"@stefanprobst/rehype-extract-toc": "^2.2.0",
Expand Down
3 changes: 1 addition & 2 deletions docs/src/app/experiments/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
import * as React from 'react';
import clsx from 'clsx';
import { Dialog } from '@base-ui-components/react/dialog';
// eslint-disable-next-line no-restricted-imports
import { useTransitionStatus } from '@base-ui-components/react/utils/useTransitionStatus';
import { useTransitionStatus } from '@base-ui-components/react/utils';
import {
animated as springAnimated,
useSpring,
Expand Down
2 changes: 1 addition & 1 deletion docs/src/blocks/GoogleAnalytics.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';
import * as React from 'react';
import { useMediaQuery } from '@base-ui-components/react/use-media-query';
import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
import { useEnhancedEffect } from '@base-ui-components/react/utils';

let boundDataGaListener = false;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';
// eslint-disable-next-line no-restricted-imports
import { useEnhancedEffect } from '@base-ui-components/react/utils/useEnhancedEffect';
import { useEnhancedEffect } from '@base-ui-components/react/utils';
import * as React from 'react';

export interface PackageManagerSnippetContext {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use client';
import * as React from 'react';
import { Tabs } from '@base-ui-components/react/tabs';
// eslint-disable-next-line no-restricted-imports
import { useEnhancedEffect } from '@base-ui-components/react/utils/useEnhancedEffect';
import { useEnhancedEffect } from '@base-ui-components/react/utils';
import { usePackageManagerSnippetContext } from './PackageManagerSnippetProvider';

export function PackageManagerSnippetRoot(props: PackageManagerSnippetRoot.Props) {
Expand Down
3 changes: 1 addition & 2 deletions docs/src/components/demo/DemoVariantSelectorProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client';
import * as React from 'react';
// eslint-disable-next-line no-restricted-imports
import { useEnhancedEffect } from '@base-ui-components/react/utils/useEnhancedEffect';
import { useEnhancedEffect } from '@base-ui-components/react/utils';

export interface DemoVariantSelectorContext {
selectedVariant: string;
Expand Down
3 changes: 1 addition & 2 deletions docs/src/design-system/ToggleButtonGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import clsx from 'clsx';
// eslint-disable-next-line no-restricted-imports
import { useControlled } from '@base-ui-components/react/utils/useControlled';
import { useControlled } from '@base-ui-components/react/utils';
import classes from './ToggleButtonGroup.module.css';

export interface ToggleButtonGroupProps<Option extends { value: string; label: string }>
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@
"@mui/internal-scripts": "^1.0.27",
"@mui/internal-test-utils": "https://pkg.csb.dev/mui/material-ui/commit/92c23999/@mui/internal-test-utils",
"@mui/monorepo": "github:mui/material-ui#v6.1.7",
"@mui/utils": "6.1.8",
"@next/eslint-plugin-next": "^14.2.17",
"@octokit/rest": "^20.1.1",
"@playwright/test": "1.49.0",
Expand Down
5 changes: 3 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
".": {
"import": "./src/index.ts"
},
"./utils": {
"import": "./src/utils/index.ts"
},
"./*": {
"import": "./src/*/index.ts"
}
Expand All @@ -54,8 +57,6 @@
"@floating-ui/react": "^0.26.28",
"@floating-ui/react-dom": "^2.1.2",
"@floating-ui/utils": "^0.2.8",
"@mui/types": "^7.2.19",
"@mui/utils": "^6.1.8",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"use-sync-external-store": "^1.2.2"
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/menu/trigger/useMenuTrigger.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use client';
import * as React from 'react';
import { unstable_useForkRef as useForkRef } from '@mui/utils';
import { FloatingEvents } from '@floating-ui/react';
import { useButton } from '../../use-button/useButton';
import { useForkRef } from '../../utils/useForkRef';
import { GenericHTMLProps } from '../../utils/types';
import { mergeReactProps } from '../../utils/mergeReactProps';
import { ownerDocument } from '../../utils/owner';
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/no-ssr/NoSsr.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { exactProp, unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/utils';
import { useEnhancedEffect } from '../utils/useEnhancedEffect';
import { exactProp } from '../utils/proptypes';
import { NoSsrProps } from './NoSsr.types';

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useScrollAreaRootContext } from '../root/ScrollAreaRootContext';
import { useEventCallback } from '../../utils/useEventCallback';
import { useEnhancedEffect } from '../../utils/useEnhancedEffect';
import { mergeReactProps } from '../../utils/mergeReactProps';
import { clamp } from '../../utils/clamp';
import { MIN_THUMB_SIZE } from '../constants';

export function useScrollAreaViewport(params: useScrollAreaViewport.Parameters) {
Expand Down Expand Up @@ -98,9 +99,6 @@ export function useScrollAreaViewport(params: useScrollAreaViewport.Parameters)
scrollbarXEl.offsetWidth - clampedNextWidth - (paddingLeft + paddingRight);
const scrollRatioX = scrollLeft / (scrollableContentWidth - viewportWidth);

const clamp = (value: number, min: number, max: number) =>
Math.min(Math.max(value, min), max);

// In Safari, don't allow it to go negative or too far as `scrollLeft` considers the rubber
// band effect.
const thumbOffsetX =
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/switch/root/SwitchRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import refType from '@mui/utils/refType';
import { useSwitchRoot } from './useSwitchRoot';
import { SwitchRootContext } from './SwitchRootContext';
import { styleHookMapping } from '../styleHooks';
import { useComponentRenderer } from '../../utils/useComponentRenderer';
import type { FieldRoot } from '../../field/root/FieldRoot';
import { useFieldRootContext } from '../../field/root/FieldRootContext';
import type { BaseUIComponentProps } from '../../utils/types';
import { refType } from '../../utils/proptypes';

/**
* The foundation for building custom-styled switches.
Expand Down
10 changes: 10 additions & 0 deletions packages/react/src/utils/clamp.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { expect } from 'chai';
import { clamp } from './clamp';

describe('clamp', () => {
it('clamps a value based on min and max', () => {
expect(clamp(1, 2, 4)).to.equal(2);
expect(clamp(5, 2, 4)).to.equal(4);
expect(clamp(-5, -1, 5)).to.equal(-1);
});
});
8 changes: 7 additions & 1 deletion packages/react/src/utils/clamp.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
export { default as clamp } from '@mui/utils/clamp';
export function clamp(
val: number,
min: number = Number.MIN_SAFE_INTEGER,
max: number = Number.MAX_SAFE_INTEGER,
): number {
return Math.max(min, Math.min(val, max));
}
39 changes: 39 additions & 0 deletions packages/react/src/utils/getReactElementRef.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from 'react';
import { expect } from 'chai';
import { getReactElementRef } from '@base-ui-components/react/utils';

describe('getReactElementRef', () => {
it('should return undefined when not used correctly', () => {
// @ts-expect-error
expect(getReactElementRef(false)).to.equal(null);
// @ts-expect-error
expect(getReactElementRef()).to.equal(null);
// @ts-expect-error
expect(getReactElementRef(1)).to.equal(null);

const children = [<div key="1" />, <div key="2" />];
// @ts-expect-error
expect(getReactElementRef(children)).to.equal(null);
});

it('should return the ref of a React element', () => {
const ref = React.createRef<HTMLDivElement>();
const element = <div ref={ref} />;
expect(getReactElementRef(element)).to.equal(ref);
});

it('should return null for a fragment', () => {
const element = (
<React.Fragment>
<p>Hello</p>
<p>Hello</p>
</React.Fragment>
);
expect(getReactElementRef(element)).to.equal(null);
});

it('should return null for element with no ref', () => {
const element = <div />;
expect(getReactElementRef(element)).to.equal(null);
});
});
18 changes: 18 additions & 0 deletions packages/react/src/utils/getReactElementRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';

/**
* Returns the ref of a React element handling differences between React 19 and older versions.
* It will throw runtime error if the element is not a valid React element.
*
* @param element React.ReactElement
* @returns React.Ref<any> | null
*/
export function getReactElementRef(element: React.ReactElement): React.Ref<any> | null {
// 'ref' is passed as prop in React 19, whereas 'ref' is directly attached to children in older versions
if (parseInt(React.version, 10) >= 19) {
return (element?.props as any)?.ref || null;
}
// @ts-expect-error element.ref is not included in the ReactElement type
// https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/70189
return element?.ref || null;
}
6 changes: 5 additions & 1 deletion packages/react/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Public utils

export * from './prepareForSlot';
export * from './getReactElementRef';
export * from './MuiCancellableEvent';
export * from './useControlled';
export * from './useEnhancedEffect';
export * from './useForkRef';
export * from './useId';
export * from './useScrollLock';
export * from './useTransitionStatus';
export * from './visuallyHidden';
10 changes: 8 additions & 2 deletions packages/react/src/utils/owner.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
export { default as ownerDocument } from '@mui/utils/ownerDocument';
export { default as ownerWindow } from '@mui/utils/ownerWindow';
export function ownerDocument(node: Node | null | undefined): Document {
return (node && node.ownerDocument) || document;
}

export function ownerWindow(node: Node | undefined): Window {
const doc = ownerDocument(node);
return doc.defaultView || window;
}
28 changes: 0 additions & 28 deletions packages/react/src/utils/prepareForSlot.spec.tsx

This file was deleted.

13 changes: 0 additions & 13 deletions packages/react/src/utils/prepareForSlot.tsx

This file was deleted.

35 changes: 35 additions & 0 deletions packages/react/src/utils/proptypes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { expect } from 'chai';
import PropTypes from 'prop-types';
import { exactProp } from './proptypes';

describe('exactProp()', () => {
beforeEach(() => {
PropTypes.resetWarningCache();
});

it('should return null for supported props', () => {
const props = {
bar: false,
};
const propTypes = {
bar: PropTypes.bool,
};

expect(() => {
PropTypes.checkPropTypes(exactProp(propTypes), props, 'props', 'Component');
}).not.toErrorDev();
});

it('should return an error for unsupported props', () => {
const props = {
foo: false,
};
const propTypes = {
bar: PropTypes.bool,
};

expect(() => {
PropTypes.checkPropTypes(exactProp(propTypes), props, 'props', 'Component');
}).toErrorDev('The following props are not supported: `foo`. Please remove them');
});
});
59 changes: 57 additions & 2 deletions packages/react/src/utils/proptypes.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,57 @@
export { default as refType } from '@mui/utils/refType';
export { default as HTMLElementType } from '@mui/utils/HTMLElementType';
import PropTypes, { ValidationMap } from 'prop-types';

export const refType = PropTypes.oneOfType([PropTypes.func, PropTypes.object]);

export function HTMLElementType(
props: { [key: string]: unknown },
propName: string,
componentName: string,
location: string,
propFullName: string,
): Error | null {
if (process.env.NODE_ENV === 'production') {
return null;
}

const propValue = props[propName];
const safePropName = propFullName || propName;

if (propValue == null) {
return null;
}

if (propValue && (propValue as any).nodeType !== 1) {
return new Error(
`Invalid ${location} \`${safePropName}\` supplied to \`${componentName}\`. ` +
`Expected an HTMLElement.`,
);
}

return null;
}

const specialProperty = 'exact-prop: \u200b';

// This module is based on https://github.com/airbnb/prop-types-exact repository.
// However, in order to reduce the number of dependencies and to remove some extra safe checks
// the module was forked.
export function exactProp<T>(propTypes: ValidationMap<T>): ValidationMap<T> {
if (process.env.NODE_ENV === 'production') {
return propTypes;
}

return {
...propTypes,
[specialProperty]: (props: { [key: string]: unknown }) => {
const unsupportedProps = Object.keys(props).filter((prop) => !propTypes.hasOwnProperty(prop));
if (unsupportedProps.length > 0) {
return new Error(
`The following props are not supported: ${unsupportedProps
.map((prop) => `\`${prop}\``)
.join(', ')}. Please remove them.`,
);
}
return null;
},
};
}
6 changes: 6 additions & 0 deletions packages/react/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,9 @@ export type BaseUIComponentProps<
| ComponentRenderFn<RenderFunctionProps, State>
| React.ReactElement<Record<string, unknown>>;
};

/**
* Simplifies the display of a type (without modifying it).
* Taken from https://effectivetypescript.com/2022/02/25/gentips-4-display/
*/
export type Simplify<T> = T extends Function ? T : { [K in keyof T]: T[K] };
Loading

0 comments on commit 09c0946

Please sign in to comment.