diff --git a/.browserslistrc b/.browserslistrc index b2736700825997..3630a85dc0733a 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -24,7 +24,7 @@ firefox 78 ios_saf 12.2 kaios 2.5 op_mini all -op_mob 76 +op_mob 73 opera 76 safari 14 samsung 13.0 @@ -44,7 +44,7 @@ firefox 78 ios_saf 12.2 kaios 2.5 op_mini all -op_mob 76 +op_mob 73 opera 76 safari 14 samsung 13.0 diff --git a/.circleci/config.yml b/.circleci/config.yml index eadf09608b7f6e..b8be5a472a645c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,7 @@ version: 2.1 orbs: - aws-s3: circleci/aws-s3@3.0.0 + aws-cli: circleci/aws-cli@4.1 + aws-s3: circleci/aws-s3@4.0 parameters: browserstack-force: @@ -44,7 +45,7 @@ defaults: &defaults AWS_REGION_ARTIFACTS: eu-central-1 working_directory: /tmp/material-ui docker: - - image: cimg/node:14.20 + - image: cimg/node:18.17 # CircleCI has disabled the cache across forks for security reasons. # Following their official statement, it was a quick solution, they @@ -136,7 +137,7 @@ jobs: steps: - run: name: Should not have any git not staged - command: git diff --exit-code + command: git add -A && git diff --exit-code --staged - run: name: Check for duplicated packages command: yarn deduplicate @@ -199,7 +200,7 @@ jobs: - install_js - run: name: Eslint - command: yarn lint:ci + command: yarn eslint:ci - run: name: Stylelint command: yarn stylelint @@ -222,29 +223,35 @@ jobs: command: yarn proptypes - run: name: '`yarn proptypes` changes committed?' - command: git diff --exit-code + command: git add -A && git diff --exit-code --staged + - run: + name: 'Write "use client" directive' + command: yarn rsc:build + - run: + name: '`yarn rsc:build` changes committed?' + command: git add -A && git diff --exit-code --staged - run: name: Generate the documentation command: yarn docs:api - run: name: '`yarn docs:api` changes committed?' - command: git diff --exit-code + command: git add -A && git diff --exit-code --staged - run: name: Update the navigation translations command: yarn docs:i18n - run: name: '`yarn docs:i18n` changes committed?' - command: git diff --exit-code + command: git add -A && git diff --exit-code --staged - run: name: '`yarn extract-error-codes` changes committed?' command: | yarn extract-error-codes - git diff --exit-code + git add -A && git diff --exit-code --staged - run: name: '`yarn docs:link-check` changes committed?' command: | yarn docs:link-check - git diff --exit-code + git add -A && git diff --exit-code --staged test_types: <<: *defaults resource_class: 'medium+' @@ -261,20 +268,11 @@ jobs: name: Tests TypeScript definitions command: yarn typescript:ci - run: - name: Test module augmenation + name: Test module augmentation command: | yarn workspace @mui/material typescript:module-augmentation yarn workspace @mui/base typescript:module-augmentation yarn workspace @mui/joy typescript:module-augmentation - - - restore_cache: - name: Restore generated declaration files - keys: - # #default-branch-switch - # We assume that the target branch is `master` and that declaration files are persisted in commit order. - # "If there are multiple matches, the most recently generated cache will be used." - - typescript-declaration-files-master - - run: name: Diff declaration files command: | @@ -283,7 +281,6 @@ jobs: git add -f packages/mui-utils/build || echo '/utils declarations do not exist' yarn lerna run build:types git --no-pager diff - - run: name: Any defect declaration files? command: node scripts/testBuiltTypes.mjs @@ -348,7 +345,7 @@ jobs: <<: *defaults resource_class: 'medium+' docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -378,7 +375,7 @@ jobs: test_e2e: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -397,7 +394,7 @@ jobs: test_e2e_website: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -412,7 +409,7 @@ jobs: test_profile: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -439,7 +436,7 @@ jobs: test_regressions: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -493,7 +490,7 @@ jobs: <<: *defaults working_directory: /tmp/material-ui/test/bundling/fixtures/next-webpack4/ docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -517,7 +514,7 @@ jobs: <<: *defaults working_directory: /tmp/material-ui/test/bundling/fixtures/next-webpack5/ docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -541,7 +538,7 @@ jobs: <<: *defaults working_directory: /tmp/material-ui/test/bundling/fixtures/create-react-app/ docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -565,7 +562,7 @@ jobs: <<: *defaults working_directory: /tmp/material-ui/test/bundling/fixtures/snowpack/ docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -589,7 +586,7 @@ jobs: <<: *defaults working_directory: /tmp/material-ui/test/bundling/fixtures/vite/ docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -613,7 +610,7 @@ jobs: <<: *defaults working_directory: /tmp/material-ui/test/bundling/fixtures/esbuild/ docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -641,7 +638,7 @@ jobs: <<: *defaults working_directory: /tmp/material-ui/test/bundling/fixtures/gatsby/ docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -672,7 +669,7 @@ jobs: DANGER_COMMAND: prepareBundleSizeReport - run: name: build @mui packages - command: yarn lerna run --ignore @mui/icons-material --parallel --scope "@mui/*" build + command: yarn lerna run --ignore @mui/icons-material --concurrency 8 --scope "@mui/*" build - run: name: create @mui/material canary distributable command: | @@ -691,11 +688,12 @@ jobs: pattern: '^pull/.+$' value: << pipeline.git.branch >> steps: + - aws-cli/setup: + aws_access_key_id: AWS_ACCESS_KEY_ID_ARTIFACTS + aws_secret_access_key: AWS_SECRET_ACCESS_KEY_ARTIFACTS + region: ${AWS_REGION_ARTIFACTS} # Upload distributables to S3 - aws-s3/copy: - aws-access-key-id: AWS_ACCESS_KEY_ID_ARTIFACTS - aws-region: AWS_REGION_ARTIFACTS - aws-secret-access-key: AWS_SECRET_ACCESS_KEY_ARTIFACTS from: mui-material.tgz to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/ - store_artifacts: @@ -719,20 +717,18 @@ jobs: pattern: '^pull/.+$' value: << pipeline.git.branch >> steps: + - aws-cli/setup: + aws_access_key_id: AWS_ACCESS_KEY_ID_ARTIFACTS + aws_secret_access_key: AWS_SECRET_ACCESS_KEY_ARTIFACTS + region: ${AWS_REGION_ARTIFACTS} # persist size snapshot on S3 - aws-s3/copy: arguments: --content-type application/json - aws-access-key-id: AWS_ACCESS_KEY_ID_ARTIFACTS - aws-region: AWS_REGION_ARTIFACTS - aws-secret-access-key: AWS_SECRET_ACCESS_KEY_ARTIFACTS from: size-snapshot.json to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/ # symlink size-snapshot to latest - aws-s3/copy: arguments: --content-type application/json - aws-access-key-id: AWS_ACCESS_KEY_ID_ARTIFACTS - aws-region: AWS_REGION_ARTIFACTS - aws-secret-access-key: AWS_SECRET_ACCESS_KEY_ARTIFACTS from: size-snapshot.json to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/latest/ - run: @@ -743,7 +739,7 @@ jobs: test_benchmark: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.27.1-focal + - image: mcr.microsoft.com/playwright:v1.37.1-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 26c16896a73608..5c7949333fbc54 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -1,7 +1,7 @@ { "buildCommand": "build:codesandbox", "installCommand": "install:codesandbox", - "node": "14", + "node": "18", "packages": [ "packages/mui-material", "packages/mui-codemod", @@ -36,10 +36,10 @@ }, "sandboxes": [ "material-ui-issue-latest-s2dsx", - "github/mui/material-ui/tree/HEAD/examples/create-react-app", - "github/mui/material-ui/tree/HEAD/examples/create-react-app-with-typescript", - "github/mui/material-ui/tree/HEAD/examples/joy-cra-typescript", - "github/mui/material-ui/tree/HEAD/examples/base-cra-typescript" + "/examples/material-ui-cra", + "/examples/material-ui-cra-ts", + "/examples/joy-ui-cra-ts", + "/examples/base-ui-cra-ts" ], "silent": true } diff --git a/.eslintignore b/.eslintignore index 23f0c7da923cc4..301ee5891779e4 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,10 +4,11 @@ /coverage /docs/export /docs/pages/playground/ -/examples/create-react-app*/src/serviceWorker.js -/examples/gatsby/public/ -/examples/preact/config -/examples/preact/scripts +/examples/material-ui-cra*/src/serviceWorker.js +/examples/material-ui-gatsby/public/ +/examples/material-ui-preact/config +/examples/material-ui-preact/scripts +/examples/material-ui-nextjs/src /packages/mui-codemod/lib /packages/mui-codemod/src/*/*.test/* /packages/mui-icons-material/fixtures diff --git a/.eslintrc.js b/.eslintrc.js index c41f51e1185310..e15c9c6197d333 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,6 +34,7 @@ module.exports = { 'eslint-plugin-material-ui', 'eslint-plugin-react-hooks', '@typescript-eslint/eslint-plugin', + 'eslint-plugin-filenames', ], settings: { 'import/resolver': { @@ -66,13 +67,6 @@ module.exports = { { patterns: [ '@mui/*/*/*', - // Begin block: Packages with files instead of packages in the top level - // Importing from the top level pulls in CommonJS instead of ES modules - // Allowing /icons as to reduce cold-start of dev builds significantly. - // There's nothing to tree-shake when importing from /icons this way: - // '@mui/icons-material/*/', - '@mui/utils/*', - // End block // Macros are fine since their import path is transpiled away '!@mui/utils/macros', '@mui/utils/macros/*', @@ -104,6 +98,7 @@ module.exports = { // Not sure why it doesn't work 'import/named': 'off', + 'import/no-cycle': 'off', // Missing yarn workspace support 'import/no-extraneous-dependencies': 'off', // The code is already coupled to webpack. Prefer explicit coupling. @@ -123,6 +118,7 @@ module.exports = { 'material-ui/docgen-ignore-before-comment': 'error', 'material-ui/rules-of-use-theme-variants': 'error', + 'material-ui/no-empty-box': 'error', 'react-hooks/exhaustive-deps': ['error', { additionalHooks: 'useEnhancedEffect' }], 'react-hooks/rules-of-hooks': 'error', @@ -167,8 +163,27 @@ module.exports = { ...baseStyleRules['no-restricted-syntax'], { message: - "Do not import default from React. Use a namespace import (import * as React from 'react';) instead.", - selector: 'ImportDeclaration[source.value="react"] ImportDefaultSpecifier', + "Do not import default or named exports from React. Use a namespace import (import * as React from 'react';) instead.", + selector: + 'ImportDeclaration[source.value="react"] ImportDefaultSpecifier, ImportDeclaration[source.value="react"] ImportSpecifier', + }, + { + message: + "Do not import default or named exports from ReactDOM. Use a namespace import (import * as ReactDOM from 'react-dom';) instead.", + selector: + 'ImportDeclaration[source.value="react-dom"] ImportDefaultSpecifier, ImportDeclaration[source.value="react-dom"] ImportSpecifier', + }, + { + message: + "Do not import default or named exports from ReactDOM. Use a namespace import (import * as ReactDOM from 'react-dom/client';) instead.", + selector: + 'ImportDeclaration[source.value="react-dom/client"] ImportDefaultSpecifier, ImportDeclaration[source.value="react-dom/client"] ImportSpecifier', + }, + { + message: + "Do not import default or named exports from ReactDOMServer. Use a namespace import (import * as ReactDOM from 'react-dom/server';) instead.", + selector: + 'ImportDeclaration[source.value="react-dom/server"] ImportDefaultSpecifier, ImportDeclaration[source.value="react-dom/server"] ImportSpecifier', }, ], @@ -180,11 +195,13 @@ module.exports = { 'react/no-invalid-html-attribute': 'off', 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }], + 'lines-around-directive': 'off', }, overrides: [ { files: [ // matching the pattern of the test runner + '*.test.mjs', '*.test.js', '*.test.mjs', '*.test.ts', @@ -253,6 +270,7 @@ module.exports = { ], }, }, + // Next.js entry points pages { files: ['docs/pages/**/*.js'], rules: { @@ -261,12 +279,7 @@ module.exports = { }, // demos { - files: [ - 'docs/src/pages/**/*.js', - 'docs/src/pages/**/*.tsx', - 'docs/data/**/*.js', - 'docs/data/**/*.tsx', - ], + files: ['docs/src/pages/**/*{.tsx,.js}', 'docs/data/**/*{.tsx,.js}'], rules: { // This most often reports data that is defined after the component definition. // This is safe to do and helps readability of the demo code since the data is mostly irrelevant. @@ -276,6 +289,28 @@ module.exports = { 'no-console': 'off', }, }, + // demos - proptype generation + { + files: ['docs/data/base/components/modal/UseModal.js'], + rules: { + 'consistent-return': 'off', + 'func-names': 'off', + 'no-else-return': 'off', + 'prefer-template': 'off', + }, + }, + { + files: ['docs/data/**/*{.tsx,.js}'], + excludedFiles: [ + 'docs/data/joy/getting-started/templates/**/*.tsx', + 'docs/data/**/css/*{.tsx,.js}', + 'docs/data/**/system/*{.tsx,.js}', + 'docs/data/**/tailwind/*{.tsx,.js}', + ], + rules: { + 'filenames/match-exported': ['error'], + }, + }, { files: ['*.d.ts'], rules: { @@ -291,7 +326,7 @@ module.exports = { 'error', { patterns: [ - // Allow deeper imports for TypeScript types. TODO? + // Allow deeper imports for TypeScript types. TODO remove '@mui/*/*/*/*', // Macros are fine since they're transpiled into something else '!@mui/utils/macros/*.macro', @@ -393,6 +428,7 @@ module.exports = { ], }, ], + 'import/no-cycle': ['error', { ignoreExternal: true }], }, }, { @@ -419,10 +455,17 @@ module.exports = { }, }, { - files: ['scripts/**/*.mjs'], + files: ['scripts/**/*.mjs', 'packages/**/*.mjs'], rules: { 'import/extensions': ['error', 'ignorePackages'], }, }, + { + files: ['packages/mui-base/src/**/**{.ts,.tsx}'], + rules: { + 'import/no-default-export': 'error', + 'import/prefer-default-export': 'off', + }, + }, ], }; diff --git a/.github/ISSUE_TEMPLATE/3.rfc.yml b/.github/ISSUE_TEMPLATE/3.rfc.yml index c80ade9784051e..bedf972a271001 100644 --- a/.github/ISSUE_TEMPLATE/3.rfc.yml +++ b/.github/ISSUE_TEMPLATE/3.rfc.yml @@ -1,7 +1,7 @@ name: RFC 💬 description: Request for comments for your proposal. title: '[RFC] ' -labels: ['RFC'] +labels: ['status: needs triage', 'RFC'] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/4.docs-feedback.yml b/.github/ISSUE_TEMPLATE/4.docs-feedback.yml new file mode 100644 index 00000000000000..88fac0f863eb2e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/4.docs-feedback.yml @@ -0,0 +1,52 @@ +name: Docs feedback +description: Improve documentation about MUI Core. +labels: ['status: needs triage', 'support: docs-feedback'] +title: '[docs] ' +body: + - type: markdown + attributes: + value: | + Please provide a searchable summary of the issue in the title above ⬆️. + + Thanks for contributing by creating an issue! ❤️ + - type: checkboxes + attributes: + label: Duplicates + description: Please [search the history](https://github.com/mui/material-ui/issues) to see if an issue already exists for the same problem. + options: + - label: I have searched the existing issues + required: true + + - type: input + id: page-url + attributes: + label: Related page + description: Which page of the documentation is this about? + placeholder: https://mui.com/material-ui/react-badge/ + validations: + required: true + + - type: dropdown + attributes: + label: Kind of issue + description: What kind of problem are you facing? + options: + - Unclear explanations + - Missing information + - Broken demonstration + - Other + validations: + required: true + + - type: textarea + attributes: + label: Issue description + description: | + Let us know what went wrong when you were using this documentation and what we could do to improve it. + value: | + I was looking for ... and it appears that ... + + - type: textarea + attributes: + label: Context 🔦 + description: What are you trying to accomplish? What brought you to this page? Your context can help us to come up with solutions that benefit the community as a whole. diff --git a/.github/ISSUE_TEMPLATE/5.priority-support.yml b/.github/ISSUE_TEMPLATE/5.priority-support.yml new file mode 100644 index 00000000000000..bae40bb08b931e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/5.priority-support.yml @@ -0,0 +1,39 @@ +name: 'Priority support: SLA ⏰' +description: I'm an MUI X Premium user and we have purchased the Priority Support add-on. I can't find a solution to my problem with MUI Core (Material UI, Base UI, MUI System, and Joy UI). +title: '[question] ' +labels: ['status: needs triage', 'support: unknown'] +body: + - type: markdown + attributes: + value: | + Please provide a searchable summary of the issue in the title above ⬆️. + - type: checkboxes + attributes: + label: Duplicates + description: Please [search the history](https://github.com/mui/material-ui/issues) to see if an issue already exists for the same problem. + options: + - label: I have searched the existing issues + required: true + - type: checkboxes + attributes: + label: Latest version + description: We roll bug fixes, performance enhancements, and other improvements into new releases. + options: + - label: I have tested the latest version + required: true + - type: textarea + attributes: + label: The problem in depth 🔍 + - type: textarea + attributes: + label: Your environment 🌎 + description: Run `npx @mui/envinfo` and post the results. If you encounter issues with TypeScript please include the used tsconfig. + value: | +
+ `npx @mui/envinfo` + + ``` + Don't forget to mention which browser you used. + Output from `npx @mui/envinfo` goes here. + ``` +
diff --git a/.github/workflows/ci-check.yml b/.github/workflows/ci-check.yml new file mode 100644 index 00000000000000..305d5ba98cc679 --- /dev/null +++ b/.github/workflows/ci-check.yml @@ -0,0 +1,25 @@ +# This workflow is a workaround for ci.yml to bypass the github checks +# +# Ref: https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks +name: CI Check + +on: + push: + branches-ignore: + - 'renovate/**' + pull_request: + paths: + - 'docs/**' + - 'examples/**' + +permissions: {} + +jobs: + test-dev: + if: ${{ github.actor != 'l10nbot' }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + steps: + - run: 'echo "No build required"' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb56112c3fcef5..57edc7787b9695 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,10 @@ on: # We don't need to run CI twice (push+pull_request) - 'renovate/**' pull_request: + paths-ignore: + # should sync with ci-check.yml as a workaround to bypass github checks + - 'docs/**' + - 'examples/**' permissions: {} @@ -21,14 +25,14 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - run: echo "${{ github.actor }}" - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: # fetch all tags which are required for `yarn release:changelog` fetch-depth: 0 - - name: Use Node.js 14.x - uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 # v3.5.1 + - name: Use Node.js 18.x + uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1 with: - node-version: 14 + node-version: 18 cache: 'yarn' # https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies - run: yarn install env: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e477d02eda1aa2..9320aa74bdcd78 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -16,10 +16,10 @@ jobs: security-events: write steps: - name: Checkout repository - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v2.1.13 + uses: github/codeql-action/init@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 # v2.21.8 with: languages: typescript config-file: ./.github/codeql/codeql-config.yml @@ -30,4 +30,4 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v2.1.13 + uses: github/codeql-action/analyze@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 # v2.21.8 diff --git a/.github/workflows/ensure-triage-label.yml b/.github/workflows/ensure-triage-label.yml new file mode 100644 index 00000000000000..c4753ccbd3ef44 --- /dev/null +++ b/.github/workflows/ensure-triage-label.yml @@ -0,0 +1,35 @@ +name: Ensure triage label is present + +on: + label: + types: + - deleted + issues: + types: + - opened + +permissions: {} + +jobs: + label_issues: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6 + with: + script: | + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }); + + if (labels.length <= 0) { + await github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['status: needs triage'] + }) + } diff --git a/.github/workflows/mark-duplicate.yml b/.github/workflows/mark-duplicate.yml index ff7ae801060380..eb9a7559663870 100644 --- a/.github/workflows/mark-duplicate.yml +++ b/.github/workflows/mark-duplicate.yml @@ -14,7 +14,7 @@ jobs: issues: write steps: - name: mark-duplicate - uses: actions-cool/issues-helper@275328970dbc3bfc3bc43f5fe741bf3638300c0a # v3.3.3 + uses: actions-cool/issues-helper@5457ae8d7c1dc20597a753501d30183ed8043c8b # v3.5.2 with: actions: 'mark-duplicate' token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/priority-support-validation-prompt.yml b/.github/workflows/priority-support-validation-prompt.yml new file mode 100644 index 00000000000000..cfc1c889df4bfb --- /dev/null +++ b/.github/workflows/priority-support-validation-prompt.yml @@ -0,0 +1,47 @@ +name: Priority Support Validation Prompt + +on: + issues: + types: + - labeled + +permissions: {} + +jobs: + comment: + name: Create or update comment + runs-on: ubuntu-latest + permissions: + issues: write + + steps: + - name: Find Comment + uses: peter-evans/find-comment@a54c31d7fa095754bfef525c0c8e5e5674c4b4b1 # v2 + id: findComment + with: + issue-number: ${{ github.event.issue.number }} + comment-author: 'github-actions[bot]' + body-includes: You have created a priority support request + + - name: Create comment + if: ${{ steps.findComment.outputs.comment-id == '' && contains(github.event.label.name, 'unknown') }} + uses: peter-evans/create-or-update-comment@c6c9a1a66007646a28c153e2a8580a5bad27bcfa # v3.0.2 + with: + issue-number: ${{ github.event.issue.number }} + body: | + You have created a support request under the ["Priority Support"](https://mui.com/legal/technical-support-sla/#priority-support) terms, which is a paid add-on to MUI X Premium ⏰. Please validate your support key using the link below: + + https://tools-public.mui.com/prod/pages/jyhs86t?repo=mui-x&issueId=${{ github.event.issue.number }} + + Do not share your support key in this issue! + + Priority support is only provided to verified customers. Once you have verified your support key, we will remove the `support: unknown` label and add the `support: priority` label to this issue. Only then the time for the SLA will start counting. + + - name: Update comment + if: ${{ steps.findComment.outputs.comment-id != '' && contains(github.event.label.name, 'priority') }} + uses: peter-evans/create-or-update-comment@c6c9a1a66007646a28c153e2a8580a5bad27bcfa # v3.0.2 + with: + comment-id: ${{ steps.findComment.outputs.comment-id }} + body: | + Thank you for verifying your support key 🔑, your SLA starts now. + edit-mode: replace diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 15a52171bae294..6b823e1e92cdfe 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -22,12 +22,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: persist-credentials: false - name: Run analysis - uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # v2.0.6 + uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0 with: results_file: results.sarif results_format: sarif @@ -43,6 +43,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@9e288b03632e540432812c08ffaef313da7fb1d9 # v1.1.31 + uses: github/codeql-action/upload-sarif@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 # v2.21.8 with: sarif_file: results.sarif diff --git a/.github/workflows/support-stackoverflow.yml b/.github/workflows/support-stackoverflow.yml index 1182dbe3d48179..17d997231655cd 100644 --- a/.github/workflows/support-stackoverflow.yml +++ b/.github/workflows/support-stackoverflow.yml @@ -14,7 +14,7 @@ jobs: contents: read issues: write steps: - - uses: dessant/support-requests@876a4de3922dd57434a451e58ad679f986c5da97 # v2.1.2 + - uses: dessant/support-requests@b1303caf4438e66dea1130aa4c30189dc28e690d # v3.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} # Label used to mark issues as support requests diff --git a/.gitignore b/.gitignore index 2281b26c1fdb3f..5722b6358f7885 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.iml .vscode/* !.vscode/launch.json +!.vscode/extensions.json *.log *.tsbuildinfo /.eslintcache @@ -15,6 +16,7 @@ /docs/.env.local /docs/export /docs/pages/playground/ +/docs/public/feed/ /examples/**/.cache /packages/mui-codemod/lib /packages/mui-envinfo/*.tgz @@ -32,3 +34,4 @@ build node_modules package-lock.json size-snapshot.json +docs/public/static/blog/feed/* diff --git a/.markdownlint-cli2.cjs b/.markdownlint-cli2.cjs index 15e0ce2163a8ef..2fad76ea77860a 100644 --- a/.markdownlint-cli2.cjs +++ b/.markdownlint-cli2.cjs @@ -1,5 +1,7 @@ const straightQuotes = require('./packages/markdownlint-rule-mui/straight-quotes'); const gitDiff = require('./packages/markdownlint-rule-mui/git-diff'); +const tableAlignment = require('./packages/markdownlint-rule-mui/table-alignment'); +const terminalLanguage = require('./packages/markdownlint-rule-mui/terminal-language'); // https://github.com/DavidAnson/markdownlint#rules--aliases module.exports = { @@ -31,8 +33,10 @@ module.exports = { MD052: false, // MD052/reference-links-images. Many false positives in the changelog. straightQuotes: true, gitDiff: true, + tableAlignment: true, + terminalLanguage: true, }, - customRules: [straightQuotes, gitDiff], + customRules: [straightQuotes, gitDiff, tableAlignment, terminalLanguage], ignores: [ 'CHANGELOG.old.md', '**/node_modules/**', diff --git a/.stylelintrc.js b/.stylelintrc.js index 90e28d4802339a..3fbebcc38f0350 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -1,5 +1,4 @@ module.exports = { - processors: ['stylelint-processor-styled-components'], extends: 'stylelint-config-standard', ignoreFiles: [ // TypeScript declaration files contain no styles. @@ -7,14 +6,22 @@ module.exports = { '**/*.d.ts', ], rules: { - 'value-no-vendor-prefix': true, - 'property-no-vendor-prefix': true, + 'alpha-value-notation': null, + 'custom-property-pattern': null, + 'declaration-colon-newline-after': null, + 'function-parentheses-newline-inside': null, // not compatible with prettier + 'media-feature-range-notation': null, 'no-empty-source': null, 'no-missing-end-of-source-newline': null, - 'declaration-colon-newline-after': null, + 'selector-class-pattern': null, + 'string-no-newline': null, // not compatible with prettier 'value-keyword-case': null, 'value-list-comma-newline-after': null, // not compatible with prettier - 'function-parentheses-newline-inside': null, // not compatible with prettier - 'string-no-newline': null, // not compatible with prettier }, + overrides: [ + { + files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.jsx', '**/*.ts', '**/*.tsx'], + customSyntax: 'postcss-styled-syntax', + }, + ], }; diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000000000..44aa9ff60a5eff --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "editorconfig.editorconfig", + "dbaeumer.vscode-eslint", + "davidanson.vscode-markdownlint", + "esbenp.prettier-vscode", + "yoavbls.pretty-ts-errors" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index b286ffa15a2dac..0d5719526e40c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,2993 @@ # [Versions](https://mui.com/versions/) +## 5.14.10 + + + +_Sep 18, 2023_ + +A big thanks to the 16 contributors who made this release possible. This release was mostly about 🐛 bug fixes and 📚 documentation improvements. + +### `@mui/material@5.14.10` + +- ​[Chip] Add cursor CSS property reset (#38984) @DiegoAndai + +### `@mui/utils@5.14.10` + +- ​[utils] Move @types/prop-types back to dependencies (#39030) @Methuselah96 + +### `@mui/base@5.0.0-beta.16` + +- ​[NumberInput][base-ui] Warn when changing control mode with `useControlled` (#38757) @sai6855 +- ​[Select][base-ui] Fix Select button layout shift, add placeholder prop (#38796) @mj12albert +- ​[useList][base-ui] Accept arbitrary external props and forward to root (#38848) @mj12albert +- ​[Autocomplete][base-ui] Added ref to getInputProps return value (#38919) @DarhkVoyd + +### `@mui/joy@5.0.0-beta.7` + +- ​[AccordionGroup][joy-ui] Fix console warning when using custom color (#38950) @sai6855 +- ​[GlobalStyles][joy-ui] Ensure compatibility with RSC (#38955) @mateuseap + +### Docs + +- ​[docs][base] Add Tailwind CSS + plain CSS demo on the NumberInput page (#38928) @alisasanib +- ​[docs][Dialog] Add non-modal dialog docs & demo (#38684) @mnajdova +- ​[docs] Fix number input wrong demo @oliviertassinari +- ​[docs] Exclude joy-ui LinearProgressCountup from visual regression (#38969) @siriwatknp +- ​[docs][joy-ui] Revise the Overview page (#38842) @danilo-leal +- ​[docs][material-ui][Pagination] Add `TablePagination` to the API components list (#38486) @MonstraG + +### Core + +- ​[core] Add more context about useEventCallback @oliviertassinari +- ​[core] Allow deeper import of @mui/utils (#38806) @oliviertassinari +- ​[core] Remove react-dom from @mui/utils peerDependencies (#38974) @michaldudak +- ​[core] Remove react from styled-engine dependencies (#38971) @michaldudak +- ​[core] Fix image loading bug on Safari @oliviertassinari +- ​[core] Fix bundle size upload to S3 job (#38956) @Janpot +- ​[core] Move eslint to peer dependencies of eslint-plugin-material-ui (#39033) @michaldudak +- ​[docs-infra] Display markdown lists correctly in docs for props description (#38973) @ZeeshanTamboli +- ​[website] Improve lighthouse score (#39011) @oliviertassinari +- ​[website] Fix lighthouse issues @oliviertassinari +- ​[website] Create the `InfoCard` component (#38987) @danilo-leal +- ​[website] Small tweaks for performance @oliviertassinari +- ​[zero][next] Setup nextjs plugin package (#38852) @brijeshb42 + +All contributors of this release in alphabetical order: @alisasanib, @brijeshb42, @danilo-leal, @DarhkVoyd, @DiegoAndai, @Janpot, @mateuseap, @Methuselah96, @michaldudak, @mj12albert, @mnajdova, @MonstraG, @oliviertassinari, @sai6855, @siriwatknp, @ZeeshanTamboli + +## 5.14.9 + + + +_Sep 13, 2023_ + +A big thanks to the 18 contributors who made this release possible. Here are some highlights ✨: + +- 🎉 Added the [`Drawer` component](https://mui.com/joy-ui/react-drawer/) to Joy UI (#38169) @mnajdova +- ✨ Material UI's [`ButtonGroup` component](https://mui.com/material-ui/react-button-group/) now styles button elements within it correctly (#38520) @ZeeshanTamboli + +### `@mui/material@5.14.9` + +- ​[ButtonGroup] Determine first, last and middle buttons to support different elements with correct styling (#38520) @ZeeshanTamboli +- ​[Modal] Fix console warning when onTransitionEnter , onTransitionExit provided (#38868) @sai6855 +- ​Revert "[Autocomplete] Type multiple values with readonly arrays." (#38827) @mnajdova +- ​[Tabs] Scrollable tabs shouldn't crash when customizing their styles in the theme with slot callbacks (#38544) @brentertz +- ​[AlertTitle][BreadCrumbs] Fix inheritance message in docs (#38876) @sai6855 + +### `@mui/base@5.0.0-beta.15` + +- ​[useSnackbar] Align externalProps handling (#38935) @mj12albert +- ​[useInput] Align ExternalProps naming (#38849) @mj12albert +- ​[FocusTrap] Refactor & cleanup (#38878) @mnajdova +- ​[FocusTrap] Fix `disableEnforceFocus` behavior (#38816) @mnajdova +- ​[Switch] Simplify source (#38910) @oliviertassinari + +### `@mui/joy@5.0.0-beta.6` + +- ​[Drawer] Add Drawer component (#38169) @mnajdova +- ​Reduce height of some variants (#38527) @zanivan +- ​Refine the default theme color palette (#38416) @zanivan +- ​[Dialog] Add `DialogActions`, `DialogTitle` and `DialogContent` (#38382) @siriwatknp +- ​[AccordionGroup] Add missing `variant` and `color` classes (#38814) @sai6855 + +### `@mui/lab@5.0.0-alpha.144` + +- ​Add TypeScript deprecations (#38833) @oliviertassinari +- ​Fix `@mui/x-tree-view` dependency (#38822) @flaviendelangle + +### `@mui/system@5.14.9` + +- ​Remove dead code (#38884) @oliviertassinari +- ​Remove getInitColorSchemeScript leading spaces (#38794) @oliviertassinari + +### `@mui/zero-vite-plugin@0.0.1-alpha.0` + +- ​[vite] Create a package for vite plugin (#38685) @brijeshb42 + +### Docs + +- ​[docs][base-ui] Improve recommended usage guide (#38570) @oliviertassinari +- ​[docs][base-ui] Create hooks contribution guide (#38679) @michaldudak +- ​[docs][base-ui] Structure and style revisions for Component docs (#38826) @samuelsycamore +- ​[docs][base-ui] Add Number Input to the all components page (#38839) @danilo-leal +- ​[docs][base-ui] Mark Popup with the Preview tag (#38851) @michaldudak +- ​[blog] Polish component reference name @oliviertassinari +- ​[blog] Fix missing card (#38834) @oliviertassinari +- ​[Button][docs][material-ui] Update the file upload demo (#38823) @danilo-leal +- ​[docs][DialogTitle] Fix props docs doesn't mention it extends `Typography` props (#38856) @sai6855 +- ​[docs] Improve npm experience (#38906) @oliviertassinari +- ​[docs] Fix redirection to Base UI URLs @oliviertassinari +- ​[docs] Fix use of callouts (#38747) @oliviertassinari +- ​[docs] Fix 301 links for SEO @oliviertassinari +- ​[docs] Remove flag from installation page @oliviertassinari +- ​[docs] Fix strange break line on mobile in between product name @oliviertassinari +- ​[docs] Clearer npm package homepages (#38864) @oliviertassinari +- ​[docs] enableColorScheme prop was removed (#38795) @oliviertassinari +- ​[docs] Fix a11y issues in tables demos (#38829) @michaldudak +- ​[docs][joy-ui] Refine the Messages template (#38807) @zanivan +- ​[docs][joy-ui] Fix copy on the Tutorial page (#38907) @danilo-leal +- ​[docs][joy-ui] Fix grammar and update Usage section in color inversion page (#38850) @ZeeshanTamboli +- ​[docs][joy-ui] Revise the Lists page (#36324) @LadyBluenotes +- ​[docs][joy-ui] Refine the Profile Dashboard template (#38599) @zanivan +- ​[docs][material-ui] Revise the Paper component docs (#38841) @danilo-leal +- ​[docs][material-ui] Revise the Typography page (#38543) @danilo-leal +- ​[docs][material-ui] Revise and split up "Styled engine" doc (#37774) @samuelsycamore +- ​[TextareaAutosize][docs] Fix component creation in render (#38577) @oliviertassinari + +### Examples + +- ​[examples] Add shortcut to open example in online IDE (#38572) @oliviertassinari +- ​[examples][base-ui] Add Base UI + Vite + Tailwind CSS example in TypeScript (#37595) @dvkam + +### Core + +- ​[core] Remove package declaration from same package dependencies (#38951) @DiegoAndai +- ​[core] Remove workspace dependencies from root package.json (#38940) @michaldudak +- ​[core] Fix prop-types generation (#38831) @flaviendelangle +- ​[core] Move types packages to docs' devDependencies (#38914) @michaldudak +- ​[core] Improve DX when browsing the package on npm and GitHub @oliviertassinari +- ​[core] TrapFocus was renamed to FocusTrap @oliviertassinari +- ​[core] Add types extension for clarity @oliviertassinari +- ​[core] Hoist rewriteImportPaths to parent scope @oliviertassinari +- ​[core] Bump aws-cli orb to 4.1 (#38857) @Janpot +- ​[core] Explicitly define package dependencies (#38859) @michaldudak +- ​[core] Fix yarn docs:create-playground script @oliviertassinari +- ​[docs-infra] Improve show code button affordance (#38824) @danilo-leal +- ​[docs–infra] Fix callout container width (#38880) @oliviertassinari +- ​[docs-infra] Catch duplicated trailing splashes in links (#38758) @oliviertassinari +- ​[website] add Michel Engelen to the about us page (#38818) @michelengelen +- ​[website] Add a templates & design kits section to the Material UI page (#38617) @danilo-leal + +All contributors of this release in alphabetical order: @brentertz, @brijeshb42, @danilo-leal, @DiegoAndai, @dvkam, @flaviendelangle, @Janpot, @LadyBluenotes, @michaldudak, @michelengelen, @mj12albert, @mnajdova, @oliviertassinari, @sai6855, @samuelsycamore, @siriwatknp, @zanivan, @ZeeshanTamboli + +## 5.14.8 + + + +_Sep 5, 2023_ + +A big thanks to the 25 contributors who made this release possible. + +### `@mui/material@5.14.8` + +- ​ImageItemList fix incorrect (below) rendering (#38452) @omriklein +- ​[Button] Add demo for file upload (#38786) @anle9650 +- ​[Slider] Add missing classes for `Slider` `InputLabel` `InputBase` `Radio` (#38401) @sai6855 +- ​[Select] Merge slotProps.paper with internal Paper props (#38703) @michaldudak +- ​[Tabs] Fix `ref` type (#38717) @ZeeshanTamboli +- ​[TabScrollButton] Extend ButtonBase types (#38719) @ZeeshanTamboli + +### `@mui/base@5.0.0-beta.14` + +- ​[Autocomplete] Type multiple values with readonly arrays. (#38253) @pcorpet +- ​[TextField] Fix unstable height of memoized multiline TextField component (#37135) @amal-qb + +### `@mui/joy@5.0.0-beta.5` + +- ​[Accordion] Fix incorrect display of classname (#38695) @sai6855 +- ​[AspectRatio] Correct `ratio` prop description (#38743) @sai6855 +- ​[Button] Fix disablity of button (#38673) @sai6855 +- ​[design] Stray design tweaks to components (#38476) @zanivan +- ​[Typography] Added position only when Skeleton is a direct child (#38799) @siriwatknp + +### `@mui/lab@5.0.0-alpha.143` + +- ​[TreeView] Use Tree View from MUI X in the lab (#38261) @flaviendelangle +- ​[LoadingButton] Fix HTML rule button > div forbidden nesting (#38584) @oliviertassinari + +### `@mui/system@5.14.8` + +- ​[system] Fix the inconsistent types of the `mergeBreakpointsInOrder` function (#38749) @imevanc +- ​[system] Fix maxWidth incorrectly resolving breakpoints with non-pixel units (#38633) @mj12albert +- ​[typescript] Introduce \*OwnProps interfaces for components (#36798) @szalonna + +### Docs + +- ​Update changelog (#38704) @mj12albert +- ​[docs][Autocomplete] Require referentially stable value (#38734) @michaldudak +- ​[docs][base-ui] Add type parameter to the button in prepareForSlot demo (#38640) @michaldudak +- ​[docs][base-ui] Fix the broken image in the Tailwind CSS guide (#38721) @michaldudak +- ​[docs][base-ui]: Working With Tailwind Guide - revises example code to avoid import errors (#38693) @christophermorin +- ​[docs][base] Add Tailwind CSS + plain CSS demo on the Menu page (#38618) @alisasanib +- ​[blog] Clearer blog release title @oliviertassinari +- ​[blog] Add a post for the Tree View migration (#38407) @flaviendelangle +- ​[docs] Fix broken links to Next.js docs (#38764) @ruflair +- ​[docs] Trim trailing whitespace (#38793) @oliviertassinari +- ​[docs] Fix a typo in lab-tree-view-to-mui-x.md @mbrookes +- ​[docs] Clean up not used Usage files (#38715) @danilo-leal +- ​[docs] Improve theme builder exceptions (#38709) @jyash97 +- ​[docs] Polish Slider demos (#38759) @oliviertassinari +- ​[docs] Fix Joy UI docs link regression (#38761) @oliviertassinari +- ​[docs] Fix typo @oliviertassinari +- ​[docs] Fix e.g. typo (#38748) @oliviertassinari +- ​[docs] Fix Next.js pages router example redirect link (#38750) @sai6855 +- ​[docs] Fix SEO issue broken links @oliviertassinari +- ​[docs] Improve SSR example reference (#38651) @oliviertassinari +- ​[docs][joy-ui] Integrate a count-up feature to the Linear Progress (#38738) @anon-phantom +- ​[docs][joy-ui] Fix Link's `overlay` prop demo (#38702) @danilo-leal +- ​[docs][joy-ui] Polish the Stack page (#38623) @danilo-leal +- ​[docs][material-ui] Adjust simple Slide demo (#38646) @rajgop1 + +### Core + +- ​[core] Re-add nx and setup build caching (#38752) @brijeshb42 +- ​[core] Remove dead code seoTitle @oliviertassinari +- ​[core] Use immutable refs (#38762) @oliviertassinari +- ​[core] Rework `typescript-to-proptypes` to share the AST parsing with `parseStyles` (#38517) @flaviendelangle +- ​[core] Fix CI @oliviertassinari +- ​[core] Remove unnecessary `@types/webpack` package (#38720) @ZeeshanTamboli +- ​[core] Remove duplicate prop @oliviertassinari + +- ​[docs-infra] Fix mobile display in CodeSandbox (#38767) @oliviertassinari +- ​[docs-infra] Remove legacy GA (#37579) @alexfauquette +- ​[docs-infra] Fix emotion :first-child console log (#38690) @oliviertassinari +- ​[docs-infra] Fix leaking callout content (#38712) @danilo-leal +- ​[docs-infra] Remove emoji from callouts (#38694) @danilo-leal + +- ​[website] Fix out of date discount value @oliviertassinari +- ​[website] Fix out-of-date label on Toolpad (#38744) @bharatkashyap +- ​[website] Fine-tune branding buttons box shadows (#38731) @danilo-leal +- ​[website] Fix pricing table style (#38681) @alexfauquette + +All contributors of this release in alphabetical order: @alexfauquette, @alisasanib, @amal-qb, @anle9650, @anon-phantom, @bharatkashyap, @brijeshb42, @christophermorin, @danilo-leal, @flaviendelangle, @imevanc, @jyash97, @mbrookes, @michaldudak, @mj12albert, @oliviertassinari, @omriklein, @pcorpet, @rajgop1, @ruflair, @sai6855, @siriwatknp, @szalonna, @zanivan, @ZeeshanTamboli + +## 5.14.7 + + + +_Aug 29, 2023_ + +A big thanks to the 11 contributors who made this release possible. This release focuses primarily on 🐛 bug fixes, 📚 documentation, and ⚙️ infrastructure improvements. + +### `@mui/material@5.14.7` + +- [Autocomplete] Fix listbox opened unexpectedly when component is `disabled` (#38611) @mj12albert +- [Select][material-ui] Fix select menu moving on scroll when disableScrollLock is true (#37773) @VishruthR + +### `@mui/base@5.0.0-beta.13` + +- [useButton][base-ui] Accept arbitrary props in getRootProps and forward them (#38475) @DiegoAndai + +### `@mui/zero-runtime@0.0.1-alpha.1` + +- [system][zero][tag] Add support for sx prop (#38535) @brijeshb42 + +### Docs + +- [docs] Number Input docs fixes (#38521) @mj12albert +- [docs] Show all the code in the usage section (#38691) @oliviertassinari +- [docs][joy-ui] Change the customization and how-to guides docs tree (#38396) @danilo-leal +- [docs][lab][LoadingButton] Improve `loading` prop documentation (#38625) @sai6855 +- [docs][material-ui] Format `key` prop JSDoc description in `Snackbar` component code correctly (#38603) @jaydenseric + +### Core + +- [core] Add use-client to custom icons (#38132) @mj12albert +- [core] Remove unnecessary `@types/jsdom` (#38657) @renovate[bot] +- [core] Improve sponsors GA labels (#38649) @oliviertassinari +- [core] Fix ESM issues with regression tests (#37963) @Janpot +- [core] Potential fix for intermittent ci crashes in e2e test (#38614) @Janpot +- [docs-infra] Mark unstable components with a chip in the nav drawer (#38573) @michaldudak +- [docs-infra] Adjust the Material You playground demo design (#38636) @danilo-leal +- [docs-infra] Hide the SkipLink button if user prefers reduced motion (#38632) @DerTimonius +- [website] Add tiny fixes the homepage Sponsors section (#38635) @danilo-leal + +All contributors of this release in alphabetical order: @brijeshb42, @danilo-leal, @DerTimonius, @DiegoAndai, @Janpot, @jaydenseric, @mj12albert, @oliviertassinari, @renovate[bot], @sai6855, @VishruthR + +## 5.14.6 + + + +_Aug 23, 2023_ + +A big thanks to the 21 contributors who made this release possible. Here are some highlights ✨: + +- 🚀 Added the [Popup](https://mui.com/base-ui/react-popup/) component to Base UI (#37960) @michaldudak + It's intended to replace the Popper component, which uses the deprecated Popper JS library. The Popup is built on top of Floating UI and has a similar API to the Popper. +- 🚀 Added the [Accordion](https://mui.com/joy-ui/react-accordion/) component to Joy UI (#38164) @siriwatknp +- 🚀 Added InputBase and ButtonBase components to `material-next` (#38319) @DiegoAndai @mj12albert +- 🔋 First iteration on the zero-runtime styling engine compatible with Server Components (#38378) @brijeshb42 + +### `@mui/material@5.14.6` + +- [Modal] Update it to use the useModal hook (#38498) @mnajdova +- [Select] Add `root` class to `SelectClasses` (#38424) @sai6855 +- [Skeleton] Soften the pulse animation (#38506) @oliviertassinari +- [TextField] Fix onClick regressions handling changes (#38474) @mj12albert +- [TextField] Fix TextField onClick test (#38597) @mj12albert + +### `@mui/base@5.0.0-beta.12` + +- [Popup] New component (#37960) @michaldudak + +### `@mui/joy@5.0.0-beta.3` + +- [Accordion] Add Joy UI Accordion components (#38164) @siriwatknp +- [Select] Add `required` prop (#38167) @siriwatknp +- Miscellaneous fixes (#38462) @siriwatknp + +### `@mui/material-next@6.0.0-alpha.98` + +- [ButtonBase] Add ButtonBase component (#38319) @DiegoAndai +- [Input] Add InputBase component (#38392) @mj12albert + +### `@mui/zero-runtime@0.0.1-alpha.0` + +- Implementation of styled tag processor for linaria (#38378) @brijeshb42 + +### Docs + +- [blog] Clarify tree view move @oliviertassinari +- [docs] Improve the "Understanding MUI packages" page images (#38619) @danilo-leal +- [docs][base-ui] Revise the structure of the Component docs (#38529) @samuelsycamore +- [docs][base-ui] Fix Menu Hooks demo (#38479) @homerchen19 +- [docs][base-ui] Correct the MUI System quickstart example (#38496) @michaldudak +- [docs][base-ui] Add Tailwind & plain CSS demos for Autocomplete page (#38157) @mj12albert +- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Input page (#38302) @alisasanib +- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Snackbar, Badge, Switch pages (#38425) @alisasanib +- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Slider page (#38413) @alisasanib +- [docs][base-ui] Add Tailwind CSS + plain CSS demo on the Select page (#38367) @alisasanib +- [docs][joy-ui] Fix typo: Classname -> Class name for consistency (#38510) @alexfauquette +- [docs][joy-ui] Revise the theme color page (#38402) @danilo-leal +- [docs][joy-ui] Sort templates by popularity (#38490) @oliviertassinari +- [docs][joy-ui] Fix the `fullWidth` prop description for the Input (#38545) @0xturner +- [docs][joy-ui] Updated the List playground demo (#38499) @zanivan +- [docs][joy-ui] Changed bgcolor of the Playground demo (#38502) @zanivan +- [docs][material-ui] Fix key warning in SimpleDialog demo (#38580) @ZeeshanTamboli +- [docs][material-ui] Fixed Google Fonts link for material two-tone icons in CodeSandbox and Stackblitz (#38247) @ZeeshanTamboli +- [docs][material-ui] Fix the Drawer's `onClose` API docs (#38273) @johnmatthiggins +- [docs][material-ui] Improve nav link tab example (#38315) @oliviertassinari +- [docs][material-ui] Fix missing import in the styled engine guide (#38450) @codersjj +- [docs][material-ui][Dialog] Improve screen reader announcement of Customized Dialog (#38592) @ZeeshanTamboli +- [docs] Add 3rd party libraries integration examples for Joy Input (#38541) @siriwatknp +- [docs] Hide translation call to action (#38449) @cristianmacedo +- [docs] Fix codemod name in changelog of v5.14.4 (#38593) @GresilleSiffle +- [docs] More space for theme builder (#38532) @oliviertassinari +- [docs] Fix the math symbol of the width sx prop range @oliviertassinari +- [docs] Fix typo on a11y section of Tabs @oliviertassinari +- [docs] Clarify System peer dependencies @oliviertassinari +- [docs] Fix horizontal scrollbar @oliviertassinari +- [docs] Code style convention @oliviertassinari +- [docs] Fix typo in Base UI @oliviertassinari +- [docs] Update the backers page (#38505) @danilo-leal +- [docs] Add stray design adjustments to the docs (#38501) @danilo-leal +- [docs] Use IBM Plex Sans in Tailwind CSS demos (#38464) @mnajdova +- [docs] Fix SEO issues reported by ahrefs (#38423) @oliviertassinari + +### Examples + +- [examples] Start to remove Gatsby (#38567) @oliviertassinari +- [examples][joy-ui] Fix Joy UI example CLI (#38531) @oliviertassinari +- [examples][joy-ui] Improve example when using Next Font (#38540) @mwskwong + +### Core + +- [CHANGELOG] Fix issues in highlight @oliviertassinari +- [core] Remove redundant `@material-ui/` aliases from regression test webpack config (#38574) @ZeeshanTamboli +- [core] Fix CI error @oliviertassinari +- [core] Remove unnecessary Box (#38461) @oliviertassinari +- [core] Set GitHub Action top level permission @oliviertassinari +- [docs-infra][joy-ui] Polish the usage and CSS vars playgrounds (#38600) @danilo-leal +- [docs-infra] Support link title (#38579) @oliviertassinari +- [docs-infra] Fix ad layout shift (#38622) @oliviertassinari +- [docs-infra] Add light tweaks to the ad container (#38504) @danilo-leal +- [docs-infra] Fix anchor scroll without tabs (#38586) @oliviertassinari +- [docs-infra] Retain velocity animation speed (#38470) @oliviertassinari +- [docs-infra] Follow import and CSS token standard (#38508) @oliviertassinari +- [docs-infra] Add icon to callouts (#38525) @alexfauquette +- [docs-infra] Fix the anchor link on headings (#38528) @danilo-leal +- [docs-infra] Cleanup code on demo code block expansion (#38522) @ZeeshanTamboli +- [docs-infra] Improve the heading buttons positioning (#38428) @danilo-leal +- [docs-infra] Customize the blockquote design (#38503) @danilo-leal +- [docs-infra] Improve the alert before a negative feedback (#38500) @danilo-leal +- [docs-infra] Fix GoogleAnalytics missing event for code copy (#38469) @alexfauquette +- [docs-infra] Improve affordance on the code block expansion (#38421) @danilo-leal +- [website] Fine-tune the branding theme buttons (#38588) @danilo-leal +- [website] Improve the Base UI hero section demo (#38585) @danilo-leal +- [website] Add stray design improvements to the Material UI page (#38590) @danilo-leal +- [website] Fix mobile view Material UI page (#38568) @oliviertassinari +- [website] Fix reference to the data grid @oliviertassinari +- [website] Configure Apple Pay @oliviertassinari +- [website] Fix template link on the homepage (#38471) @danilo-leal + +All contributors of this release in alphabetical order: @0xturner, @alexfauquette, @alisasanib, @brijeshb42, @codersjj, @cristianmacedo, @danilo-leal, @DiegoAndai, @GresilleSiffle, @homerchen19, @johnmatthiggins, @michaldudak, @mj12albert, @mnajdova, @mwskwong, @oliviertassinari, @sai6855, @samuelsycamore, @siriwatknp, @zanivan, @ZeeshanTamboli + +## 5.14.5 + + + +_Aug 14, 2023_ + +A big thanks to the 17 contributors who made this release possible. Here are some highlights ✨: + +- @mnajdova [made it easier to use third-party components in Base UI slots](https://mui.com/base-ui/getting-started/customization/#overriding-subcomponent-slots) with the introduction of the `prepareForSlot` utility (#38138) + +### `@mui/material@5.14.5` + +- ​[TextField] Fix to handle `onClick` on root element (#38072) @LukasTy + +### `@mui/codemod@5.14.5` + +- ​[codemod] Add v5.0.0/tree-view-moved-to-x codemod (#38248) @flaviendelangle + +### `@mui/joy@5.0.0-beta.2` + +- ​[Input][joy-ui] Fix the `FormHelperText` icon color (#38387) @TheNatkat +- ​[Skeleton][joy-ui] Soften the pulse animation (#38384) @zanivan +- ​[TabPanel][joy-ui] Add `keepMounted` prop (#38293) @decadef20 + +### `@mui/base@5.0.0-beta.11` + +- ​[base-ui] Remove the legacy Extend\* types (#38184) @michaldudak +- ​[base-ui] Add `useModal` hook (#38187) @mnajdova +- ​[base-ui] Add `prepareForSlot` util (#38138) @mnajdova +- ​[useButton][base-ui] Fix tabIndex not being forwarded (#38417) @DiegoAndai +- ​[useButton][base-ui] Fix onFocusVisible not being handled (#38399) @DiegoAndai + +### Docs + +- ​[blog] Blog post for MUI X mid v6. Date Pickers, Data Grid, and Charts (#38241) @richbustos +- ​[docs][base-ui] Update number input API docs (#38363) @mj12albert +- ​[docs] Improve page transition speed (#38394) @oliviertassinari +- ​[docs] Improve examples (#38398) @oliviertassinari +- ​[docs][docs] Add `FileUpload` demo (#38420) @sai6855 +- ​[docs][joy-ui] Refine the Order Dashboard template design (#38395) @zanivan +- ​[docs][material-ui][joy-ui] Simplify the Quickstart section on the Usage page (#38385) @danilo-leal +- ​[docs][Menu][joy] Explain how to control the open state (#38355) @michaldudak +- ​[docs][material] Revise the Support page (#38207) @samuelsycamore +- ​[docs][material-ui] Remove incorrect `aria-label`s in extended variant examples of Floating Action Button (#37170) @ashleykolodziej +- ​[docs][material-ui] Adjust slightly the installation page content (#38380) @danilo-leal +- ​[docs][Switch] Fix the readOnly class name in docs (#38277) @michaldudak +- ​[docs][TablePagination] Add Tailwind CSS & plain CSS introduction demo (#38286) @mnajdova + +### Examples + +- ​[examples] Add Joy UI + Vite.js + TypeScript example app (#37406) @nithins1 + +### Core + +- ​[core] Consistent URL add leading / @oliviertassinari +- ​[docs-infra] Fix rebase issue @oliviertassinari +- ​[docs-infra] Fix typo in docs infra docs @oliviertassinari +- ​[docs-infra] Fix nested list margin (#38456) @oliviertassinari +- ​[docs-infra] Move the Diamond Sponsors to the TOC (#38410) @danilo-leal +- ​[docs-infra] Move imports into page data (#38297) @alexfauquette +- ​[docs-infra] Adjust heading styles (#38365) @danilo-leal +- ​[docs-infra] Fix info callout border color (#38370) @danilo-leal +- ​[website] Upgrade the homepage hero demos design (#38388) @danilo-leal +- ​[website] Improve Base UI hero section demo (#38255) @danilo-leal +- ​[website] Fix EmailSubscribe look (#38429) @oliviertassinari +- ​[website] Link Discord in footer (#38369) @richbustos +- ​[website] Clean up the `GetStartedButtons` component (#38256) @danilo-leal + +All contributors of this release in alphabetical order: @alexfauquette, @ashleykolodziej, @danilo-leal, @decadef20, @DiegoAndai, @flaviendelangle, @LukasTy, @michaldudak, @mj12albert, @mnajdova, @nithins1, @oliviertassinari, @richbustos, @sai6855, @samuelsycamore, @TheNatkat, @zanivan + +## 5.14.4 + + + +_Aug 8, 2023_ + +A big thanks to the 18 contributors who made this release possible. Here are some highlights ✨: + +- 🎉 Added [Number Input](https://mui.com/base-ui/react-number-input/) component & [useNumberInput](https://mui.com/base-ui/react-number-input/#hook) hook in [Base UI](https://mui.com/base-ui/getting-started/) @mj12albert + +### `@mui/material@5.14.4` + +- ​[Checkbox][material] Add size classes (#38182) @michaldudak +- ​[Typography] Improve inherit variant logic (#38123) @ZeeshanTamboli + +### `@mui/system@5.14.4` + +- ​Revert "[Box] Remove `component` from TypeMap (#38168)" (#38356) @michaldudak + +### `@mui/base@5.0.0-beta.10` + +#### Breaking changes + +- ​[base] Ban default exports (#38200) @michaldudak + + Base UI default exports were changed to named ones. Previously we had a mix of default and named ones. + This was changed to improve consistency and avoid problems some bundlers have with default exports. + See https://github.com/mui/material-ui/issues/21862 for more context. + + ```diff + - import Button, { buttonClasses } from '@mui/base/Button'; + + import { Button, buttonClasses } from '@mui/base/Button'; + - import BaseMenu from '@mui/base/Menu'; + + import { Menu as BaseMenu } from '@mui/base/Menu'; + ``` + + Additionally, the `ClassNameGenerator` has been moved to the directory matching its name: + + ```diff + - import ClassNameGenerator from '@mui/base/className'; + + import { ClassNameGenerator } from '@mui/base/ClassNameGenerator'; + ``` + + A codemod is provided to help with the migration: + + ```bash + npx @mui/codemod v5.0.0/base-use-named-exports + ``` + +#### Changes + +- ​[base] Create useNumberInput and NumberInput (#36119) @mj12albert +- ​[Select][base] Fix flicker on click of controlled Select button (#37855) @VishruthR +- ​[Dropdown] Fix imports of types (#38296) @yash-thakur + +### `@mui/joy@5.0.0-beta.1` + +- ​[joy-ui][MenuButton] Fix disable of `MenuButton` (#38342) @sai6855 + +### Docs + +- ​[docs][AppBar] Fix `ResponsiveAppBar` demo logo href (#38346) @iownthegame +- ​[docs][base] Add Tailwind CSS + plain CSS demo on the Button page (#38240) @alisasanib +- ​[docs][Menu][base] Remove `Unstyled` prefix from demos' function names (#38270) @sai6855 +- ​[docs] Add themeable component guide (#37908) @siriwatknp +- ​[docs] Fix Joy UI demo background color (#38307) @oliviertassinari +- ​[docs] Update API docs for Number Input component (#38301) @ZeeshanTamboli +- ​[docs][joy-ui] Revise the theme typography page (#38285) @danilo-leal +- ​[docs][joy-ui] Add TS demo for Menu Bar (#38308) @sai6855 +- ​[docs][joy-ui] Updated Typography callout at getting started (#38289) @zanivan +- ​[docs][joy-ui] Fix the Inter font installation instructions (#38284) @danilo-leal +- ​[docs][material] Add note to Autocomplete about ref forwarding (#38305) @samuelsycamore +- ​[docs][Skeleton] Make the demos feel more realistic (#38212) @oliviertassinari + +- ​[examples] Swap Next.js examples between App Router and Pages Router; update naming convention (#38204) @samuelsycamore +- ​[examples][material-ui] Add Material UI + Next.js (App Router) example in JS (#38323) @samuelsycamore +- ​[blog] Discord announcement blog (#38258) @richbustos +- ​[blog] Fix 301 links to Toolpad @oliviertassinari +- ​[website] Updating Charts demo with real charts usage for MUI X marketing page (#38317) @richbustos +- ​[website] Adjust styles of the Product section on the homepage (#38366) @danilo-leal +- ​[website] Add Nora teamMember card to 'About' (#38358) @noraleonte +- ​[website] Fix image layout shift (#38326) @oliviertassinari + +### Core + +- ​[core] Fix docs demo export function consistency (#38191) @oliviertassinari +- ​[core] Fix the link-check script on Windows (#38276) @michaldudak +- ​[core] Use @testing-library/user-event direct API (#38325) @mj12albert +- ​[core] Port GitHub workflow for ensuring triage label is present (#38312) @DanailH +- ​[docs-infra] Consider files ending with .types.ts as props files (#37533) @mnajdova +- ​[docs-infra] Fix skip to content design (#38304) @oliviertassinari +- ​[docs-infra] Add a general round of polish to the API content display (#38282) @danilo-leal +- ​[docs-infra] Make the side nav collapse animation snappier (#38259) @danilo-leal +- ​[docs-infra] New Component API design followup (#38183) @cherniavskii +- ​[test] Remove unnecessary `async` keyword from test (#38373) @ZeeshanTamboli + +All contributors of this release in alphabetical order: @alisasanib, @cherniavskii, @DanailH, @danilo-leal, @iownthegame, @michaldudak, @mj12albert, @mnajdova, @noraleonte, @oliviertassinari, @richbustos, @sai6855, @samuelsycamore, @siriwatknp, @VishruthR, @yash-thakur, @zanivan, @ZeeshanTamboli + +## 5.14.3 + + + +_Jul 31, 2023_ + +A big thanks to the 17 contributors who made this release possible. Here are some highlights ✨: + +- 🚀 [Joy UI](https://mui.com/joy-ui/getting-started/) is now in Beta +- ✨ Refine [Joy UI](https://mui.com/joy-ui/getting-started/)'s default theme @siriwatknp @zanivan +- 🎉 Added Dropdown higher-level menu component [Base UI](https://mui.com/base-ui/getting-started/) @michaldudak +- 💫 Added Material You [Badge](https://mui.com/material-ui/react-badge/#material-you-version) to `material-next` (#37850) @DiegoAndai + +### `@mui/material@5.14.3` + +- ​[Autocomplete][material][joy] Add default `getOptionLabel` prop in ownerState (#38100) @DSK9012 +- ​[Menu][Divider][material] Do not allow focus on Divider when inside Menu list (#38102) @divyammadhok +- ​[typescript][material] Rename one letter type parameters (#38155) @michaldudak +- ​[Menu][material] Fixes slots and slotProps overriding defaults completely (#37902) @gitstart +- ​[Theme][material] Add missing styleOverrides type for theme MuiStack (#38189) @DiegoAndai +- ​[typescript][material] Add `component` field to `*Props` types (#38084) @michaldudak + +### `@mui/base@5.0.0-beta.9` + +#### Breaking changes + +- ​[Dropdown][base][joy] Introduce higher-level menu component (#37667) @michaldudak + +#### Other changes + +- ​[typescript][base] Rename one letter type parameters (#38171) @michaldudak + +### `@mui/joy@5.0.0-beta.0` + +- ​[joy] Refine the default theme (#36843) @siriwatknp + +### `@mui/material-next@6.0.0-alpha.95` + +- ​[Badge][material-next] Add Badge component (#37850) @DiegoAndai +- ​[Chip][material-next] Copy chip component from material (#38053) @DiegoAndai +- ​[typescript][material-next] Rename one letter type parameters (#38172) @michaldudak + +### `@mui/system@5.14.3` + +- ​[Box][system] Remove `component` from TypeMap (#38168) @michaldudak +- ​[Stack][system] Fix CSS selector (#37525) @sai6855 + +### Docs + +- ​[docs] Update Joy UI's package README (#38262) @ZeeshanTamboli +- ​[docs][base-ui] Add new batch of coming soon pages (#38025) @danilo-leal +- ​[docs] fix links to standardized examples (#38193) @emmanuel-ferdman +- ​[docs-infra] Small design polish to the Diamond Sponsor container (#38257) @danilo-leal +- ​[docs-infra] Show props in the table of content (#38173) @alexfauquette +- ​[docs-infra] Polish API page design (#38196) @oliviertassinari +- ​[docs-infra] Search with productCategory when product is missing (#38239) @oliviertassinari +- ​[docs][material] Revise and update Examples doc (#38205) @samuelsycamore +- ​[docs] Fix typo in notifications.json @mbrookes +- ​[docs-infra] Remove leftover standardNavIcon (#38252) @DiegoAndai +- ​[docs][base] Add Tailwind CSS & plain CSS demos on the Popper page (#37953) @zanivan +- ​[docs][Button][joy] Improve `loading` prop documentation (#38156) @sai6855 +- ​[docs] Prepare docs infra for Tree View migration to X (#38202) @flaviendelangle +- ​[docs] Fix SEO issues reported by ahrefs @oliviertassinari +- ​[docs] Fix palette pages - live edit not working (#38195) @oliviertassinari +- ​[docs] Add Google Analytics action for the styling menu (#38085) @mnajdova +- ​[docs] Fix Discord redirection chain @oliviertassinari +- ​[docs] Cover pnpm in more places (#38161) @oliviertassinari +- ​[docs] Avoid broken link (#38154) @oliviertassinari +- ​[docs] Add notification for beta release of Toolpad (#38152) @prakhargupta1 +- ​[docs-infra] Remove sidenav icons (#38174) @oliviertassinari +- ​[docs-infra] Fix search ranking when no productId (#38162) @oliviertassinari +- ​[docs-infra] Adjust the side nav for deeper nested items (#38047) @cherniavskii +- ​[docs][joy] Update TS file of adding more typography levels demo to match the corresponding JS file's styles (#38232) @ZeeshanTamboli +- ​[docs][joy] Add TS demo for reusable component section in approaches page (#38210) @sai6855 +- ​[docs][joy] Add TS demo for theme typography new level customization (#38199) @sai6855 + +### Core + +- ​[blog] Fix blog post slug Base UI (#38254) @oliviertassinari +- ​[core] Use native Node's fetch instead of node-fetch package (#38263) @michaldudak +- ​[core] Remove dead code @oliviertassinari +- ​[core] Polish Stack test to closer CSS injection order @oliviertassinari +- ​[core] Remove unnecessary `Required` utility type from Typography font style type (#38203) @ZeeshanTamboli +- ​[core] Fix generate Proptypes script skipping unstable items (#38198) @mj12albert +- ​[website] Adding Rich Bustos Twitter handle in bio (#38213) @richbustos +- ​[website] Prepare importing data from HiBob (#38238) @oliviertassinari +- ​[website] Sync team member with HiBob, add Raffaella (#38201) @rluzists1 + +All contributors of this release in alphabetical order: @cherniavskii, @DiegoAndai, @divyammadhok, @DSK9012, @flaviendelangle, @gitstart, @michaldudak, @mj12albert, @mnajdova, @oliviertassinari, @prakhargupta1, @richbustos, @rluzists1, @sai6855, @siriwatknp, @zanivan, @ZeeshanTamboli + +## 5.14.2 + + + +_Jul 25, 2023_ + +A big thanks to the 23 contributors who made this release possible. + +### @mui/material@5.14.2 + +- ​Revert "[core] Adds `component` prop to `OverrideProps` type (#35924)" (#38150) @michaldudak +- ​[Chip][material] Fix base cursor style to be "auto" not "default" (#38076) @DiegoAndai +- ​[Tabs] Refactor IntersectionObserver logic (#38133) @ZeeshanTamboli +- ​[Tabs] Fix and improve visibility of tab scroll buttons using the IntersectionObserver API (#36071) @SaidMarar + +### @mui/joy@5.0.0-alpha.89 + +- ​[Joy] Replace leftover `Joy-` prefix with `Mui-` (#38086) @siriwatknp +- ​[Skeleton][joy] Fix WebkitMaskImage CSS property (#38077) @Bestwebdesign +- ​[Link][Joy UI] Fix font inherit (#38124) @oliviertassinari + +### Docs + +- ​[docs] Add listbox placement demo for Select (#38130) @sai6855 +- ​[docs][base] Add Tailwind CSS & plain CSS demo on the Tabs page (#37910) @mnajdova +- ​[docs][base] Add Tailwind CSS & plain CSS demos on the Textarea page (#37943) @zanivan +- ​[docs] Fix Joy UI menu example (#38140) @harikrishnanp +- ​[docs] Remove translations section from contributing guide (#38125) @nikohoffren +- ​[docs] Fix Base UI Button Tailwind CSS padding @oliviertassinari +- ​[docs] Mention in hompage hero that Core is free (#38075) @mbrookes +- ​[docs] Fix a typo in notifications.json (#38078) @mbrookes +- ​[docs] Add Tailwind CSS & plain CSS demo on the table pagination page (#37937) @mnajdova +- ​[docs] Implement the new API display design (#37405) @alexfauquette +- ​[docs] Update migration installation code blocks (#38028) @danilo-leal +- ​[docs][joy] Revise the Joy UI Link page (#37829) @danilo-leal +- ​[docs][joy] Add playground for Card component (#37820) @Studio384 +- ​[docs][joy] Add adjustments to the color inversion page (#37143) @danilo-leal +- ​[docs][material] Improve documentation about adding custom colors (#37743) @DiegoAndai +- ​[examples] Fix Joy UI Next.js App Router font loading (#38095) @IgnacioUtrilla +- ​[examples] Fix material-next Font Usage with next/font (#38026) @onderonur + +### Core + +- ​[blog] Update Discord invite link in Toolpad beta announcement (#38143) @samuelsycamore +- ​[blog] Update discord server link (#38131) @prakhargupta1 +- ​[core] Fix rsc-builder removing the first line (#38134) @michaldudak +- ​[core] Remove the deprecation rule in tslint (#38087) @michaldudak +- ​[website] Mobile navigation: Toolpad to Beta (#38146) @bharatkashyap +- ​[website] Fix typo on pricing page @oliviertassinari +- ​[website] Fix a few regression (#38050) @oliviertassinari +- ​[website] Update Demo footers on MUI X landing page (#38027) @richbustos +- ​[website] Fix 301 redirection to base index page @oliviertassinari +- ​[website] Fix Cell selection feature name (#38029) @oliviertassinari +- ​[website] Improve button look (#38052) @oliviertassinari +- ​[website] Link new core page to new Base UI landing page (#38030) @mj12albert +- ​[website] Polish pricing page (#37975) @oliviertassinari +- ​[test] Fail the CI when new unexpected files are created (#38039) @oliviertassinari +- ​[test] Fix linting error by matching main component demo name to filename (#38122) @ZeeshanTamboli + +All contributors of this release in alphabetical order: @alexfauquette, @Bestwebdesign, @bharatkashyap, @danilo-leal, @DiegoAndai, @harikrishnanp, @IgnacioUtrilla, @mbrookes, @michaldudak, @mj12albert, @mnajdova, @nikohoffren, @oliviertassinari, @onderonur, @prakhargupta1, @richbustos, @sai6855, @SaidMarar, @samuelsycamore, @siriwatknp, @Studio384, @zanivan, @ZeeshanTamboli + +## 5.14.1 + + + +_Jul 19, 2023_ + +A big thanks to the 24 contributors who made this release possible. Here are some highlights ✨: + +- 💫 Introducing some new components for Joy UI: + - [Skeleton](https://mui.com/joy-ui/react-skeleton/) component (#37893) @siriwatknp + - [ToggleButton](https://mui.com/joy-ui/react-toggle-button-group/) (#37716) @siriwatknp +- 🎉 Base UI has its own [landing page](https://www.mui.com/base-ui)! +- 🐛 bug fixes, 📚 documentation, and ⚙️ infrastructure improvements. + +### `@mui/material@5.14.1` + +- ​[FormControlLabel] Fix misplaced asterisk when `labelPlacement` is provided (#37831) @ZeeshanTamboli +- ​[Slider][material] Fix type dependency on @types/prop-types (#37853) @Methuselah96 +- ​[Menu] Add MuiMenuList to createTheme components key (#37956) @mj12albert +- ​[Modal] Remove deprecated `BackdropComponent` and `BackdropProps` from tests (#38018) @sai6855 + +### `@mui/material-next@6.0.0-alpha.93` + +- ​[Slider][material-next] Add use client directive to useSliderElementsOverlap (#37955) @mj12albert +- ​[Button][material-next] Fix some event handlers being ignored (#37647) @DiegoAndai + +### `@mui/base@5.0.0-beta.8` + +- ​[Autocomplete] Make touch and click behavior on an option consistent (#37972) @divyammadhok + +### `@mui/joy@5.0.0-alpha.88` + +- ​[Joy][Select] Fix type error caused by custom variant (#37996) @OmPr366 +- ​[ToggleButton][Joy] Add `ToggleButton` component (#37716) @siriwatknp +- ​[Skeleton] Add Joy UI `Skeleton` component (#37893) @siriwatknp + +### `@mui/utils@5.14.1` + +- ​[utils] Add function overload for `useEventCallback` (#37827) @cherniavskii + +### Docs + +- ​[docs][base] Add Tailwind CSS & plain CSS demo on the form control page (#37914) @mnajdova +- ​[docs][base] Make Base UI Select demos denser (#37836) @zanivan +- ​[docs] Link Material UI from the landing page (#37971) @oliviertassinari +- ​[docs] Fix the empty /components page (#38010) @brijeshb42 +- ​[docs] Checkout template follows user's color scheme preference (#37928) @OndrejHj04 +- ​[docs] Disable ad for onboarding pages (#37998) @oliviertassinari +- ​[docs] Fix broken link to Base UI Next.js App Router (#37973) @oliviertassinari +- ​[docs] Fix typo in next-js-app-router.md (#37974) @ericbrian +- ​[docs] Add pnpm commands to Material UI Installation page (#36650) @officialrajdeepsingh +- ​[docs] Link charts in the roadmap (#37944) @oliviertassinari +- ​[docs] Improve changelog @oliviertassinari +- ​[docs] Improve the Select docs (#37279) @michaldudak +- ​[docs][menu] Add Tailwind CSS & plain CSS demo on the Menu page (#37856) @mnajdova +- ​[example] Update EmotionCacheProvider to work with GlobalStyles (#37962) @siriwatknp + +### Core + +- ​[blog] Add blog post about support for Next.js App Router (#37929) @samuelsycamore +- ​[blog] Blog MUI X pro statement removed (#38015) @prakhargupta1 +- ​[blog] Add Toolpad beta announcement blog (#37799) @prakhargupta1 +- ​[core] Increase space available for sidenav @oliviertassinari +- ​[core] Adds `component` prop to `OverrideProps` type (#35924) @sai6855 +- ​[core] Fix rsc build step in CI (#38019) @mj12albert +- ​[core] Remove nx dependency (#37964) @Janpot +- ​[core] Lock `@types/node` to v18 (#37965) @ZeeshanTamboli +- ​[core] Update priority support issue template and prompt (#37824) @DanailH +- ​[core] Remove warnings in docs:api (#37858) @alexfauquette +- ​[core] Make rimraf work after a major update (#37930) @michaldudak +- ​[docs-infra] Change the Diamond Sponsor block positioning on the side nav (#37933) @danilo-leal +- ​[docs-infra] Support backticks in the codeblocks (#37950) @cherniavskii +- ​[docs-infra] Improve performance hideToolbar: true (#37969) @oliviertassinari +- ​[docs-infra] Fix button label on mobile (#37997) @oliviertassinari +- ​[docs-infra] Square drawer corners (#37970) @oliviertassinari +- ​[docs-infra] Improve tab contrast in codeblock (#38000) @oliviertassinari +- ​[docs-infra] Fix API generation for Base UI (#37941) @oliviertassinari +- ​[docs-infra] Fix layout shift on xGrid (#37954) @oliviertassinari +- ​[docs-infra] Update installation commands to use the new tabs code component (#37927) @danilo-leal +- ​[docs-infra] Improve disableToc={true} support (#37931) @oliviertassinari +- ​[docs-infra] Remove icons and tweak the design of the side nav (#37860) @danilo-leal +- ​[docs-infra] Fix TypeScrit error in demo export (#37830) @oliviertassinari +- ​[notifications] Add notification for first Charts release (#37679) @joserodolfofreitas +- ​[website] Add Base UI marketing page (#36622) @siriwatknp +- ​[website] Update MUI X landing page (#37966) @cherniavskii +- ​[website] Fix a11y issues (#37999) @oliviertassinari +- ​[website] Make the Core page refer to group of products (#37608) @danilo-leal +- ​[website] Add perpetual option to pricing page (#35504) @joserodolfofreitas + +All contributors of this release in alphabetical order: @alexfauquette, @brijeshb42, @cherniavskii, @DanailH, @danilo-leal, @DiegoAndai, @divyammadhok, @ericbrian, @Janpot, @joserodolfofreitas, @Methuselah96, @michaldudak, @mj12albert, @mnajdova, @officialrajdeepsingh, @oliviertassinari, @OmPr366, @OndrejHj04, @prakhargupta1, @sai6855, @samuelsycamore, @siriwatknp, @zanivan, @ZeeshanTamboli + +## 5.14.0 + + + +_Jul 11, 2023_ + +A big thanks to the 15 contributors who made this release possible. Here are some highlights ✨: + +- 💫 Material UI, Joy UI, and Base UI are compatible with [Next.js App Router](https://nextjs.org/docs/app) (#37656) @mj12albert +- 📚 Added new guides for integrating with Next.js 13 App Router (#37656) @mj12albert + - Ⓜ️ [Material UI guide](https://mui.com/material-ui/guides/next-js-app-router/) + - 🅙 [Joy UI guide](https://mui.com/joy-ui/integrations/next-js-app-router/) + - 🅱️ [Base UI guide](https://mui.com/base-ui/guides/next-js-app-router/) +- 🐛 bug fixes, 📚 documentation, and ⚙️ infrastructure improvements. + +### `@mui/material@5.14.0` + +- [Autocomplete] Enable global customization of different options (#36971) @nicolas-ot + +### `@mui/material-next@6.0.0-alpha.92` + +- [Slider][material-next] Slider restructure and style improvements (#37644) @DiegoAndai + +### `@mui/joy@5.0.0-alpha.87` + +- [ButtonGroup] Fix style for single Button (#37692) @MaybePixem +- Fix theme typography fallback value (#37845) @siriwatknp + +### `@mui/icons-material@5.14.0` + +- [icons-material] Rebuild icons with `"use client"` (#37894) @mj12albert + +### Docs + +- [docs] Polish Ukraine banner (#37905) @oliviertassinari +- [docs] Reduce Ukraine banner size (#34795) @oliviertassinari +- [docs] Add callouts about controlled vs uncontrolled components in Core docs (#37849) @samuelsycamore +- [docs] Add missing Portal elements to Tailwind CSS interoperability guide (#37807) @enrique-ramirez +- [docs] Small pickers migration improvement (#37815) @alexfauquette +- [docs] Fix pickers product name (#37825) @LukasTy +- [docs][Joy][Link] Set `variant` and `color` defaults for the playground (#37817) @Studio384 +- [docs][Joy][Table] Add `undefined` as an option to `stripe` (#37816) @Studio384 +- [docs][base] Add Tailwind CSS & plain CSS demo on the Snackbar page (#37812) @mnajdova +- [docs][base] Add Tailwind CSS & plain CSS demo on Badge page (#37768) @mnajdova +- [docs][base] Fix Nested modal demo positioning (#37506) @gitstart +- [docs][base] Add Tailwind CSS & plain CSS demo on the Switch page (#37728) @mnajdova +- [docs-infra] Remove code tags in ToC (#37834) @cherniavskii +- [docs-infra] Fixes in API pages generation (#37813) @mnajdova +- [docs-infra] Add test case when using sh (#37818) @oliviertassinari +- [docs-infra] Use icons instead of words for the code copy button (#37664) @danilo-leal +- [docs-infra] Fix code parser (#37828) @alexfauquette +- [docs-infra] Fix `marked` deprecation warning (#37769) @alexfauquette +- [docs-infra] Allows to use codeblock in the docs (#37643) @alexfauquette +- [docs-infra][joy] Change Joy UI's playground variant selector (#37821) @danilo-leal + +### Core + +- [core] Prepend "use-client" directive + add docs and examples for using MUI libraries with Next.js App Router (#37656) @mj12albert +- [core] Fix imports to React (#37863) @oliviertassinari +- [core] Disambiguate eslint plugin name @oliviertassinari +- [core] Sync the lint script name with the other repositories @oliviertassinari +- [core] Point to Crowdin directly @oliviertassinari +- [website] Sync career page (#37847) @oliviertassinari + +All contributors of this release in alphabetical order: @alexfauquette, @cherniavskii, @danilo-leal, @DiegoAndai, @enrique-ramirez, @gitstart, @LukasTy, @MaybePixem, @mj12albert, @mnajdova, @nicolas-ot, @oliviertassinari, @samuelsycamore, @siriwatknp, @Studio384 + +## 5.13.7 + + + +_Jul 4, 2023_ + +A big thanks to the 21 contributors who made this release possible. +This release focuses primarily on 🐛 bug fixes, 📚 documentation, and ⚙️ infrastructure improvements. + +### `@mui/material@5.13.7` + +- [OutlinedInput] Fix form control properties in `ownerState` (#37668) @vonagam + +### `@mui/system@5.13.7` + +- [Stack] Fix spacing when there are ` + ); +} + +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/AutocompleteIntroduction/css/index.tsx b/docs/data/base/components/autocomplete/AutocompleteIntroduction/css/index.tsx new file mode 100644 index 00000000000000..41dd0c1dc33fe5 --- /dev/null +++ b/docs/data/base/components/autocomplete/AutocompleteIntroduction/css/index.tsx @@ -0,0 +1,438 @@ +import * as React from 'react'; +import { useAutocomplete, UseAutocompleteProps } from '@mui/base/useAutocomplete'; +import { Button } from '@mui/base/Button'; +import { Input } from '@mui/base/Input'; +import { Popper } from '@mui/base/Popper'; +import { useTheme } from '@mui/system'; +import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import ClearIcon from '@mui/icons-material/Clear'; +import clsx from 'clsx'; + +const Autocomplete = React.forwardRef(function Autocomplete( + props: UseAutocompleteProps<(typeof top100Films)[number], false, false, false>, + ref: React.ForwardedRef, +) { + const { + disableClearable = false, + disabled = false, + readOnly = false, + options, + ...other + } = props; + + const { + getRootProps, + getInputProps, + getPopupIndicatorProps, + getClearProps, + getListboxProps, + getOptionProps, + dirty, + id, + popupOpen, + focused, + anchorEl, + setAnchorEl, + groupedOptions, + } = useAutocomplete({ + ...props, + componentName: 'BaseAutocompleteIntroduction', + }); + + const hasClearIcon = !disableClearable && !disabled && dirty && !readOnly; + + const rootRef = useForkRef(ref, setAnchorEl); + + return ( + +
+ + {hasClearIcon && ( + + )} + +
+ {anchorEl ? ( + +
    + {(groupedOptions as typeof top100Films).map((option, index) => { + const optionProps = getOptionProps({ option, index }); + + return ( +
  • + {option.label} +
  • + ); + })} + + {groupedOptions.length === 0 && ( +
  • No results
  • + )} +
+
+ ) : null} + +
+ ); +}); + +export default function AutocompleteIntroduction() { + return ; +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + + return ( + + ); +} + +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/AutocompleteIntroduction/css/index.tsx.preview b/docs/data/base/components/autocomplete/AutocompleteIntroduction/css/index.tsx.preview new file mode 100644 index 00000000000000..0991a681b06d02 --- /dev/null +++ b/docs/data/base/components/autocomplete/AutocompleteIntroduction/css/index.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/base/components/autocomplete/AutocompleteIntroduction/system/index.js b/docs/data/base/components/autocomplete/AutocompleteIntroduction/system/index.js new file mode 100644 index 00000000000000..47593a14592705 --- /dev/null +++ b/docs/data/base/components/autocomplete/AutocompleteIntroduction/system/index.js @@ -0,0 +1,434 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { useAutocomplete } from '@mui/base/useAutocomplete'; +import { Button } from '@mui/base/Button'; +import { Popper } from '@mui/base/Popper'; +import { styled } from '@mui/system'; +import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import ClearIcon from '@mui/icons-material/Clear'; + +const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { + const { + disableClearable = false, + disabled = false, + readOnly = false, + ...other + } = props; + + const { + getRootProps, + getInputProps, + getPopupIndicatorProps, + getClearProps, + getListboxProps, + getOptionProps, + dirty, + id, + popupOpen, + focused, + anchorEl, + setAnchorEl, + groupedOptions, + } = useAutocomplete({ + ...props, + componentName: 'BaseAutocompleteIntroduction', + }); + + const hasClearIcon = !disableClearable && !disabled && dirty && !readOnly; + + const rootRef = useForkRef(ref, setAnchorEl); + + return ( + + + + {hasClearIcon && ( + + + + )} + + + + + + {anchorEl ? ( + + + {groupedOptions.map((option, index) => { + const optionProps = getOptionProps({ option, index }); + + return {option.label}; + })} + + {groupedOptions.length === 0 && ( + No results + )} + + + ) : null} + + ); +}); + +Autocomplete.propTypes = { + /** + * If `true`, the input can't be cleared. + * @default false + */ + disableClearable: PropTypes.oneOf([false]), + /** + * If `true`, the component is disabled. + * @default false + */ + disabled: PropTypes.bool, + /** + * If `true`, the component becomes readonly. It is also supported for multiple tags where the tag cannot be deleted. + * @default false + */ + readOnly: PropTypes.bool, +}; + +export default function AutocompleteIntroduction() { + return ; +} + +const blue = { + 100: '#DAECFF', + 200: '#99CCF3', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledAutocompleteRoot = styled('div')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 400; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 4px 6px ${ + theme.palette.mode === 'dark' ? 'rgba(0,0,0, 0.50)' : 'rgba(0,0,0, 0.05)' + }; + display: flex; + gap: 5px; + padding-right: 5px; + overflow: hidden; + width: 320px; + + &.focused { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &:hover { + background: ${theme.palette.mode === 'dark' ? grey[800] : grey[50]}; + border-color: ${theme.palette.mode === 'dark' ? grey[600] : grey[300]}; + } + + &:focus-visible { + outline: 0; + } +`, +); + +const StyledInput = styled('input')( + ({ theme }) => ` + font-size: 0.875rem; + font-family: inherit; + font-weight: 400; + line-height: 1.5; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: inherit; + border: none; + border-radius: inherit; + padding: 8px 12px; + outline: 0; + flex: 1 0 auto; +`, +); + +// ComponentPageTabs has z-index: 1000 +const StyledPopper = styled('div')` + position: relative; + z-index: 1001; + width: 320px; +`; + +const StyledListbox = styled('ul')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + box-sizing: border-box; + padding: 6px; + margin: 12px 0; + min-width: 320px; + border-radius: 12px; + overflow: auto; + outline: 0px; + max-height: 300px; + z-index: 1; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + box-shadow: 0px 4px 6px ${ + theme.palette.mode === 'dark' ? 'rgba(0,0,0, 0.50)' : 'rgba(0,0,0, 0.05)' + }; + `, +); + +const StyledOption = styled('li')( + ({ theme }) => ` + list-style: none; + padding: 8px; + border-radius: 8px; + cursor: default; + + &:last-of-type { + border-bottom: none; + } + + &:hover { + cursor: pointer; + } + + &[aria-selected=true] { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + + &.Mui-focused, + &.Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + } + + &.Mui-focusVisible { + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &[aria-selected=true].Mui-focused, + &[aria-selected=true].Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + `, +); + +const StyledPopupIndicator = styled(Button)( + ({ theme }) => ` + outline: 0; + box-shadow: none; + border: 0; + border-radius: 4px; + background-color: transparent; + align-self: center; + padding: 0 2px; + + &:hover { + background-color: ${theme.palette.mode === 'dark' ? grey[700] : blue[100]}; + cursor: pointer; + } + + & > svg { + transform: translateY(2px); + } + + &.popupOpen > svg { + transform: translateY(2px) rotate(180deg); + } + `, +); + +const StyledClearIndicator = styled(Button)( + ({ theme }) => ` + outline: 0; + box-shadow: none; + border: 0; + border-radius: 4px; + background-color: transparent; + align-self: center; + padding: 0 2px; + + &:hover { + background-color: ${theme.palette.mode === 'dark' ? grey[700] : blue[100]}; + cursor: pointer; + } + + & > svg { + transform: translateY(2px) scale(0.9); + } + `, +); + +const StyledNoOptions = styled('li')` + list-style: none; + padding: 8px; + cursor: default; +`; + +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/AutocompleteIntroduction/system/index.tsx b/docs/data/base/components/autocomplete/AutocompleteIntroduction/system/index.tsx new file mode 100644 index 00000000000000..32447970f4c752 --- /dev/null +++ b/docs/data/base/components/autocomplete/AutocompleteIntroduction/system/index.tsx @@ -0,0 +1,417 @@ +import * as React from 'react'; +import { useAutocomplete, UseAutocompleteProps } from '@mui/base/useAutocomplete'; +import { Button } from '@mui/base/Button'; +import { Popper } from '@mui/base/Popper'; +import { styled } from '@mui/system'; +import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import ClearIcon from '@mui/icons-material/Clear'; + +const Autocomplete = React.forwardRef(function Autocomplete( + props: UseAutocompleteProps<(typeof top100Films)[number], false, false, false>, + ref: React.ForwardedRef, +) { + const { + disableClearable = false, + disabled = false, + readOnly = false, + ...other + } = props; + + const { + getRootProps, + getInputProps, + getPopupIndicatorProps, + getClearProps, + getListboxProps, + getOptionProps, + dirty, + id, + popupOpen, + focused, + anchorEl, + setAnchorEl, + groupedOptions, + } = useAutocomplete({ + ...props, + componentName: 'BaseAutocompleteIntroduction', + }); + + const hasClearIcon = !disableClearable && !disabled && dirty && !readOnly; + + const rootRef = useForkRef(ref, setAnchorEl); + + return ( + + + + {hasClearIcon && ( + + + + )} + + + + + {anchorEl ? ( + + + {(groupedOptions as typeof top100Films).map((option, index) => { + const optionProps = getOptionProps({ option, index }); + + return {option.label}; + })} + + {groupedOptions.length === 0 && ( + No results + )} + + + ) : null} + + ); +}); + +export default function AutocompleteIntroduction() { + return ; +} + +const blue = { + 100: '#DAECFF', + 200: '#99CCF3', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledAutocompleteRoot = styled('div')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 400; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 4px 6px ${ + theme.palette.mode === 'dark' ? 'rgba(0,0,0, 0.50)' : 'rgba(0,0,0, 0.05)' + }; + display: flex; + gap: 5px; + padding-right: 5px; + overflow: hidden; + width: 320px; + + &.focused { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &:hover { + background: ${theme.palette.mode === 'dark' ? grey[800] : grey[50]}; + border-color: ${theme.palette.mode === 'dark' ? grey[600] : grey[300]}; + } + + &:focus-visible { + outline: 0; + } +`, +); + +const StyledInput = styled('input')( + ({ theme }) => ` + font-size: 0.875rem; + font-family: inherit; + font-weight: 400; + line-height: 1.5; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: inherit; + border: none; + border-radius: inherit; + padding: 8px 12px; + outline: 0; + flex: 1 0 auto; +`, +); + +// ComponentPageTabs has z-index: 1000 +const StyledPopper = styled('div')` + position: relative; + z-index: 1001; + width: 320px; +`; + +const StyledListbox = styled('ul')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + box-sizing: border-box; + padding: 6px; + margin: 12px 0; + min-width: 320px; + border-radius: 12px; + overflow: auto; + outline: 0px; + max-height: 300px; + z-index: 1; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + box-shadow: 0px 4px 6px ${ + theme.palette.mode === 'dark' ? 'rgba(0,0,0, 0.50)' : 'rgba(0,0,0, 0.05)' + }; + `, +); + +const StyledOption = styled('li')( + ({ theme }) => ` + list-style: none; + padding: 8px; + border-radius: 8px; + cursor: default; + + &:last-of-type { + border-bottom: none; + } + + &:hover { + cursor: pointer; + } + + &[aria-selected=true] { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + + &.Mui-focused, + &.Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + } + + &.Mui-focusVisible { + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &[aria-selected=true].Mui-focused, + &[aria-selected=true].Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + `, +); + +const StyledPopupIndicator = styled(Button)( + ({ theme }) => ` + outline: 0; + box-shadow: none; + border: 0; + border-radius: 4px; + background-color: transparent; + align-self: center; + padding: 0 2px; + + &:hover { + background-color: ${theme.palette.mode === 'dark' ? grey[700] : blue[100]}; + cursor: pointer; + } + + & > svg { + transform: translateY(2px); + } + + &.popupOpen > svg { + transform: translateY(2px) rotate(180deg); + } + `, +); + +const StyledClearIndicator = styled(Button)( + ({ theme }) => ` + outline: 0; + box-shadow: none; + border: 0; + border-radius: 4px; + background-color: transparent; + align-self: center; + padding: 0 2px; + + &:hover { + background-color: ${theme.palette.mode === 'dark' ? grey[700] : blue[100]}; + cursor: pointer; + } + + & > svg { + transform: translateY(2px) scale(0.9); + } + `, +); + +const StyledNoOptions = styled('li')` + list-style: none; + padding: 8px; + cursor: default; +`; + +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/AutocompleteIntroduction/system/index.tsx.preview b/docs/data/base/components/autocomplete/AutocompleteIntroduction/system/index.tsx.preview new file mode 100644 index 00000000000000..0991a681b06d02 --- /dev/null +++ b/docs/data/base/components/autocomplete/AutocompleteIntroduction/system/index.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/base/components/autocomplete/AutocompleteIntroduction/tailwind/index.js b/docs/data/base/components/autocomplete/AutocompleteIntroduction/tailwind/index.js new file mode 100644 index 00000000000000..d80f7a1d5c54f7 --- /dev/null +++ b/docs/data/base/components/autocomplete/AutocompleteIntroduction/tailwind/index.js @@ -0,0 +1,294 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { useAutocomplete } from '@mui/base/useAutocomplete'; +import { Button } from '@mui/base/Button'; +import { Popper } from '@mui/base/Popper'; +import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import ClearIcon from '@mui/icons-material/Clear'; +import clsx from 'clsx'; + +const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { + const { + disableClearable = false, + disabled = false, + readOnly = false, + options, + isOptionEqualToValue, + ...other + } = props; + + const { + getRootProps, + getInputProps, + getPopupIndicatorProps, + getClearProps, + getListboxProps, + getOptionProps, + dirty, + id, + popupOpen, + focused, + anchorEl, + setAnchorEl, + groupedOptions, + } = useAutocomplete({ + ...props, + componentName: 'BaseAutocompleteIntroduction', + }); + + const hasClearIcon = !disableClearable && !disabled && dirty && !readOnly; + + const rootRef = useForkRef(ref, setAnchorEl); + + return ( + +
+ + {hasClearIcon && ( + + )} + + +
+ {anchorEl ? ( + +
    + {groupedOptions.map((option, index) => { + const optionProps = getOptionProps({ option, index }); + + return ( +
  • + {option.label} +
  • + ); + })} + + {groupedOptions.length === 0 && ( +
  • No results
  • + )} +
+
+ ) : null} +
+ ); +}); + +Autocomplete.propTypes = { + /** + * If `true`, the input can't be cleared. + * @default false + */ + disableClearable: PropTypes.oneOf([false]), + /** + * If `true`, the component is disabled. + * @default false + */ + disabled: PropTypes.bool, + /** + * Used to determine if the option represents the given value. + * Uses strict equality by default. + * ⚠️ Both arguments need to be handled, an option can only match with one value. + * + * @param {Value} option The option to test. + * @param {Value} value The value to test against. + * @returns {boolean} + */ + isOptionEqualToValue: PropTypes.func, + /** + * Array of options. + */ + options: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string.isRequired, + year: PropTypes.number.isRequired, + }), + ).isRequired, + /** + * If `true`, the component becomes readonly. It is also supported for multiple tags where the tag cannot be deleted. + * @default false + */ + readOnly: PropTypes.bool, +}; + +export default function AutocompleteIntroduction() { + return ( + option.label === value.label} + /> + ); +} + +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/AutocompleteIntroduction/tailwind/index.tsx b/docs/data/base/components/autocomplete/AutocompleteIntroduction/tailwind/index.tsx new file mode 100644 index 00000000000000..bb3f5d16d9cbaf --- /dev/null +++ b/docs/data/base/components/autocomplete/AutocompleteIntroduction/tailwind/index.tsx @@ -0,0 +1,258 @@ +import * as React from 'react'; +import { useAutocomplete, UseAutocompleteProps } from '@mui/base/useAutocomplete'; +import { Button } from '@mui/base/Button'; +import { Popper } from '@mui/base/Popper'; +import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import ClearIcon from '@mui/icons-material/Clear'; +import clsx from 'clsx'; + +const Autocomplete = React.forwardRef(function Autocomplete( + props: UseAutocompleteProps<(typeof top100Films)[number], false, false, false>, + ref: React.ForwardedRef, +) { + const { + disableClearable = false, + disabled = false, + readOnly = false, + options, + isOptionEqualToValue, + ...other + } = props; + + const { + getRootProps, + getInputProps, + getPopupIndicatorProps, + getClearProps, + getListboxProps, + getOptionProps, + dirty, + id, + popupOpen, + focused, + anchorEl, + setAnchorEl, + groupedOptions, + } = useAutocomplete({ + ...props, + componentName: 'BaseAutocompleteIntroduction', + }); + + const hasClearIcon = !disableClearable && !disabled && dirty && !readOnly; + + const rootRef = useForkRef(ref, setAnchorEl); + + return ( + +
+ + {hasClearIcon && ( + + )} + +
+ {anchorEl ? ( + +
    + {(groupedOptions as typeof top100Films).map((option, index) => { + const optionProps = getOptionProps({ option, index }); + + return ( +
  • + {option.label} +
  • + ); + })} + + {groupedOptions.length === 0 && ( +
  • No results
  • + )} +
+
+ ) : null} +
+ ); +}); + +export default function AutocompleteIntroduction() { + return ( + option.label === value.label} + /> + ); +} + +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/AutocompleteIntroduction/tailwind/index.tsx.preview b/docs/data/base/components/autocomplete/AutocompleteIntroduction/tailwind/index.tsx.preview new file mode 100644 index 00000000000000..78a5cab783fea3 --- /dev/null +++ b/docs/data/base/components/autocomplete/AutocompleteIntroduction/tailwind/index.tsx.preview @@ -0,0 +1,4 @@ + option.label === value.label} +/> \ No newline at end of file diff --git a/docs/data/base/components/autocomplete/ControlledStates.js b/docs/data/base/components/autocomplete/ControlledStates.js new file mode 100644 index 00000000000000..e795ac73d10e07 --- /dev/null +++ b/docs/data/base/components/autocomplete/ControlledStates.js @@ -0,0 +1,200 @@ +import * as React from 'react'; +import { useAutocomplete } from '@mui/base/useAutocomplete'; +import { styled } from '@mui/system'; + +const options = ['Firefox', 'Google Chrome', 'Microsoft Edge', 'Safari', 'Opera']; + +export default function ControlledStates() { + const [value, setValue] = React.useState(options[0]); + const [inputValue, setInputValue] = React.useState(''); + + const { + getRootProps, + getInputProps, + getListboxProps, + getOptionProps, + groupedOptions, + focused, + } = useAutocomplete({ + id: 'controlled-state-demo', + options, + value, + onChange: (event, newValue) => setValue(newValue), + inputValue, + onInputChange: (event, newInputValue) => setInputValue(newInputValue), + }); + + return ( + +
+        value: {value ?? ' '}
+      
+
+        inputValue: {inputValue ?? ' '}
+      
+ + + + + {groupedOptions.length > 0 && ( + + {groupedOptions.map((option, index) => ( + + {option} + + ))} + + )} + +
+ ); +} + +const blue = { + 100: '#DAECFF', + 200: '#99CCF3', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledAutocomplete = styled('div')` + position: relative; + margin: 1.5rem 0; +`; + +const StyledInputRoot = styled('div')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 400; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; + display: flex; + gap: 5px; + padding-right: 5px; + overflow: hidden; + width: 320px; + + &.focused { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &:hover { + border-color: ${blue[400]}; + } + + &:focus-visible { + outline: 0; + } +`, +); + +const StyledInput = styled('input')( + ({ theme }) => ` + font-size: 0.875rem; + font-family: inherit; + font-weight: 400; + line-height: 1.5; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: inherit; + border: none; + border-radius: inherit; + padding: 8px 12px; + outline: 0; + flex: 1 0 auto; +`, +); + +const StyledListbox = styled('ul')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + box-sizing: border-box; + padding: 6px; + margin: 12px 0; + max-width: 320px; + border-radius: 12px; + overflow: auto; + outline: 0px; + max-height: 300px; + z-index: 1; + position: absolute; + left: 0; + right: 0; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[200]}; + `, +); + +const StyledOption = styled('li')( + ({ theme }) => ` + list-style: none; + padding: 8px; + border-radius: 8px; + cursor: default; + + &:last-of-type { + border-bottom: none; + } + + &:hover { + cursor: pointer; + } + + &[aria-selected=true] { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + + &.Mui-focused, + &.Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + } + + &.Mui-focusVisible { + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &[aria-selected=true].Mui-focused, + &[aria-selected=true].Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + `, +); + +const Layout = styled('div')` + display: flex; + flex-flow: column nowrap; +`; + +const Pre = styled('pre')(({ theme }) => ({ + margin: '0.5rem 0', + '& code': { + backgroundColor: theme.palette.mode === 'light' ? '#ebebef' : '#25252d', + color: theme.palette.mode === 'light' ? '#000' : '#fff', + padding: '0.125rem 0.25rem', + borderRadius: 3, + }, +})); diff --git a/docs/data/base/components/autocomplete/ControlledStates.tsx b/docs/data/base/components/autocomplete/ControlledStates.tsx new file mode 100644 index 00000000000000..4bb540fe33578c --- /dev/null +++ b/docs/data/base/components/autocomplete/ControlledStates.tsx @@ -0,0 +1,200 @@ +import * as React from 'react'; +import { useAutocomplete } from '@mui/base/useAutocomplete'; +import { styled } from '@mui/system'; + +const options = ['Firefox', 'Google Chrome', 'Microsoft Edge', 'Safari', 'Opera']; + +export default function ControlledStates() { + const [value, setValue] = React.useState(options[0]); + const [inputValue, setInputValue] = React.useState(''); + + const { + getRootProps, + getInputProps, + getListboxProps, + getOptionProps, + groupedOptions, + focused, + } = useAutocomplete({ + id: 'controlled-state-demo', + options, + value, + onChange: (event, newValue) => setValue(newValue), + inputValue, + onInputChange: (event, newInputValue) => setInputValue(newInputValue), + }); + + return ( + +
+        value: {value ?? ' '}
+      
+
+        inputValue: {inputValue ?? ' '}
+      
+ + + + + {groupedOptions.length > 0 && ( + + {(groupedOptions as string[]).map((option, index) => ( + + {option} + + ))} + + )} + +
+ ); +} + +const blue = { + 100: '#DAECFF', + 200: '#99CCF3', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledAutocomplete = styled('div')` + position: relative; + margin: 1.5rem 0; +`; + +const StyledInputRoot = styled('div')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 400; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; + display: flex; + gap: 5px; + padding-right: 5px; + overflow: hidden; + width: 320px; + + &.focused { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &:hover { + border-color: ${blue[400]}; + } + + &:focus-visible { + outline: 0; + } +`, +); + +const StyledInput = styled('input')( + ({ theme }) => ` + font-size: 0.875rem; + font-family: inherit; + font-weight: 400; + line-height: 1.5; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: inherit; + border: none; + border-radius: inherit; + padding: 8px 12px; + outline: 0; + flex: 1 0 auto; +`, +); + +const StyledListbox = styled('ul')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + box-sizing: border-box; + padding: 6px; + margin: 12px 0; + max-width: 320px; + border-radius: 12px; + overflow: auto; + outline: 0px; + max-height: 300px; + z-index: 1; + position: absolute; + left: 0; + right: 0; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[200]}; + `, +); + +const StyledOption = styled('li')( + ({ theme }) => ` + list-style: none; + padding: 8px; + border-radius: 8px; + cursor: default; + + &:last-of-type { + border-bottom: none; + } + + &:hover { + cursor: pointer; + } + + &[aria-selected=true] { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + + &.Mui-focused, + &.Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + } + + &.Mui-focusVisible { + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &[aria-selected=true].Mui-focused, + &[aria-selected=true].Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + `, +); + +const Layout = styled('div')` + display: flex; + flex-flow: column nowrap; +`; + +const Pre = styled('pre')(({ theme }) => ({ + margin: '0.5rem 0', + '& code': { + backgroundColor: theme.palette.mode === 'light' ? '#ebebef' : '#25252d', + color: theme.palette.mode === 'light' ? '#000' : '#fff', + padding: '0.125rem 0.25rem', + borderRadius: 3, + }, +})); diff --git a/docs/data/base/components/autocomplete/UseAutocomplete.js b/docs/data/base/components/autocomplete/UseAutocomplete.js new file mode 100644 index 00000000000000..47acbc2343b82a --- /dev/null +++ b/docs/data/base/components/autocomplete/UseAutocomplete.js @@ -0,0 +1,309 @@ +import * as React from 'react'; +import { useAutocomplete } from '@mui/base/useAutocomplete'; +import { styled } from '@mui/system'; + +export default function UseAutocomplete() { + const [value, setValue] = React.useState(null); + + const { + getRootProps, + getInputLabelProps, + getInputProps, + getListboxProps, + getOptionProps, + groupedOptions, + focused, + } = useAutocomplete({ + id: 'use-autocomplete-demo', + options: top100Films, + getOptionLabel: (option) => option.label, + value, + onChange: (event, newValue) => setValue(newValue), + }); + + return ( +
+ Pick a movie + + + + {groupedOptions.length > 0 && ( + + {groupedOptions.map((option, index) => ( + + {option.label} + + ))} + + )} +
+ ); +} + +const blue = { + 100: '#DAECFF', + 200: '#99CCF3', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledLabel = styled('label')` + display: block; + font-family: sans-serif; + font-size: 14px; + font-weight: 500; + margin-bottom: 4px; +`; + +const StyledAutocompleteRoot = styled('div')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 400; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; + display: flex; + gap: 5px; + padding-right: 5px; + overflow: hidden; + width: 320px; + + &.focused { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &:hover { + border-color: ${blue[400]}; + } + + &:focus-visible { + outline: 0; + } +`, +); + +const StyledInput = styled('input')( + ({ theme }) => ` + font-size: 0.875rem; + font-family: inherit; + font-weight: 400; + line-height: 1.5; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: inherit; + border: none; + border-radius: inherit; + padding: 8px 12px; + outline: 0; + flex: 1 0 auto; +`, +); + +const StyledListbox = styled('ul')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + box-sizing: border-box; + padding: 6px; + margin: 12px 0; + width: 320px; + border-radius: 12px; + overflow: auto; + outline: 0px; + max-height: 300px; + z-index: 1; + position: absolute; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + box-shadow: 0px 4px 6px ${ + theme.palette.mode === 'dark' ? 'rgba(0,0,0, 0.50)' : 'rgba(0,0,0, 0.05)' + }; + `, +); + +const StyledOption = styled('li')( + ({ theme }) => ` + list-style: none; + padding: 8px; + border-radius: 8px; + cursor: default; + + &:last-of-type { + border-bottom: none; + } + + &:hover { + cursor: pointer; + } + + &[aria-selected=true] { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + + &.Mui-focused, + &.Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + } + + &.Mui-focusVisible { + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &[aria-selected=true].Mui-focused, + &[aria-selected=true].Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + `, +); + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/UseAutocomplete.tsx b/docs/data/base/components/autocomplete/UseAutocomplete.tsx new file mode 100644 index 00000000000000..be8b95aa2e66db --- /dev/null +++ b/docs/data/base/components/autocomplete/UseAutocomplete.tsx @@ -0,0 +1,311 @@ +import * as React from 'react'; +import { useAutocomplete } from '@mui/base/useAutocomplete'; +import { styled } from '@mui/system'; + +export default function UseAutocomplete() { + const [value, setValue] = React.useState<(typeof top100Films)[number] | null>( + null, + ); + + const { + getRootProps, + getInputLabelProps, + getInputProps, + getListboxProps, + getOptionProps, + groupedOptions, + focused, + } = useAutocomplete({ + id: 'use-autocomplete-demo', + options: top100Films, + getOptionLabel: (option) => option.label, + value, + onChange: (event, newValue) => setValue(newValue), + }); + + return ( +
+ Pick a movie + + + + {groupedOptions.length > 0 && ( + + {(groupedOptions as typeof top100Films).map((option, index) => ( + + {option.label} + + ))} + + )} +
+ ); +} + +const blue = { + 100: '#DAECFF', + 200: '#99CCF3', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledLabel = styled('label')` + display: block; + font-family: sans-serif; + font-size: 14px; + font-weight: 500; + margin-bottom: 4px; +`; + +const StyledAutocompleteRoot = styled('div')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 400; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; + display: flex; + gap: 5px; + padding-right: 5px; + overflow: hidden; + width: 320px; + + &.focused { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &:hover { + border-color: ${blue[400]}; + } + + &:focus-visible { + outline: 0; + } +`, +); + +const StyledInput = styled('input')( + ({ theme }) => ` + font-size: 0.875rem; + font-family: inherit; + font-weight: 400; + line-height: 1.5; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: inherit; + border: none; + border-radius: inherit; + padding: 8px 12px; + outline: 0; + flex: 1 0 auto; +`, +); + +const StyledListbox = styled('ul')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + box-sizing: border-box; + padding: 6px; + margin: 12px 0; + width: 320px; + border-radius: 12px; + overflow: auto; + outline: 0px; + max-height: 300px; + z-index: 1; + position: absolute; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + box-shadow: 0px 4px 6px ${ + theme.palette.mode === 'dark' ? 'rgba(0,0,0, 0.50)' : 'rgba(0,0,0, 0.05)' + }; + `, +); + +const StyledOption = styled('li')( + ({ theme }) => ` + list-style: none; + padding: 8px; + border-radius: 8px; + cursor: default; + + &:last-of-type { + border-bottom: none; + } + + &:hover { + cursor: pointer; + } + + &[aria-selected=true] { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + + &.Mui-focused, + &.Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + } + + &.Mui-focusVisible { + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &[aria-selected=true].Mui-focused, + &[aria-selected=true].Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + `, +); + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/UseAutocomplete.tsx.preview b/docs/data/base/components/autocomplete/UseAutocomplete.tsx.preview new file mode 100644 index 00000000000000..1d31a9250a4ceb --- /dev/null +++ b/docs/data/base/components/autocomplete/UseAutocomplete.tsx.preview @@ -0,0 +1,16 @@ +Pick a movie + + + +{groupedOptions.length > 0 && ( + + {(groupedOptions as typeof top100Films).map((option, index) => ( + + {option.label} + + ))} + +)} \ No newline at end of file diff --git a/docs/data/base/components/autocomplete/UseAutocompletePopper.js b/docs/data/base/components/autocomplete/UseAutocompletePopper.js new file mode 100644 index 00000000000000..1953f2a977ee5c --- /dev/null +++ b/docs/data/base/components/autocomplete/UseAutocompletePopper.js @@ -0,0 +1,332 @@ +import * as React from 'react'; +import { useAutocomplete } from '@mui/base/useAutocomplete'; +import { Popper } from '@mui/base/Popper'; +import { styled } from '@mui/system'; +import useForkRef from '@mui/utils/useForkRef'; + +const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { + const { + getRootProps, + getInputProps, + getListboxProps, + getOptionProps, + groupedOptions, + focused, + popupOpen, + anchorEl, + setAnchorEl, + } = useAutocomplete(props); + + const rootRef = useForkRef(ref, setAnchorEl); + + return ( + + + + + {anchorEl && ( + + + {groupedOptions.length > 0 ? ( + groupedOptions.map((option, index) => ( + + {option.label} + + )) + ) : ( + No results + )} + + + )} + + ); +}); + +export default function UseAutocompletePopper() { + const [value, setValue] = React.useState(null); + + const handleChange = (event, newValue) => setValue(newValue); + + return ( + + ); +} + +const blue = { + 100: '#DAECFF', + 200: '#99CCF3', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledAutocompleteRoot = styled('div')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 400; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; + display: flex; + gap: 5px; + padding-right: 5px; + overflow: hidden; + width: 320px; + margin: 1.5rem 0; + + &.focused { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &:hover { + border-color: ${blue[400]}; + } + + &:focus-visible { + outline: 0; + } +`, +); + +const StyledInput = styled('input')( + ({ theme }) => ` + font-size: 0.875rem; + font-family: inherit; + font-weight: 400; + line-height: 1.5; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: inherit; + border: none; + border-radius: inherit; + padding: 8px 12px; + outline: 0; + flex: 1 0 auto; +`, +); + +// ComponentPageTabs has z-index: 1000 +const StyledPopper = styled('div')` + position: relative; + z-index: 1001; + width: 320px; +`; + +const StyledListbox = styled('ul')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + box-sizing: border-box; + padding: 6px; + margin: 12px 0; + min-width: 320px; + border-radius: 12px; + overflow: auto; + outline: 0px; + max-height: 300px; + z-index: 1; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[200]}; + `, +); + +const StyledOption = styled('li')( + ({ theme }) => ` + list-style: none; + padding: 8px; + border-radius: 8px; + cursor: default; + + &:last-of-type { + border-bottom: none; + } + + &:hover { + cursor: pointer; + } + + &[aria-selected=true] { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + + &.Mui-focused, + &.Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + } + + &.Mui-focusVisible { + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &[aria-selected=true].Mui-focused, + &[aria-selected=true].Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + `, +); + +const StyledNoOptions = styled('li')` + list-style: none; + padding: 8px; + cursor: default; +`; + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/UseAutocompletePopper.tsx b/docs/data/base/components/autocomplete/UseAutocompletePopper.tsx new file mode 100644 index 00000000000000..64a624887ccdf0 --- /dev/null +++ b/docs/data/base/components/autocomplete/UseAutocompletePopper.tsx @@ -0,0 +1,340 @@ +import * as React from 'react'; +import { useAutocomplete, UseAutocompleteProps } from '@mui/base/useAutocomplete'; +import { Popper } from '@mui/base/Popper'; +import { styled } from '@mui/system'; +import useForkRef from '@mui/utils/useForkRef'; + +const Autocomplete = React.forwardRef(function Autocomplete( + props: UseAutocompleteProps<(typeof top100Films)[number], false, false, false>, + ref: React.ForwardedRef, +) { + const { + getRootProps, + getInputProps, + getListboxProps, + getOptionProps, + groupedOptions, + focused, + popupOpen, + anchorEl, + setAnchorEl, + } = useAutocomplete(props); + + const rootRef = useForkRef(ref, setAnchorEl); + + return ( + + + + + {anchorEl && ( + + + {groupedOptions.length > 0 ? ( + (groupedOptions as typeof top100Films).map((option, index) => ( + + {option.label} + + )) + ) : ( + No results + )} + + + )} + + ); +}); + +export default function UseAutocompletePopper() { + const [value, setValue] = React.useState<(typeof top100Films)[number] | null>( + null, + ); + + const handleChange = ( + event: React.SyntheticEvent, + newValue: (typeof top100Films)[number] | null, + ) => setValue(newValue); + + return ( + + ); +} + +const blue = { + 100: '#DAECFF', + 200: '#99CCF3', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledAutocompleteRoot = styled('div')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 400; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; + display: flex; + gap: 5px; + padding-right: 5px; + overflow: hidden; + width: 320px; + margin: 1.5rem 0; + + &.focused { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &:hover { + border-color: ${blue[400]}; + } + + &:focus-visible { + outline: 0; + } +`, +); + +const StyledInput = styled('input')( + ({ theme }) => ` + font-size: 0.875rem; + font-family: inherit; + font-weight: 400; + line-height: 1.5; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: inherit; + border: none; + border-radius: inherit; + padding: 8px 12px; + outline: 0; + flex: 1 0 auto; +`, +); + +// ComponentPageTabs has z-index: 1000 +const StyledPopper = styled('div')` + position: relative; + z-index: 1001; + width: 320px; +`; + +const StyledListbox = styled('ul')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + box-sizing: border-box; + padding: 6px; + margin: 12px 0; + min-width: 320px; + border-radius: 12px; + overflow: auto; + outline: 0px; + max-height: 300px; + z-index: 1; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[200]}; + `, +); + +const StyledOption = styled('li')( + ({ theme }) => ` + list-style: none; + padding: 8px; + border-radius: 8px; + cursor: default; + + &:last-of-type { + border-bottom: none; + } + + &:hover { + cursor: pointer; + } + + &[aria-selected=true] { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + + &.Mui-focused, + &.Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]}; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + } + + &.Mui-focusVisible { + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &[aria-selected=true].Mui-focused, + &[aria-selected=true].Mui-focusVisible { + background-color: ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + color: ${theme.palette.mode === 'dark' ? blue[100] : blue[900]}; + } + `, +); + +const StyledNoOptions = styled('li')` + list-style: none; + padding: 8px; + cursor: default; +`; + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { label: 'The Shawshank Redemption', year: 1994 }, + { label: 'The Godfather', year: 1972 }, + { label: 'The Godfather: Part II', year: 1974 }, + { label: 'The Dark Knight', year: 2008 }, + { label: '12 Angry Men', year: 1957 }, + { label: "Schindler's List", year: 1993 }, + { label: 'Pulp Fiction', year: 1994 }, + { + label: 'The Lord of the Rings: The Return of the King', + year: 2003, + }, + { label: 'The Good, the Bad and the Ugly', year: 1966 }, + { label: 'Fight Club', year: 1999 }, + { + label: 'The Lord of the Rings: The Fellowship of the Ring', + year: 2001, + }, + { + label: 'Star Wars: Episode V - The Empire Strikes Back', + year: 1980, + }, + { label: 'Forrest Gump', year: 1994 }, + { label: 'Inception', year: 2010 }, + { + label: 'The Lord of the Rings: The Two Towers', + year: 2002, + }, + { label: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { label: 'Goodfellas', year: 1990 }, + { label: 'The Matrix', year: 1999 }, + { label: 'Seven Samurai', year: 1954 }, + { + label: 'Star Wars: Episode IV - A New Hope', + year: 1977, + }, + { label: 'City of God', year: 2002 }, + { label: 'Se7en', year: 1995 }, + { label: 'The Silence of the Lambs', year: 1991 }, + { label: "It's a Wonderful Life", year: 1946 }, + { label: 'Life Is Beautiful', year: 1997 }, + { label: 'The Usual Suspects', year: 1995 }, + { label: 'Léon: The Professional', year: 1994 }, + { label: 'Spirited Away', year: 2001 }, + { label: 'Saving Private Ryan', year: 1998 }, + { label: 'Once Upon a Time in the West', year: 1968 }, + { label: 'American History X', year: 1998 }, + { label: 'Interstellar', year: 2014 }, + { label: 'Casablanca', year: 1942 }, + { label: 'City Lights', year: 1931 }, + { label: 'Psycho', year: 1960 }, + { label: 'The Green Mile', year: 1999 }, + { label: 'The Intouchables', year: 2011 }, + { label: 'Modern Times', year: 1936 }, + { label: 'Raiders of the Lost Ark', year: 1981 }, + { label: 'Rear Window', year: 1954 }, + { label: 'The Pianist', year: 2002 }, + { label: 'The Departed', year: 2006 }, + { label: 'Terminator 2: Judgment Day', year: 1991 }, + { label: 'Back to the Future', year: 1985 }, + { label: 'Whiplash', year: 2014 }, + { label: 'Gladiator', year: 2000 }, + { label: 'Memento', year: 2000 }, + { label: 'The Prestige', year: 2006 }, + { label: 'The Lion King', year: 1994 }, + { label: 'Apocalypse Now', year: 1979 }, + { label: 'Alien', year: 1979 }, + { label: 'Sunset Boulevard', year: 1950 }, + { + label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { label: 'The Great Dictator', year: 1940 }, + { label: 'Cinema Paradiso', year: 1988 }, + { label: 'The Lives of Others', year: 2006 }, + { label: 'Grave of the Fireflies', year: 1988 }, + { label: 'Paths of Glory', year: 1957 }, + { label: 'Django Unchained', year: 2012 }, + { label: 'The Shining', year: 1980 }, + { label: 'WALL·E', year: 2008 }, + { label: 'American Beauty', year: 1999 }, + { label: 'The Dark Knight Rises', year: 2012 }, + { label: 'Princess Mononoke', year: 1997 }, + { label: 'Aliens', year: 1986 }, + { label: 'Oldboy', year: 2003 }, + { label: 'Once Upon a Time in America', year: 1984 }, + { label: 'Witness for the Prosecution', year: 1957 }, + { label: 'Das Boot', year: 1981 }, + { label: 'Citizen Kane', year: 1941 }, + { label: 'North by Northwest', year: 1959 }, + { label: 'Vertigo', year: 1958 }, + { + label: 'Star Wars: Episode VI - Return of the Jedi', + year: 1983, + }, + { label: 'Reservoir Dogs', year: 1992 }, + { label: 'Braveheart', year: 1995 }, + { label: 'M', year: 1931 }, + { label: 'Requiem for a Dream', year: 2000 }, + { label: 'Amélie', year: 2001 }, + { label: 'A Clockwork Orange', year: 1971 }, + { label: 'Like Stars on Earth', year: 2007 }, + { label: 'Taxi Driver', year: 1976 }, + { label: 'Lawrence of Arabia', year: 1962 }, + { label: 'Double Indemnity', year: 1944 }, + { + label: 'Eternal Sunshine of the Spotless Mind', + year: 2004, + }, + { label: 'Amadeus', year: 1984 }, + { label: 'To Kill a Mockingbird', year: 1962 }, + { label: 'Toy Story 3', year: 2010 }, + { label: 'Logan', year: 2017 }, + { label: 'Full Metal Jacket', year: 1987 }, + { label: 'Dangal', year: 2016 }, + { label: 'The Sting', year: 1973 }, + { label: '2001: A Space Odyssey', year: 1968 }, + { label: "Singin' in the Rain", year: 1952 }, + { label: 'Toy Story', year: 1995 }, + { label: 'Bicycle Thieves', year: 1948 }, + { label: 'The Kid', year: 1921 }, + { label: 'Inglourious Basterds', year: 2009 }, + { label: 'Snatch', year: 2000 }, + { label: '3 Idiots', year: 2009 }, + { label: 'Monty Python and the Holy Grail', year: 1975 }, +]; diff --git a/docs/data/base/components/autocomplete/UseAutocompletePopper.tsx.preview b/docs/data/base/components/autocomplete/UseAutocompletePopper.tsx.preview new file mode 100644 index 00000000000000..1d9614ad6d6287 --- /dev/null +++ b/docs/data/base/components/autocomplete/UseAutocompletePopper.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/base/components/autocomplete/autocomplete.md b/docs/data/base/components/autocomplete/autocomplete.md new file mode 100644 index 00000000000000..d0a0cb5de11e64 --- /dev/null +++ b/docs/data/base/components/autocomplete/autocomplete.md @@ -0,0 +1,151 @@ +--- +productId: base-ui +title: React Autocomplete hook +hooks: useAutocomplete +githubLabel: 'component: autocomplete' +waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/ +--- + +# Autocomplete + +

An autocomplete component is a text input enhanced by a panel of suggested options.

+ +{{"component": "modules/components/ComponentLinkHeader.js", "design": false}} + +{{"component": "modules/components/ComponentPageTabs.js"}} + +## Introduction + +An autocomplete component is an enhanced text input that shows a list of suggested options as users type, and lets them select an option from the list. + +Base UI provides the `useAutocomplete` hook for building a custom Autocomplete. +It implements the WAI-ARIA Combobox pattern, and is typically used to assist users in completing form inputs or search queries faster. + +{{"demo": "AutocompleteIntroduction", "defaultCodeOpen": false, "bg": "gradient"}} + +:::warning +Material UI and Joy UI have Autocomplete components that are built using the `useAutocomplete` hook, and they include many features not yet described here. + +To learn more about implementing a custom Autocomplete, you can explore the [`useAutocomplete` API docs](/base-ui/react-autocomplete/hooks-api/#use-autocomplete), or reference the Material UI and Joy UI implementations: + +- [Material UI Autocomplete](/material-ui/react-autocomplete/) +- [Joy UI Autocomplete](/joy-ui/react-autocomplete/) + +::: + +## Hook + +```jsx +import { useAutocomplete } from '@mui/base/useAutocomplete'; +``` + +The `useAutocomplete` hook requires a list of `options` to be displayed when the textbox receives focus. +The value must be chosen from a predefined set of values. + +The following demo shows how to create a simple combobox, apply styles, and write the selected value to a state variable using the `onChange` prop: + +{{"demo": "UseAutocomplete.js"}} + +## Customization + +### Rendering options + +By default, the `options` prop accepts an array of `string`s or `{ label: string }`: + +```js +const options = [ + { label: 'The Godfather', id: 1 }, + { label: 'Pulp Fiction', id: 2 }, +]; +// or +const options = ['The Godfather', 'Pulp Fiction']; +``` + +If you need to use a different structure for options, you must provide a function to the `getOptionLabel` prop that resolves each option to a unique value. + +```js +const options = [ + { issuer: 'Bank of America', brand: 'Visa', last4: '1234' }, + { issuer: 'Bank of America', brand: 'MasterCard', last4: '5678' }, + { issuer: 'Barclays', brand: 'Visa', last4: '4698' }, + // ... +]; + +const { + getRootProps, + // etc +} = useAutocomplete({ + getOptionLabel: (option) => option.last4, +}); +``` + +### Controlled states + +The `useAutocomplete` hook has two states that can be controlled: + +1. the "value" state with the `value`/`onChange` props combination. This state represents the value selected by the user, for instance when pressing Enter. +2. the "input value" state with the `inputValue`/`onInputChange` props combination. This state represents the value displayed in the textbox. + +These two states are isolated, and should be controlled independently. + +:::info + +- A component is **controlled** when it's managed by its parent using props. +- A component is **uncontrolled** when it's managed by its own local state. + +Learn more about controlled and uncontrolled components in the [React documentation](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). +::: + +{{"demo": "ControlledStates.js"}} + +### Using a portal + +React Portals can be used to render the listbox outside of the DOM hierarchy, making it easier to allow it to "float" above adjacent elements. + +Base UI provides a [Popper](/base-ui/react-popper/) component built around React's `createPortal()` for exactly this purpose, and additionally helps you manage keyboard focus as it moves in and out of the portal. + +To render the listbox in Base UI's Popper, the `ref`s must be merged as follows: + +```jsx +import { useAutocomplete } from '@mui/base/useAutocomplete'; +import { Popper } from '@mui/base/Popper'; +import { unstable_useForkRef as useForkRef } from '@mui/utils'; + +export default function App(props) { + const { + getRootProps, + getInputProps, + getListboxProps, + getOptionProps, + popupOpen, + anchorEl, + setAnchorEl, + groupedOptions, + } = useAutocomplete(props); + + const rootRef = useForkRef(ref, setAnchorEl); + + return ( + +
+ +
+ {anchorEl && ( + + {groupedOptions.length > 0 && ( +
    + {groupedOptions.map((option, index) => ( +
  • {option.label}
  • + ))} +
+ )} +
+ )} +
+ ); +} +``` + +Here's a complete demo that renders the listbox inside a Popper: + +{{"demo": "UseAutocompletePopper.js", "defaultCodeOpen": false}} diff --git a/docs/data/base/components/badge/AccessibleBadges.js b/docs/data/base/components/badge/AccessibleBadges.js index 9eb163092ff855..cfca9428e4a6e3 100644 --- a/docs/data/base/components/badge/AccessibleBadges.js +++ b/docs/data/base/components/badge/AccessibleBadges.js @@ -1,8 +1,27 @@ import * as React from 'react'; import { styled } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; +import { Badge, badgeClasses } from '@mui/base/Badge'; import MailIcon from '@mui/icons-material/Mail'; +function notificationsLabel(count) { + if (count === 0) { + return 'no notifications'; + } + if (count > 99) { + return 'more than 99 notifications'; + } + return `${count} notifications`; +} + +export default function AccessibleBadges() { + return ( +
+ + + +
+ ); +} const blue = { 500: '#007FFF', }; @@ -12,7 +31,7 @@ const grey = { 900: '#24292f', }; -const StyledBadge = styled(BadgeUnstyled)( +const StyledBadge = styled(Badge)( ({ theme }) => ` box-sizing: border-box; margin: 0; @@ -24,7 +43,7 @@ const StyledBadge = styled(BadgeUnstyled)( display: inline-block; line-height: 1; - & .${badgeUnstyledClasses.badge} { + & .${badgeClasses.badge} { z-index: auto; position: absolute; top: 0; @@ -46,23 +65,3 @@ const StyledBadge = styled(BadgeUnstyled)( } `, ); - -function notificationsLabel(count) { - if (count === 0) { - return 'no notifications'; - } - if (count > 99) { - return 'more than 99 notifications'; - } - return `${count} notifications`; -} - -export default function AccessibleBadges() { - return ( -
- - - -
- ); -} diff --git a/docs/data/base/components/badge/AccessibleBadges.tsx b/docs/data/base/components/badge/AccessibleBadges.tsx index 1d6006093cd0d0..151ef75b977c8f 100644 --- a/docs/data/base/components/badge/AccessibleBadges.tsx +++ b/docs/data/base/components/badge/AccessibleBadges.tsx @@ -1,8 +1,27 @@ import * as React from 'react'; import { styled } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; +import { Badge, badgeClasses } from '@mui/base/Badge'; import MailIcon from '@mui/icons-material/Mail'; +function notificationsLabel(count: number) { + if (count === 0) { + return 'no notifications'; + } + if (count > 99) { + return 'more than 99 notifications'; + } + return `${count} notifications`; +} + +export default function AccessibleBadges() { + return ( +
+ + + +
+ ); +} const blue = { 500: '#007FFF', }; @@ -12,7 +31,7 @@ const grey = { 900: '#24292f', }; -const StyledBadge = styled(BadgeUnstyled)( +const StyledBadge = styled(Badge)( ({ theme }) => ` box-sizing: border-box; margin: 0; @@ -24,7 +43,7 @@ const StyledBadge = styled(BadgeUnstyled)( display: inline-block; line-height: 1; - & .${badgeUnstyledClasses.badge} { + & .${badgeClasses.badge} { z-index: auto; position: absolute; top: 0; @@ -46,23 +65,3 @@ const StyledBadge = styled(BadgeUnstyled)( } `, ); - -function notificationsLabel(count: number) { - if (count === 0) { - return 'no notifications'; - } - if (count > 99) { - return 'more than 99 notifications'; - } - return `${count} notifications`; -} - -export default function AccessibleBadges() { - return ( -
- - - -
- ); -} diff --git a/docs/data/base/components/badge/BadgeMax.js b/docs/data/base/components/badge/BadgeMax.js index df0553434f79f8..0e00288246b31a 100644 --- a/docs/data/base/components/badge/BadgeMax.js +++ b/docs/data/base/components/badge/BadgeMax.js @@ -1,9 +1,24 @@ import * as React from 'react'; import Stack from '@mui/material/Stack'; import { styled } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; +import { Badge, badgeClasses } from '@mui/base/Badge'; import MailIcon from '@mui/icons-material/Mail'; +export default function BadgeMax() { + return ( + + + + + + + + + + + + ); +} const blue = { 500: '#007FFF', }; @@ -13,7 +28,7 @@ const grey = { 900: '#24292f', }; -const StyledBadge = styled(BadgeUnstyled)( +const StyledBadge = styled(Badge)( ({ theme }) => ` box-sizing: border-box; margin: 0; @@ -25,7 +40,7 @@ const StyledBadge = styled(BadgeUnstyled)( display: inline-block; line-height: 1; - & .${badgeUnstyledClasses.badge} { + & .${badgeClasses.badge} { z-index: auto; position: absolute; top: 0; @@ -47,19 +62,3 @@ const StyledBadge = styled(BadgeUnstyled)( } `, ); - -export default function BadgeMax() { - return ( - - - - - - - - - - - - ); -} diff --git a/docs/data/base/components/badge/BadgeMax.tsx b/docs/data/base/components/badge/BadgeMax.tsx index df0553434f79f8..0e00288246b31a 100644 --- a/docs/data/base/components/badge/BadgeMax.tsx +++ b/docs/data/base/components/badge/BadgeMax.tsx @@ -1,9 +1,24 @@ import * as React from 'react'; import Stack from '@mui/material/Stack'; import { styled } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; +import { Badge, badgeClasses } from '@mui/base/Badge'; import MailIcon from '@mui/icons-material/Mail'; +export default function BadgeMax() { + return ( + + + + + + + + + + + + ); +} const blue = { 500: '#007FFF', }; @@ -13,7 +28,7 @@ const grey = { 900: '#24292f', }; -const StyledBadge = styled(BadgeUnstyled)( +const StyledBadge = styled(Badge)( ({ theme }) => ` box-sizing: border-box; margin: 0; @@ -25,7 +40,7 @@ const StyledBadge = styled(BadgeUnstyled)( display: inline-block; line-height: 1; - & .${badgeUnstyledClasses.badge} { + & .${badgeClasses.badge} { z-index: auto; position: absolute; top: 0; @@ -47,19 +62,3 @@ const StyledBadge = styled(BadgeUnstyled)( } `, ); - -export default function BadgeMax() { - return ( - - - - - - - - - - - - ); -} diff --git a/docs/data/base/components/badge/BadgeVisibility.js b/docs/data/base/components/badge/BadgeVisibility.js index 9cf0cb40f4b627..c9bd9ce045219c 100644 --- a/docs/data/base/components/badge/BadgeVisibility.js +++ b/docs/data/base/components/badge/BadgeVisibility.js @@ -1,7 +1,7 @@ import * as React from 'react'; import Box from '@mui/material/Box'; import { styled } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; +import { Badge, badgeClasses } from '@mui/base/Badge'; import ButtonGroup from '@mui/material/ButtonGroup'; import Button from '@mui/material/Button'; import AddIcon from '@mui/icons-material/Add'; @@ -10,55 +10,6 @@ import MailIcon from '@mui/icons-material/Mail'; import Switch from '@mui/material/Switch'; import FormControlLabel from '@mui/material/FormControlLabel'; -const blue = { - 500: '#007FFF', -}; - -const grey = { - 300: '#afb8c1', - 900: '#24292f', -}; - -const StyledBadge = styled(BadgeUnstyled)( - ({ theme }) => ` - box-sizing: border-box; - margin: 0; - padding: 0; - font-size: 14px; - list-style: none; - font-family: IBM Plex Sans, sans-serif; - position: relative; - display: inline-block; - line-height: 1; - - & .${badgeUnstyledClasses.badge} { - z-index: auto; - position: absolute; - top: 0; - right: 0; - min-width: 22px; - height: 22px; - padding: 0 6px; - color: #fff; - font-weight: 600; - font-size: 12px; - line-height: 22px; - white-space: nowrap; - text-align: center; - border-radius: 12px; - background: ${blue[500]}; - box-shadow: 0px 4px 6x ${theme.palette.mode === 'dark' ? grey[900] : grey[300]}; - transform: translate(50%, -50%); - transform-origin: 100% 0; - } - - & .${badgeUnstyledClasses.invisible} { - opacity: 0; - pointer-events: none; - } - `, -); - export default function BadgeVisibility() { const [count, setCount] = React.useState(1); const [invisible, setInvisible] = React.useState(false); @@ -76,7 +27,7 @@ export default function BadgeVisibility() { '& > *': { marginBottom: 2, }, - [`& .${badgeUnstyledClasses.root}`]: { + [`& .${badgeClasses.root}`]: { marginRight: 4, }, }} @@ -117,3 +68,51 @@ export default function BadgeVisibility() { ); } +const blue = { + 500: '#007FFF', +}; + +const grey = { + 300: '#afb8c1', + 900: '#24292f', +}; + +const StyledBadge = styled(Badge)( + ({ theme }) => ` + box-sizing: border-box; + margin: 0; + padding: 0; + font-size: 14px; + list-style: none; + font-family: IBM Plex Sans, sans-serif; + position: relative; + display: inline-block; + line-height: 1; + + & .${badgeClasses.badge} { + z-index: auto; + position: absolute; + top: 0; + right: 0; + min-width: 22px; + height: 22px; + padding: 0 6px; + color: #fff; + font-weight: 600; + font-size: 12px; + line-height: 22px; + white-space: nowrap; + text-align: center; + border-radius: 12px; + background: ${blue[500]}; + box-shadow: 0px 4px 6x ${theme.palette.mode === 'dark' ? grey[900] : grey[300]}; + transform: translate(50%, -50%); + transform-origin: 100% 0; + } + + & .${badgeClasses.invisible} { + opacity: 0; + pointer-events: none; + } + `, +); diff --git a/docs/data/base/components/badge/BadgeVisibility.tsx b/docs/data/base/components/badge/BadgeVisibility.tsx index 9cf0cb40f4b627..c9bd9ce045219c 100644 --- a/docs/data/base/components/badge/BadgeVisibility.tsx +++ b/docs/data/base/components/badge/BadgeVisibility.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import Box from '@mui/material/Box'; import { styled } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; +import { Badge, badgeClasses } from '@mui/base/Badge'; import ButtonGroup from '@mui/material/ButtonGroup'; import Button from '@mui/material/Button'; import AddIcon from '@mui/icons-material/Add'; @@ -10,55 +10,6 @@ import MailIcon from '@mui/icons-material/Mail'; import Switch from '@mui/material/Switch'; import FormControlLabel from '@mui/material/FormControlLabel'; -const blue = { - 500: '#007FFF', -}; - -const grey = { - 300: '#afb8c1', - 900: '#24292f', -}; - -const StyledBadge = styled(BadgeUnstyled)( - ({ theme }) => ` - box-sizing: border-box; - margin: 0; - padding: 0; - font-size: 14px; - list-style: none; - font-family: IBM Plex Sans, sans-serif; - position: relative; - display: inline-block; - line-height: 1; - - & .${badgeUnstyledClasses.badge} { - z-index: auto; - position: absolute; - top: 0; - right: 0; - min-width: 22px; - height: 22px; - padding: 0 6px; - color: #fff; - font-weight: 600; - font-size: 12px; - line-height: 22px; - white-space: nowrap; - text-align: center; - border-radius: 12px; - background: ${blue[500]}; - box-shadow: 0px 4px 6x ${theme.palette.mode === 'dark' ? grey[900] : grey[300]}; - transform: translate(50%, -50%); - transform-origin: 100% 0; - } - - & .${badgeUnstyledClasses.invisible} { - opacity: 0; - pointer-events: none; - } - `, -); - export default function BadgeVisibility() { const [count, setCount] = React.useState(1); const [invisible, setInvisible] = React.useState(false); @@ -76,7 +27,7 @@ export default function BadgeVisibility() { '& > *': { marginBottom: 2, }, - [`& .${badgeUnstyledClasses.root}`]: { + [`& .${badgeClasses.root}`]: { marginRight: 4, }, }} @@ -117,3 +68,51 @@ export default function BadgeVisibility() { ); } +const blue = { + 500: '#007FFF', +}; + +const grey = { + 300: '#afb8c1', + 900: '#24292f', +}; + +const StyledBadge = styled(Badge)( + ({ theme }) => ` + box-sizing: border-box; + margin: 0; + padding: 0; + font-size: 14px; + list-style: none; + font-family: IBM Plex Sans, sans-serif; + position: relative; + display: inline-block; + line-height: 1; + + & .${badgeClasses.badge} { + z-index: auto; + position: absolute; + top: 0; + right: 0; + min-width: 22px; + height: 22px; + padding: 0 6px; + color: #fff; + font-weight: 600; + font-size: 12px; + line-height: 22px; + white-space: nowrap; + text-align: center; + border-radius: 12px; + background: ${blue[500]}; + box-shadow: 0px 4px 6x ${theme.palette.mode === 'dark' ? grey[900] : grey[300]}; + transform: translate(50%, -50%); + transform-origin: 100% 0; + } + + & .${badgeClasses.invisible} { + opacity: 0; + pointer-events: none; + } + `, +); diff --git a/docs/data/base/components/badge/ShowZeroBadge.js b/docs/data/base/components/badge/ShowZeroBadge.js index 67d776c969e496..324ae1d2e4b9f9 100644 --- a/docs/data/base/components/badge/ShowZeroBadge.js +++ b/docs/data/base/components/badge/ShowZeroBadge.js @@ -1,9 +1,22 @@ import * as React from 'react'; import Stack from '@mui/material/Stack'; import { styled } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; +import { Badge, badgeClasses } from '@mui/base/Badge'; import MailIcon from '@mui/icons-material/Mail'; +export default function ShowZeroBadge() { + return ( + + + + + + + + + ); +} + const blue = { 500: '#007FFF', }; @@ -13,7 +26,7 @@ const grey = { 900: '#24292f', }; -const StyledBadge = styled(BadgeUnstyled)( +const StyledBadge = styled(Badge)( ({ theme }) => ` box-sizing: border-box; margin: 0; @@ -25,7 +38,7 @@ const StyledBadge = styled(BadgeUnstyled)( display: inline-block; line-height: 1; - & .${badgeUnstyledClasses.badge} { + & .${badgeClasses.badge} { z-index: auto; position: absolute; top: 0; @@ -46,21 +59,8 @@ const StyledBadge = styled(BadgeUnstyled)( transform-origin: 100% 0; } - & .${badgeUnstyledClasses.invisible} { + & .${badgeClasses.invisible} { display: none; } `, ); - -export default function ShowZeroBadge() { - return ( - - - - - - - - - ); -} diff --git a/docs/data/base/components/badge/ShowZeroBadge.tsx b/docs/data/base/components/badge/ShowZeroBadge.tsx index 67d776c969e496..324ae1d2e4b9f9 100644 --- a/docs/data/base/components/badge/ShowZeroBadge.tsx +++ b/docs/data/base/components/badge/ShowZeroBadge.tsx @@ -1,9 +1,22 @@ import * as React from 'react'; import Stack from '@mui/material/Stack'; import { styled } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; +import { Badge, badgeClasses } from '@mui/base/Badge'; import MailIcon from '@mui/icons-material/Mail'; +export default function ShowZeroBadge() { + return ( + + + + + + + + + ); +} + const blue = { 500: '#007FFF', }; @@ -13,7 +26,7 @@ const grey = { 900: '#24292f', }; -const StyledBadge = styled(BadgeUnstyled)( +const StyledBadge = styled(Badge)( ({ theme }) => ` box-sizing: border-box; margin: 0; @@ -25,7 +38,7 @@ const StyledBadge = styled(BadgeUnstyled)( display: inline-block; line-height: 1; - & .${badgeUnstyledClasses.badge} { + & .${badgeClasses.badge} { z-index: auto; position: absolute; top: 0; @@ -46,21 +59,8 @@ const StyledBadge = styled(BadgeUnstyled)( transform-origin: 100% 0; } - & .${badgeUnstyledClasses.invisible} { + & .${badgeClasses.invisible} { display: none; } `, ); - -export default function ShowZeroBadge() { - return ( - - - - - - - - - ); -} diff --git a/docs/data/base/components/badge/UnstyledBadge.js b/docs/data/base/components/badge/UnstyledBadge.js deleted file mode 100644 index 7ad760e63bf762..00000000000000 --- a/docs/data/base/components/badge/UnstyledBadge.js +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from 'react'; -import { styled, Box } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; - -const blue = { - 500: '#007FFF', -}; - -const grey = { - 300: '#afb8c1', - 400: '#bdbdbd', - 900: '#24292f', -}; - -const StyledBadge = styled(BadgeUnstyled)( - ({ theme }) => ` - box-sizing: border-box; - margin: 0; - padding: 0; - font-size: 14px; - font-variant: tabular-nums; - list-style: none; - font-family: IBM Plex Sans, sans-serif; - position: relative; - display: inline-block; - line-height: 1; - - & .${badgeUnstyledClasses.badge} { - z-index: auto; - position: absolute; - top: 0; - right: 0; - min-width: 22px; - height: 22px; - padding: 0 6px; - color: #fff; - font-weight: 600; - font-size: 12px; - line-height: 22px; - white-space: nowrap; - text-align: center; - border-radius: 12px; - background: ${blue[500]}; - box-shadow: 0px 4px 8px ${theme.palette.mode === 'dark' ? grey[900] : grey[300]}; - transform: translate(50%, -50%); - transform-origin: 100% 0; - } - `, -); - -function BadgeContent() { - return ( - - theme.palette.mode === 'dark' ? grey[400] : grey[300], - display: 'inline-block', - verticalAlign: 'middle', - }} - /> - ); -} - -export default function UnstyledBadge() { - return ( - - - - ); -} diff --git a/docs/data/base/components/badge/UnstyledBadge.tsx b/docs/data/base/components/badge/UnstyledBadge.tsx deleted file mode 100644 index 7ad760e63bf762..00000000000000 --- a/docs/data/base/components/badge/UnstyledBadge.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from 'react'; -import { styled, Box } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; - -const blue = { - 500: '#007FFF', -}; - -const grey = { - 300: '#afb8c1', - 400: '#bdbdbd', - 900: '#24292f', -}; - -const StyledBadge = styled(BadgeUnstyled)( - ({ theme }) => ` - box-sizing: border-box; - margin: 0; - padding: 0; - font-size: 14px; - font-variant: tabular-nums; - list-style: none; - font-family: IBM Plex Sans, sans-serif; - position: relative; - display: inline-block; - line-height: 1; - - & .${badgeUnstyledClasses.badge} { - z-index: auto; - position: absolute; - top: 0; - right: 0; - min-width: 22px; - height: 22px; - padding: 0 6px; - color: #fff; - font-weight: 600; - font-size: 12px; - line-height: 22px; - white-space: nowrap; - text-align: center; - border-radius: 12px; - background: ${blue[500]}; - box-shadow: 0px 4px 8px ${theme.palette.mode === 'dark' ? grey[900] : grey[300]}; - transform: translate(50%, -50%); - transform-origin: 100% 0; - } - `, -); - -function BadgeContent() { - return ( - - theme.palette.mode === 'dark' ? grey[400] : grey[300], - display: 'inline-block', - verticalAlign: 'middle', - }} - /> - ); -} - -export default function UnstyledBadge() { - return ( - - - - ); -} diff --git a/docs/data/base/components/badge/UnstyledBadge/css/index.js b/docs/data/base/components/badge/UnstyledBadge/css/index.js new file mode 100644 index 00000000000000..45c108191e6660 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadge/css/index.js @@ -0,0 +1,97 @@ +import * as React from 'react'; +import { Badge } from '@mui/base/Badge'; +import { useTheme } from '@mui/system'; + +export default function UnstyledBadge() { + return ( + + + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +const grey = { + 300: '#afb8c1', + 400: '#bdbdbd', + 900: '#24292f', +}; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + return ( + + ); +} diff --git a/docs/data/base/components/badge/UnstyledBadge/css/index.tsx b/docs/data/base/components/badge/UnstyledBadge/css/index.tsx new file mode 100644 index 00000000000000..45c108191e6660 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadge/css/index.tsx @@ -0,0 +1,97 @@ +import * as React from 'react'; +import { Badge } from '@mui/base/Badge'; +import { useTheme } from '@mui/system'; + +export default function UnstyledBadge() { + return ( + + + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +const grey = { + 300: '#afb8c1', + 400: '#bdbdbd', + 900: '#24292f', +}; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + return ( + + ); +} diff --git a/docs/data/base/components/badge/UnstyledBadge/css/index.tsx.preview b/docs/data/base/components/badge/UnstyledBadge/css/index.tsx.preview new file mode 100644 index 00000000000000..a1f344f2a6d616 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadge/css/index.tsx.preview @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/docs/data/base/components/badge/UnstyledBadge/system/index.js b/docs/data/base/components/badge/UnstyledBadge/system/index.js new file mode 100644 index 00000000000000..9444b19da8c047 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadge/system/index.js @@ -0,0 +1,74 @@ +import * as React from 'react'; +import { styled, Box } from '@mui/system'; +import { Badge, badgeClasses } from '@mui/base/Badge'; + +function BadgeContent() { + return ( + + theme.palette.mode === 'dark' ? grey[400] : grey[300], + display: 'inline-block', + verticalAlign: 'middle', + }} + /> + ); +} + +export default function UnstyledBadge() { + return ( + + + + ); +} + +const blue = { + 500: '#007FFF', +}; + +const grey = { + 300: '#afb8c1', + 400: '#bdbdbd', + 900: '#24292f', +}; + +const StyledBadge = styled(Badge)( + ({ theme }) => ` + box-sizing: border-box; + margin: 0; + padding: 0; + font-size: 14px; + font-variant: tabular-nums; + list-style: none; + font-family: IBM Plex Sans, sans-serif; + position: relative; + display: inline-block; + line-height: 1; + + & .${badgeClasses.badge} { + z-index: auto; + position: absolute; + top: 0; + right: 0; + min-width: 22px; + height: 22px; + padding: 0 6px; + color: #fff; + font-weight: 600; + font-size: 12px; + line-height: 22px; + white-space: nowrap; + text-align: center; + border-radius: 12px; + background: ${blue[500]}; + box-shadow: 0px 4px 8px ${theme.palette.mode === 'dark' ? grey[900] : grey[300]}; + transform: translate(50%, -50%); + transform-origin: 100% 0; + } + `, +); diff --git a/docs/data/base/components/badge/UnstyledBadge/system/index.tsx b/docs/data/base/components/badge/UnstyledBadge/system/index.tsx new file mode 100644 index 00000000000000..9444b19da8c047 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadge/system/index.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import { styled, Box } from '@mui/system'; +import { Badge, badgeClasses } from '@mui/base/Badge'; + +function BadgeContent() { + return ( + + theme.palette.mode === 'dark' ? grey[400] : grey[300], + display: 'inline-block', + verticalAlign: 'middle', + }} + /> + ); +} + +export default function UnstyledBadge() { + return ( + + + + ); +} + +const blue = { + 500: '#007FFF', +}; + +const grey = { + 300: '#afb8c1', + 400: '#bdbdbd', + 900: '#24292f', +}; + +const StyledBadge = styled(Badge)( + ({ theme }) => ` + box-sizing: border-box; + margin: 0; + padding: 0; + font-size: 14px; + font-variant: tabular-nums; + list-style: none; + font-family: IBM Plex Sans, sans-serif; + position: relative; + display: inline-block; + line-height: 1; + + & .${badgeClasses.badge} { + z-index: auto; + position: absolute; + top: 0; + right: 0; + min-width: 22px; + height: 22px; + padding: 0 6px; + color: #fff; + font-weight: 600; + font-size: 12px; + line-height: 22px; + white-space: nowrap; + text-align: center; + border-radius: 12px; + background: ${blue[500]}; + box-shadow: 0px 4px 8px ${theme.palette.mode === 'dark' ? grey[900] : grey[300]}; + transform: translate(50%, -50%); + transform-origin: 100% 0; + } + `, +); diff --git a/docs/data/base/components/badge/UnstyledBadge.tsx.preview b/docs/data/base/components/badge/UnstyledBadge/system/index.tsx.preview similarity index 100% rename from docs/data/base/components/badge/UnstyledBadge.tsx.preview rename to docs/data/base/components/badge/UnstyledBadge/system/index.tsx.preview diff --git a/docs/data/base/components/badge/UnstyledBadge/tailwind/index.js b/docs/data/base/components/badge/UnstyledBadge/tailwind/index.js new file mode 100644 index 00000000000000..917bc867dc1b83 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadge/tailwind/index.js @@ -0,0 +1,74 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import { Badge as BaseBadge } from '@mui/base/Badge'; +import { useTheme } from '@mui/system'; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +export default function UnstyledBadge() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + + return ( +
+ + + +
+ ); +} + +const resolveSlotProps = (fn, args) => (typeof fn === 'function' ? fn(args) : fn); + +const Badge = React.forwardRef((props, ref) => { + return ( + { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.root, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'box-border m-0 p-0 text-xs font-sans list-none relative inline-block leading-none', + resolvedSlotProps?.className, + ), + }; + }, + badge: (ownerState) => { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.badge, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'z-auto absolute top-0 right-0 min-w-badge min-h-badge font-sans p-0 text-white font-semibold font-xs rounded-xl bg-purple-500 leading-5.5 whitespace-nowrap text-center translate-x-1/2 -translate-y-1/2 drop-shadow-lg origin-right', + resolvedSlotProps?.className, + ), + }; + }, + }} + /> + ); +}); + +Badge.propTypes = { + /** + * The props used for each slot inside the Badge. + * @default {} + */ + slotProps: PropTypes.shape({ + badge: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), +}; diff --git a/docs/data/base/components/badge/UnstyledBadge/tailwind/index.tsx b/docs/data/base/components/badge/UnstyledBadge/tailwind/index.tsx new file mode 100644 index 00000000000000..837644d21d60d0 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadge/tailwind/index.tsx @@ -0,0 +1,63 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import { Badge as BaseBadge, BadgeProps } from '@mui/base/Badge'; +import { useTheme } from '@mui/system'; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +export default function UnstyledBadge() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + + return ( +
+ + + +
+ ); +} + +const resolveSlotProps = (fn: any, args: any) => + typeof fn === 'function' ? fn(args) : fn; + +const Badge = React.forwardRef((props, ref) => { + return ( + { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.root, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'box-border m-0 p-0 text-xs font-sans list-none relative inline-block leading-none', + resolvedSlotProps?.className, + ), + }; + }, + badge: (ownerState) => { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.badge, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'z-auto absolute top-0 right-0 min-w-badge min-h-badge font-sans p-0 text-white font-semibold font-xs rounded-xl bg-purple-500 leading-5.5 whitespace-nowrap text-center translate-x-1/2 -translate-y-1/2 drop-shadow-lg origin-right', + resolvedSlotProps?.className, + ), + }; + }, + }} + /> + ); +}); diff --git a/docs/data/base/components/badge/UnstyledBadge/tailwind/index.tsx.preview b/docs/data/base/components/badge/UnstyledBadge/tailwind/index.tsx.preview new file mode 100644 index 00000000000000..bd190ede6b357d --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadge/tailwind/index.tsx.preview @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction.js b/docs/data/base/components/badge/UnstyledBadgeIntroduction.js deleted file mode 100644 index cb3aba3d7eb4e8..00000000000000 --- a/docs/data/base/components/badge/UnstyledBadgeIntroduction.js +++ /dev/null @@ -1,76 +0,0 @@ -import * as React from 'react'; -import { styled, Box } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; - -const blue = { - 500: '#007FFF', -}; - -const grey = { - 300: '#afb8c1', - 400: '#bdbdbd', - 900: '#24292f', -}; - -const StyledBadge = styled(BadgeUnstyled)( - ({ theme }) => ` - box-sizing: border-box; - margin: 0; - padding: 0; - font-size: 14px; - font-variant: tabular-nums; - list-style: none; - font-family: IBM Plex Sans, sans-serif; - position: relative; - display: inline-block; - line-height: 1; - - & .${badgeUnstyledClasses.badge} { - z-index: auto; - position: absolute; - top: 0; - right: 0; - min-width: 22px; - height: 22px; - padding: 0 6px; - color: #fff; - font-weight: 600; - font-size: 12px; - line-height: 22px; - white-space: nowrap; - text-align: center; - border-radius: 12px; - background: ${blue[500]}; - box-shadow: 0px 4px 16px ${ - theme.palette.mode === 'dark' ? grey[900] : grey[300] - }; - transform: translate(50%, -50%); - transform-origin: 100% 0; - } - `, -); - -function BadgeContent() { - return ( - - theme.palette.mode === 'dark' ? grey[400] : grey[300], - display: 'inline-block', - verticalAlign: 'middle', - }} - /> - ); -} - -export default function UnstyledBadgeIntroduction() { - return ( - - - - ); -} diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction.tsx b/docs/data/base/components/badge/UnstyledBadgeIntroduction.tsx deleted file mode 100644 index cb3aba3d7eb4e8..00000000000000 --- a/docs/data/base/components/badge/UnstyledBadgeIntroduction.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import * as React from 'react'; -import { styled, Box } from '@mui/system'; -import BadgeUnstyled, { badgeUnstyledClasses } from '@mui/base/BadgeUnstyled'; - -const blue = { - 500: '#007FFF', -}; - -const grey = { - 300: '#afb8c1', - 400: '#bdbdbd', - 900: '#24292f', -}; - -const StyledBadge = styled(BadgeUnstyled)( - ({ theme }) => ` - box-sizing: border-box; - margin: 0; - padding: 0; - font-size: 14px; - font-variant: tabular-nums; - list-style: none; - font-family: IBM Plex Sans, sans-serif; - position: relative; - display: inline-block; - line-height: 1; - - & .${badgeUnstyledClasses.badge} { - z-index: auto; - position: absolute; - top: 0; - right: 0; - min-width: 22px; - height: 22px; - padding: 0 6px; - color: #fff; - font-weight: 600; - font-size: 12px; - line-height: 22px; - white-space: nowrap; - text-align: center; - border-radius: 12px; - background: ${blue[500]}; - box-shadow: 0px 4px 16px ${ - theme.palette.mode === 'dark' ? grey[900] : grey[300] - }; - transform: translate(50%, -50%); - transform-origin: 100% 0; - } - `, -); - -function BadgeContent() { - return ( - - theme.palette.mode === 'dark' ? grey[400] : grey[300], - display: 'inline-block', - verticalAlign: 'middle', - }} - /> - ); -} - -export default function UnstyledBadgeIntroduction() { - return ( - - - - ); -} diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction/css/index.js b/docs/data/base/components/badge/UnstyledBadgeIntroduction/css/index.js new file mode 100644 index 00000000000000..901efb400391f9 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadgeIntroduction/css/index.js @@ -0,0 +1,99 @@ +import * as React from 'react'; +import { Badge } from '@mui/base/Badge'; +import { useTheme } from '@mui/system'; + +export default function UnstyledBadgeIntroduction() { + return ( + + + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +const grey = { + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#bdbdbd', + 700: '#424a53', + 900: '#24292f', +}; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + return ( + + ); +} diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction/css/index.tsx b/docs/data/base/components/badge/UnstyledBadgeIntroduction/css/index.tsx new file mode 100644 index 00000000000000..901efb400391f9 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadgeIntroduction/css/index.tsx @@ -0,0 +1,99 @@ +import * as React from 'react'; +import { Badge } from '@mui/base/Badge'; +import { useTheme } from '@mui/system'; + +export default function UnstyledBadgeIntroduction() { + return ( + + + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +const grey = { + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#bdbdbd', + 700: '#424a53', + 900: '#24292f', +}; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + return ( + + ); +} diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction/css/index.tsx.preview b/docs/data/base/components/badge/UnstyledBadgeIntroduction/css/index.tsx.preview new file mode 100644 index 00000000000000..4b5d65f54f525e --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadgeIntroduction/css/index.tsx.preview @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction/system/index.js b/docs/data/base/components/badge/UnstyledBadgeIntroduction/system/index.js new file mode 100644 index 00000000000000..6f117fa8300f22 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadgeIntroduction/system/index.js @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { styled, Box } from '@mui/system'; +import { Badge, badgeClasses } from '@mui/base/Badge'; + +const blue = { + 100: '#DAECFF', + 500: '#007FFF', + 900: '#003A75', +}; + +const grey = { + 200: '#d0d7de', + 700: '#424a53', +}; + +function BadgeContent() { + return ( + + theme.palette.mode === 'dark' ? grey[700] : grey[200], + display: 'inline-block', + verticalAlign: 'middle', + }} + /> + ); +} + +export default function UnstyledBadgeIntroduction() { + return ( + + + + ); +} + +const StyledBadge = styled(Badge)( + ({ theme }) => ` + box-sizing: border-box; + margin: 0; + padding: 0; + font-size: 14px; + font-variant: tabular-nums; + list-style: none; + font-family: IBM Plex Sans, sans-serif; + position: relative; + display: inline-block; + line-height: 1; + + & .${badgeClasses.badge} { + z-index: auto; + position: absolute; + top: 0; + right: 0; + min-width: 22px; + height: 22px; + padding: 0 6px; + color: #fff; + font-weight: 600; + font-size: 12px; + line-height: 22px; + white-space: nowrap; + text-align: center; + border-radius: 12px; + background: ${blue[500]}; + box-shadow: 0px 2px 24px ${ + theme.palette.mode === 'dark' ? blue[900] : blue[100] + }; + transform: translate(50%, -50%); + transform-origin: 100% 0; + } + `, +); diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction/system/index.tsx b/docs/data/base/components/badge/UnstyledBadgeIntroduction/system/index.tsx new file mode 100644 index 00000000000000..6f117fa8300f22 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadgeIntroduction/system/index.tsx @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { styled, Box } from '@mui/system'; +import { Badge, badgeClasses } from '@mui/base/Badge'; + +const blue = { + 100: '#DAECFF', + 500: '#007FFF', + 900: '#003A75', +}; + +const grey = { + 200: '#d0d7de', + 700: '#424a53', +}; + +function BadgeContent() { + return ( + + theme.palette.mode === 'dark' ? grey[700] : grey[200], + display: 'inline-block', + verticalAlign: 'middle', + }} + /> + ); +} + +export default function UnstyledBadgeIntroduction() { + return ( + + + + ); +} + +const StyledBadge = styled(Badge)( + ({ theme }) => ` + box-sizing: border-box; + margin: 0; + padding: 0; + font-size: 14px; + font-variant: tabular-nums; + list-style: none; + font-family: IBM Plex Sans, sans-serif; + position: relative; + display: inline-block; + line-height: 1; + + & .${badgeClasses.badge} { + z-index: auto; + position: absolute; + top: 0; + right: 0; + min-width: 22px; + height: 22px; + padding: 0 6px; + color: #fff; + font-weight: 600; + font-size: 12px; + line-height: 22px; + white-space: nowrap; + text-align: center; + border-radius: 12px; + background: ${blue[500]}; + box-shadow: 0px 2px 24px ${ + theme.palette.mode === 'dark' ? blue[900] : blue[100] + }; + transform: translate(50%, -50%); + transform-origin: 100% 0; + } + `, +); diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction.tsx.preview b/docs/data/base/components/badge/UnstyledBadgeIntroduction/system/index.tsx.preview similarity index 100% rename from docs/data/base/components/badge/UnstyledBadgeIntroduction.tsx.preview rename to docs/data/base/components/badge/UnstyledBadgeIntroduction/system/index.tsx.preview diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction/tailwind/index.js b/docs/data/base/components/badge/UnstyledBadgeIntroduction/tailwind/index.js new file mode 100644 index 00000000000000..070d1e6cdba04a --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadgeIntroduction/tailwind/index.js @@ -0,0 +1,74 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import { Badge as BaseBadge } from '@mui/base/Badge'; +import { useTheme } from '@mui/system'; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +export default function UnstyledBadgeIntroduction() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + + return ( +
+ + + +
+ ); +} + +const resolveSlotProps = (fn, args) => (typeof fn === 'function' ? fn(args) : fn); + +const Badge = React.forwardRef((props, ref) => { + return ( + { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.root, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'box-border m-0 p-0 text-xs list-none relative inline-block leading-none', + resolvedSlotProps?.className, + ), + }; + }, + badge: (ownerState) => { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.badge, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'z-auto absolute top-0 right-0 min-w-badge min-h-badge font-sans p-0 text-white font-semibold font-xs font-sans rounded-xl bg-purple-500 leading-5.5 whitespace-nowrap text-center translate-x-1/2 -translate-y-1/2 drop-shadow-lg origin-right', + resolvedSlotProps?.className, + ), + }; + }, + }} + /> + ); +}); + +Badge.propTypes = { + /** + * The props used for each slot inside the Badge. + * @default {} + */ + slotProps: PropTypes.shape({ + badge: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), +}; diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction/tailwind/index.tsx b/docs/data/base/components/badge/UnstyledBadgeIntroduction/tailwind/index.tsx new file mode 100644 index 00000000000000..2724498fae1d94 --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadgeIntroduction/tailwind/index.tsx @@ -0,0 +1,63 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import { Badge as BaseBadge, BadgeProps } from '@mui/base/Badge'; +import { useTheme } from '@mui/system'; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +export default function UnstyledBadgeIntroduction() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + + return ( +
+ + + +
+ ); +} + +const resolveSlotProps = (fn: any, args: any) => + typeof fn === 'function' ? fn(args) : fn; + +const Badge = React.forwardRef((props, ref) => { + return ( + { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.root, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'box-border m-0 p-0 text-xs list-none relative inline-block leading-none', + resolvedSlotProps?.className, + ), + }; + }, + badge: (ownerState) => { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.badge, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'z-auto absolute top-0 right-0 min-w-badge min-h-badge font-sans p-0 text-white font-semibold font-xs font-sans rounded-xl bg-purple-500 leading-5.5 whitespace-nowrap text-center translate-x-1/2 -translate-y-1/2 drop-shadow-lg origin-right', + resolvedSlotProps?.className, + ), + }; + }, + }} + /> + ); +}); diff --git a/docs/data/base/components/badge/UnstyledBadgeIntroduction/tailwind/index.tsx.preview b/docs/data/base/components/badge/UnstyledBadgeIntroduction/tailwind/index.tsx.preview new file mode 100644 index 00000000000000..bd190ede6b357d --- /dev/null +++ b/docs/data/base/components/badge/UnstyledBadgeIntroduction/tailwind/index.tsx.preview @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/docs/data/base/components/badge/badge-pt.md b/docs/data/base/components/badge/badge-pt.md index af79492369dc0e..2a5969087d0b53 100644 --- a/docs/data/base/components/badge/badge-pt.md +++ b/docs/data/base/components/badge/badge-pt.md @@ -1,5 +1,5 @@ --- -product: base +productId: base-ui title: Unstyled React Badge component and hook components: BadgeUnstyled githubLabel: 'component: badge' @@ -21,7 +21,7 @@ The `BadgeUnstyled` component creates a badge that is applied to its child eleme ### Usage -After [installation](/base/getting-started/installation/), you can start building with this component using the following basic elements: +After [installation](/base-ui/getting-started/quickstart/#installation), you can start building with this component using the following basic elements: ```jsx import BadgeUnstyled from '@mui/base/BadgeUnstyled'; @@ -57,7 +57,7 @@ The `BadgeUnstyled` component is composed of a root `` that houses the ele ### Slot props :::info -The following props are available on all non-utility Base components. See [Usage](/base/getting-started/usage/) for full details. +The following props are available on all non-utility Base components. See [Usage](/base-ui/getting-started/usage/) for full details. ::: Use the `component` prop to override the root slot with a custom element: diff --git a/docs/data/base/components/badge/badge-zh.md b/docs/data/base/components/badge/badge-zh.md index 79607246503b3f..64abd2fb25837a 100644 --- a/docs/data/base/components/badge/badge-zh.md +++ b/docs/data/base/components/badge/badge-zh.md @@ -1,5 +1,5 @@ --- -product: base +productId: base-ui title: Unstyled React Badge component and hook components: BadgeUnstyled githubLabel: 'component: badge' @@ -21,7 +21,7 @@ The `BadgeUnstyled` component creates a badge that is applied to its child eleme ### Usage -After [installation](/base/getting-started/installation/), you can start building with this component using the following basic elements: +After [installation](/base-ui/getting-started/quickstart/#installation), you can start building with this component using the following basic elements: ```jsx import BadgeUnstyled from '@mui/base/BadgeUnstyled'; @@ -57,7 +57,7 @@ The `BadgeUnstyled` component is composed of a root `` that houses the ele ### Slot props :::info -The following props are available on all non-utility Base components. See [Usage](/base/getting-started/usage/) for full details. +The following props are available on all non-utility Base components. See [Usage](/base-ui/getting-started/usage/) for full details. ::: Use the `component` prop to override the root slot with a custom element: diff --git a/docs/data/base/components/badge/badge.md b/docs/data/base/components/badge/badge.md index 86d50f69308bab..71f557d58fd79c 100644 --- a/docs/data/base/components/badge/badge.md +++ b/docs/data/base/components/badge/badge.md @@ -1,55 +1,39 @@ --- -product: base -title: Unstyled React Badge component and hook -components: BadgeUnstyled +productId: base-ui +title: React Badge component and hook +components: Badge +hooks: useBadge githubLabel: 'component: badge' --- -# Unstyled badge +# Badge -

The BadgeUnstyled component generates a small label that is attached to its child element.

+

The Badge component generates a small label that is attached to its child element.

+ +{{"component": "modules/components/ComponentLinkHeader.js", "design": false}} + +{{"component": "modules/components/ComponentPageTabs.js"}} ## Introduction A badge is a small descriptor for UI elements. It typically sits on or near an element and indicates the status of that element by displaying a number, icon, or other short set of characters. -The `BadgeUnstyled` component creates a badge that is applied to its child element. +The Badge component creates a badge that is applied to its child element. -{{"demo": "UnstyledBadgeIntroduction.tsx", "defaultCodeOpen": false, "bg": "gradient"}} - -{{"component": "modules/components/ComponentLinkHeader.js", "design": false}} +{{"demo": "UnstyledBadgeIntroduction", "defaultCodeOpen": false, "bg": "gradient"}} ## Component -### Usage - -After [installation](/base/getting-started/installation/), you can start building with this component using the following basic elements: - ```jsx -import BadgeUnstyled from '@mui/base/BadgeUnstyled'; - -export default function MyApp() { - return ( - {/* the element that the badge is attached to */} - ); -} +import { Badge } from '@mui/base/Badge'; ``` -### Basics - -`BadgeUnstyled` wraps around the UI element that it's attached to. -For instance, if the badge indicates the number of emails in an inbox, then the component will be structured like this: - -```jsx - - - -``` +The Badge wraps around the UI element that it's attached to. ### Anatomy -The `BadgeUnstyled` component is composed of a root `` that houses the element that the badge is attached to, followed by a `` slot to represent the badge itself: +The Badge component is composed of a root `` that houses the element that the Badge is attached to, followed by a `` slot to represent the Badge itself: ```html @@ -58,46 +42,51 @@ The `BadgeUnstyled` component is composed of a root `` that houses the ele ``` -### Slot props +### Custom structure + +Use the `slots` prop to override the root or any other interior slot: + +```jsx + +``` :::info -The following props are available on all non-utility Base components. -See [Usage](/base/getting-started/usage/) for full details. +The `slots` prop is available on all non-utility Base components. +See [Overriding component structure](/base-ui/guides/overriding-component-structure/) for full details. ::: -Use the `component` prop to override the root slot with a custom element: +Use the `slotProps` prop to pass custom props to internal slots. +The following code snippet applies a CSS class called `my-badge` to the badge slot: ```jsx - + ``` -Use the `slots` prop to override any interior slots in addition to the root: +### Usage with TypeScript -```jsx - -``` +In TypeScript, you can specify the custom component type used in the `slots.root` as a generic parameter of the unstyled component. +This way, you can safely provide the custom root's props directly on the component: -:::warning -If the root element is customized with both the `component` and `slots` props, then `component` will take precedence. -::: +```tsx + slots={{ root: CustomComponent }} customProp /> +``` -Use the `slotProps` prop to pass custom props to internal slots. -The following code snippet applies a CSS class called `my-badge` to the badge slot: +The same applies for props specific to custom primitive elements: -```jsx - +```tsx + slots={{ root: 'img' }} src="badge.png" /> ``` ## Hook ```jsx -import { useBadge } from '@mui/base/BadgeUnstyled'; +import { useBadge } from '@mui/base/useBadge'; ``` -The `useBadge` hook lets you apply the functionality of `BadgeUnstyled` to a fully custom component. +The `useBadge` hook lets you apply the functionality of a Badge to a fully custom component. It returns props to be placed on the custom component, along with fields representing the component's internal state. -Hooks _do not_ support [slot props](#slot-props), but they do support [customization props](#customization). +Hooks _do not_ support [slot props](#custom-structure), but they do support [customization props](#customization). :::info Hooks give you the most room for customization, but require more work to implement. @@ -115,27 +104,27 @@ For the sake of simplicity, demos and code snippets primarily feature components ### Badge content -The `badgeContent` prop defines the content that's displayed inside the badge. -When this content is a number, there are additional props you can use for further customization—see the [Numerical badges section](#numerical-badges) below. +The `badgeContent` prop defines the content that's displayed inside the Badge. +When this content is a number, there are additional props you can use for further customization—see the [Numerical Badges section](#numerical-badges) below. -The following demo shows how to create and style a typical numerical badge that's attached to a generic box element: +The following demo shows how to create and style a typical numerical Badge that's attached to a generic box element: -{{"demo": "UnstyledBadge.js", "defaultCodeOpen": false}} +{{"demo": "UnstyledBadge", "defaultCodeOpen": false}} ### Badge visibility -You can control the visibility of a badge by using the `invisible` prop. -Setting a badge to `invisible` does not actually hide it—instead, this prop adds the `BaseBadge-invisible` class to the badge, which you can target with styles to hide however you prefer: +You can control the visibility of a Badge by using the `invisible` prop. +Setting a Badge to `invisible` does not actually hide it—instead, this prop adds the `BaseBadge-invisible` class to the Badge, which you can target with styles to hide however you prefer: {{"demo": "BadgeVisibility.js"}} -### Numerical badges +### Numerical Badges The following props are useful when `badgeContent` is a number. #### The showZero prop -By default, badges automatically hide when `badgeContent={0}`. +By default, Badges automatically hide when `badgeContent={0}`. You can override this behavior with the `showZero` prop: {{"demo": "ShowZeroBadge.js"}} @@ -149,7 +138,7 @@ The default is 99. ## Accessibility -Screen readers may not provide users with enough information about a badge's contents. +Screen readers may not provide users with enough information about a Badge's contents. To make your badge accessible, you must provide a full description with `aria-label`, as shown in the demo below: {{"demo": "AccessibleBadges.js"}} diff --git a/docs/data/base/components/button/UnstyledButtonCustom.js b/docs/data/base/components/button/UnstyledButtonCustom.js index 3044a6a7a3893a..4f4e2399fafeaf 100644 --- a/docs/data/base/components/button/UnstyledButtonCustom.js +++ b/docs/data/base/components/button/UnstyledButtonCustom.js @@ -1,6 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import ButtonUnstyled, { buttonUnstyledClasses } from '@mui/base/ButtonUnstyled'; +import { Button, buttonClasses } from '@mui/base/Button'; import { styled } from '@mui/system'; const ButtonRoot = React.forwardRef(function ButtonRoot(props, ref) { @@ -21,6 +21,14 @@ ButtonRoot.propTypes = { children: PropTypes.node, }; +const SvgButton = React.forwardRef(function SvgButton(props, ref) { + return + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +function Styles() { + return ( + + ); +} diff --git a/docs/data/base/components/button/UnstyledButtonIntroduction/css/index.tsx b/docs/data/base/components/button/UnstyledButtonIntroduction/css/index.tsx new file mode 100644 index 00000000000000..3dae77721c653b --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonIntroduction/css/index.tsx @@ -0,0 +1,59 @@ +import * as React from 'react'; +import { Button, buttonClasses } from '@mui/base/Button'; +import Stack from '@mui/material/Stack'; + +export default function UnstyledButtonsIntroduction() { + return ( + + + + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +function Styles() { + return ( + + ); +} diff --git a/docs/data/base/components/button/UnstyledButtonIntroduction/css/index.tsx.preview b/docs/data/base/components/button/UnstyledButtonIntroduction/css/index.tsx.preview new file mode 100644 index 00000000000000..2d592aac5304f0 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonIntroduction/css/index.tsx.preview @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/docs/data/base/components/button/UnstyledButtonIntroduction/system/index.js b/docs/data/base/components/button/UnstyledButtonIntroduction/system/index.js new file mode 100644 index 00000000000000..49175792bc2e54 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonIntroduction/system/index.js @@ -0,0 +1,60 @@ +import * as React from 'react'; +import { Button, buttonClasses } from '@mui/base/Button'; +import { styled } from '@mui/system'; +import Stack from '@mui/material/Stack'; + +export default function UnstyledButtonsIntroduction() { + return ( + + Button + Disabled + + ); +} + +const blue = { + 500: '#007FFF', + 600: '#0072E5', + 700: '#0059B2', +}; + +const grey = { + 100: '#eaeef2', + 300: '#afb8c1', + 900: '#24292f', +}; + +const CustomButton = styled(Button)( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 600; + font-size: 0.875rem; + line-height: 1.5; + background-color: ${blue[500]}; + padding: 8px 16px; + border-radius: 8px; + color: white; + transition: all 150ms ease; + cursor: pointer; + border: none; + box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[100]}; + + &:hover { + background-color: ${blue[600]}; + } + + &.${buttonClasses.active} { + background-color: ${blue[700]}; + } + + &.${buttonClasses.focusVisible} { + box-shadow: 0 3px 20px 0 rgba(61, 71, 82, 0.1), 0 0 0 5px rgba(0, 127, 255, 0.5); + outline: none; + } + + &.${buttonClasses.disabled} { + opacity: 0.5; + cursor: not-allowed; + } + `, +); diff --git a/docs/data/base/components/button/UnstyledButtonIntroduction/system/index.tsx b/docs/data/base/components/button/UnstyledButtonIntroduction/system/index.tsx new file mode 100644 index 00000000000000..49175792bc2e54 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonIntroduction/system/index.tsx @@ -0,0 +1,60 @@ +import * as React from 'react'; +import { Button, buttonClasses } from '@mui/base/Button'; +import { styled } from '@mui/system'; +import Stack from '@mui/material/Stack'; + +export default function UnstyledButtonsIntroduction() { + return ( + + Button + Disabled + + ); +} + +const blue = { + 500: '#007FFF', + 600: '#0072E5', + 700: '#0059B2', +}; + +const grey = { + 100: '#eaeef2', + 300: '#afb8c1', + 900: '#24292f', +}; + +const CustomButton = styled(Button)( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 600; + font-size: 0.875rem; + line-height: 1.5; + background-color: ${blue[500]}; + padding: 8px 16px; + border-radius: 8px; + color: white; + transition: all 150ms ease; + cursor: pointer; + border: none; + box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[100]}; + + &:hover { + background-color: ${blue[600]}; + } + + &.${buttonClasses.active} { + background-color: ${blue[700]}; + } + + &.${buttonClasses.focusVisible} { + box-shadow: 0 3px 20px 0 rgba(61, 71, 82, 0.1), 0 0 0 5px rgba(0, 127, 255, 0.5); + outline: none; + } + + &.${buttonClasses.disabled} { + opacity: 0.5; + cursor: not-allowed; + } + `, +); diff --git a/docs/data/base/components/button/UnstyledButtonIntroduction.tsx.preview b/docs/data/base/components/button/UnstyledButtonIntroduction/system/index.tsx.preview similarity index 100% rename from docs/data/base/components/button/UnstyledButtonIntroduction.tsx.preview rename to docs/data/base/components/button/UnstyledButtonIntroduction/system/index.tsx.preview diff --git a/docs/data/base/components/button/UnstyledButtonIntroduction/tailwind/index.js b/docs/data/base/components/button/UnstyledButtonIntroduction/tailwind/index.js new file mode 100644 index 00000000000000..3afdd444109f23 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonIntroduction/tailwind/index.js @@ -0,0 +1,32 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { Button } from '@mui/base/Button'; +import Stack from '@mui/material/Stack'; +import clsx from 'clsx'; + +export default function UnstyledButtonsIntroduction() { + return ( + + Button + Disabled + + ); +} + +const CustomButton = React.forwardRef((props, ref) => { + const { className, ...other } = props; + return ( + + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +function Styles() { + return ( + + ); +} diff --git a/docs/data/base/components/button/UnstyledButtonsSimple/css/index.tsx b/docs/data/base/components/button/UnstyledButtonsSimple/css/index.tsx new file mode 100644 index 00000000000000..f7cdde1f0fd0be --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSimple/css/index.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import { Button, buttonClasses } from '@mui/base/Button'; +import Stack from '@mui/material/Stack'; + +export default function UnstyledButtonsSimple() { + return ( + + + + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +function Styles() { + return ( + + ); +} diff --git a/docs/data/base/components/button/UnstyledButtonsSimple/css/index.tsx.preview b/docs/data/base/components/button/UnstyledButtonsSimple/css/index.tsx.preview new file mode 100644 index 00000000000000..2d592aac5304f0 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSimple/css/index.tsx.preview @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/docs/data/base/components/button/UnstyledButtonsSimple/system/index.js b/docs/data/base/components/button/UnstyledButtonsSimple/system/index.js new file mode 100644 index 00000000000000..69d470d99c3198 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSimple/system/index.js @@ -0,0 +1,51 @@ +import * as React from 'react'; +import { Button, buttonClasses } from '@mui/base/Button'; +import { styled } from '@mui/system'; +import Stack from '@mui/material/Stack'; + +export default function UnstyledButtonsSimple() { + return ( + + Button + Disabled + + ); +} + +const blue = { + 500: '#007FFF', + 600: '#0072E5', + 700: '#0059B2', +}; + +const CustomButton = styled(Button)` + font-family: 'IBM Plex Sans', sans-serif; + font-size: 0.875rem; + line-height: 1.5; + background-color: ${blue[500]}; + color: white; + border-radius: 8px; + font-weight: 600; + padding: 8px 16px; + cursor: pointer; + transition: all 150ms ease; + border: none; + + &:hover:not(:disabled) { + background-color: ${blue[600]}; + } + + &:active:not(:disabled) { + background-color: ${blue[700]}; + } + + &.${buttonClasses.focusVisible} { + box-shadow: 0 4px 20px 0 rgb(61 71 82 / 0.1), 0 0 0 5px rgb(0 127 255 / 0.5); + outline: none; + } + + &.${buttonClasses.disabled} { + opacity: 0.5; + cursor: not-allowed; + } +`; diff --git a/docs/data/base/components/button/UnstyledButtonsSimple/system/index.tsx b/docs/data/base/components/button/UnstyledButtonsSimple/system/index.tsx new file mode 100644 index 00000000000000..69d470d99c3198 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSimple/system/index.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import { Button, buttonClasses } from '@mui/base/Button'; +import { styled } from '@mui/system'; +import Stack from '@mui/material/Stack'; + +export default function UnstyledButtonsSimple() { + return ( + + Button + Disabled + + ); +} + +const blue = { + 500: '#007FFF', + 600: '#0072E5', + 700: '#0059B2', +}; + +const CustomButton = styled(Button)` + font-family: 'IBM Plex Sans', sans-serif; + font-size: 0.875rem; + line-height: 1.5; + background-color: ${blue[500]}; + color: white; + border-radius: 8px; + font-weight: 600; + padding: 8px 16px; + cursor: pointer; + transition: all 150ms ease; + border: none; + + &:hover:not(:disabled) { + background-color: ${blue[600]}; + } + + &:active:not(:disabled) { + background-color: ${blue[700]}; + } + + &.${buttonClasses.focusVisible} { + box-shadow: 0 4px 20px 0 rgb(61 71 82 / 0.1), 0 0 0 5px rgb(0 127 255 / 0.5); + outline: none; + } + + &.${buttonClasses.disabled} { + opacity: 0.5; + cursor: not-allowed; + } +`; diff --git a/docs/data/base/components/button/UnstyledButtonsSimple/system/index.tsx.preview b/docs/data/base/components/button/UnstyledButtonsSimple/system/index.tsx.preview new file mode 100644 index 00000000000000..702ae5216245c1 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSimple/system/index.tsx.preview @@ -0,0 +1,2 @@ +Button +Disabled \ No newline at end of file diff --git a/docs/data/base/components/button/UnstyledButtonsSimple/tailwind/index.js b/docs/data/base/components/button/UnstyledButtonsSimple/tailwind/index.js new file mode 100644 index 00000000000000..917ebda0a7b6f9 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSimple/tailwind/index.js @@ -0,0 +1,34 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { Button } from '@mui/base/Button'; +import Stack from '@mui/material/Stack'; +import clsx from 'clsx'; + +export default function UnstyledButtonsSimple() { + return ( + + + Button + + Disabled + + ); +} + +const CustomButton = React.forwardRef((props, ref) => { + const { className, ...other } = props; + return ( + + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +function Styles() { + return ( + + ); +} diff --git a/docs/data/base/components/button/UnstyledButtonsSpan/css/index.tsx b/docs/data/base/components/button/UnstyledButtonsSpan/css/index.tsx new file mode 100644 index 00000000000000..07b3949ac8f65e --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSpan/css/index.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { Button, buttonClasses } from '@mui/base/Button'; +import Stack from '@mui/material/Stack'; + +export default function UnstyledButtonsSpan() { + return ( + + + + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +function Styles() { + return ( + + ); +} diff --git a/docs/data/base/components/button/UnstyledButtonsSpan/css/index.tsx.preview b/docs/data/base/components/button/UnstyledButtonsSpan/css/index.tsx.preview new file mode 100644 index 00000000000000..f5854b7a13d486 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSpan/css/index.tsx.preview @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/docs/data/base/components/button/UnstyledButtonsSpan/system/index.js b/docs/data/base/components/button/UnstyledButtonsSpan/system/index.js new file mode 100644 index 00000000000000..45dd963617f567 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSpan/system/index.js @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { Button, buttonClasses } from '@mui/base/Button'; +import { styled } from '@mui/system'; +import Stack from '@mui/material/Stack'; + +export default function UnstyledButtonsSpan() { + return ( + + Button + + Disabled + + + ); +} + +const blue = { + 500: '#007FFF', + 600: '#0072E5', + 700: '#0059B2', +}; + +const CustomButton = styled(Button)` + font-family: 'IBM Plex Sans', sans-serif; + font-size: 0.875rem; + line-height: 1.5; + background-color: ${blue[500]}; + color: white; + border-radius: 8px; + font-weight: 600; + padding: 8px 16px; + cursor: pointer; + transition: all 150ms ease; + border: none; + + &:hover { + background-color: ${blue[600]}; + } + + &.${buttonClasses.active} { + background-color: ${blue[700]}; + } + + &.${buttonClasses.focusVisible} { + box-shadow: 0 4px 20px 0 rgb(61 71 82 / 0.1), 0 0 0 5px rgb(0 127 255 / 0.5); + outline: none; + } + + &.${buttonClasses.disabled} { + opacity: 0.5; + cursor: not-allowed; + } +`; diff --git a/docs/data/base/components/button/UnstyledButtonsSpan/system/index.tsx b/docs/data/base/components/button/UnstyledButtonsSpan/system/index.tsx new file mode 100644 index 00000000000000..28456f29f69b20 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSpan/system/index.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import { Button, buttonClasses, ButtonTypeMap } from '@mui/base/Button'; +import { styled } from '@mui/system'; +import Stack from '@mui/material/Stack'; +import { PolymorphicComponent } from '@mui/base/utils'; + +export default function UnstyledButtonsSpan() { + return ( + + Button + + Disabled + + + ); +} + +const blue = { + 500: '#007FFF', + 600: '#0072E5', + 700: '#0059B2', +}; + +const CustomButton = styled(Button)` + font-family: 'IBM Plex Sans', sans-serif; + font-size: 0.875rem; + line-height: 1.5; + background-color: ${blue[500]}; + color: white; + border-radius: 8px; + font-weight: 600; + padding: 8px 16px; + cursor: pointer; + transition: all 150ms ease; + border: none; + + &:hover { + background-color: ${blue[600]}; + } + + &.${buttonClasses.active} { + background-color: ${blue[700]}; + } + + &.${buttonClasses.focusVisible} { + box-shadow: 0 4px 20px 0 rgb(61 71 82 / 0.1), 0 0 0 5px rgb(0 127 255 / 0.5); + outline: none; + } + + &.${buttonClasses.disabled} { + opacity: 0.5; + cursor: not-allowed; + } +` as PolymorphicComponent; diff --git a/docs/data/base/components/button/UnstyledButtonsSpan/system/index.tsx.preview b/docs/data/base/components/button/UnstyledButtonsSpan/system/index.tsx.preview new file mode 100644 index 00000000000000..c38f8399833ef2 --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSpan/system/index.tsx.preview @@ -0,0 +1,4 @@ +Button + + Disabled + \ No newline at end of file diff --git a/docs/data/base/components/button/UnstyledButtonsSpan/tailwind/index.js b/docs/data/base/components/button/UnstyledButtonsSpan/tailwind/index.js new file mode 100644 index 00000000000000..4af8f4712b740c --- /dev/null +++ b/docs/data/base/components/button/UnstyledButtonsSpan/tailwind/index.js @@ -0,0 +1,36 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { Button } from '@mui/base/Button'; +import Stack from '@mui/material/Stack'; +import clsx from 'clsx'; + +export default function UnstyledButtonsSimple() { + return ( + + + Button + + + Disabled + + + ); +} + +const CustomButton = React.forwardRef((props, ref) => { + const { className, ...other } = props; + return ( + ``` -### Slot props - -:::info -The following props are available on all non-utility Base components. -See [Usage](/base/getting-started/usage/) for full details. -::: +### Custom structure -Use the `component` prop to override the root slot with a custom element: +Use the `slots.root` prop to override the root slot with a custom element: ```jsx - + - + +``` + +The following demo shows how to create and style a form that uses Form Control to wrap the elements of the form. +Note that it also uses the `useFormControlContext` hook in order to pass props to the custom Input—see the [Hook](#hook) section below for more details. + +{{"demo": "BasicFormControl"}} + +### Usage with TypeScript + +In TypeScript, you can specify the custom component type used in the `slots.root` as a generic parameter of the unstyled component. +This way, you can safely provide the custom root's props directly on the component: + +```tsx + slots={{ root: CustomComponent }} customProp /> ``` -The following demo shows how to create and style a form that uses `FormControlUnstyled` to wrap the elements of the form. -Note that it also uses the `useFormControlUnstyledContext` hook in order to pass props to the custom `InputUnstyled`—see the [Hook section](#hook) below for more details. +The same applies for props specific to custom primitive elements: -{{"demo": "BasicFormControl.js"}} +```tsx + slots={{ root: 'button' }} onClick={() => {}} /> +``` ## Hook ```jsx -import { useFormControlUnstyledContext } from '@mui/base/FormControlUnstyled'; +import { useFormControlContext } from '@mui/base/FormControl'; ``` -The `useFormControlUnstyledContext` hook reads the context provided by `FormControlUnstyled`. -This hook lets you work with custom input components inside of the form control. +The `useFormControlContext` hook reads the context provided by Form Control. +This hook lets you work with custom input components inside of the Form Control. You can also use it to read the form control's state and react to its changes in a custom component. -Hooks _do not_ support [slot props](#slot-props), but they do support [customization props](#customization). +Hooks _do not_ support [slot props](#custom-structure), but they do support [customization props](#customization). :::info Hooks give you the most room for customization, but require more work to implement. @@ -73,33 +77,21 @@ You may not need to use hooks unless you find that you're limited by the customi The demo below shows how to integrate this hook with its component counterpart: -- `CustomInput` is a wrapper around a native HTML `` that adds `FormControlUnstyled` integration. +- `CustomInput` is a wrapper around a native HTML `` that adds Form Control integration. - `ControlStateDisplay` reads the state of the form control and displays it as text. {{"demo": "UseFormControl.js", "defaultCodeOpen": false}} -Note that even though `FormControlUnstyled` supports both controlled and uncontrolled-style APIs -(i.e. it accepts `value` and `defaultValue` props), `useFormControlUnstyledContext` returns only the controlled `value`. -This way, you don't have to implement both in your custom input—`FormControlUnstyled` does this for you. - -`useFormControlUnstyledContext` returns an object with the following fields: +Note that even though Form Control supports both controlled and uncontrolled-style APIs (i.e. it accepts `value` and `defaultValue` props), `useFormControlContext` returns only the controlled `value`. +This way, you don't have to implement both in your custom input—Form Control does this for you. -| Name | Type | Description | -| ---------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `disabled` | boolean | Represents the value of the FormControlUnstyled's `disabled` prop. | -| `error` | boolean | Represents the value of the `FormControlUnstyled` component's `error` prop. Note that it is not calculated automatically (i.e. it's not set when `required: true` and `value: ''`). | -| `filled` | boolean | Set to `true` if `value` is not empty. | -| `focused` | boolean | Set to `true` if the wrapped input has received focus. | -| `required` | boolean | Represents the value of the `FormControlUnstyled` component's `required` prop. | -| `value` | unknown | The current value of the form control. | +:::info -The following callbacks are also part of the returned object—they are meant to be used when creating custom inputs: +- A component is **controlled** when it's managed by its parent using props. +- A component is **uncontrolled** when it's managed by its own local state. -| Name | Type | Description | -| ---------- | ------------------------- | ------------------------------------------------------------- | -| `onChange` | React.ChangeEvent => void | Value change handler. Should be forwarded to the inner input. | -| `onBlur` | () => void | Focus change handler. Should be forwarded to the inner input. | -| `onFocus` | () => void | Focus change handler. Should be forwarded to the inner input. | +Learn more about controlled and uncontrolled components in the [React documentation](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). +::: ## Customization @@ -110,9 +102,9 @@ For the sake of simplicity, demos and code snippets primarily feature components ### Accessing the form control state -You can access the state of the form control by providing a function as a child of the `FormControlUnstyled`. +You can access the state of the Form Control by providing a function as a child. The state will be provided as a parameter to this function. -The following demo shows how to access the state of the form control in an `InputUnstyled` component nested inside of `FormControlUnstyled`: +The following demo shows how to access the state of the Form Control in an Input component nested inside: {{"demo": "FormControlFunctionChild.js"}} diff --git a/docs/data/base/components/input/InputAdornments.js b/docs/data/base/components/input/InputAdornments.js index 25f30675047bc8..cd730267f763e1 100644 --- a/docs/data/base/components/input/InputAdornments.js +++ b/docs/data/base/components/input/InputAdornments.js @@ -1,97 +1,16 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import Box from '@mui/material/Box'; -import ButtonUnstyled from '@mui/base/ButtonUnstyled'; -import InputUnstyled, { inputUnstyledClasses } from '@mui/base/InputUnstyled'; +import { Button } from '@mui/base/Button'; +import { Input, inputClasses } from '@mui/base/Input'; import Visibility from '@mui/icons-material/Visibility'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; import { styled } from '@mui/system'; -const blue = { - 100: '#DAECFF', - 200: '#80BFFF', - 400: '#3399FF', - 500: '#007FFF', - 600: '#0072E5', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E7EBF0', - 200: '#E0E3E7', - 300: '#CDD2D7', - 400: '#B2BAC2', - 500: '#A0AAB4', - 600: '#6F7E8C', - 700: '#3E5060', - 800: '#2D3843', - 900: '#1A2027', -}; - -const StyledInputRoot = styled('div')( - ({ theme }) => ` - font-family: IBM Plex Sans, sans-serif; - font-weight: 400; - border-radius: 12px; - color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; - background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; - border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; - box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; - display: flex; - align-items: center; - justify-content: center; - - - &.${inputUnstyledClasses.focused} { - border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; - } - - &:hover { - border-color: ${blue[400]}; - } -`, -); - -const StyledInputElement = styled('input')( - ({ theme }) => ` - font-size: 0.875rem; - font-family: inherit; - font-weight: 400; - line-height: 1.5; - flex-grow: 1; - color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; - background: inherit; - border: none; - border-radius: inherit; - padding: 12px 12px; - outline: 0; -`, -); - -const IconButton = styled(ButtonUnstyled)( - ({ theme }) => ` - display: inline-flex; - align-items: center; - justify-content: center; - border: none; - background: inherit; - cursor: pointer; - color: ${theme.palette.mode === 'dark' ? grey[300] : grey[700]}; - `, -); - -const InputAdornment = styled('div')` - margin: 8px; - display: inline-flex; - align-items: center; - justify-content: center; -`; - const CustomInput = React.forwardRef(function CustomInput(props, ref) { const { slots, ...other } = props; return ( - * + *': { ml: 1 } }}> + kg} @@ -166,3 +91,89 @@ export default function InputAdornments() { ); } + +const blue = { + 100: '#DAECFF', + 200: '#80BFFF', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', +}; + +const grey = { + 50: '#F3F6F9', + 100: '#E7EBF0', + 200: '#E0E3E7', + 300: '#CDD2D7', + 400: '#B2BAC2', + 500: '#A0AAB4', + 600: '#6F7E8C', + 700: '#3E5060', + 800: '#2D3843', + 900: '#1A2027', +}; + +const StyledInputRoot = styled('div')( + ({ theme }) => ` + font-family: IBM Plex Sans, sans-serif; + font-weight: 400; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; + display: flex; + align-items: center; + justify-content: center; + + + &.${inputClasses.focused} { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + &:hover { + border-color: ${blue[400]}; + } + + // firefox + &:focus-visible { + outline: 0; + } +`, +); + +const StyledInputElement = styled('input')( + ({ theme }) => ` + font-size: 0.875rem; + font-family: inherit; + font-weight: 400; + line-height: 1.5; + flex-grow: 1; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: inherit; + border: none; + border-radius: inherit; + padding: 8px 12px; + outline: 0; +`, +); + +const IconButton = styled(Button)( + ({ theme }) => ` + display: inline-flex; + align-items: center; + justify-content: center; + border: none; + background: inherit; + cursor: pointer; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[700]}; + `, +); + +const InputAdornment = styled('div')` + margin: 8px; + display: inline-flex; + align-items: center; + justify-content: center; +`; diff --git a/docs/data/base/components/input/InputAdornments.tsx b/docs/data/base/components/input/InputAdornments.tsx index 7323943130a11d..a70f25adc30bdd 100644 --- a/docs/data/base/components/input/InputAdornments.tsx +++ b/docs/data/base/components/input/InputAdornments.tsx @@ -1,14 +1,95 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import ButtonUnstyled from '@mui/base/ButtonUnstyled'; -import InputUnstyled, { - InputUnstyledProps, - inputUnstyledClasses, -} from '@mui/base/InputUnstyled'; +import { Button } from '@mui/base/Button'; +import { Input, InputProps, inputClasses } from '@mui/base/Input'; import Visibility from '@mui/icons-material/Visibility'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; import { styled } from '@mui/system'; +const CustomInput = React.forwardRef(function CustomInput( + props: InputProps, + ref: React.ForwardedRef, +) { + const { slots, ...other } = props; + return ( + + ); +}); + +interface State { + amount: string; + password: string; + weight: string; + weightRange: string; + showPassword: boolean; +} + +export default function InputAdornments() { + const [values, setValues] = React.useState({ + amount: '', + password: '', + weight: '', + weightRange: '', + showPassword: false, + }); + + const handleChange = + (prop: keyof State) => (event: React.ChangeEvent) => { + setValues({ ...values, [prop]: event.target.value }); + }; + + const handleClickShowPassword = () => { + setValues({ + ...values, + showPassword: !values.showPassword, + }); + }; + + const handleMouseDownPassword = (event: React.MouseEvent) => { + event.preventDefault(); + }; + + return ( + + kg} + /> + + + {values.showPassword ? : } + + + } + /> + + ); +} + const blue = { 100: '#DAECFF', 200: '#80BFFF', @@ -34,7 +115,7 @@ const StyledInputRoot = styled('div')( ({ theme }) => ` font-family: IBM Plex Sans, sans-serif; font-weight: 400; - border-radius: 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[500]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -44,14 +125,19 @@ const StyledInputRoot = styled('div')( justify-content: center; - &.${inputUnstyledClasses.focused} { + &.${inputClasses.focused} { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; } &:hover { border-color: ${blue[400]}; } + + // firefox + &:focus-visible { + outline: 0; + } `, ); @@ -66,12 +152,12 @@ const StyledInputElement = styled('input')( background: inherit; border: none; border-radius: inherit; - padding: 12px 12px; + padding: 8px 12px; outline: 0; `, ); -const IconButton = styled(ButtonUnstyled)( +const IconButton = styled(Button)( ({ theme }) => ` display: inline-flex; align-items: center; @@ -89,81 +175,3 @@ const InputAdornment = styled('div')` align-items: center; justify-content: center; `; - -const CustomInput = React.forwardRef(function CustomInput( - props: InputUnstyledProps, - ref: React.ForwardedRef, -) { - const { slots, ...other } = props; - return ( - - ); -}); - -interface State { - amount: string; - password: string; - weight: string; - weightRange: string; - showPassword: boolean; -} - -export default function InputAdornments() { - const [values, setValues] = React.useState({ - amount: '', - password: '', - weight: '', - weightRange: '', - showPassword: false, - }); - - const handleChange = - (prop: keyof State) => (event: React.ChangeEvent) => { - setValues({ ...values, [prop]: event.target.value }); - }; - - const handleClickShowPassword = () => { - setValues({ - ...values, - showPassword: !values.showPassword, - }); - }; - - const handleMouseDownPassword = (event: React.MouseEvent) => { - event.preventDefault(); - }; - - return ( - * + *': { ml: 1 } }}> - kg} - /> - - - {values.showPassword ? : } - - - } - /> - - ); -} diff --git a/docs/data/base/components/input/InputMultiline.js b/docs/data/base/components/input/InputMultiline.js index 533e652264b6e8..ee6e92fad87c95 100644 --- a/docs/data/base/components/input/InputMultiline.js +++ b/docs/data/base/components/input/InputMultiline.js @@ -1,7 +1,23 @@ import * as React from 'react'; -import InputUnstyled from '@mui/base/InputUnstyled'; +import { Input } from '@mui/base/Input'; import { styled } from '@mui/system'; +const CustomInput = React.forwardRef(function CustomInput(props, ref) { + return ( + + ); +}); + +export default function InputMultiline() { + return ( + + ); +} + const blue = { 100: '#DAECFF', 200: '#80BFFF', @@ -30,8 +46,8 @@ const StyledInputElement = styled('input')( font-size: 0.875rem; font-weight: 400; line-height: 1.5; - padding: 12px; - border-radius: 12px; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -43,7 +59,12 @@ const StyledInputElement = styled('input')( &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); @@ -57,9 +78,9 @@ const StyledTextareaElement = styled('textarea', { font-family: IBM Plex Sans, sans-serif; font-size: 0.875rem; font-weight: 400; - line-height: 1.5; - padding: 12px; - border-radius: 12px; + line-height: 1.5rem; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -71,23 +92,12 @@ const StyledTextareaElement = styled('textarea', { &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); - -const CustomInput = React.forwardRef(function CustomInput(props, ref) { - return ( - - ); -}); - -export default function UnstyledInputBasic() { - return ( - - ); -} diff --git a/docs/data/base/components/input/InputMultiline.tsx b/docs/data/base/components/input/InputMultiline.tsx index e377f1de40adc7..f0f32e173784a9 100644 --- a/docs/data/base/components/input/InputMultiline.tsx +++ b/docs/data/base/components/input/InputMultiline.tsx @@ -1,7 +1,26 @@ import * as React from 'react'; -import InputUnstyled, { InputUnstyledProps } from '@mui/base/InputUnstyled'; +import { Input, InputProps } from '@mui/base/Input'; import { styled } from '@mui/system'; +const CustomInput = React.forwardRef(function CustomInput( + props: InputProps, + ref: React.ForwardedRef, +) { + return ( + + ); +}); + +export default function InputMultiline() { + return ( + + ); +} + const blue = { 100: '#DAECFF', 200: '#80BFFF', @@ -30,8 +49,8 @@ const StyledInputElement = styled('input')( font-size: 0.875rem; font-weight: 400; line-height: 1.5; - padding: 12px; - border-radius: 12px; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -43,7 +62,12 @@ const StyledInputElement = styled('input')( &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); @@ -57,9 +81,9 @@ const StyledTextareaElement = styled('textarea', { font-family: IBM Plex Sans, sans-serif; font-size: 0.875rem; font-weight: 400; - line-height: 1.5; - padding: 12px; - border-radius: 12px; + line-height: 1.5rem; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -71,26 +95,12 @@ const StyledTextareaElement = styled('textarea', { &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); - -const CustomInput = React.forwardRef(function CustomInput( - props: InputUnstyledProps, - ref: React.ForwardedRef, -) { - return ( - - ); -}); - -export default function UnstyledInputBasic() { - return ( - - ); -} diff --git a/docs/data/base/components/input/InputMultilineAutosize.js b/docs/data/base/components/input/InputMultilineAutosize.js index c202de92f4a90e..68334fb0cc9e09 100644 --- a/docs/data/base/components/input/InputMultilineAutosize.js +++ b/docs/data/base/components/input/InputMultilineAutosize.js @@ -1,8 +1,24 @@ import * as React from 'react'; -import InputUnstyled from '@mui/base/InputUnstyled'; -import TextareaAutosize from '@mui/base/TextareaAutosize'; +import { Input } from '@mui/base/Input'; +import { TextareaAutosize } from '@mui/base/TextareaAutosize'; import { styled } from '@mui/system'; +const CustomInput = React.forwardRef(function CustomInput(props, ref) { + return ( + + ); +}); + +export default function InputMultilineAutosize() { + return ( + + ); +} + const blue = { 100: '#DAECFF', 200: '#80BFFF', @@ -31,8 +47,8 @@ const StyledInputElement = styled('input')( font-size: 0.875rem; font-weight: 400; line-height: 1.5; - padding: 12px; - border-radius: 12px; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -44,7 +60,12 @@ const StyledInputElement = styled('input')( &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); @@ -55,9 +76,9 @@ const StyledTextareaElement = styled(TextareaAutosize)( font-family: IBM Plex Sans, sans-serif; font-size: 0.875rem; font-weight: 400; - line-height: 1.5; - padding: 12px; - border-radius: 12px; + line-height: 1.5rem; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -69,23 +90,12 @@ const StyledTextareaElement = styled(TextareaAutosize)( &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); - -const CustomInput = React.forwardRef(function CustomInput(props, ref) { - return ( - - ); -}); - -export default function UnstyledInputBasic() { - return ( - - ); -} diff --git a/docs/data/base/components/input/InputMultilineAutosize.tsx b/docs/data/base/components/input/InputMultilineAutosize.tsx index 7b4e67b8ec6375..712aa421366615 100644 --- a/docs/data/base/components/input/InputMultilineAutosize.tsx +++ b/docs/data/base/components/input/InputMultilineAutosize.tsx @@ -1,8 +1,27 @@ import * as React from 'react'; -import InputUnstyled, { InputUnstyledProps } from '@mui/base/InputUnstyled'; -import TextareaAutosize from '@mui/base/TextareaAutosize'; +import { Input, InputProps } from '@mui/base/Input'; +import { TextareaAutosize } from '@mui/base/TextareaAutosize'; import { styled } from '@mui/system'; +const CustomInput = React.forwardRef(function CustomInput( + props: InputProps, + ref: React.ForwardedRef, +) { + return ( + + ); +}); + +export default function InputMultilineAutosize() { + return ( + + ); +} + const blue = { 100: '#DAECFF', 200: '#80BFFF', @@ -31,8 +50,8 @@ const StyledInputElement = styled('input')( font-size: 0.875rem; font-weight: 400; line-height: 1.5; - padding: 12px; - border-radius: 12px; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -44,7 +63,12 @@ const StyledInputElement = styled('input')( &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); @@ -55,9 +79,9 @@ const StyledTextareaElement = styled(TextareaAutosize)( font-family: IBM Plex Sans, sans-serif; font-size: 0.875rem; font-weight: 400; - line-height: 1.5; - padding: 12px; - border-radius: 12px; + line-height: 1.5rem; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -69,26 +93,12 @@ const StyledTextareaElement = styled(TextareaAutosize)( &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); - -const CustomInput = React.forwardRef(function CustomInput( - props: InputUnstyledProps, - ref: React.ForwardedRef, -) { - return ( - - ); -}); - -export default function UnstyledInputBasic() { - return ( - - ); -} diff --git a/docs/data/base/components/input/UnstyledInputBasic.js b/docs/data/base/components/input/UnstyledInputBasic.js deleted file mode 100644 index be86fab1ec7dfd..00000000000000 --- a/docs/data/base/components/input/UnstyledInputBasic.js +++ /dev/null @@ -1,59 +0,0 @@ -import * as React from 'react'; -import InputUnstyled from '@mui/base/InputUnstyled'; -import { styled } from '@mui/system'; - -const blue = { - 100: '#DAECFF', - 200: '#80BFFF', - 400: '#3399FF', - 500: '#007FFF', - 600: '#0072E5', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E7EBF0', - 200: '#E0E3E7', - 300: '#CDD2D7', - 400: '#B2BAC2', - 500: '#A0AAB4', - 600: '#6F7E8C', - 700: '#3E5060', - 800: '#2D3843', - 900: '#1A2027', -}; - -const StyledInputElement = styled('input')( - ({ theme }) => ` - width: 320px; - font-family: IBM Plex Sans, sans-serif; - font-size: 0.875rem; - font-weight: 400; - line-height: 1.5; - padding: 12px; - border-radius: 12px; - color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; - background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; - border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; - box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; - - &:hover { - border-color: ${blue[400]}; - } - - &:focus { - border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; - } -`, -); - -const CustomInput = React.forwardRef(function CustomInput(props, ref) { - return ( - - ); -}); - -export default function UnstyledInputBasic() { - return ; -} diff --git a/docs/data/base/components/input/UnstyledInputBasic.tsx b/docs/data/base/components/input/UnstyledInputBasic.tsx deleted file mode 100644 index ec7e71c96c72c7..00000000000000 --- a/docs/data/base/components/input/UnstyledInputBasic.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from 'react'; -import InputUnstyled, { InputUnstyledProps } from '@mui/base/InputUnstyled'; -import { styled } from '@mui/system'; - -const blue = { - 100: '#DAECFF', - 200: '#80BFFF', - 400: '#3399FF', - 500: '#007FFF', - 600: '#0072E5', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E7EBF0', - 200: '#E0E3E7', - 300: '#CDD2D7', - 400: '#B2BAC2', - 500: '#A0AAB4', - 600: '#6F7E8C', - 700: '#3E5060', - 800: '#2D3843', - 900: '#1A2027', -}; - -const StyledInputElement = styled('input')( - ({ theme }) => ` - width: 320px; - font-family: IBM Plex Sans, sans-serif; - font-size: 0.875rem; - font-weight: 400; - line-height: 1.5; - padding: 12px; - border-radius: 12px; - color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; - background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; - border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; - box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; - - &:hover { - border-color: ${blue[400]}; - } - - &:focus { - border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; - } -`, -); - -const CustomInput = React.forwardRef(function CustomInput( - props: InputUnstyledProps, - ref: React.ForwardedRef, -) { - return ( - - ); -}); - -export default function UnstyledInputBasic() { - return ; -} diff --git a/docs/data/base/components/input/UnstyledInputBasic/css/index.js b/docs/data/base/components/input/UnstyledInputBasic/css/index.js new file mode 100644 index 00000000000000..a64877d635bed1 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputBasic/css/index.js @@ -0,0 +1,85 @@ +import * as React from 'react'; +import { Input } from '@mui/base/Input'; +import { useTheme } from '@mui/system'; + +export default function UnstyledInputBasic() { + return ( + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +const grey = { + 50: '#F3F6F9', + 100: '#E7EBF0', + 200: '#E0E3E7', + 300: '#CDD2D7', + 400: '#B2BAC2', + 500: '#A0AAB4', + 600: '#6F7E8C', + 700: '#3E5060', + 800: '#2D3843', + 900: '#1A2027', +}; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + + return ( + + ); +} diff --git a/docs/data/base/components/input/UnstyledInputBasic/css/index.tsx b/docs/data/base/components/input/UnstyledInputBasic/css/index.tsx new file mode 100644 index 00000000000000..a64877d635bed1 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputBasic/css/index.tsx @@ -0,0 +1,85 @@ +import * as React from 'react'; +import { Input } from '@mui/base/Input'; +import { useTheme } from '@mui/system'; + +export default function UnstyledInputBasic() { + return ( + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +const grey = { + 50: '#F3F6F9', + 100: '#E7EBF0', + 200: '#E0E3E7', + 300: '#CDD2D7', + 400: '#B2BAC2', + 500: '#A0AAB4', + 600: '#6F7E8C', + 700: '#3E5060', + 800: '#2D3843', + 900: '#1A2027', +}; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + + return ( + + ); +} diff --git a/docs/data/base/components/input/UnstyledInputBasic/css/index.tsx.preview b/docs/data/base/components/input/UnstyledInputBasic/css/index.tsx.preview new file mode 100644 index 00000000000000..b4003b693f4f3a --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputBasic/css/index.tsx.preview @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/docs/data/base/components/input/UnstyledInputBasic/system/index.js b/docs/data/base/components/input/UnstyledInputBasic/system/index.js new file mode 100644 index 00000000000000..cd745b1e1a8d5a --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputBasic/system/index.js @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { Input } from '@mui/base/Input'; +import { styled } from '@mui/system'; + +const CustomInput = React.forwardRef(function CustomInput(props, ref) { + return ; +}); + +export default function UnstyledInputBasic() { + return ; +} + +const blue = { + 100: '#DAECFF', + 200: '#80BFFF', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', +}; + +const grey = { + 50: '#F3F6F9', + 100: '#E7EBF0', + 200: '#E0E3E7', + 300: '#CDD2D7', + 400: '#B2BAC2', + 500: '#A0AAB4', + 600: '#6F7E8C', + 700: '#3E5060', + 800: '#2D3843', + 900: '#1A2027', +}; + +const StyledInputElement = styled('input')( + ({ theme }) => ` + width: 320px; + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.5; + padding: 8px 12px; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; + + &:hover { + border-color: ${blue[400]}; + } + + &:focus { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; + } +`, +); diff --git a/docs/data/base/components/input/UnstyledInputBasic/system/index.tsx b/docs/data/base/components/input/UnstyledInputBasic/system/index.tsx new file mode 100644 index 00000000000000..1d4631d3080346 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputBasic/system/index.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import { Input, InputProps } from '@mui/base/Input'; +import { styled } from '@mui/system'; + +const CustomInput = React.forwardRef(function CustomInput( + props: InputProps, + ref: React.ForwardedRef, +) { + return ; +}); + +export default function UnstyledInputBasic() { + return ; +} + +const blue = { + 100: '#DAECFF', + 200: '#80BFFF', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', +}; + +const grey = { + 50: '#F3F6F9', + 100: '#E7EBF0', + 200: '#E0E3E7', + 300: '#CDD2D7', + 400: '#B2BAC2', + 500: '#A0AAB4', + 600: '#6F7E8C', + 700: '#3E5060', + 800: '#2D3843', + 900: '#1A2027', +}; + +const StyledInputElement = styled('input')( + ({ theme }) => ` + width: 320px; + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.5; + padding: 8px 12px; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 2px ${theme.palette.mode === 'dark' ? grey[900] : grey[50]}; + + &:hover { + border-color: ${blue[400]}; + } + + &:focus { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; + } +`, +); diff --git a/docs/data/base/components/input/UnstyledInputBasic.tsx.preview b/docs/data/base/components/input/UnstyledInputBasic/system/index.tsx.preview similarity index 100% rename from docs/data/base/components/input/UnstyledInputBasic.tsx.preview rename to docs/data/base/components/input/UnstyledInputBasic/system/index.tsx.preview diff --git a/docs/data/base/components/input/UnstyledInputBasic/tailwind/index.js b/docs/data/base/components/input/UnstyledInputBasic/tailwind/index.js new file mode 100644 index 00000000000000..128b9e468e4288 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputBasic/tailwind/index.js @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { Input } from '@mui/base/Input'; +import { useTheme } from '@mui/system'; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +export default function UnstyledInputBasic() { + // Replace this with your app logic for determining dark modes + const isDarkMode = useIsDarkMode(); + + return ( + + ); +} diff --git a/docs/data/base/components/input/UnstyledInputBasic/tailwind/index.tsx b/docs/data/base/components/input/UnstyledInputBasic/tailwind/index.tsx new file mode 100644 index 00000000000000..128b9e468e4288 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputBasic/tailwind/index.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { Input } from '@mui/base/Input'; +import { useTheme } from '@mui/system'; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +export default function UnstyledInputBasic() { + // Replace this with your app logic for determining dark modes + const isDarkMode = useIsDarkMode(); + + return ( + + ); +} diff --git a/docs/data/base/components/input/UnstyledInputBasic/tailwind/index.tsx.preview b/docs/data/base/components/input/UnstyledInputBasic/tailwind/index.tsx.preview new file mode 100644 index 00000000000000..2431f649ca2a90 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputBasic/tailwind/index.tsx.preview @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/docs/data/base/components/input/UnstyledInputIntroduction.js b/docs/data/base/components/input/UnstyledInputIntroduction.js deleted file mode 100644 index 08c9c848375c0e..00000000000000 --- a/docs/data/base/components/input/UnstyledInputIntroduction.js +++ /dev/null @@ -1,59 +0,0 @@ -import * as React from 'react'; -import InputUnstyled from '@mui/base/InputUnstyled'; -import { styled } from '@mui/system'; - -const blue = { - 100: '#DAECFF', - 200: '#b6daff', - 400: '#3399FF', - 500: '#007FFF', - 600: '#0072E5', -}; - -const grey = { - 50: '#f6f8fa', - 100: '#eaeef2', - 200: '#d0d7de', - 300: '#afb8c1', - 400: '#8c959f', - 500: '#6e7781', - 600: '#57606a', - 700: '#424a53', - 800: '#32383f', - 900: '#24292f', -}; - -const StyledInputElement = styled('input')( - ({ theme }) => ` - width: 320px; - font-family: IBM Plex Sans, sans-serif; - font-size: 0.875rem; - font-weight: 400; - line-height: 1.5; - padding: 12px; - border-radius: 12px; - color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; - background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; - border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; - box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[200]}; - - &:hover { - border-color: ${blue[400]}; - } - - &:focus { - border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; - } -`, -); - -const CustomInput = React.forwardRef(function CustomInput(props, ref) { - return ( - - ); -}); - -export default function UnstyledInputIntroduction() { - return ; -} diff --git a/docs/data/base/components/input/UnstyledInputIntroduction.tsx b/docs/data/base/components/input/UnstyledInputIntroduction.tsx deleted file mode 100644 index 7f5703901e9a3b..00000000000000 --- a/docs/data/base/components/input/UnstyledInputIntroduction.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from 'react'; -import InputUnstyled from '@mui/base/InputUnstyled'; -import { styled } from '@mui/system'; - -const blue = { - 100: '#DAECFF', - 200: '#b6daff', - 400: '#3399FF', - 500: '#007FFF', - 600: '#0072E5', -}; - -const grey = { - 50: '#f6f8fa', - 100: '#eaeef2', - 200: '#d0d7de', - 300: '#afb8c1', - 400: '#8c959f', - 500: '#6e7781', - 600: '#57606a', - 700: '#424a53', - 800: '#32383f', - 900: '#24292f', -}; - -const StyledInputElement = styled('input')( - ({ theme }) => ` - width: 320px; - font-family: IBM Plex Sans, sans-serif; - font-size: 0.875rem; - font-weight: 400; - line-height: 1.5; - padding: 12px; - border-radius: 12px; - color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; - background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; - border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; - box-shadow: 0px 4px 30px ${theme.palette.mode === 'dark' ? grey[900] : grey[200]}; - - &:hover { - border-color: ${blue[400]}; - } - - &:focus { - border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; - } -`, -); - -const CustomInput = React.forwardRef(function CustomInput( - props: React.InputHTMLAttributes, - ref: React.ForwardedRef, -) { - return ( - - ); -}); - -export default function UnstyledInputIntroduction() { - return ; -} diff --git a/docs/data/base/components/input/UnstyledInputIntroduction/css/index.js b/docs/data/base/components/input/UnstyledInputIntroduction/css/index.js new file mode 100644 index 00000000000000..7db613fae34b34 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputIntroduction/css/index.js @@ -0,0 +1,84 @@ +import * as React from 'react'; +import { Input } from '@mui/base/Input'; +import { useTheme } from '@mui/system'; + +export default function UnstyledInputIntroduction() { + return ( + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +const grey = { + 50: '#F3F6F9', + 100: '#E7EBF0', + 200: '#E0E3E7', + 300: '#CDD2D7', + 400: '#B2BAC2', + 500: '#A0AAB4', + 600: '#6F7E8C', + 700: '#3E5060', + 800: '#2D3843', + 900: '#1A2027', +}; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + + return ( + + ); +} diff --git a/docs/data/base/components/input/UnstyledInputIntroduction/css/index.tsx b/docs/data/base/components/input/UnstyledInputIntroduction/css/index.tsx new file mode 100644 index 00000000000000..7db613fae34b34 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputIntroduction/css/index.tsx @@ -0,0 +1,84 @@ +import * as React from 'react'; +import { Input } from '@mui/base/Input'; +import { useTheme } from '@mui/system'; + +export default function UnstyledInputIntroduction() { + return ( + + + + + ); +} + +const cyan = { + 50: '#E9F8FC', + 100: '#BDEBF4', + 200: '#99D8E5', + 300: '#66BACC', + 400: '#1F94AD', + 500: '#0D5463', + 600: '#094855', + 700: '#063C47', + 800: '#043039', + 900: '#022127', +}; + +const grey = { + 50: '#F3F6F9', + 100: '#E7EBF0', + 200: '#E0E3E7', + 300: '#CDD2D7', + 400: '#B2BAC2', + 500: '#A0AAB4', + 600: '#6F7E8C', + 700: '#3E5060', + 800: '#2D3843', + 900: '#1A2027', +}; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + + return ( + + ); +} diff --git a/docs/data/base/components/input/UnstyledInputIntroduction/css/index.tsx.preview b/docs/data/base/components/input/UnstyledInputIntroduction/css/index.tsx.preview new file mode 100644 index 00000000000000..7ae3c657ead360 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputIntroduction/css/index.tsx.preview @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/docs/data/base/components/input/UnstyledInputIntroduction/system/index.js b/docs/data/base/components/input/UnstyledInputIntroduction/system/index.js new file mode 100644 index 00000000000000..dfd3541fc0e85f --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputIntroduction/system/index.js @@ -0,0 +1,63 @@ +import * as React from 'react'; +import { Input } from '@mui/base/Input'; +import { styled } from '@mui/system'; + +const CustomInput = React.forwardRef(function CustomInput(props, ref) { + return ; +}); + +export default function UnstyledInputIntroduction() { + return ; +} + +const blue = { + 100: '#DAECFF', + 200: '#b6daff', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledInputElement = styled('input')( + ({ theme }) => ` + width: 320px; + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.5; + padding: 8px 12px; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 24px ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + + &:hover { + border-color: ${blue[400]}; + } + + &:focus { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[600] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; + } +`, +); diff --git a/docs/data/base/components/input/UnstyledInputIntroduction/system/index.tsx b/docs/data/base/components/input/UnstyledInputIntroduction/system/index.tsx new file mode 100644 index 00000000000000..9bcd5bd37f4db8 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputIntroduction/system/index.tsx @@ -0,0 +1,66 @@ +import * as React from 'react'; +import { Input } from '@mui/base/Input'; +import { styled } from '@mui/system'; + +const CustomInput = React.forwardRef(function CustomInput( + props: React.InputHTMLAttributes, + ref: React.ForwardedRef, +) { + return ; +}); + +export default function UnstyledInputIntroduction() { + return ; +} + +const blue = { + 100: '#DAECFF', + 200: '#b6daff', + 400: '#3399FF', + 500: '#007FFF', + 600: '#0072E5', + 900: '#003A75', +}; + +const grey = { + 50: '#f6f8fa', + 100: '#eaeef2', + 200: '#d0d7de', + 300: '#afb8c1', + 400: '#8c959f', + 500: '#6e7781', + 600: '#57606a', + 700: '#424a53', + 800: '#32383f', + 900: '#24292f', +}; + +const StyledInputElement = styled('input')( + ({ theme }) => ` + width: 320px; + font-family: IBM Plex Sans, sans-serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.5; + padding: 8px 12px; + border-radius: 8px; + color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; + background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; + border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; + box-shadow: 0px 2px 24px ${theme.palette.mode === 'dark' ? blue[900] : blue[100]}; + + &:hover { + border-color: ${blue[400]}; + } + + &:focus { + border-color: ${blue[400]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[600] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; + } +`, +); diff --git a/docs/data/base/components/input/UnstyledInputIntroduction.tsx.preview b/docs/data/base/components/input/UnstyledInputIntroduction/system/index.tsx.preview similarity index 100% rename from docs/data/base/components/input/UnstyledInputIntroduction.tsx.preview rename to docs/data/base/components/input/UnstyledInputIntroduction/system/index.tsx.preview diff --git a/docs/data/base/components/input/UnstyledInputIntroduction/tailwind/index.js b/docs/data/base/components/input/UnstyledInputIntroduction/tailwind/index.js new file mode 100644 index 00000000000000..863bce82d7ee99 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputIntroduction/tailwind/index.js @@ -0,0 +1,60 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { Input as BaseInput } from '@mui/base/Input'; +import { useTheme } from '@mui/system'; +import clsx from 'clsx'; + +export default function UnstyledInputIntroduction() { + return ; +} + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +const resolveSlotProps = (fn, args) => (typeof fn === 'function' ? fn(args) : fn); + +const Input = React.forwardRef((props, ref) => { + // Replace this with your app logic for determining dark modes + const isDarkMode = useIsDarkMode(); + + return ( + { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.input, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'w-80 text-sm font-normal font-sans leading-5 px-3 py-2 rounded-lg shadow-md shadow-slate-100 dark:shadow-slate-900 focus:shadow-outline-purple dark:focus:shadow-outline-purple focus:shadow-lg border border-solid border-slate-300 hover:border-purple-500 dark:hover:border-purple-500 focus:border-purple-500 dark:focus:border-purple-500 dark:border-slate-600 bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-300 focus-visible:outline-0', + resolvedSlotProps?.className, + ), + }; + }, + }} + /> + ); +}); + +Input.propTypes = { + /** + * Class name applied to the root element. + */ + className: PropTypes.string, + /** + * The props used for each slot inside the Input. + * @default {} + */ + slotProps: PropTypes.shape({ + input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), +}; diff --git a/docs/data/base/components/input/UnstyledInputIntroduction/tailwind/index.tsx b/docs/data/base/components/input/UnstyledInputIntroduction/tailwind/index.tsx new file mode 100644 index 00000000000000..521b5e2b822c6e --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputIntroduction/tailwind/index.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { Input as BaseInput, InputProps } from '@mui/base/Input'; +import { useTheme } from '@mui/system'; +import clsx from 'clsx'; + +export default function UnstyledInputIntroduction() { + return ; +} + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +const resolveSlotProps = (fn: any, args: any) => + typeof fn === 'function' ? fn(args) : fn; + +const Input = React.forwardRef((props, ref) => { + // Replace this with your app logic for determining dark modes + const isDarkMode = useIsDarkMode(); + + return ( + { + const resolvedSlotProps = resolveSlotProps( + props.slotProps?.input, + ownerState, + ); + return { + ...resolvedSlotProps, + className: clsx( + 'w-80 text-sm font-normal font-sans leading-5 px-3 py-2 rounded-lg shadow-md shadow-slate-100 dark:shadow-slate-900 focus:shadow-outline-purple dark:focus:shadow-outline-purple focus:shadow-lg border border-solid border-slate-300 hover:border-purple-500 dark:hover:border-purple-500 focus:border-purple-500 dark:focus:border-purple-500 dark:border-slate-600 bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-300 focus-visible:outline-0', + resolvedSlotProps?.className, + ), + }; + }, + }} + /> + ); +}); diff --git a/docs/data/base/components/input/UnstyledInputIntroduction/tailwind/index.tsx.preview b/docs/data/base/components/input/UnstyledInputIntroduction/tailwind/index.tsx.preview new file mode 100644 index 00000000000000..12d25a9fa99b24 --- /dev/null +++ b/docs/data/base/components/input/UnstyledInputIntroduction/tailwind/index.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/base/components/input/UseInput.js b/docs/data/base/components/input/UseInput.js index bb5461650e1765..ef81f4af696f38 100644 --- a/docs/data/base/components/input/UseInput.js +++ b/docs/data/base/components/input/UseInput.js @@ -1,8 +1,27 @@ import * as React from 'react'; -import { useInput } from '@mui/base'; +import { useInput } from '@mui/base/useInput'; import { styled } from '@mui/system'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; +const CustomInput = React.forwardRef(function CustomInput(props, ref) { + const { getRootProps, getInputProps } = useInput(props); + + const inputProps = getInputProps(); + + // Make sure that both the forwarded ref and the ref returned from the getInputProps are applied on the input element + inputProps.ref = useForkRef(inputProps.ref, ref); + + return ( +
+ +
+ ); +}); + +export default function UseInput() { + return ; +} + const blue = { 100: '#DAECFF', 200: '#80BFFF', @@ -31,8 +50,8 @@ const StyledInputElement = styled('input')( font-size: 0.875rem; font-weight: 400; line-height: 1.5; - padding: 12px; - border-radius: 12px; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -44,26 +63,12 @@ const StyledInputElement = styled('input')( &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); - -const CustomInput = React.forwardRef(function CustomInput(props, ref) { - const { getRootProps, getInputProps } = useInput(props); - - const inputProps = getInputProps(); - - // Make sure that both the forwarded ref and the ref returned from the getInputProps are applied on the input element - inputProps.ref = useForkRef(inputProps.ref, ref); - - return ( -
- -
- ); -}); - -export default function UseInput() { - return ; -} diff --git a/docs/data/base/components/input/UseInput.tsx b/docs/data/base/components/input/UseInput.tsx index 40cfb41ae8f012..244addad780d32 100644 --- a/docs/data/base/components/input/UseInput.tsx +++ b/docs/data/base/components/input/UseInput.tsx @@ -1,8 +1,30 @@ import * as React from 'react'; -import { useInput } from '@mui/base'; +import { useInput } from '@mui/base/useInput'; import { styled } from '@mui/system'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; +const CustomInput = React.forwardRef(function CustomInput( + props: React.InputHTMLAttributes, + ref: React.ForwardedRef, +) { + const { getRootProps, getInputProps } = useInput(props); + + const inputProps = getInputProps(); + + // Make sure that both the forwarded ref and the ref returned from the getInputProps are applied on the input element + inputProps.ref = useForkRef(inputProps.ref, ref); + + return ( +
+ +
+ ); +}); + +export default function UseInput() { + return ; +} + const blue = { 100: '#DAECFF', 200: '#80BFFF', @@ -31,8 +53,8 @@ const StyledInputElement = styled('input')( font-size: 0.875rem; font-weight: 400; line-height: 1.5; - padding: 12px; - border-radius: 12px; + padding: 8px 12px; + border-radius: 8px; color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]}; background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; @@ -44,29 +66,12 @@ const StyledInputElement = styled('input')( &:focus { border-color: ${blue[400]}; - outline: 3px solid ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + box-shadow: 0 0 0 3px ${theme.palette.mode === 'dark' ? blue[500] : blue[200]}; + } + + // firefox + &:focus-visible { + outline: 0; } `, ); - -const CustomInput = React.forwardRef(function CustomInput( - props: React.InputHTMLAttributes, - ref: React.ForwardedRef, -) { - const { getRootProps, getInputProps } = useInput(props); - - const inputProps = getInputProps(); - - // Make sure that both the forwarded ref and the ref returned from the getInputProps are applied on the input element - inputProps.ref = useForkRef(inputProps.ref, ref); - - return ( -
- -
- ); -}); - -export default function UseInput() { - return ; -} diff --git a/docs/data/base/components/input/input-pt.md b/docs/data/base/components/input/input-pt.md index 6f6b67e0dc4eca..7214f1abebd5d9 100644 --- a/docs/data/base/components/input/input-pt.md +++ b/docs/data/base/components/input/input-pt.md @@ -1,5 +1,5 @@ --- -product: base +productId: base-ui title: Unstyled React Input component and hook components: InputUnstyled githubLabel: 'component: input' @@ -25,7 +25,7 @@ An input is a UI element that accepts text data from the user. The `InputUnstyle ### Usage -After [installation](/base/getting-started/installation/), you can start building with this component using the following basic elements: +After [installation](/base-ui/getting-started/quickstart/#installation), you can start building with this component using the following basic elements: ```jsx import InputUnstyled from '@mui/base/InputUnstyled'; @@ -56,7 +56,7 @@ The `InputUnstyled` component is composed of a root `
` slot that houses one ### Slot props :::info -The following props are available on all non-utility Base components. See [Usage](/base/getting-started/usage/) for full details. +The following props are available on all non-utility Base components. See [Usage](/base-ui/getting-started/usage/) for full details. ::: Use the `component` prop to override the root slot with a custom element: @@ -124,7 +124,7 @@ The `multiline` prop transforms the `` field into a `