init project
Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled

This commit is contained in:
how2ice
2025-12-12 14:26:25 +09:00
commit 005cf56baf
43188 changed files with 1079531 additions and 0 deletions

81
.browserslistrc Normal file
View File

@@ -0,0 +1,81 @@
# Default/Fallback
# `npx browserslist --mobile-to-desktop "> 0.5%, last 2 versions, Firefox ESR, not dead, safari >= 15.4, iOS >= 15.4"` when the last major is released.
# Explicit safari versions are here based on the agreed terms in: https://github.com/mui/material-ui/issues/40958#issuecomment-1953215043
#
# After you update the version, you might need to run `npx update-browserslist-db@latest` to update caniuse-lite to gather latest browser versions.
# Otherwise, running `pnpm build` might fail due to unknown browser versions.
#
# On update, sync references where "#stable-snapshot" is mentioned in the codebase.
[stable]
and_chr 122
and_chr 121
and_ff 123
and_ff 122
and_qq 14.9
and_uc 15.5
android 122
android 121
chrome 122
chrome 121
chrome 120
chrome 119
chrome 109
edge 122
edge 121
firefox 123
firefox 122
firefox 115
ios_saf 17.4
ios_saf 17.3
ios_saf 17.2
ios_saf 17.1
ios_saf 17.0
ios_saf 16.6-16.7
ios_saf 16.5
ios_saf 16.4
ios_saf 16.3
ios_saf 16.2
ios_saf 16.1
ios_saf 16.0
ios_saf 15.6-15.8
ios_saf 15.5
ios_saf 15.4
kaios 3.0-3.1
op_mob 80
opera 108
opera 107
opera 106
safari 17.4
safari 17.3
safari 17.2
safari 17.1
safari 17.0
safari 16.6
safari 16.5
safari 16.4
safari 16.3
safari 16.2
safari 16.1
safari 16.0
safari 15.6
safari 15.5
safari 15.4
samsung 23
samsung 22
# snapshot of `npx browserslist "maintained node versions"`
# On update check all #stable-snapshot markers
[node]
node 14.0
# same as `node`
[coverage]
node 14.0
# same as `node`
[development]
node 14.0
# same as `node`
[test]
node 14.0

755
.circleci/config.yml Normal file
View File

@@ -0,0 +1,755 @@
version: 2.1
orbs:
aws-cli: circleci/aws-cli@5.4.1
aws-s3: circleci/aws-s3@4.1.3
code-infra: https://raw.githubusercontent.com/mui/mui-public/39dc8ce86d032c503f47dcd369a4f903b6d3a985/.circleci/orbs/code-infra.yml
parameters:
workflow:
description: The name of the workflow to run
type: string
default: pipeline
e2e-base-url:
description: The base url for running end-to-end test
type: string
default: ''
default-job: &default-job
parameters:
react-version:
description: The version of react to be used
type: string
default: stable
typescript-version:
description: The version of typescript to be used
type: string
default: stable
e2e-base-url:
description: The base url for running end-to-end test
type: string
default: << pipeline.parameters.e2e-base-url >>
environment:
AWS_REGION_ARTIFACTS: eu-central-1
COREPACK_ENABLE_DOWNLOAD_PROMPT: '0'
DANGER_DISABLE_TRANSPILATION: 'true'
working_directory: /tmp/material-ui
executor:
name: code-infra/mui-node
node-version: '22.18'
default-context: &default-context
context:
- org-global
# CircleCI has disabled the cache across forks for security reasons.
# Following their official statement, it was a quick solution, they
# are working on providing this feature back with appropriate security measures.
# https://discuss.circleci.com/t/saving-cache-stopped-working-warning-skipping-this-step-disabled-in-configuration/24423/21
#
# restore_repo: &restore_repo
# restore_cache:
# key: v1-repo-{{ .Branch }}-{{ .Revision }}
commands:
install-deps:
parameters:
ignore-workspace:
type: boolean
default: false
react-version:
type: string
default: stable
typescript-version:
type: string
default: stable
playwright-version:
type: string
default: stable
steps:
- code-infra/install-deps:
package-overrides: react@<< parameters.react-version >> typescript@<< parameters.typescript-version >> @playwright/test@<< parameters.playwright-version >> playwright@<< parameters.playwright-version >>
ignore-workspace: << parameters.ignore-workspace >>
jobs:
test_unit:
<<: *default-job
steps:
- checkout
- install-deps:
react-version: << parameters.react-version >>
- run:
name: Tests fake browser
command: pnpm test:node --no-isolate --no-file-parallelism --coverage
- run:
name: internal-scripts
command: |
# latest commit
LATEST_COMMIT=$(git rev-parse HEAD)
# latest commit where internal-scripts was changed
FOLDER_COMMIT=$(git log -1 --format=format:%H --full-diff packages-internal/scripts)
if [ $FOLDER_COMMIT = $LATEST_COMMIT ]; then
echo "changes, let's run the tests"
pnpm --filter @mui/internal-scripts test
else
echo "no changes"
fi
- code-infra/upload-coverage:
key: '<< parameters.react-version >>-jsdom'
- store_test_results:
path: test-results
test_lint:
<<: *default-job
steps:
- checkout
- install-deps
- code-infra/run-linters
test_static:
<<: *default-job
steps:
- checkout
- install-deps
- code-infra/check-static-changes
- run:
name: Generate PropTypes
command: pnpm proptypes
- run:
name: '`pnpm proptypes` changes committed?'
command: git add -A && git diff --exit-code --staged
- run:
name: Generate the documentation
command: pnpm docs:api
- run:
name: '`pnpm docs:api` changes committed?'
command: git add -A && git diff --exit-code --staged
- run:
name: Update the navigation translations
command: pnpm docs:i18n
- run:
name: '`pnpm docs:i18n` changes committed?'
command: git add -A && git diff --exit-code --staged
- run:
name: '`pnpm extract-error-codes` changes committed?'
command: |
pnpm extract-error-codes
git add -A && git diff --exit-code --staged
test_types:
<<: *default-job
resource_class: 'medium+'
steps:
- checkout
- install-deps
- run:
name: Transpile TypeScript demos
command: pnpm docs:typescript:formatted
- run:
name: '`pnpm docs:typescript:formatted` changes committed?'
command: git add -A && git diff --exit-code --staged
- run:
name: Tests TypeScript definitions
command: pnpm typescript:ci
environment:
NODE_OPTIONS: --max-old-space-size=3072
- run:
name: Test module augmentation
command: pnpm typescript:module-augmentation
test_types_next:
<<: *default-job
resource_class: 'medium+'
steps:
- checkout
- install-deps:
typescript-version: << parameters.typescript-version >>
- run:
name: Tests TypeScript definitions
command: pnpm typescript:ci
environment:
NODE_OPTIONS: --max-old-space-size=3072
- run:
name: Prepare files
# Enable configuration that is only valid in typescript@next
command: sed -i 's|// <ts@next uncomment>||g' test/moduleResolution/tsconfig.node.json
- run:
name: Build declaration files
command: |
pnpm lerna run --scope "@mui/*" build
- run:
name: Log defect declaration files
command: |
node scripts/testBuiltTypes.mjs
test_browser_legacy:
<<: *default-job
resource_class: 'medium+'
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.49.1-noble
steps:
- checkout
- install-deps:
react-version: << parameters.react-version >>
playwright-version: '1.49.1'
- run:
name: Tests chromium
command: pnpm test:browser --no-isolate --no-file-parallelism
- run:
name: Tests webkit
environment:
VITEST_BROWSERS: 'webkit'
command: pnpm test:browser --no-isolate --no-file-parallelism
- run:
name: Tests firefox
environment:
VITEST_BROWSERS: 'firefox'
command: pnpm test:browser --no-isolate --no-file-parallelism
- store_test_results:
path: test-results
test_browser:
<<: *default-job
resource_class: 'medium+'
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
steps:
- checkout
- install-deps:
react-version: << parameters.react-version >>
- run:
name: Tests chromium
command: pnpm test:browser --no-isolate --no-file-parallelism --coverage
- run:
name: Tests webkit
environment:
VITEST_BROWSERS: 'webkit'
command: pnpm test:browser --no-isolate --no-file-parallelism
- run:
name: Tests firefox
environment:
VITEST_BROWSERS: 'firefox'
command: pnpm test:browser --no-isolate --no-file-parallelism
- run:
name: Check coverage generated
command: |
if ! [[ -s coverage/lcov.info ]]
then
exit 1
fi
- code-infra/upload-coverage:
key: '<< parameters.react-version >>-browser'
- store_test_results:
path: test-results
test_e2e:
<<: *default-job
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
steps:
- checkout
- install-deps:
react-version: << parameters.react-version >>
- run:
name: pnpm test:e2e
command: pnpm test:e2e
test_e2e_website:
# NOTE: This workflow runs after successful docs deploy. See /test/e2e-website/README.md#ci
<<: *default-job
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
steps:
- checkout
- install-deps
- run:
name: pnpm test:e2e-website
command: pnpm test:e2e-website
environment:
PLAYWRIGHT_TEST_BASE_URL: << parameters.e2e-base-url >>
test_regressions:
<<: *default-job
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
steps:
- checkout
- install-deps:
react-version: << parameters.react-version >>
- run:
name: Run visual regression tests
command: xvfb-run pnpm test:regressions
- run:
name: Build packages for fixtures
command: pnpm release:build
- run:
name: Validate type declarations
command: pnpm validate-declarations
- run:
name: Analyze exported typescript
command: pnpm test:attw
- run:
name: Any defect declaration files?
command: node scripts/testBuiltTypes.mjs
- run:
name: test exported typescript
command: pnpm --filter @mui-internal/test-module-resolution typescript:all
- run:
name: Upload screenshots to Argos CI
command: pnpm test:argos
test_bundling_prepare:
<<: *default-job
steps:
- checkout
- install-deps
- run:
name: Build packages for fixtures
command: pnpm lerna run --scope "@mui/*" build
- run:
name: Pack packages
command: pnpm release:pack
- persist_to_workspace:
root: packed
paths:
- '*'
test_bundling_node_cjs:
<<: *default-job
working_directory: /tmp/material-ui/test/bundling/fixtures/node-cjs/
steps:
- checkout:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- install-deps:
ignore-workspace: true
- run:
name: Test fixture
command: pnpm start
test_bundling_node_esm:
<<: *default-job
working_directory: /tmp/material-ui/test/bundling/fixtures/node-esm/
steps:
- checkout:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- install-deps:
ignore-workspace: true
- run:
name: Test fixture
# TODO: Known failure
command: pnpm start
test_bundling_next_webpack4:
<<: *default-job
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
working_directory: /tmp/material-ui/test/bundling/fixtures/next-webpack4/
steps:
- checkout:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- install-deps:
ignore-workspace: true
- run:
name: Test fixture
command: pnpm start
test_bundling_next_webpack5:
<<: *default-job
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
working_directory: /tmp/material-ui/test/bundling/fixtures/next-webpack5/
steps:
- checkout:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- install-deps:
ignore-workspace: true
- run:
name: Test fixture
command: pnpm start
test_bundling_create_react_app:
<<: *default-job
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
working_directory: /tmp/material-ui/test/bundling/fixtures/create-react-app/
steps:
- checkout:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- install-deps:
ignore-workspace: true
- run:
name: Test fixture
command: pnpm start
test_bundling_snowpack:
<<: *default-job
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
working_directory: /tmp/material-ui/test/bundling/fixtures/snowpack/
steps:
- checkout:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- install-deps:
ignore-workspace: true
- run:
name: Test fixture
command: pnpm start
test_bundling_vite:
<<: *default-job
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
working_directory: /tmp/material-ui/test/bundling/fixtures/vite/
steps:
- checkout:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- install-deps:
ignore-workspace: true
- run:
name: Test fixture
command: pnpm start
test_bundling_esbuild:
<<: *default-job
docker:
- image: mcr.microsoft.com/playwright:v1.56.1-noble
working_directory: /tmp/material-ui/test/bundling/fixtures/esbuild/
steps:
- checkout:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- install-deps:
ignore-workspace: true
- run:
name: Test fixture
command: pnpm start
test_bundling_gatsby:
<<: *default-job
executor:
name: code-infra/mui-node-browser
playwright-img-version: v1.56.1-noble
environment:
GATSBY_CPU_COUNT: '3'
working_directory: /tmp/material-ui/test/bundling/fixtures/gatsby/
steps:
- checkout:
path: /tmp/material-ui
- attach_workspace:
at: /tmp/material-ui/packed
- install-deps:
ignore-workspace: true
- run:
name: Test fixture
command: pnpm start
test_bundle_size_monitor:
<<: *default-job
steps:
- checkout
- install-deps
- run:
name: prepare danger on PRs
command: pnpm danger ci
environment:
DANGER_COMMAND: prepareBundleSizeReport
- run:
name: build @mui packages
command: pnpm release:build
- 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}
# @TODO: Not using code-infra/upload-size-snapshot since it doesn't support setting concurrency yet
- run:
name: create and upload a size snapshot
command: |
export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID_ARTIFACTS
export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY_ARTIFACTS
export AWS_REGION=$AWS_REGION_ARTIFACTS
pnpm size:snapshot --concurrency 6
# === LEGACY START ===
# remove once the UI can handle the new format
# persist size snapshot on S3
- when:
# don't run on PRs
condition:
not:
matches:
pattern: '.+'
value: '${CIRCLE_PULL_REQUEST}'
steps:
- aws-s3/copy:
arguments: --content-type application/json
from: size-snapshot.json
to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/
# === LEGACY END ===
# Keep the artifact storage as a CircleCI artifact
- store_artifacts:
name: persist size snapshot as pipeline artifact
path: size-snapshot.json
destination: size-snapshot.json
- run:
name: Run danger on PRs
command: pnpm danger ci --fail-on-errors
environment:
DANGER_COMMAND: reportBundleSize
workflows:
version: 2
pipeline:
when:
equal: [pipeline, << pipeline.parameters.workflow >>]
jobs:
- test_unit:
<<: *default-context
- test_lint:
<<: *default-context
- test_static:
<<: *default-context
- test_types:
<<: *default-context
- test_browser:
<<: *default-context
- test_browser_legacy:
<<: *default-context
- test_regressions:
<<: *default-context
- test_e2e:
<<: *default-context
- test_bundle_size_monitor:
<<: *default-context
e2e-website:
when:
equal: [e2e-website, << pipeline.parameters.workflow >>]
jobs:
- test_e2e_website:
<<: *default-context
bundling:
when:
equal: [bundling, << pipeline.parameters.workflow >>]
jobs:
- test_bundling_prepare:
<<: *default-context
- test_bundling_node_cjs:
<<: *default-context
requires:
- test_bundling_prepare
- test_bundling_node_esm:
<<: *default-context
requires:
- test_bundling_prepare
- test_bundling_create_react_app:
<<: *default-context
requires:
- test_bundling_prepare
- test_bundling_snowpack:
<<: *default-context
requires:
- test_bundling_prepare
- test_bundling_vite:
<<: *default-context
requires:
- test_bundling_prepare
- test_bundling_esbuild:
<<: *default-context
requires:
- test_bundling_prepare
- test_bundling_gatsby:
<<: *default-context
requires:
- test_bundling_prepare
- test_bundling_next_webpack4:
<<: *default-context
requires:
- test_bundling_prepare
- test_bundling_next_webpack5:
<<: *default-context
requires:
- test_bundling_prepare
# This workflow can be triggered manually on the PR
react-17:
when:
equal: [react-17, << pipeline.parameters.workflow >>]
jobs:
- test_unit:
<<: *default-context
react-version: ^17.0.0
name: test_unit-react@17
- test_browser:
<<: *default-context
react-version: ^17.0.0
name: test_browser-react@17
- test_regressions:
<<: *default-context
react-version: ^17.0.0
name: test_regressions-react@17
- test_e2e:
<<: *default-context
react-version: ^17.0.0
name: test_e2e-react@17
# This workflow is identical to react-17, but scheduled
# TODO: The v17 tests have deteriorated to the point of no return. Fix for v18 once we
# deprecate v17, and reenable this workflow.
# react-17-cron:
# triggers:
# - schedule:
# cron: '0 0 * * *'
# filters:
# branches:
# only:
# - master
# - next
# jobs:
# - test_unit:
# <<: *default-context
# react-version: ^17.0.0
# name: test_unit-react@17
# - test_browser:
# <<: *default-context
# react-version: ^17.0.0
# name: test_browser-react@17
# - test_regressions:
# <<: *default-context
# react-version: ^17.0.0
# name: test_regressions-react@17
# - test_e2e:
# <<: *default-context
# react-version: ^17.0.0
# name: test_e2e-react@17
# This workflow can be triggered manually on the PR
react-18:
when:
equal: [react-18, << pipeline.parameters.workflow >>]
jobs:
- test_unit:
<<: *default-context
react-version: ^18.0.0
name: test_unit-react@18
- test_browser:
<<: *default-context
react-version: ^18.0.0
name: test_browser-react@18
- test_regressions:
<<: *default-context
react-version: ^18.0.0
name: test_regressions-react@18
- test_e2e:
<<: *default-context
react-version: ^18.0.0
name: test_e2e-react@18
# This workflow is identical to react-18, but scheduled
react-18-cron:
triggers:
- schedule:
cron: '0 0 * * *'
filters:
branches:
only:
# #target-branch-reference
- master
- v5.x
- v6.x
jobs:
- test_unit:
<<: *default-context
react-version: ^18.0.0
name: test_unit-react@18
- test_browser:
<<: *default-context
react-version: ^18.0.0
name: test_browser-react@18
- test_regressions:
<<: *default-context
react-version: ^18.0.0
name: test_regressions-react@18
- test_e2e:
<<: *default-context
react-version: ^18.0.0
name: test_e2e-react@18
# This workflow can be triggered manually on the PR
react-next:
when:
equal: [react-next, << pipeline.parameters.workflow >>]
jobs:
- test_unit:
<<: *default-context
react-version: next
name: test_unit-react@next
- test_browser:
<<: *default-context
react-version: next
name: test_browser-react@next
- test_regressions:
<<: *default-context
react-version: next
name: test_regressions-react@next
- test_e2e:
<<: *default-context
react-version: next
name: test_e2e-react@next
# This workflow is identical to react-next, but scheduled
react-next-cron:
triggers:
- schedule:
cron: '0 0 * * *'
filters:
branches:
only:
# #target-branch-reference
- master
- v6.x
jobs:
- test_unit:
<<: *default-context
react-version: next
name: test_unit-react@next
- test_browser:
<<: *default-context
react-version: next
name: test_browser-react@next
- test_regressions:
<<: *default-context
react-version: next
name: test_regressions-react@next
- test_e2e:
<<: *default-context
react-version: next
name: test_e2e-react@next
typescript-next:
when:
equal: [typescript-next, << pipeline.parameters.workflow >>]
jobs:
- test_types_next:
<<: *default-context
typescript-version: next
typescript-next-cron:
triggers:
- schedule:
cron: '0 0 * * *'
filters:
branches:
only:
# #target-branch-reference
- master
- v6.x
jobs:
- test_types_next:
<<: *default-context
typescript-version: next

19
.editorconfig Normal file
View File

@@ -0,0 +1,19 @@
# EditorConfig is awesome: https://editorconfig.org/
# top-most EditorConfig file
root = true
[*.md]
trim_trailing_whitespace = false
[*.js]
trim_trailing_whitespace = true
# Unix-style newlines with a newline ending every file
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
insert_final_newline = true
max_line_length = 100

26
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,26 @@
# Enable via `git config blame.ignoreRevsFile .git-blame-ignore-revs`
# DO NOT ENABLE THIS CONFIG GLOBALLY
# OTHERWISE git-blame WILL CRASH IN ANY REPOSITORY WITHOUT SUCH A FILE
# changes to prettier config
5dc1cbca1055c8440ae8c02fffbad7e6a1e2674e
ef96151de118fe2560682bdedf130e69739082ea
7dc5fce440428d557c111b6c81ddf5dc02ad5d13
27471b4564eb40ff769352d73a29938d25804e45
98263e7470fc07a42480fdfe83c5ef719265a1d3
4f2a07e140c954b478a6670c009c23a59ec3e2d4
d10f8369897f9faa98548c7e51d6637e0f75e267
048ea981091a8441747cf4cc1662a702fe7d0de1
3ab4356063cfcca29277f3703635c850f918d56a
e0e959a13d2a219dcd674829510b3afafccc687c
# prettier upgrades
519518fd1b2ac8d6fb16bf0f4b4cb87841c4c53b
811c1abe39e9865873f23c7ba2c8c575714c2e4a
c26253c58e610032b3f5ece73bebb032b245e45b
02b722a249d1afe001e01827f6197c4b223ea0ce
c4bd047854861c0658d94b0c2614fc2aa61a39d4
fd7a2545a6832ef342051b293615acf975f408e5
65d5646a36c2250075534971fae79ea9cf624663
36a9ed95671f83c3e8443e5752964f4181e02894
9b2b1942eb2a3be39012b24f2df6e48973f3a019

12
.gitattributes vendored Normal file
View File

@@ -0,0 +1,12 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto eol=lf
# Undo the GitHub's default
# https://github.com/github/linguist/blob/96ad1185828f44bb9b774328a584551ee57ed264/lib/linguist/vendor.yml#L177
packages/**/*.d.ts -linguist-vendored
# These icons are imported from Google.
# Kept so that we can review the impact when changing the generation script.
packages/mui-icons-material/lib/** linguist-generated
packages/mui-icons-material/material-icons/** linguist-generated
packages/mui-icons-material/legacy/** linguist-vendored
# bundling fixtures
test/bundling/scripts/packages.js linguist-generated

8
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: mui-org
ko_fi: # Replace with a single Ko-fi username
tidelift: npm/@mui/material
custom: # Replace with a single custom sponsorship URL

66
.github/ISSUE_TEMPLATE/1.bug.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
name: Bug report 🐛
description: Create a bug report for Material UI, MUI System, or Joy UI.
labels: ['status: waiting for maintainer']
body:
- type: markdown
attributes:
value: Thanks for contributing by creating an issue! ❤️ Please provide a searchable summary of the issue in the title above ⬆️.
- type: input
attributes:
label: Search keywords
description: |
Your issue may have already been reported! First search for duplicates among the [existing issues](https://github.com/mui/material-ui/issues?q=is%3Aopen+is%3Aclosed).
If your issue isn't a duplicate, great! Please list the keywords you used so people in the future can find this one more easily:
validations:
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: Steps to reproduce
description: |
**⚠️ Issues that we can't reproduce can't be fixed.**
Please provide a link to a live example and an unambiguous set of steps to reproduce this bug. See our [documentation](https://mui.com/material-ui/getting-started/support/#bug-reproductions) on how to build a reproduction case.
You can use this StackBlitz template as a starting point: [material-ui-vite-ts](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-vite-ts)
value: |
Steps:
1. Open this link to live example: (required)
2.
3.
- type: textarea
attributes:
label: Current behavior
description: Describe what happens instead of the expected behavior.
- type: textarea
attributes:
label: Expected behavior
description: Describe what should happen.
- type: textarea
attributes:
label: Context
description: What are you trying to accomplish? Providing context helps us come up with a solution that is more useful in the real world.
- 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: |
<details>
<summary><code>npx @mui/envinfo</code></summary>
```
Don't forget to mention which browser you used.
Output from `npx @mui/envinfo` goes here.
```
</details>
- type: markdown
attributes:
value: |
## :heart: Love Material UI?
Consider donating $10 to sustain our open-source work: [https://opencollective.com/mui-org](https://opencollective.com/mui-org).

40
.github/ISSUE_TEMPLATE/2.feature.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Feature request 💄
description: Suggest a new idea for Material UI, MUI System, or Joy UI.
labels: ['status: waiting for maintainer']
body:
- type: markdown
attributes:
value: Thanks for contributing by creating an issue! ❤️ Please provide a searchable summary of the issue in the title above ⬆️.
- type: input
attributes:
label: Search keywords
description: |
Your issue may have already been reported! First search for duplicates among the [existing issues](https://github.com/mui/material-ui/issues?q=is%3Aopen+is%3Aclosed).
If your issue isn't a duplicate, great! Please list the keywords you used so people in the future can find this one more easily:
validations:
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: Summary
description: Describe how it should work.
- type: textarea
attributes:
label: Examples
description: Provide a link to the Material Design specification, other implementations, or screenshots of the expected behavior.
- type: textarea
attributes:
label: Motivation
description: What are you trying to accomplish? Providing context helps us come up with a solution that is more useful in the real world.
- type: markdown
attributes:
value: |
## :heart: Love Material UI?
Consider donating $10 to sustain our open-source work: [https://opencollective.com/mui-org](https://opencollective.com/mui-org).

37
.github/ISSUE_TEMPLATE/3.rfc.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: RFC 💬
description: Request for comments for your proposal.
title: '[RFC] '
labels: ['status: waiting for maintainer', 'RFC']
body:
- type: markdown
attributes:
value: |
Please provide a searchable summary of the RFC in the title above. ⬆️
Thanks for contributing by creating an RFC! ❤️
- type: textarea
attributes:
label: What's the problem?
description: Write a short paragraph or bulleted list to briefly explain what you're trying to do, what outcomes you're aiming for.
- type: textarea
attributes:
label: What are the requirements?
description: Provide a list of requirements that should be met by the accepted proposal.
- type: textarea
attributes:
label: What are our options?
description: What are the alternative options to achieve the desired outcome?
- type: textarea
attributes:
label: Proposed solution
description: |
This is the core of the RFC. Please clearly explain the reasoning behind your proposed solution, including why it would be preferred over possible alternatives.
Consider:
- using diagrams to help illustrate your ideas
- including code examples if you're proposing an interface or system contract
- linking to relevant project briefs or wireframes
- type: textarea
attributes:
label: Resources and benchmarks
description: Attach any issues, PRs, links, documents, etc… that might be relevant to the RFC.

View File

@@ -0,0 +1,50 @@
name: Docs feedback
description: Improve documentation about Material UI, MUI System, or Joy UI.
labels: ['status: waiting for maintainer', 'support: docs-feedback']
title: '[docs] '
body:
- type: markdown
attributes:
value: Thanks for contributing by creating an issue! ❤️ Please provide a searchable summary of the issue in the title above ⬆️.
- type: input
attributes:
label: Search keywords
description: |
Your issue may have already been reported! First search for duplicates among the [existing issues](https://github.com/mui/material-ui/issues?q=is%3Aopen+is%3Aclosed).
If your issue isn't a duplicate, great! Please list the keywords you used so people in the future can find this one more easily:
validations:
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/
validations:
required: true
- type: dropdown
attributes:
label: Kind of issue
description: What kind of problem are you facing?
options:
- Unclear explanations
- Missing information
- Broken demo
- 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.
- type: textarea
attributes:
label: Context
description: What are you trying to accomplish? Providing context helps us come up with a solution that is more useful in the real world.
- type: markdown
attributes:
value: |
## :heart: Love Material UI?
Consider donating $10 to sustain our open-source work: [https://opencollective.com/mui-org](https://opencollective.com/mui-org).

View File

@@ -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 Material UI, MUI System, or Joy UI.
title: '[question] '
labels: ['status: waiting for maintainer', 'support: unknown']
body:
- type: markdown
attributes:
value: |
Please provide a searchable summary of the issue in the title above ⬆️.
- type: input
attributes:
label: Search keywords
description: |
Your issue may have already been reported! First search for duplicates among the [existing issues](https://github.com/mui/material-ui/issues?q=is%3Aopen+is%3Aclosed).
If your issue isn't a duplicate, great! Please list the keywords you used so people in the future can find this one more easily:
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: |
<details>
<summary>`npx @mui/envinfo`</summary>
```
Don't forget to mention which browser you used.
Output from `npx @mui/envinfo` goes here.
```
</details>

4
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
contact_links:
- name: Support ❔
url: https://mui.com/material-ui/getting-started/support/
about: I need support with Material UI, MUI System, or Joy UI.

3
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,3 @@
<!-- Thanks so much for your PR, your contribution is appreciated! ❤️ -->
- [ ] I have followed (at least) the [PR section of the contributing guide](https://github.com/mui/material-ui/blob/HEAD/CONTRIBUTING.md#sending-a-pull-request).

4
.github/codeql/codeql-config.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
name: CodeQL configuration
paths-ignore:
- 'packages/mui-icons-material/lib/**'

13
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
version: 2
updates:
# Enable version updates for npm
- package-ecosystem: npm
# Look for `package.json` and `lock` files in the `root` directory
directory: /
schedule:
interval: yearly
# https://stackoverflow.com/questions/64047526/how-to-get-dependabot-to-trigger-for-security-updates-only
open-pull-requests-limit: 0
labels:
- dependencies
- security

View File

@@ -0,0 +1,20 @@
name: Check if PR has label
on:
pull_request:
types: [opened, reopened, labeled, unlabeled]
permissions: {}
jobs:
test-label-applied:
# Tests that label is added on the PR
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: mnajdova/github-action-required-labels@ca0df9249827e43aa4b4a0d25d9fe3e9b19b0705 # v2.1.0
with:
mode: minimum
count: 1
labels: ''

32
.github/workflows/ci-check.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
# 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:
# should sync with ci.yml as a workaround to bypass github checks
- master
- next
- v*.x
pull_request:
paths:
# should sync with ci.yml as a workaround to bypass github checks
- 'docs/**'
permissions: {}
jobs:
continuous-releases:
name: Continuous releases
uses: mui/mui-public/.github/workflows/ci-base.yml@master
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"'

68
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,68 @@
name: CI
on:
push:
branches:
# should sync with ci-check.yml as a workaround to bypass github checks
- master
- next
- v*.x
pull_request:
paths-ignore:
# should sync with ci-check.yml as a workaround to bypass github checks
- 'docs/**'
permissions: {}
jobs:
continuous-releases:
name: Continuous releases
uses: mui/mui-public/.github/workflows/ci-base.yml@master
# Tests dev-only scripts across all supported dev environments
test-dev:
# l10nbot does not affect dev scripts.
if: ${{ github.actor != 'l10nbot' }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
permissions:
contents: read
steps:
- run: echo '${{ github.actor }}'
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
# fetch all tags which are required for `pnpm release:changelog`
fetch-depth: 0
- name: Set up pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- name: Use Node.js
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: '22.18.0'
cache: 'pnpm' # https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies
- run: pnpm install
- name: Cache Next.js build
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: docs/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml') }}-${{ hashFiles('docs/**/*.js', 'docs/**/*.ts', 'docs/**/*.tsx', 'docs/**/*.jsx', 'docs/**/*.json', 'docs/**/*.md', 'docs/**/*.mdx') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml') }}-
${{ runner.os }}-nextjs-
- run: pnpm release:build
- name: Build docs
run: pnpm docs:build
env:
NODE_OPTIONS: --max_old_space_size=6144
# macOS-latest has 3 CPUs, but we get "EMFILE: too many open files" errors with that parallelism
# Limit Next.js to 2 CPUs to prevent file descriptor exhaustion. Empty string uses os.availableParallelism()
NEXT_PARALLELISM: ${{ runner.os == 'macOS' && '2' || '' }}
GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }}
# - run: pnpm release:changelog
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Debug export-detail.json on when pnpm docs:build fails with EMFILE error
if: failure()
run: cat ./docs/.next/export-detail.json || true

View File

@@ -0,0 +1,17 @@
name: Add closing message to issue
on:
issues:
types:
- closed
permissions: {}
jobs:
add-comment:
name: Add closing message
if: github.event.issue.state_reason == 'completed'
uses: mui/mui-public/.github/workflows/issues_add-closing-message.yml@master
permissions:
contents: read
issues: write

33
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: CodeQL
on:
schedule:
- cron: '0 2 * * *'
permissions: {}
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
with:
languages: typescript
config-file: ./.github/codeql/codeql-config.yml
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# 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@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5

View File

@@ -0,0 +1,18 @@
name: Create cherry-pick PR
on:
pull_request_target:
branches:
- 'next'
- 'v*.x'
- 'master'
types: ['closed']
permissions: {}
jobs:
create_pr:
name: Create cherry-pick PR
uses: mui/mui-public/.github/workflows/prs_create-cherry-pick-pr.yml@master
permissions:
contents: write
pull-requests: write

View File

@@ -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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
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: waiting for maintainer']
})
}

52
.github/workflows/issue-cleanup.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Cleanup issue comment
on:
issues:
types:
- opened
permissions: {}
jobs:
issue_cleanup:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
const issue = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
})
const lines = issue.data.body.split('\n')
const _ = extractInputSection(lines, 'Latest version')
const searchKeywords = extractInputSection(lines, 'Search keywords')
const orderID = extractInputSection(lines, 'Order ID or Support key')
lines.push('')
lines.push('**Search keywords**: ' + searchKeywords)
if (orderID !== '' && orderID !== '_No response_') {
lines.push('**Order ID**: ' + orderID)
}
const body = lines.join('\n')
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
})
function extractInputSection(lines, title) {
const index = lines.findIndex(line => line.startsWith('###') && line.includes(title))
if (index === -1) {
return ''
}
return lines.splice(index, 4)[2].trim()
}

40
.github/workflows/maintenance.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Maintenance
on:
# So that PRs touching the same files as the push are updated
push:
branches:
# #target-branch-reference
- master
- v6.x
# So that the `dirtyLabel` is removed if conflicts are resolved
# Could put too much strain on rate limit
# If we hit the rate limit too often remove this event
pull_request_target:
branches:
# #target-branch-reference
- master
- v6.x
types: [synchronize]
permissions: {}
jobs:
main:
# l10nbot creates a lot of commits at once which starves CI.
# We rely on other pushes to mark these branches as outdated.
if: ${{ github.actor != 'l10nbot' }}
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- run: echo '${{ github.actor }}'
- name: Check if prs are dirty
uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3
with:
dirtyLabel: 'PR: out-of-date'
removeOnDirtyLabel: 'PR: ready to ship'
repoToken: '${{ secrets.GITHUB_TOKEN }}'
retryAfter: 130
retryMax: 10

23
.github/workflows/mark-duplicate.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Mark duplicate
on:
issue_comment:
types: [created]
permissions: {}
jobs:
mark-duplicate:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Mark duplicate
uses: actions-cool/issues-helper@9861779a695cf1898bd984c727f685f351cfc372 # v3.7.2
with:
actions: 'mark-duplicate'
token: ${{ secrets.GITHUB_TOKEN }}
duplicate-labels: 'duplicate'
remove-labels: 'status: incomplete,status: waiting for maintainer'
close-issue: true

37
.github/workflows/no-response.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: No response
# `issues`.`closed`, `issue_comment`.`created`, and `scheduled` event types are required for this Action
# to work properly.
on:
issues:
types: [closed]
issue_comment:
types: [created]
schedule:
# These runs in our repos are spread evenly throughout the day to avoid hitting rate limits.
# If you change this schedule, consider changing the remaining repositories as well.
# Runs at 12 am, 12 pm
- cron: '0 0,12 * * *'
permissions: {}
jobs:
noResponse:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: MBilalShafi/no-response-add-label@8336c12292902f27b931154c34ba4670cb9899a2
with:
token: ${{ secrets.GITHUB_TOKEN }}
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 7
# Label requiring a response
responseRequiredLabel: 'status: waiting for author'
# Label to add back when required label is removed
optionalFollowupLabel: 'status: waiting for maintainer'
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
Since the issue is missing key information and has been inactive for 7 days, it has been automatically closed.
If you wish to see the issue reopened, please provide the missing information.

View File

@@ -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@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4
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@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
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/validateSupport?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@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
with:
comment-id: ${{ steps.findComment.outputs.comment-id }}
body: |
Thank you for verifying your support key 🔑, your SLA starts now.
edit-mode: replace

25
.github/workflows/publish-canaries.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Publish canary packages to npm
on:
workflow_dispatch:
permissions: {}
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
fetch-depth: 0
- name: Set up pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- name: Use Node.js
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: '22.18.0'
cache: 'pnpm' # https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies
- run: pnpm install
- run: pnpm canary:release --ignore @mui/icons-material --yes --skip-last-commit-comparison
env:
NPM_TOKEN: ${{secrets.NPM_TOKEN}}

60
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: Publish packages
on:
workflow_dispatch:
inputs:
sha:
description: 'Commit SHA to release from'
required: true
type: string
dry-run:
description: 'Run in dry-run mode without actually publishing packages'
required: false
type: boolean
default: false
github-release:
description: 'Create a GitHub release after publishing'
required: false
type: boolean
default: true
dist-tag:
description: 'npm dist tag to publish to'
required: false
type: string
default: 'latest'
permissions: {}
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write # Required for pushing tags and creating releases
id-token: write # Required for provenance
environment:
name: npm-publish
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
ref: ${{ inputs.sha }}
fetch-depth: 0 # Fetch full history for proper git operations
- name: Prepare for publishing
uses: mui/mui-public/.github/actions/publish-prepare@master
- name: Publish packages
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Build common flags
ARGS=""
if [ "${{ inputs.dry-run }}" = "true" ]; then
ARGS="$ARGS --dry-run"
fi
if [ "${{ inputs.github-release }}" = "true" ]; then
ARGS="$ARGS --github-release"
fi
if [ -n "${{ inputs.dist-tag }}" ]; then
ARGS="$ARGS --tag ${{ inputs.dist-tag }}"
fi
pnpm code-infra publish --ci $ARGS

45
.github/workflows/scorecards.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Scorecards supply-chain security
on:
# Only the default branch is supported.
branch_protection_rule:
schedule:
- cron: '0 2 * * *'
permissions: {}
jobs:
analysis:
name: Scorecards analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Used to receive a badge.
id-token: write
# Needs for private repositories.
contents: read
actions: read
steps:
- name: Checkout code
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
# (Optional) Read-only PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecards on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
# Publish the results for public repositories to enable scorecard badges. For more details, see
# https://github.com/ossf/scorecard-action#publishing-results.
publish_results: true
# Upload the results to GitHub's code scanning dashboard.
- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
with:
sarif_file: results.sarif

View File

@@ -0,0 +1,35 @@
# Configuration for support-requests - https://github.com/dessant/support-requests
name: Support Stack Overflow
on:
issues:
types: [labeled, unlabeled, reopened]
permissions: {}
jobs:
mark-support:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- uses: dessant/support-requests@47d5ea12f6c9e4a081637de9626b7319b415a3bf # v4.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
# Label used to mark issues as support requests
support-label: 'support: Stack Overflow'
# Comment to post on issues marked as support requests. Add a link
# to a support page, or set to `false` to disable
issue-comment: |
👋 Thanks for using this project!
We use GitHub issues exclusively as a bug and feature requests tracker, however, this issue appears to be a support request.
For support with Material UI please check out https://mui.com/material-ui/getting-started/support/. Thanks!
If you have a question on Stack Overflow, you are welcome to link to it here, it might help others.
If your issue is subsequently confirmed as a bug, and the report follows the issue template, it can be reopened.
close-issue: true
issue-close-reason: 'not planned'
lock-issue: false

33
.github/workflows/vale-action.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Vale action
on: [pull_request]
permissions: {}
jobs:
vale:
name: runner / vale
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Extract Vale version from pnpm-lock.yaml
id: vale-version
run: |
# Extract version from lock file
VERSION=$(awk -F"@|'" '/@vvago\/vale@/ {print $4}' pnpm-lock.yaml | head -n1)
echo "Extracted Vale version: $VERSION"
echo "vale_version=$VERSION" >> $GITHUB_OUTPUT
- uses: errata-ai/vale-action@d89dee975228ae261d22c15adcd03578634d429c # v2.1.1
continue-on-error: true # GitHub Action flag needed until https://github.com/errata-ai/vale-action/issues/89 is fixed
with:
version: ${{ steps.vale-version.outputs.vale_version }}
# Errors should be more visible
fail_on_error: true
# The other reports don't work, not really https://github.com/reviewdog/reviewdog#reporters
reporter: github-pr-check
# Required, set by GitHub actions automatically:
# https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret
token: ${{secrets.GITHUB_TOKEN}}

53
.gitignore vendored Normal file
View File

@@ -0,0 +1,53 @@
# It is best to ignoring editor and system files in a local .gitignore configuration file.
# However, in order to prevent issues, they are ignored here.
.DS_STORE
.idea
# IntelliJ IDEA module file
*.iml
.vscode/*
!.vscode/launch.json
!.vscode/extensions.json
*.log
/.eslintcache
/.nyc_output
/coverage
/docs/.env.local
/docs/export
/docs/pages/playground/
/docs/public/feed/
/docs/public/material-ui/
/examples/**/.cache
/packages/mui-codemod/lib
/packages/mui-envinfo/*.tgz
/packages/mui-icons-material/src/*.js
/test/bundling/fixtures/*/yarn.lock
/test/bundling/fixtures/*/pnpm-lock.yaml
# created by test/bundling/scripts/createFixture
/test/bundling/fixtures/**/*.fixture.js
# created by test/bundling/fixtures/gatsby gatsby build
/test/bundling/fixtures/gatsby/.cache
/test/bundling/fixtures/gatsby/public
/test/regressions/screenshots
/tmp
.next
# created by netlify dev (to perform local debug)
.netlify
build
dist
node_modules
package-lock.json
size-snapshot.json
bundle-sizes/
docs/public/static/blog/feed/*
# vale downloaded config
.github/styles/
.nx/cache
.nx/workspace-data
screenshots
packed
test-results
.env
# typescript
*.tsbuildinfo
next-env.d.ts

39
.lintignore Normal file
View File

@@ -0,0 +1,39 @@
/.git
/coverage
/docs/export
/docs/pages/playground/
/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-codemod/src/**/test-cases/*
/packages/mui-icons-material/fixtures
/packages/mui-icons-material/legacy
/packages/mui-icons-material/lib
/packages/mui-icons-material/material-icons/
/packages/mui-icons-material/src/*.js
/packages/mui-icons-material/templateSvgIcon.js
# Ignore fixtures
/packages-internal/scripts/typescript-to-proptypes/test/*/*
/test/bundling/fixtures/**/*.fixture.js
# just an import that reports eslint errors depending on whether the fixture (which is not checked in) exists
/test/bundling/fixtures/create-react-app/src/index.js
/test/bundling/fixtures/gatsby/.cache
/test/bundling/fixtures/gatsby/public
/tmp
.next
build
dist
node_modules
.nyc_output
pnpm-lock.yaml
# These come from crowdin.
# If we would commit changes crowdin would immediately try to revert.
# If we want to format these files we'd need to do it in crowdin
docs/**/*-pt.md
docs/**/*-zh.md
__fixtures__

3
.markdownlint-cli2.mjs Normal file
View File

@@ -0,0 +1,3 @@
import { createBaseConfig } from '@mui/internal-code-infra/markdownlint';
export default createBaseConfig();

2
.npmrc Normal file
View File

@@ -0,0 +1,2 @@
enable-pre-post-scripts = true
manage-package-manager-versions = true

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
pnpm-lock.yaml

11
.tidelift.yml Normal file
View File

@@ -0,0 +1,11 @@
ci:
platform:
npm:
# Don't run unmainted test on nomnom, it's only used by build tools, not in MUI.
nomnom:
tests:
unmaintained: skip
# Don't run vulnerabity test on os-locale, it's only used by yargs in build tools, not in MUI.
os-locale:
tests:
vulnerable: skip

33
.vale.ini Normal file
View File

@@ -0,0 +1,33 @@
# Vale config. More information at https://vale.sh/docs/topics/config/
StylesPath = .github/styles
MinAlertLevel = warning
# To update mui-vale package:
# 1. Go to the docs folder
# 2. Update/create YAML files
# 3. Run `pnpm docs:zipRules` to generate the zip files
# 4. You can test locally by replacing the url with the file path of the generated zip
Packages = Google, docs/mui-vale.zip
[formats]
mdx = md
[*.md]
# Ignore React component calls
TokenIgnores = (<\/?[A-Z].+>)
# Ignore code injections that start with {{...
BlockIgnores = {{.*
BasedOnStyles = MUI
# Google errors:
Google.GenderBias = YES # No Gender bias
# Google warings:
Google.FirstPerson = YES # Avoid first-person
Google.We = YES # Avoid first-person plural
Google.Will = YES # Avoid future tense
Google.OxfordComma = YES # Prefer Oxford comma
[CHANGELOG*.md]
MUI.CorrectReferenceAllCases = NO

9
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"recommendations": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"davidanson.vscode-markdownlint",
"esbenp.prettier-vscode",
"yoavbls.pretty-ts-errors"
]
}

15
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Test Current File",
"program": "test/cli.js",
"args": ["${relativeFile}", "--inspecting"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true
}
]
}

1125
CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

31074
CHANGELOG.old.md Normal file

File diff suppressed because it is too large Load Diff

381
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,381 @@
# Contributing to Material UI, MUI System, Joy UI
If you're reading this, you're awesome!
Thank you for being a part of the community and helping us make these projects great.
Here are a few guidelines that will help you along the way.
## Summary
- [Code of conduct](#code-of-conduct)
- [A large spectrum of contributions](#a-large-spectrum-of-contributions)
- [Your first pull request](#your-first-pull-request)
- [Sending a pull request](#sending-a-pull-request)
- [Trying changes on the documentation site](#trying-changes-on-the-documentation-site)
- [Trying changes on the playground](#trying-changes-on-the-playground)
- [How to increase the chances of being accepted](#how-to-increase-the-chances-of-being-accepted)
- [CI checks and how to fix them](#ci-checks-and-how-to-fix-them)
- [Updating the component API documentation](#updating-the-component-api-documentation)
- [Coding style](#coding-style)
- [Contributing to the documentation](#contributing-to-the-documentation)
- [How to find docs issues to work on](#how-to-find-docs-issues-to-work-on)
- [How to add a new demo to the docs](#how-to-add-a-new-demo-to-the-docs)
- [How can I use a change that hasn't been released yet?](#how-can-i-use-a-change-that-hasnt-been-released-yet)
- [Roadmap](#roadmap)
- [License](#license)
## Code of conduct
We have adopted the [Contributor Covenant](https://www.contributor-covenant.org/) as our code of conduct, and we expect project participants to adhere to it.
Please read [the full text](https://github.com/mui/.github/blob/master/CODE_OF_CONDUCT.md) to understand what actions will and will not be tolerated.
## A large spectrum of contributions
There are [many ways](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) to contribute to the library, and writing code is only one part of the story—[documentation improvements](#contributing-to-the-documentation) can be just as important as code changes.
If you have an idea for an improvement to the code or the docs, we encourage you to open an issue as a first step, to discuss your proposed changes with the maintainers before proceeding.
## Your first pull request
Working on your first pull request? You can learn how in this free video series: [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github).
Get started with [good first issues](https://github.com/mui/material-ui/issues?q=is:open+is:issue+label:"good+first+issue"), which have a limited scope and a working solution that's already been discussed.
This makes them ideal for newer developers, or those who are new to these libraries and want to see how the contribution process works.
We also have a list of [ready to take issues](https://github.com/mui/material-ui/issues?q=is:open+is:issue+label:"ready+to+take"), which are issues that have already been at least partially resolved in discussion, to the point that it's clear what to do next.
These issues are great for developers who want to reduce their chances of falling down a rabbit hole in search of a solution.
Of course, you can work on any other issue you like—the "good first" and "ready to take" issues are simply those where the scope and timeline may be better defined.
Pull requests for other issues, or completely novel problems, may take a bit longer to review if they don't fit into our current development cycle.
If you decide to fix an issue, please make sure to check the comment thread in case somebody is already working on a fix.
If nobody is working on it at the moment, please leave a comment stating that you've started to work on it, so other people don't accidentally duplicate your effort.
If somebody claims an issue but doesn't follow up after more than a week, it's fine to take over, but you should still leave a comment.
If there has been no activity on the issue for 7 to 14 days, then it's safe to assume that nobody is working on it.
## Sending a pull request
MUI Core projects are community-driven, so pull requests are always welcome, but before working on a large change, it's best to open an issue first to discuss it with the maintainers.
When in doubt, keep your pull requests small.
For the best chances of being accepted, don't bundle more than one feature or bug fix per PR.
It's often best to create two smaller PRs rather than one big one.
1. Fork the repository.
2. Clone the fork to your local machine and add the upstream remote:
```bash
git clone https://github.com/<your username>/material-ui.git
cd material-ui
git remote add upstream https://github.com/mui/material-ui.git
```
<!-- #target-branch-reference -->
3. Synchronize your local `master` branch with the upstream one:
```bash
git checkout master
git pull upstream master
```
4. Install the dependencies with pnpm (yarn or npm aren't supported):
```bash
pnpm install
```
5. Create a new topic branch:
```bash
git checkout -b my-topic-branch
```
6. Make changes, commit, and push to your fork:
```bash
git push -u origin HEAD
```
7. Go to [the repository](https://github.com/mui/material-ui) and open a pull request.
The core team actively monitors for new pull requests.
We will review your PR and either merge it, request changes to it, or close it with an explanation.
### Trying changes on the documentation site
The documentation site is built with Material UI and contains examples of all of the components.
This is the best place to experiment with your changes—it's the local development environment used by the maintainers.
To get started, run:
```bash
pnpm start
```
You can now access the documentation site locally: http://localhost:3000.
Changes to the docs will hot reload the site.
### Trying changes on the playground
While we do recommend trying your changes on the documentation site, this is not always ideal.
You might face the following problems:
- Updating the existing demos prevents you from working in isolation on a single instance of the component
- Emptying an existing page to try your changes in isolation leads to a noisy `git diff`
- Static linters may report issues that you might not care about
To avoid these problems, you can use this playground:
```bash
pnpm docs:create-playground && pnpm start
```
Access it locally at: http://localhost:3000/playground/.
You can create as many playgrounds as you want by going to the `/docs/pages/playground/` folder and duplicating the `index.tsx` file with a different name: `<file_name>.tsx`.
The new playground will be accessible at: `http://localhost:3000/playground/<file_name>`.
### How to increase the chances of being accepted
Continuous Integration (CI) automatically runs a series of checks when a PR is opened.
If you're unsure whether your changes will pass, you can always open a PR, and the GitHub UI will display a summary of the results.
If any of these checks fail, refer to [CI checks and how to fix them](#ci-checks-and-how-to-fix-them).
Make sure the following is true:
<!-- #target-branch-reference -->
- The branch is targeted at `master` for ongoing development. All tests are passing. Code that lands in `master` must be compatible with the latest stable release. It may contain additional features but no breaking changes. We should be able to release a new minor version from the tip of `master` at any time.
- If a feature is being added:
- If the result was already achievable with the core library, you've explained why this feature needs to be added to the core.
- If this is a common use case, you've added an example to the documentation.
- If adding new features or modifying existing ones, you've included tests to confirm the new behavior. You can read more about our test setup in our test [README](https://github.com/mui/material-ui/blob/HEAD/test/README.md).
- If props were added or prop types were changed, you've updated the TypeScript declarations.
- If submitting a new component, you've added it to the [lab](https://github.com/mui/material-ui/tree/HEAD/packages/mui-lab).
- The branch is not [behind its target branch](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/keeping-your-pull-request-in-sync-with-the-base-branch).
We will only merge a PR when all tests pass.
The following statements must be true:
- The code is formatted. If the code was changed, run `pnpm prettier`.
- The code is linted. If the code was changed, run `pnpm eslint`.
- The code is type-safe. If TypeScript sources or declarations were changed, run `pnpm typescript` to confirm that the check passes.
- The API docs are up to date. If API was changed, run `pnpm proptypes && pnpm docs:api`.
- The demos are up to date. If demos were changed, run `pnpm docs:typescript:formatted`. See [about writing demos](#2-write-the-demo-code).
- The pull request title follows the pattern `[product-name][Component] Imperative commit message`. (See: [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) for a great explanation).
Don't worry if you miss a step—the Continuous Integration will run a thorough set of tests on your commits, and the maintainers of the project can assist you if you run into problems.
If your pull request addresses an open issue, make sure to link the PR to that issue.
Use any [supported GitHub keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) in the PR description to automatically link them.
This makes it easier to understand where the PR is coming from, and also speeds things up because the issue will be closed automatically when the PR is merged.
### CI checks and how to fix them
If any of the checks fail, click on the **Details** link and review the logs of the build to find out why it failed.
For CircleCI, you need to log in first.
No further permissions are required to view the build logs.
The following sections give an overview of what each check is responsible for.
#### Continuous Releases
This task publishes a preview for the packages to pkg.pr.new. It should not fail in isolation. Use it to test more complex scenarios.
#### ci/circleci: checkout
This is a preflight check to see if the dependencies and lockfile are ok.
Running `pnpm` and `pnpm deduplicate` should fix most issues.
#### ci/circleci: test_static
This checks code format and lints the repository.
The log of the failed build should explain how to fix any issues.
It also runs commands that generate or change some files (like `pnpm docs:api` for API documentation).
If the CI job fails, then you most likely need to run the commands that failed locally and commit the changes.
#### ci/circleci: test_unit-1
This runs the unit tests in a `jsdom` environment.
If it fails then `pnpm test:unit` should<sup>[1](test/README.md#accessibility-tree-exclusion)</sup> fail locally as well.
You can narrow the scope of tests run with `pnpm test:unit --grep ComponentName`.
If `pnpm test:unit` passes locally, but fails in CI, consider [Accessibility tree exclusion in CI](test/README.md#accessibility-tree-exclusion).
#### ci/circleci: test_browser
This runs the unit tests in multiple browsers (via Playwright).
The log of the failed build should list which browsers failed.
If Chrome failed then `pnpm test:browser` should<sup>[1](test/README.md#accessibility-tree-exclusion)</sup> fail locally as well.
If other browsers failed, you can debug using `VITEST_BROWSERS=firefox,webkit pnpm test:browser`.
#### ci/circleci: test_regression
This renders tests in `test/regressions/tests` and takes screenshots.
This step shouldn't fail if the others pass.
Otherwise, a maintainer will take a look.
The screenshots are evaluated in another step.
#### ci/circleci: test_types
This typechecks the repository.
The log of the failed build should list any issues.
#### ci/circleci: test_bundle_size_monitor
This task is primarily responsible for monitoring the bundle size.
It will only report the size if the change exceeds a certain threshold.
If it fails, then there's usually something wrong with the way the packages or docs were built.
#### argos
This evaluates the screenshots taken in `test/regressions/tests`, and fails if it detects
differences.
This doesn't necessarily mean that your PR will be rejected, as a failure might be intended.
Clicking on **Details** will show you the differences.
#### deploy/netlify
This renders a preview of the docs with your changes if it succeeds.
Otherwise `pnpm docs:build` usually fails locally as well.
#### codecov/project
This monitors coverage of the tests.
A reduction in coverage isn't necessarily bad, but it's always appreciated if it can be improved.
#### Misc
There are various other checks done by Netlify to validate the integrity of the docs.
Click on **Details** to find out more about them.
### Updating the component API documentation
The component API in the component `propTypes` and under `docs/pages/api-docs` is auto-generated from the [JSDoc](https://jsdoc.app/about-getting-started.html) in the TypeScript declarations.
Be sure to update the documentation in the corresponding `.d.ts` files (for example `packages/mui-material/src/Button/Button.d.ts` for `<Button>`) and then run:
```bash
$ pnpm proptypes
$ pnpm docs:api
```
### Coding style
Please follow the coding style of the project.
It uses Prettier and ESLint, so if possible, enable linting in your editor to get real-time feedback.
- `pnpm prettier` reformats the code.
- `pnpm eslint` runs the linting rules.
When you submit a PR, these checks are run again by our continuous integration tools, but hopefully your code is already clean!
## Contributing to the documentation
Contributing to open-source docs involves a lot more than just fixing typos—developers frequently request more in-depth explanations of component features, and this requires both coding and technical writing to accomplish.
Every documentation PR will be reviewed by an editor following [MUI's writing style guide](https://mui-org.notion.site/Writing-style-guide-2a957a4168a54d47b14bae026d06a24b), and if you plan to contribute regularly, you should familiarize yourself with this guide to speed up the editing process.
### How to find docs issues to work on
If you're interested in contributing to the docs but aren't sure where to start, you can use this search prompt in the GitHub repo to find open issues tagged with both `docs` and `ready to take`:
`is:issue is:open label:docs label:"ready to take"`
Or [follow this link directly to the results of that search](https://github.com/mui/material-ui/issues?q=is%3Aissue+is%3Aopen+label%3Adocs+label%3A%22ready+to+take%22).
### How to add a new demo to the docs
The following steps explain how to add a new demo to the docs using the Button component as an example:
#### 1. Add a new component file to the directory
Add the new file to the component's corresponding directory...
```bash
docs/src/pages/components/buttons/
```
...and give it a name: how about `SuperButtons.tsx`?
#### 2. Write the demo code
We use TypeScript to document our components.
We prefer demos written in TypeScript (using the `.tsx` file format).
After creating a TypeScript demo, run `pnpm docs:typescript:formatted` to automatically create the JavaScript version, which is also required.
If you're not familiar with TypeScript, you can write the demo in JavaScript, and a core contributor may help you migrate it to TypeScript.
#### 3. Edit the page's Markdown file
The Markdown file in the component's respective folder—in this case, `/buttons/buttons.md`—is the source of content for the document.
Any changes you make there will be reflected on the website.
Add a header and a brief description of the demo and its use case, along with the `"demo"` code snippet to inject it into the page:
```diff
+### Super buttons
+
+To create a super button for a specific use case, add the `super` prop:
+
+{{"demo": "pages/components/buttons/SuperButtons.js"}}
```
#### 4. Submit your PR
Now you're ready to [open a PR](#sending-a-pull-request) to add your new demo to the docs.
Check out [this Toggle Button demo PR](https://github.com/mui/material-ui/pull/19582/files) for an example of what your new and edited files should look like when opening your own demo PR.
## How can I use a change that hasn't been released yet?
We use [pkg.pr.new](https://pkg.pr.new) to publish a working version of the packages for each pull request as a "preview."
You can check the _Continuous Releases_ status of a pull request to get the URL needed to install these preview packages:
```diff
diff --git a//package.json b//package.json
index 791a7da1f4..a5db13b414 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
"dependencies": {
"@babel/runtime": "^7.4.4",
"@mui/styled-engine": "^5.0.0-alpha.16",
- "@mui/material": "^5.0.0-alpha.15",
+ "@mui/material": "https://pkg.pr.new/mui/material-ui/@mui/material@b0f26aa",
"@mui/system": "^5.0.0-alpha.16",
```
Alternatively, you can open the Netlify preview of the documentation, and open any demo in CodeSandbox or StackBlitz.
The documentation automatically configures the dependencies to use the preview packages.
You can also package and test your changes locally.
The following example shows how to package `@mui/material`, but you can package any npm package with this process:
```bash
$> cd packages/mui-material # or path to any other mui package
$packages\mui-material> pnpm build
$packages\mui-material> cd ./build
$packages\mui-material> pnpm pack
```
Navigate to the build folder of your respective package and locate a file with the format `mui-material-x.x.x.tar.gz`.
Copy this file and move it to the project directory you want to test in, then run:
```bash
$test-project> npm i ./path-to-file/mui-material-x.x.x.tar.gz
```
> **Note**
>
> If you've already installed this package, your changes will not be reflected when you reinstall it.
> As a quick fix, you can temporarily bump the version number in your `package.json` before running `pnpm build`.
## Roadmap
Learn more about the future by visiting our different projects' roadmaps:
- [Material UI roadmap](https://mui.com/material-ui/discover-more/roadmap/).
## License
By contributing your code to the [mui/material-ui](https://github.com/mui/material-ui) GitHub repository, you agree to license your contribution under the [MIT license](/LICENSE).

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Call-Em-All
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

179
README.md Normal file
View File

@@ -0,0 +1,179 @@
<!-- #host-reference -->
<!-- markdownlint-disable-next-line -->
<p align="center">
<a href="https://mui.com/core/" rel="noopener" target="_blank"><img width="150" height="133" src="https://mui.com/static/logo.svg" alt="Material UI logo"></a>
</p>
<h1 align="center">Material UI</h1>
<div align="center">
[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/mui/material-ui/blob/HEAD/LICENSE)
[![npm latest package](https://img.shields.io/npm/v/@mui/material/latest.svg)](https://www.npmjs.com/package/@mui/material)
[![npm next package](https://img.shields.io/npm/v/@mui/material/next.svg)](https://www.npmjs.com/package/@mui/material)
[![npm downloads](https://img.shields.io/npm/dm/@mui/material.svg)](https://www.npmjs.com/package/@mui/material)
[![GitHub branch status](https://img.shields.io/github/checks-status/mui/material-ui/HEAD)](https://github.com/mui/material-ui/commits/HEAD/)
[![Coverage Status](https://img.shields.io/codecov/c/github/mui/material-ui.svg)](https://app.codecov.io/gh/mui/material-ui/)
[![Follow on X](https://img.shields.io/twitter/follow/MaterialUI.svg?label=follow+Material+UI)](https://x.com/MaterialUI)
[![Renovate status](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://github.com/mui/material-ui/issues/27062)
[![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/mui/material-ui.svg)](https://isitmaintained.com/project/mui/material-ui 'Average time to resolve an issue')
[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/mui-org)](https://opencollective.com/mui-org)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/1320/badge)](https://www.bestpractices.dev/projects/1320)
</div>
[Material UI](https://mui.com/material-ui/) is a comprehensive library of React components that features our independent implementation of Google's [Material Design](https://m2.material.io/design/introduction/) system.
It's trusted by some of the world's greatest product teams because it's been rigorously battle-tested through more than a decade of development by thousands of open-source contributors.
Material UI's core functionality is extended by [MUI X](https://github.com/mui/mui-x), a suite of complex components for advanced use cases.
## Documentation
Get started in the [Material UI documentation](https://mui.com/material-ui/getting-started/).
<details>
<summary>Older versions</summary>
- **[v5.x](https://v5.mui.com/)** ([Upgrading from v5 to v6](https://mui.com/material-ui/migration/upgrade-to-v6/))
- **[v4.x](https://v4.mui.com/)** ([Upgrading from v4 to v5](https://mui.com/material-ui/migration/migration-v4/))
- **[v3.x](https://v3.mui.com/)** ([Upgrading from v3 to v4](https://mui.com/material-ui/migration/migration-v3/))
- **[v0.x](https://v0.mui.com/)** ([Upgrading to v1](https://mui.com/material-ui/migration/migration-v0x/))
</details>
**Note:** `@next` points to pre-releases.
Use `@latest` for the latest stable release.
## Joy UI
This repository also contains Joy UI, an experimental component library that implements our own in-house Joy Design.
Joy UI is in beta and _development is currently on hold_.
When starting a new project from scratch, we recommend Material UI over Joy UI because we can guarantee ongoing support.
Keep in mind that the maintainers are primarily focused on other projects and may not be able to respond in a timely manner to issues or pull requests related to Joy UI.
View the [Joy UI documentation](https://mui.com/joy-ui/getting-started/).
## Sponsors
### Diamond 💎
<p>
<a href="https://www.doit.com/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank"><img height="128" width="128" src="https://mui.com/static/sponsors/doit-square.svg" alt="doit" title="Management Platform for Google Cloud and AWS" loading="lazy" /></a>
</p>
Diamond sponsors are those who have pledged \$1,500/month or more to MUI.
### Gold 🏆
via [Open Collective](https://opencollective.com/mui-org) or via [Patreon](https://www.patreon.com/oliviertassinari)
<p>
<a href="https://tidelift.com/" rel="noopener sponsored" target="_blank"><img height="96" width="96" src="https://avatars.githubusercontent.com/u/30204434?s=288" alt="tidelift.com" title="Tidelift: Enterprise-ready open-source software." loading="lazy" /></a>
&nbsp;
<a href="https://www.dialmycalls.com/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank"><img height="96" width="96" src="https://images.opencollective.com/dialmycalls/f5ae9ab/avatar/288.png" alt="dialmycalls.com" title="DialMyCalls: Send text messages, calls, and emails." loading="lazy" /></a>
&nbsp;
<a href="https://ref.wisprflow.ai/ZSPYrru?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://mui.com/static/sponsors/wispr-square-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://mui.com/static/sponsors/wispr-square-light.svg">
<img height="96" width="96" src="https://mui.com/static/sponsors/wispr-square-light.svg" alt="wisprflow" title="AI Dictation: from speech to clear, polished text." loading="lazy" />
</picture>
</a>
&nbsp;
</p>
<p>
<a href="https://goread.io/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank">Goread.io</a>
&nbsp;
<a href="https://buzzoid.com/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank">Buzzoid</a>
&nbsp;
<a href="https://twicsy.com/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank">Twicsy</a>
&nbsp;
<a href="https://views4you.com/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank">Views4You</a>
&nbsp;
<a href="https://poprey.com/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank">Poprey</a>
&nbsp;
<a href="https://www.socialwick.com/instagram/followers/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank">SocialWick</a>
&nbsp;
<a href="https://www.follower24.de/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank">Follower24</a>
&nbsp;
<a href="https://reputationmanage.co/?utm_source=mui.com&utm_medium=referral&utm_content=readme" rel="noopener sponsored" target="_blank">Reputation Manage</a>
&nbsp;
</p>
Gold sponsors are those who have pledged \$500/month or more to MUI.
### More backers
See the full list of [our backers](https://mui.com/material-ui/discover-more/backers/).
## Questions
For how-to questions that don't involve making changes to the code base, please use [Stack Overflow](https://stackoverflow.com/questions/) instead of GitHub issues.
## Examples
<!-- #target-branch-reference -->
Our documentation features [a collection of example projects](https://github.com/mui/material-ui/tree/master/examples).
## Premium templates
You can find complete templates and themes in the [MUI Store](https://mui.com/store/?utm_source=docs&utm_medium=referral&utm_campaign=readme-store).
## Contributing
Read the [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes.
Contributing is about more than just issues and pull requests!
There are many other ways to [support Material UI](https://mui.com/material-ui/getting-started/faq/#mui-is-an-awesome-organization-how-can-i-support-it) beyond contributing to the code base.
## Changelog
The [changelog](https://github.com/mui/material-ui/releases) is regularly updated to reflect what's changed in each new release.
## Roadmap
Future plans and high-priority features and enhancements can be found in the [roadmap](https://mui.com/material-ui/discover-more/roadmap/).
## License
This project is licensed under the terms of the [MIT license](/LICENSE).
## Security
For details on supported versions and contact information for reporting security issues, please refer to the [security policy](https://github.com/mui/material-ui/security/policy).
## Sponsoring services
These great services sponsor MUI's core infrastructure:
<div>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://mui.com/static/readme/github-darkmode.svg">
<source media="(prefers-color-scheme: light)" srcset="https://mui.com/static/readme/github-lightmode.svg">
<img alt="GitHub logo" src="https://mui.com/static/readme/github-lightmode.svg" width="80" height="43">
</picture>
[GitHub](https://github.com/) lets us host the Git repository and coordinate contributions.
</div>
<div>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://mui.com/static/readme/netlify-darkmode.svg">
<source media="(prefers-color-scheme: light)" srcset="https://mui.com/static/readme/netlify-lightmode.svg">
<img alt="Netlify logo" src="https://mui.com/static/readme/netlify-lightmode.svg" width="100" height="27">
</picture>
[Netlify](https://www.netlify.com/) lets us distribute the documentation.
</div>
<div>
<img loading="lazy" alt="CodeCov logo" src="https://avatars.githubusercontent.com/u/8226205?s=105" width="35" height="35">
[CodeCov](https://about.codecov.io/) lets us monitor test coverage.
</div>

20
SECURITY.md Normal file
View File

@@ -0,0 +1,20 @@
# Security policy
## Supported versions
The versions of the project that are currently supported with security updates.
| Material UI version | Release | Supported |
| ------------------: | :--------- | :----------------------------------- |
| ^7.0.0 | 2025-03-26 | :white_check_mark: Stable major |
| ^6.0.0 | 2024-08-26 | :white_check_mark: Long-term support |
| ^5.0.0 | 2021-09-16 | :white_check_mark: Long-term support |
| ^4.0.0 | 2019-06-23 | :x: |
| ^3.0.0 | 2018-08-27 | :x: |
| ^2.0.0 | / | :x: |
| ^1.0.0 | 2018-06-18 | :x: |
| <=1.0.0 | 2014-10-05 | :x: |
## Reporting a vulnerability
You can report a vulnerability by contacting us via email at [security@mui.com](mailto:security@mui.com).

230
TYPESCRIPT_CONVENTION.md Normal file
View File

@@ -0,0 +1,230 @@
# TypeScript convention
## Component
> **Public components** are considered all components exported from `@mui/material` or `@mui/lab`.
>
> **Internal components** are considered all components that are not exported from the packages, but only used in some public component.
### `Props Interface`
- export interface `{ComponentName}classes` from `{component}Classes.ts` and add comment for generating API docs (for internal components, may or may not expose classes but don't need comment)
- export interface `{ComponentName}Props`
- always export props interface (use `interface` over `type`) from the component file
<details>
<summary>Public component</summary>
```ts
// fooClasses.tsx
export interface FooClasses {
/** Styles applied to the root element. */
root: string;
/** Styles applied to the foo element. */
foo: string;
/** Styles applied to the root element if `disabled=true`. */
disabled: string;
}
const fooClasses: FooClasses = generateUtilityClasses('MuiFoo', ['root', 'foo', 'disabled']);
export default fooClasses;
```
```ts
// Foo.tsx
import { FooClasses } from './fooClasses';
export interface FooProps {
/**
* Override or extend the styles applied to the component.
*/
classes?: Partial<FooClasses>;
// ...other props
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
}
```
</details>
<details>
<summary>internal component</summary>
```ts
// Bar.tsx
// if this internal component can accept classes as prop
export interface BarClasses {
root: string;
}
export interface BarProps {
classes?: Partial<BarClasses>;
sx?: SxProps<Theme>;
}
```
</details>
### `ClassKey`
- naming as `{ComponentName}ClassKey`
- export if `classes` exists in props interface using `keyof` from `{component}Classes.ts`
```ts
// fooClasses.ts
export interface FooClasses {
...
}
export type FooClassKey = keyof FooClasses;
// verify that FooClassKey is union of string literal
```
### `Classes generator & Utility`
- export if `classes` exists in props interface from the component file
- use `{Component}Classes` as type to preventing typo and missing classes
- use `Private` prefix for internal component
<details>
<summary>Public component</summary>
```ts
// fooClasses.ts
export function getFooUtilityClass(slot: string) {
return generateUtilityClass('MuiFoo', slot);
}
const useUtilityClasses = (ownerState: FooProps & { extraProp: boolean }) => {
// extraProp might be the key/value from react context that this component access
const { foo, disabled, classes } = ownerState;
const slots = {
root: ['root', foo && 'foo', disabled && 'disabled'],
};
return composeClasses(slots, getFooUtilityClass, classes);
};
```
</details>
<details>
<summary>internal component</summary>
```ts
// Bar.tsx
// in case that classes is not exposed.
// `classes` is used internally in this component
const classes = generateUtilityClasses('PrivateBar', ['root', 'bar']);
```
</details>
### `StyledComponent`
- naming using slot `{ComponentName}{Slot}`
- to extend interface of the styled component, pass argument to generic
<details>
<summary>public component</summary>
```ts
const FooRoot = styled(Typography, {
name: 'MuiFoo',
slot: 'Root',
overridesResolver: (props, styles) => styles.root,
})({
// styling
});
```
</details>
<details>
<summary>internal component</summary>
```ts
const BarRoot = styled(Typography)({
// styling
});
```
</details>
<details>
<summary>extends interface</summary>
```ts
const BarRoot = styled(Typography)<{
component?: React.ElementType;
ownerState: BarProps;
}>(({ theme, ownerState }) => ({
// styling
}));
// passing `component` to BarRoot is safe and we don't forget to pass ownerState
// <BarRoot component="span" ownerState={ownerState} />
```
</details>
### `Component declaration`
- prefer `function Component() {}` over `React.FC`
- naming the render function in `React.forwardRef` (for devtools)
- `useThemeProps` is needed only for public component
- pass `ownerState` to StyledComponent for styling
<details>
<summary>public component</summary>
```ts
const Foo = React.forwardRef<HTMLSpanElement, FooProps>(function Foo(inProps, ref) => {
// pass args like this, otherwise will get error about theme at return section
const props = useThemeProps<Theme, FooProps, 'MuiFoo'>({
props: inProps,
name: 'MuiFoo',
});
const { children, className, ...other } = props
// ...implementation
const ownerState = { ...props, ...otherValue }
const classes = useUtilityClasses(ownerState);
return (
<FooRoot
ref={ref}
className={clsx(classes.root, className)}
ownerState={ownerState}
{...other}
>
{children}
</FooRoot>
)
})
```
</details>
<details>
<summary>internal component</summary>
```ts
const classes = generateUtilityClasses('PrivateBar', ['selected']);
const BarRoot = styled('div')(({ theme }) => ({
[`&.${classes.selected}`]: {
color: theme.palette.text.primary,
},
}));
// if this component does not need React.forwardRef, don't use React.FC
const Bar = (props: BarProps) => {
const { className, selected, ...other } = props;
return <BarRoot className={clsx({ [classes.selected]: selected })} {...other} />;
};
```
</details>

127
babel.config.mjs Normal file
View File

@@ -0,0 +1,127 @@
// @ts-check
import { fileURLToPath } from 'node:url';
import * as path from 'node:path';
// @ts-ignore
import getBaseConfig from '@mui/internal-code-infra/babel-config';
/**
* @typedef {import('@babel/core')} babel
*/
const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);
const errorCodesPath = path.resolve(dirname, './docs/public/static/error-codes.json');
/**
* @param {string} relativeToBabelConf
* @returns {string}
*/
function resolveAliasPath(relativeToBabelConf) {
const resolvedPath = path.relative(process.cwd(), path.resolve(dirname, relativeToBabelConf));
return `./${resolvedPath.replace('\\', '/')}`;
}
/** @type {babel.ConfigFunction} */
export default function getBabelConfig(api) {
const baseConfig = getBaseConfig(api);
const useESModules = api.env(['regressions', 'stable']);
const defaultAlias = {
'@mui/material': resolveAliasPath('./packages/mui-material/src'),
'@mui/docs': resolveAliasPath('./packages/mui-docs/src'),
'@mui/icons-material': resolveAliasPath(
`./packages/mui-icons-material/lib${useESModules ? '/esm' : ''}`,
),
'@mui/lab': resolveAliasPath('./packages/mui-lab/src'),
'@mui/internal-markdown': resolveAliasPath('./packages/markdown'),
'@mui/styled-engine': resolveAliasPath('./packages/mui-styled-engine/src'),
'@mui/styled-engine-sc': resolveAliasPath('./packages/mui-styled-engine-sc/src'),
'@mui/system': resolveAliasPath('./packages/mui-system/src'),
'@mui/private-theming': resolveAliasPath('./packages/mui-private-theming/src'),
'@mui/utils': resolveAliasPath('./packages/mui-utils/src'),
'@mui/joy': resolveAliasPath('./packages/mui-joy/src'),
'@mui/internal-docs-utils': resolveAliasPath('./packages-internal/docs-utils/src'),
'@mui/internal-test-utils': resolveAliasPath('./packages-internal/test-utils/src'),
docs: resolveAliasPath('./docs'),
test: resolveAliasPath('./test'),
};
/** @type {babel.PluginItem[]} */
const plugins = [
[
'@mui/internal-babel-plugin-minify-errors',
{
missingError: 'annotate',
errorCodesPath,
runtimeModule: '@mui/utils/formatMuiErrorMessage',
},
],
];
if (process.env.NODE_ENV === 'test') {
plugins.push([
'babel-plugin-module-resolver',
{
alias: defaultAlias,
root: ['./'],
},
]);
}
const basePlugins = (baseConfig.plugins || []).filter(
(/** @type {[unknown, unknown, string]} */ [, , pluginName]) =>
pluginName !== '@mui/internal-babel-plugin-display-name',
);
basePlugins.push(...plugins);
return {
...baseConfig,
plugins: basePlugins,
overrides: [
{
exclude: /\.test\.(m?js|ts|tsx)$/,
plugins: ['@babel/plugin-transform-react-constant-elements'],
},
],
env: {
coverage: {
plugins: [
'babel-plugin-istanbul',
[
'babel-plugin-module-resolver',
{
root: ['./'],
alias: defaultAlias,
},
],
],
},
development: {
plugins: [
[
'babel-plugin-module-resolver',
{
alias: {
...defaultAlias,
modules: './modules',
},
root: ['./'],
},
],
],
},
test: {
sourceMaps: 'both',
plugins: [
[
'babel-plugin-module-resolver',
{
root: ['./'],
alias: defaultAlias,
},
],
],
},
},
};
}

8
codecov.yml Normal file
View File

@@ -0,0 +1,8 @@
coverage:
status:
project:
default:
target: auto
threshold: 1%
patch: off
comment: false

26
crowdin.yml Normal file
View File

@@ -0,0 +1,26 @@
commit_message: '[skip ci]'
pull_request_title: '[docs] Sync translations with Crowdin'
pull_request_labels: [l10n]
files:
- source: /docs/src/**/*.md
ignore:
- /**/%file_name%-%two_letters_code%.md
- /docs/src/pages/getting-started/templates/*/**/*
- /docs/src/pages/premium-themes/*/**/*
- /docs/src/pages/discover-more/backers/*
- /docs/src/pages/discover-more/roadmap/*
- /docs/src/pages/company/**/*
- /docs/pages/careers/**/*
translation: /%original_path%/%file_name%-%two_letters_code%.%file_extension%
- source: /docs/data/**/*.md
ignore:
- /**/%file_name%-%two_letters_code%.md
- /docs/data/material/getting-started/templates/*/**/*
- /docs/data/material/discover-more/backers/*
- /docs/data/material/discover-more/roadmap/*
- /docs/data/material/premium-themes/*/**/*
translation: /%original_path%/%file_name%-%two_letters_code%.%file_extension%
- source: /docs/translations/**/*.json
ignore:
- /**/%file_name%-%two_letters_code%.json
translation: /%original_path%/%file_name%-%two_letters_code%.%file_extension%

6
dangerfile.js Normal file
View File

@@ -0,0 +1,6 @@
const tsx = require('tsx/cjs/api');
// danger uses babelify under the hood. The implementation is buggy and fails on our
// custom plugins in our codebase. We'll just disable it and do our own compilation.
// Danger must always be run with envirnonent variable `DANGER_DISABLE_TRANSPILATION="true"`.
tsx.require('./scripts/dangerFileContent', __filename);

23
docs/README.md Normal file
View File

@@ -0,0 +1,23 @@
# Docs
This is the website of the company MUI and the documentation for Material UI.
To start the docs site in development mode, from the project root, run:
```bash
pnpm docs:dev
```
If you do not have pnpm installed, select your OS and follow the instructions on the [pnpm website](https://pnpm.io/installation).
Package managers other than pnpm (like npm or Yarn) are not supported and will not work.
## How can I add a new demo to the documentation?
[You can follow this guide](https://github.com/mui/material-ui/blob/HEAD/CONTRIBUTING.md)
on how to get started contributing to MUI.
## How do I help to improve the translations?
Please visit https://crowdin.com/project/material-ui-docs where you will be able to select a language and edit the translations.
Please don't submit pull requests directly.

22
docs/config.ts Normal file
View File

@@ -0,0 +1,22 @@
// Valid languages to server-side render in production
export const LANGUAGES = ['en'];
// Server side rendered languages
export const LANGUAGES_SSR = ['en'];
// Work in progress
export const LANGUAGES_IN_PROGRESS = LANGUAGES.slice();
export const LANGUAGES_IGNORE_PAGES = (pathname: string) => {
// We don't have the bandwidth like Qt to translate our blog posts
// https://www.qt.io/zh-cn/blog
if (pathname === '/blog' || pathname.startsWith('/blog/')) {
return true;
}
if (pathname === '/size-snapshot/') {
return true;
}
return false;
};

View File

@@ -0,0 +1,331 @@
[
{
"name": "Olivier Tassinari",
"title": "Co-founder, CEO",
"location": "Paris, France",
"locationCountry": "fr",
"about": "Exercise addict and lifelong learner.",
"twitter": "olivtassinari",
"github": "oliviertassinari"
},
{
"name": "Matt Brookes",
"title": "Co-founder, Sales",
"location": "London, UK",
"locationCountry": "gb",
"about": "When I'm not 👨🏻‍💻, I'm 🧗🏼‍♂️.",
"twitter": "randomtechdude",
"github": "mbrookes"
},
{
"name": "Marija Najdova",
"title": "Engineering Manager",
"location": "Skopje, North Macedonia",
"locationCountry": "mk",
"about": "I do karate 🥋 and read 📚. A lot!",
"twitter": "marijanajdova",
"github": "mnajdova"
},
{
"name": "Danail Hadjiatanasov",
"title": "Engineering Manager",
"location": "Sofia, Bulgaria",
"locationCountry": "bg",
"about": "Boringly normal, geek deep down. I like 🚗 and 🏂.",
"twitter": "danail_h",
"github": "DanailH"
},
{
"name": "Jun Kunaporn",
"title": "UI Engineer — Core",
"location": "Bangkok, Thailand",
"locationCountry": "th",
"about": "UI Lover and ⛷ skiing newbie.",
"twitter": "siriwatknp",
"github": "siriwatknp"
},
{
"name": "Michał Dudak",
"title": "Software Engineer — Base UI",
"location": "Katowice, Poland",
"locationCountry": "pl",
"about": "Motorcyclist, gamer, and coder (UI and more!).",
"twitter": "michaldudak",
"github": "michaldudak"
},
{
"name": "Flavien Delangle",
"title": "Tech Lead — eXplore",
"location": "Lille, France",
"locationCountry": "fr",
"about": "Love cycling 🚴‍♂️ and reading 📚.",
"github": "flaviendelangle"
},
{
"name": "Alexandre Fauquette",
"title": "Tech Lead — xCharts",
"location": "Lille, France",
"locationCountry": "fr",
"about": "Love hacking and cycling 🚴‍♂️.",
"github": "alexfauquette"
},
{
"name": "Jan Potoms",
"title": "Tech Lead — Core",
"location": "Brussels, Belgium",
"locationCountry": "be",
"about": "Always curious, I enjoy cinema and hiking.",
"github": "Janpot"
},
{
"name": "Prakhar Gupta",
"title": "Product Manager — X",
"location": "New Delhi, India",
"locationCountry": "in",
"about": "Into sports and hiking!",
"twitter": "gprakhar123",
"github": "prakhargupta1"
},
{
"name": "José Freitas",
"title": "Technical Product Manager",
"location": "Augsburg, Germany",
"locationCountry": "de",
"about": "Art, fiction, and bar philosophy.",
"twitter": "zehdefreitas",
"github": "joserodolfofreitas"
},
{
"name": "Andrew Cherniavskyi",
"title": "Tech Lead — xGrid",
"location": "Wrocław, Poland",
"locationCountry": "pl",
"about": "Love playing music - electric and bass guitar 🎸.",
"twitter": "iamcherniavskii",
"github": "cherniavskii"
},
{
"name": "Sam Sycamore",
"title": "Head of Documentation",
"location": "Washington state, US",
"locationCountry": "us",
"about": "Musician and edible wild plant enthusiast 🌱.",
"github": "mapache-salvaje"
},
{
"name": "Lukas Tyla",
"title": "Software Engineer — eXplore",
"location": "Vilnius, Lithuania",
"locationCountry": "lt",
"about": "Learning and experimenting 📚.",
"github": "LukasTy"
},
{
"name": "Tina Deinekhovska",
"title": "Business Administrator",
"location": "London, UK",
"locationCountry": "gb",
"about": "Empathic optimist keen on dancing, biking and gardening.",
"github": "TinaDein"
},
{
"name": "Bilal Shafi",
"title": "Software Engineer — xGrid",
"location": "Islamabad, Pakistan",
"locationCountry": "pk",
"about": "DIY 🛠️, Learning 📚 and 🏓.",
"twitter": "MBilalShafi",
"github": "MBilalShafi"
},
{
"name": "Albert Yu",
"title": "Software Engineer — Core",
"location": "Hong Kong",
"locationCountry": "hk",
"about": "Minimalist, dog lover 🏔🐕.",
"twitter": "mj12albert",
"github": "mj12albert"
},
{
"name": "Romain Gregoire",
"title": "Software Engineer — xGrid",
"location": "Montréal, Canada",
"locationCountry": "ca",
"about": "Open-source tinkerer.",
"twitter": "romgrk",
"github": "romgrk"
},
{
"name": "Brijesh Bittu",
"title": "Software Engineer — Core",
"location": "Bengaluru, India",
"locationCountry": "in",
"about": "Loves cooking.",
"twitter": "brijeshb42",
"github": "brijeshb42"
},
{
"name": "Diego Andai",
"title": "UI Engineer — Core",
"location": "Santiago, Chile",
"locationCountry": "cl",
"about": "I love tennis 🎾 and cats 🐈.",
"twitter": "DiegoAndaiC",
"github": "DiegoAndai"
},
{
"name": "Nora Leonte",
"title": "Design Engineer — eXplore",
"location": "Cluj-Napoca, Romania",
"locationCountry": "ro",
"about": "Art enthusiast 🎨 outdoor person 🌳 animal lover 🐾.",
"github": "noraleonte"
},
{
"name": "Raffaella Luzi",
"title": "Head of Operations",
"location": "Rome, Italy",
"locationCountry": "it",
"about": "NYT crossword 📝 in one hand and a croissant 🥐 in the other.",
"github": "rluzists1"
},
{
"name": "Michel Engelen",
"title": "React Community Engineer — X",
"location": "Zeven, Germany",
"locationCountry": "de",
"about": "Geeking out on Badminton 🏸, everything Japan 🇯🇵 and Pizza 🍕.",
"twitter": "jsNerdic",
"github": "michelengelen"
},
{
"name": "Vadym Raksha",
"title": "Software Engineer — Services",
"location": "Prague, Czech Republic",
"locationCountry": "cz",
"about": "Product experience geek 🛋, Mediterranean vibes 🍊.",
"twitter": "vadym_raksha",
"github": "hasdfa"
},
{
"name": "Greg Abaoag",
"title": "Executive Assistant",
"location": "Baguio, Philippines",
"locationCountry": "ph",
"about": "Loves DIY, singing and learning.",
"twitter": "gzraenga",
"github": "zannager"
},
{
"name": "Colm Tuite",
"title": "Director of Design Engineering",
"location": "Dublin, Ireland",
"locationCountry": "ie",
"about": "Hi :). Designer / developer / entrepreneur.",
"twitter": "colmtuite",
"github": "colmtuite"
},
{
"name": "Nadja Kovacev",
"title": "Talent & Culture Partner",
"location": "Novi Sad, Serbia",
"locationCountry": "rs",
"about": "🧠 Psychology geek, 🏂 amateur snowboarder, and 𐃡 pottery enthusiast.",
"github": "nadjakovacev"
},
{
"name": "James Nelson",
"title": "UI Engineer — Base UI",
"location": "Gold Coast, Australia",
"locationCountry": "au",
"about": "Enjoy creating open source libraries and reading about AI 🤖.",
"twitter": "atomiksdev",
"github": "atomiks"
},
{
"name": "Aarón García",
"title": "Design Engineer — Core",
"location": "Alicante, Spain",
"locationCountry": "es",
"about": "Minimalism, web development, user experience, football, cycling.",
"twitter": "aarongarciah",
"github": "aarongarciah"
},
{
"name": "Jose Quintas",
"title": "Software Engineer — xCharts",
"location": "Barcelona, Spain",
"locationCountry": "es",
"about": "A general knowledge gatherer. Gamer and programmer in the off hours.",
"github": "JCQuintas"
},
{
"name": "Nikita Ossei",
"title": "Customer Support Agent",
"location": "Northampton, UK",
"locationCountry": "gb",
"about": "Astrology ♊️, learning 📚 and designing stuff 🎨.",
"github": "nikitaa24"
},
{
"name": "Kenan Yusuf",
"title": "Design Engineer — xGrid",
"location": "London, UK",
"locationCountry": "gb",
"about": "Amateur cook, DIYer, and film photographer.",
"twitter": "KebabYusuf",
"github": "KenanYusuf"
},
{
"name": "Alejandra Thomas",
"title": "DevEx Lead",
"location": "New York, US",
"locationCountry": "us",
"about": "Books 📚, long coffee walks ☕️, and my cat 🐈‍⬛.",
"twitter": "pikacodes",
"github": "alelthomas"
},
{
"name": "Armin Mehinović",
"title": "Software Engineer — xGrid",
"location": "Amsterdam, Netherlands",
"locationCountry": "nl",
"about": "♟️ Chess, 🛠️ Small DIY projects and learning about space 🌌.",
"github": "arminmeh"
},
{
"name": "Bernardo Belchior",
"title": "Software Engineer — xCharts",
"location": "Berlin, Germany",
"locationCountry": "de",
"about": "Soccer ⚽️ and coding 💻.",
"twitter": "bernardobelchi1",
"github": "bernardobelchior"
},
{
"name": "Rita Iglesias",
"title": "Software Engineer — eXplore",
"location": "Seville, Spain",
"locationCountry": "es",
"about": "Learning 📚, my dog 🐶 and Japanese culture 🇯🇵.",
"twitter": "rita_codes",
"github": "rita-codes"
},
{
"name": "Connor Davis",
"title": "Software Engineer — Core",
"location": "New York City, US",
"locationCountry": "us",
"about": "Community gardener, non-profit organizer, and nature enthusiast.",
"twitter": "connordav_is",
"github": "dav-is"
},
{
"name": "Luis Vasallo",
"title": "Account Executive — Sales",
"location": "Edinburgh, UK",
"locationCountry": "gb",
"about": "Loves drums 🥁, food 🍖 and books 📚.",
"github": "LuisHVasallo"
}
]

View File

@@ -0,0 +1,239 @@
import type { MuiPage } from 'docs/src/MuiPage';
const pages: readonly MuiPage[] = [
{
pathname: '/experiments/docs/writing',
title: 'Writing',
children: [
{ pathname: '/experiments/docs/headers' },
{ pathname: '/experiments/docs/markdown' },
{ pathname: '/experiments/docs/og-card', title: 'OG Image' },
],
},
{
pathname: '/experiments/docs/components',
children: [
{ pathname: '/experiments/docs/callouts' },
{ pathname: '/experiments/docs/codeblock' },
{ pathname: '/experiments/docs/custom-components' },
{ pathname: '/experiments/docs/demos' },
{ pathname: '/experiments/docs/pro-feature', title: 'Pro feature', plan: 'pro' },
{ pathname: '/experiments/docs/data-grid-premium', title: 'API DataGridPremium' },
],
},
{
pathname: '/experiments/docs/main-parent',
title: 'Test: pages.js',
children: [
{
pathname: '/experiments/docs/first-level-child-1',
title: 'First-level child 1',
},
{
pathname: '/experiments/docs/first-level-child-2',
title: 'First-level child 2',
children: [
{ pathname: '/experiments/docs/second-level-child', title: 'Second-level child' },
{ pathname: '/experiments/docs/subheader-divider', subheader: 'Subheader divider' },
{
pathname: '/experiments/docs/api/data-grid-group',
title: 'Second-level child parent',
children: [
{ pathname: '/experiments/docs/third-level-child', title: 'Third-level child' },
{
pathname: '/experiments/docs/api/data-grid-components-group',
subheader: 'Subheader divider',
children: [
{
pathname: '/experiments/docs/api/data-grid/data-grid',
title: 'Fourth-level child',
},
],
},
],
},
],
},
{
pathname: '/experiments/docs/first-level-child-3',
title: 'Pro plan',
plan: 'pro',
},
{
pathname: '/experiments/docs/first-level-child-4',
title: 'New feature',
newFeature: true,
},
{
pathname: '/experiments/docs/first-level-child-5',
title: 'Planned feature',
planned: true,
},
{
pathname: '/experiments/docs/first-level-child-6',
title: 'Unstable feature',
unstable: true,
},
{
pathname: '/experiments/docs/first-level-child-7',
title: 'Beta feature',
beta: true,
},
{
pathname: '/experiments/docs/first-level-child-8',
title: 'Legacy feature',
legacy: true,
},
{
pathname: '/experiments/docs/first-level-child-9',
title: 'OverflowWithLongApiComponent',
plan: 'pro',
},
],
},
{
pathname: '/x/react-data-grid-group',
title: 'Test: Data Grid e2e',
children: [
{ pathname: '/x/react-data-grid', title: 'Overview' },
{ pathname: '/x/react-data-grid/demo' },
{ pathname: '/x/react-data-grid/getting-started' },
{ pathname: '/x/react-data-grid/layout' },
{
pathname: '/x/react-data-grid/columns',
children: [
{ pathname: '/x/react-data-grid/column-definition' },
{ pathname: '/x/react-data-grid/column-dimensions' },
{ pathname: '/x/react-data-grid/column-visibility' },
{ pathname: '/x/react-data-grid/column-header' },
{ pathname: '/x/react-data-grid/column-menu' },
{ pathname: '/x/react-data-grid/column-spanning' },
{ pathname: '/x/react-data-grid/column-groups' },
{ pathname: '/x/react-data-grid/column-ordering', plan: 'pro' },
{ pathname: '/x/react-data-grid/column-pinning', plan: 'pro' },
],
},
{
pathname: '/x/react-data-grid/rows',
children: [
{ pathname: '/x/react-data-grid/row-definition' },
{ pathname: '/x/react-data-grid/row-updates' },
{ pathname: '/x/react-data-grid/row-height' },
{ pathname: '/x/react-data-grid/row-spanning', title: 'Row spanning 🚧' },
{ pathname: '/x/react-data-grid/master-detail', plan: 'pro' },
{ pathname: '/x/react-data-grid/row-ordering', plan: 'pro' },
{ pathname: '/x/react-data-grid/row-pinning', plan: 'pro' },
{ pathname: '/x/react-data-grid/row-recipes', title: 'Recipes' },
],
},
{ pathname: '/x/react-data-grid/editing' },
{ pathname: '/x/react-data-grid/clipboard', title: 'Copy and paste', newFeature: true },
{ pathname: '/x/react-data-grid/performance' },
{
pathname: '/x/react-data-grid-group-pivot',
title: 'Group & Pivot',
children: [
{ pathname: '/x/react-data-grid/tree-data', plan: 'pro' },
{ pathname: '/x/react-data-grid/row-grouping', plan: 'premium' },
{ pathname: '/x/react-data-grid/aggregation', title: 'Aggregation', plan: 'premium' },
{ pathname: '/x/react-data-grid/pivoting', title: 'Pivoting 🚧', plan: 'premium' },
],
},
{
title: 'Recipes',
pathname: '/x/react-data-grid/recipes',
children: [
{ pathname: '/x/react-data-grid/recipes-editing', title: 'Editing' },
{ pathname: '/x/react-data-grid/recipes-row-grouping', title: 'Row grouping' },
],
},
{
pathname: '/x/api/data-grid-group',
title: 'API Reference',
children: [
{ pathname: '/x/api/data-grid', title: 'Index' },
{
pathname: '/x/api/data-grid-interfaces-group',
subheader: 'Interfaces',
children: [
{ pathname: '/x/api/data-grid/grid-api', title: 'GridApi' },
{ pathname: '/x/api/data-grid/grid-cell-params', title: 'GridCellParams' },
{ pathname: '/x/api/data-grid/grid-col-def', title: 'GridColDef' },
{
pathname: '/x/api/data-grid/grid-single-select-col-def',
title: 'GridSingleSelectColDef',
},
{ pathname: '/x/api/data-grid/grid-actions-col-def', title: 'GridActionsColDef' },
{
pathname: '/x/api/data-grid/grid-export-state-params',
title: 'GridExportStateParams',
},
{ pathname: '/x/api/data-grid/grid-filter-item', title: 'GridFilterItem' },
{ pathname: '/x/api/data-grid/grid-filter-model', title: 'GridFilterModel' },
{ pathname: '/x/api/data-grid/grid-filter-operator', title: 'GridFilterOperator' },
{
pathname: '/x/api/data-grid/grid-row-class-name-params',
title: 'GridRowClassNameParams',
},
{ pathname: '/x/api/data-grid/grid-row-params', title: 'GridRowParams' },
{
pathname: '/x/api/data-grid/grid-row-spacing-params',
title: 'GridRowSpacingParams',
},
{
pathname: '/x/api/data-grid/grid-aggregation-function',
title: 'GridAggregationFunction',
},
{
pathname: '/x/api/data-grid/grid-csv-export-options',
title: 'GridCsvExportOptions',
},
{
pathname: '/x/api/data-grid/grid-print-export-options',
title: 'GridPrintExportOptions',
},
{
pathname: '/x/api/data-grid/grid-excel-export-options',
title: 'GridExcelExportOptions',
},
],
},
],
},
],
},
{
pathname: '/x/migration-group',
title: 'Test: Migration',
children: [
{
pathname: '/x/migration-v6',
subheader: 'Upgrade to v6',
children: [
{ pathname: '/x/migration/migration-data-grid-v5', title: 'Breaking changes: Data Grid' },
{
pathname: '/x/migration/migration-pickers-v5',
title: 'Breaking changes: Date and Time Pickers',
},
],
},
{
pathname: '/x/migration-earlier',
subheader: 'Earlier versions',
children: [
{
pathname: '/x/migration/migration-pickers-lab',
title: 'Migration from lab to v5 (Date and Time Pickers)',
},
{
pathname: '/x/migration/migration-data-grid-v4',
title: 'Migration from v4 to v5 (Data Grid)',
},
],
},
],
},
];
export default pages;

19
docs/data/docs/pages.ts Normal file
View File

@@ -0,0 +1,19 @@
import type { MuiPage } from 'docs/src/MuiPage';
import standardNavIcons from 'docs/src/modules/components/AppNavIcons';
const pages: readonly MuiPage[] = [
{ pathname: 'https://mui.com/versions/' },
{
pathname: 'https://mui.com/store/',
title: 'Templates',
icon: standardNavIcons.ReaderIcon,
linkProps: {
'data-ga-event-category': 'store',
'data-ga-event-action': 'click',
'data-ga-event-label': 'sidenav',
},
},
{ pathname: 'https://mui.com/blog/', title: 'Blog', icon: standardNavIcons.BookIcon },
];
export default pages;

View File

@@ -0,0 +1,15 @@
import Avatar from '@mui/material/Avatar';
export function renderAvatar(params) {
if (params.value == null) {
return '';
}
return (
<Avatar style={{ backgroundColor: params.value.color }}>
{params.value.name.toUpperCase().substring(0, 1)}
</Avatar>
);
}
export default renderAvatar;

View File

@@ -0,0 +1,126 @@
import * as React from 'react';
import { useGridApiContext } from '@mui/x-data-grid';
// eslint-disable-next-line no-restricted-imports
import { COUNTRY_ISO_OPTIONS } from '@mui/x-data-grid-generator/services/static-data';
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
import Box from '@mui/material/Box';
import InputBase from '@mui/material/InputBase';
import { styled } from '@mui/material/styles';
const Country = React.memo(function Country(props) {
const { value } = props;
return (
<Box
sx={{
width: '100%',
display: 'flex',
alignItems: 'center',
'& > img': {
mr: 0.5,
flexShrink: 0,
width: '20px',
},
}}
>
<img
loading="lazy"
width="20"
src={`https://flagcdn.com/w20/${value.code.toLowerCase()}.png`}
srcSet={`https://flagcdn.com/w40/${value.code.toLowerCase()}.png 2x`}
alt=""
/>
<Box component="span" sx={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>
{value.label}
</Box>
</Box>
);
});
const StyledAutocomplete = styled(Autocomplete)(({ theme }) => ({
height: '100%',
[`& .${autocompleteClasses.inputRoot}`]: {
...theme.typography.body2,
padding: '1px 0',
height: '100%',
'& input': {
padding: '0 16px',
height: '100%',
},
},
}));
function EditCountry(props) {
const { id, value, field } = props;
const apiRef = useGridApiContext();
const handleChange = React.useCallback(
async (event, newValue) => {
await apiRef.current.setEditCellValue({ id, field, value: newValue }, event);
apiRef.current.stopCellEditMode({ id, field });
},
[apiRef, field, id],
);
return (
<StyledAutocomplete
value={value}
onChange={handleChange}
options={COUNTRY_ISO_OPTIONS}
getOptionLabel={(option) => option.label}
autoHighlight
fullWidth
open
disableClearable
renderOption={(optionProps, option) => (
<Box
component="li"
sx={{
'& > img': {
mr: 1.5,
flexShrink: 0,
},
}}
{...optionProps}
key={option.code}
>
<img
loading="lazy"
width="20"
src={`https://flagcdn.com/w20/${option.code.toLowerCase()}.png`}
srcSet={`https://flagcdn.com/w40/${option.code.toLowerCase()}.png 2x`}
alt=""
/>
{option.label}
</Box>
)}
renderInput={(params) => (
<InputBase
autoFocus
fullWidth
id={params.id}
inputProps={{
...params.inputProps,
autoComplete: 'new-password', // disable autocomplete and autofill
}}
{...params.InputProps}
/>
)}
/>
);
}
export function renderCountry(params) {
if (params.value == null) {
return '';
}
return <Country value={params.value} />;
}
export function renderEditCountry(params) {
return <EditCountry {...params} />;
}
export default renderCountry;

View File

@@ -0,0 +1,34 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
const Link = styled('a')({
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflow: 'hidden',
color: 'inherit',
});
const DemoLink = React.memo(function DemoLink(props) {
const handleClick = (event) => {
event.preventDefault();
event.stopPropagation();
};
return (
<Link tabIndex={props.tabIndex} onClick={handleClick} href={props.href}>
{props.children}
</Link>
);
});
export function renderEmail(params) {
const email = params.value ?? '';
return (
<DemoLink href={`mailto:${email}`} tabIndex={params.tabIndex}>
{email}
</DemoLink>
);
}
export default renderEmail;

View File

@@ -0,0 +1,97 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
import Tooltip from '@mui/material/Tooltip';
import InfoIcon from '@mui/icons-material/Info';
import { useGridApiContext } from '@mui/x-data-grid';
// eslint-disable-next-line no-restricted-imports
import { INCOTERM_OPTIONS } from '@mui/x-data-grid-generator/services/static-data';
const Incoterm = React.memo(function Incoterm(props) {
const { value } = props;
if (!value) {
return null;
}
const valueStr = value.toString();
const tooltip = valueStr.slice(valueStr.indexOf('(') + 1, valueStr.indexOf(')'));
const code = valueStr.slice(0, valueStr.indexOf('(')).trim();
return (
<Box
sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
>
<span>{code}</span>
<Tooltip title={tooltip}>
<InfoIcon sx={{ color: '#2196f3', alignSelf: 'center', ml: '8px' }} />
</Tooltip>
</Box>
);
});
function EditIncoterm(props) {
const { id, value, field } = props;
const apiRef = useGridApiContext();
const handleChange = async (event) => {
await apiRef.current.setEditCellValue(
{ id, field, value: event.target.value },
event,
);
apiRef.current.stopCellEditMode({ id, field });
};
const handleClose = (event, reason) => {
if (reason === 'backdropClick') {
apiRef.current.stopCellEditMode({ id, field });
}
};
return (
<Select
value={value}
onChange={handleChange}
MenuProps={{
onClose: handleClose,
}}
sx={{
height: '100%',
'& .MuiSelect-select': {
display: 'flex',
alignItems: 'center',
pl: 1,
},
}}
autoFocus
fullWidth
open
>
{INCOTERM_OPTIONS.map((option) => {
const tooltip = option.slice(option.indexOf('(') + 1, option.indexOf(')'));
const code = option.slice(0, option.indexOf('(')).trim();
return (
<MenuItem key={option} value={option}>
<ListItemIcon sx={{ minWidth: 36 }}>{code}</ListItemIcon>
<ListItemText primary={tooltip} sx={{ overflow: 'hidden' }} />
</MenuItem>
);
})}
</Select>
);
}
export function renderIncoterm(params) {
return <Incoterm value={params.value} />;
}
export function renderEditIncoterm(params) {
return <EditIncoterm {...params} />;
}
export default renderIncoterm;

View File

@@ -0,0 +1,180 @@
import * as React from 'react';
import clsx from 'clsx';
import { useGridApiContext } from '@mui/x-data-grid';
import Slider, { sliderClasses } from '@mui/material/Slider';
import Tooltip from '@mui/material/Tooltip';
import { alpha, styled } from '@mui/material/styles';
import { debounce } from '@mui/material/utils';
const Center = styled('div')({
height: '100%',
display: 'flex',
alignItems: 'center',
});
const Element = styled('div')(({ theme }) => ({
border: `1px solid ${(theme.vars || theme).palette.divider}`,
position: 'relative',
overflow: 'hidden',
width: '100%',
height: 26,
borderRadius: 2,
}));
const Value = styled('div')({
position: 'absolute',
lineHeight: '24px',
width: '100%',
display: 'flex',
justifyContent: 'center',
});
const Bar = styled('div')({
height: '100%',
'&.low': {
backgroundColor: '#f44336',
},
'&.medium': {
backgroundColor: '#efbb5aa3',
},
'&.high': {
backgroundColor: '#088208a3',
},
});
const ProgressBar = React.memo(function ProgressBar(props) {
const { value } = props;
const valueInPercent = value * 100;
return (
<Element>
<Value>{`${valueInPercent.toLocaleString()} %`}</Value>
<Bar
className={clsx({
low: valueInPercent < 30,
medium: valueInPercent >= 30 && valueInPercent <= 70,
high: valueInPercent > 70,
})}
style={{ maxWidth: `${valueInPercent}%` }}
/>
</Element>
);
});
const StyledSlider = styled(Slider)(({ theme }) => ({
display: 'flex',
height: '100%',
width: '100%',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
borderRadius: 0,
[`& .${sliderClasses.rail}`]: {
height: '100%',
backgroundColor: 'transparent',
},
[`& .${sliderClasses.track}`]: {
height: '100%',
transition: theme.transitions.create('background-color', {
duration: theme.transitions.duration.shorter,
}),
'&.low': {
backgroundColor: '#f44336',
},
'&.medium': {
backgroundColor: '#efbb5aa3',
},
'&.high': {
backgroundColor: '#088208a3',
},
},
[`& .${sliderClasses.thumb}`]: {
height: '100%',
width: 5,
borderRadius: 0,
marginTop: 0,
backgroundColor: alpha('#000000', 0.2),
},
}));
const ValueLabelComponent = React.memo(function ValueLabelComponent(props) {
const { children, open, value } = props;
return (
<Tooltip open={open} enterTouchDelay={0} placement="top" title={value}>
{children}
</Tooltip>
);
});
function EditProgress(props) {
const { id, value, field } = props;
const [valueState, setValueState] = React.useState(Number(value));
const apiRef = useGridApiContext();
const updateCellEditProps = React.useCallback(
(newValue) => {
apiRef.current.setEditCellValue({ id, field, value: newValue });
},
[apiRef, field, id],
);
const debouncedUpdateCellEditProps = React.useMemo(
() => debounce(updateCellEditProps, 60),
[updateCellEditProps],
);
const handleChange = (event, newValue) => {
setValueState(newValue);
debouncedUpdateCellEditProps(newValue);
};
React.useEffect(() => {
setValueState(Number(value));
}, [value]);
const handleRef = (element) => {
if (element) {
element.querySelector('[type="range"]').focus();
}
};
return (
<StyledSlider
ref={handleRef}
classes={{
track: clsx({
low: valueState < 0.3,
medium: valueState >= 0.3 && valueState <= 0.7,
high: valueState > 0.7,
}),
}}
value={valueState}
max={1}
step={0.00001}
onChange={handleChange}
components={{ ValueLabel: ValueLabelComponent }}
valueLabelDisplay="auto"
valueLabelFormat={(newValue) => `${(newValue * 100).toLocaleString()} %`}
/>
);
}
export function renderProgress(params) {
if (params.value == null) {
return '';
}
return (
<Center>
<ProgressBar value={params.value} />
</Center>
);
}
export function renderEditProgress(params) {
return <EditProgress {...params} />;
}
export default renderProgress;

View File

@@ -0,0 +1,95 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Rating from '@mui/material/Rating';
import { useGridApiContext } from '@mui/x-data-grid';
const RatingValue = React.memo(function RatingValue(props) {
const { value } = props;
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
lineHeight: '24px',
color: 'text.secondary',
}}
>
<Rating value={value} sx={{ mr: 1 }} readOnly />{' '}
{Math.round(Number(value) * 10) / 10}
</Box>
);
});
function EditRating(props) {
const { id, value, field } = props;
const apiRef = useGridApiContext();
const changedThroughKeyboard = React.useRef(false);
const handleChange = async (event) => {
await apiRef.current.setEditCellValue(
{ id, field, value: Number(event.target.value) },
event,
);
if (!changedThroughKeyboard.current) {
apiRef.current.stopCellEditMode({ id, field });
}
changedThroughKeyboard.current = false;
};
const handleRef = (element) => {
if (element) {
if (value !== 0) {
element.querySelector(`input[value="${value}"]`).focus();
} else {
element.querySelector('input[value=""]').focus();
}
}
};
const handleKeyDown = (event) => {
if (event.key.startsWith('Arrow')) {
changedThroughKeyboard.current = true;
} else {
changedThroughKeyboard.current = false;
}
};
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
lineHeight: '24px',
color: 'text.secondary',
mr: 1,
}}
>
<Rating
ref={handleRef}
name="rating"
value={Number(value)}
precision={1}
onChange={handleChange}
sx={{ mr: 1 }}
onKeyDown={handleKeyDown}
/>
{Number(value)}
</Box>
);
}
export function renderRating(params) {
if (params.value == null) {
return '';
}
return <RatingValue value={params.value} />;
}
export function renderEditRating(params) {
return <EditRating {...params} />;
}
export default renderRating;

View File

@@ -0,0 +1,17 @@
import { SparkLineChart } from '@mui/x-charts/SparkLineChart';
export function renderSparkline(params) {
if (params.value == null) {
return '';
}
return (
<SparkLineChart
data={params.value}
width={params.colDef.computedWidth}
plotType="bar"
/>
);
}
export default renderSparkline;

View File

@@ -0,0 +1,157 @@
import * as React from 'react';
import Chip from '@mui/material/Chip';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
import { styled } from '@mui/material/styles';
import ReportProblemIcon from '@mui/icons-material/ReportProblem';
import InfoIcon from '@mui/icons-material/Info';
import AutorenewIcon from '@mui/icons-material/Autorenew';
import DoneIcon from '@mui/icons-material/Done';
import {
GridEditModes,
useGridApiContext,
useGridRootProps,
} from '@mui/x-data-grid';
// eslint-disable-next-line no-restricted-imports
import { STATUS_OPTIONS } from '@mui/x-data-grid-generator/services/static-data';
const StyledChip = styled(Chip)(({ theme }) => ({
justifyContent: 'left',
'& .icon': {
color: 'inherit',
},
'&.Open': {
color: (theme.vars || theme).palette.info.dark,
border: `1px solid ${(theme.vars || theme).palette.info.main}`,
},
'&.Filled': {
color: (theme.vars || theme).palette.success.dark,
border: `1px solid ${(theme.vars || theme).palette.success.main}`,
},
'&.PartiallyFilled': {
color: (theme.vars || theme).palette.warning.dark,
border: `1px solid ${(theme.vars || theme).palette.warning.main}`,
},
'&.Rejected': {
color: (theme.vars || theme).palette.error.dark,
border: `1px solid ${(theme.vars || theme).palette.error.main}`,
},
}));
const Status = React.memo((props) => {
const { status } = props;
let icon = null;
if (status === 'Rejected') {
icon = <ReportProblemIcon className="icon" />;
} else if (status === 'Open') {
icon = <InfoIcon className="icon" />;
} else if (status === 'PartiallyFilled') {
icon = <AutorenewIcon className="icon" />;
} else if (status === 'Filled') {
icon = <DoneIcon className="icon" />;
}
let label = status;
if (status === 'PartiallyFilled') {
label = 'Partially Filled';
}
return (
<StyledChip
className={status}
icon={icon}
size="small"
label={label}
variant="outlined"
/>
);
});
function EditStatus(props) {
const { id, value, field } = props;
const rootProps = useGridRootProps();
const apiRef = useGridApiContext();
const handleChange = async (event) => {
const isValid = await apiRef.current.setEditCellValue({
id,
field,
value: event.target.value,
});
if (isValid && rootProps.editMode === GridEditModes.Cell) {
apiRef.current.stopCellEditMode({ id, field, cellToFocusAfter: 'below' });
}
};
const handleClose = (event, reason) => {
if (reason === 'backdropClick') {
apiRef.current.stopCellEditMode({ id, field, ignoreModifications: true });
}
};
return (
<Select
value={value}
onChange={handleChange}
MenuProps={{
onClose: handleClose,
}}
sx={{
height: '100%',
'& .MuiSelect-select': {
display: 'flex',
alignItems: 'center',
pl: 1,
},
}}
autoFocus
fullWidth
open
>
{STATUS_OPTIONS.map((option) => {
let IconComponent = null;
if (option === 'Rejected') {
IconComponent = ReportProblemIcon;
} else if (option === 'Open') {
IconComponent = InfoIcon;
} else if (option === 'PartiallyFilled') {
IconComponent = AutorenewIcon;
} else if (option === 'Filled') {
IconComponent = DoneIcon;
}
let label = option;
if (option === 'PartiallyFilled') {
label = 'Partially Filled';
}
return (
<MenuItem key={option} value={option}>
<ListItemIcon sx={{ minWidth: 36 }}>
<IconComponent fontSize="small" />
</ListItemIcon>
<ListItemText primary={label} sx={{ overflow: 'hidden' }} />
</MenuItem>
);
})}
</Select>
);
}
export function renderStatus(params) {
if (params.value == null) {
return '';
}
return <Status status={params.value} />;
}
export function renderEditStatus(params) {
return <EditStatus {...params} />;
}
export default renderStatus;

View File

@@ -0,0 +1,32 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionBasic() {
return (
<AccordionGroup sx={{ maxWidth: 400 }}>
<Accordion>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,32 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionBasic() {
return (
<AccordionGroup sx={{ maxWidth: 400 }}>
<Accordion>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionControlled() {
const [index, setIndex] = React.useState(0);
return (
<AccordionGroup sx={{ maxWidth: 400 }}>
<Accordion
expanded={index === 0}
onChange={(event, expanded) => {
setIndex(expanded ? 0 : null);
}}
>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion
expanded={index === 1}
onChange={(event, expanded) => {
setIndex(expanded ? 1 : null);
}}
>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion
expanded={index === 2}
onChange={(event, expanded) => {
setIndex(expanded ? 2 : null);
}}
>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionControlled() {
const [index, setIndex] = React.useState<number | null>(0);
return (
<AccordionGroup sx={{ maxWidth: 400 }}>
<Accordion
expanded={index === 0}
onChange={(event, expanded) => {
setIndex(expanded ? 0 : null);
}}
>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion
expanded={index === 1}
onChange={(event, expanded) => {
setIndex(expanded ? 1 : null);
}}
>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion
expanded={index === 2}
onChange={(event, expanded) => {
setIndex(expanded ? 2 : null);
}}
>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,52 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails, {
accordionDetailsClasses,
} from '@mui/joy/AccordionDetails';
import AccordionSummary, {
accordionSummaryClasses,
} from '@mui/joy/AccordionSummary';
export default function AccordionDepthPanel() {
return (
<AccordionGroup
variant="outlined"
transition="0.2s"
sx={(theme) => ({
maxWidth: 400,
borderRadius: 'lg',
[`& .${accordionSummaryClasses.button}:hover`]: {
bgcolor: 'transparent',
},
[`& .${accordionDetailsClasses.content}`]: {
boxShadow: `inset 0 1px ${theme.vars.palette.divider}`,
[`&.${accordionDetailsClasses.expanded}`]: {
paddingBlock: '0.75rem',
},
},
})}
>
<Accordion defaultExpanded>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails variant="soft">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails variant="soft">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails variant="soft">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,52 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails, {
accordionDetailsClasses,
} from '@mui/joy/AccordionDetails';
import AccordionSummary, {
accordionSummaryClasses,
} from '@mui/joy/AccordionSummary';
export default function AccordionDepthPanel() {
return (
<AccordionGroup
variant="outlined"
transition="0.2s"
sx={(theme) => ({
maxWidth: 400,
borderRadius: 'lg',
[`& .${accordionSummaryClasses.button}:hover`]: {
bgcolor: 'transparent',
},
[`& .${accordionDetailsClasses.content}`]: {
boxShadow: `inset 0 1px ${theme.vars.palette.divider}`,
[`&.${accordionDetailsClasses.expanded}`]: {
paddingBlock: '0.75rem',
},
},
})}
>
<Accordion defaultExpanded>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails variant="soft">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails variant="soft">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails variant="soft">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,32 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionDisabled() {
return (
<AccordionGroup sx={{ maxWidth: 400 }}>
<Accordion disabled>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion disabled>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion expanded disabled>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,32 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionDisabled() {
return (
<AccordionGroup sx={{ maxWidth: 400 }}>
<Accordion disabled>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion disabled>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion expanded disabled>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,153 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails, {
accordionDetailsClasses,
} from '@mui/joy/AccordionDetails';
import AccordionSummary, {
accordionSummaryClasses,
} from '@mui/joy/AccordionSummary';
import Switch from '@mui/joy/Switch';
import Stack from '@mui/joy/Stack';
import Typography from '@mui/joy/Typography';
import Avatar from '@mui/joy/Avatar';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
import ListItemContent from '@mui/joy/ListItemContent';
import AirplanemodeActiveRoundedIcon from '@mui/icons-material/AirplanemodeActiveRounded';
import WifiRoundedIcon from '@mui/icons-material/WifiRounded';
import BluetoothRoundedIcon from '@mui/icons-material/BluetoothRounded';
import TapAndPlayRoundedIcon from '@mui/icons-material/TapAndPlayRounded';
import EditNotificationsRoundedIcon from '@mui/icons-material/EditNotificationsRounded';
import AdUnitsRoundedIcon from '@mui/icons-material/AdUnitsRounded';
import MessageRoundedIcon from '@mui/icons-material/MessageRounded';
import EmailRoundedIcon from '@mui/icons-material/EmailRounded';
import AccessibilityNewRoundedIcon from '@mui/icons-material/AccessibilityNewRounded';
import ZoomInRoundedIcon from '@mui/icons-material/ZoomInRounded';
import SpatialTrackingRoundedIcon from '@mui/icons-material/SpatialTrackingRounded';
import SettingsVoiceRoundedIcon from '@mui/icons-material/SettingsVoiceRounded';
export default function AccordionFilter() {
return (
<AccordionGroup
variant="plain"
transition="0.2s"
sx={{
maxWidth: 400,
borderRadius: 'md',
[`& .${accordionDetailsClasses.content}.${accordionDetailsClasses.expanded}`]:
{
paddingBlock: '1rem',
},
[`& .${accordionSummaryClasses.button}`]: {
paddingBlock: '1rem',
},
}}
>
<Accordion>
<AccordionSummary>
<Avatar color="primary">
<TapAndPlayRoundedIcon />
</Avatar>
<ListItemContent>
<Typography level="title-md">Connections</Typography>
<Typography level="body-sm">
Activate or deactivate your connections
</Typography>
</ListItemContent>
</AccordionSummary>
<AccordionDetails>
<Stack spacing={1.5}>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<AirplanemodeActiveRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Airplane Mode</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<WifiRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Wi-Fi</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<BluetoothRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Bluetooth</FormLabel>
<Switch size="sm" />
</FormControl>
</Stack>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>
<Avatar color="success">
<EditNotificationsRoundedIcon />
</Avatar>
<ListItemContent>
<Typography level="title-md">Notifications</Typography>
<Typography level="body-sm">
Enable or disable your notifications
</Typography>
</ListItemContent>
</AccordionSummary>
<AccordionDetails>
<Stack spacing={1.5}>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<EmailRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>E-mail</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<MessageRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Messages</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<AdUnitsRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Push</FormLabel>
<Switch size="sm" />
</FormControl>
</Stack>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>
<Avatar color="danger">
<AccessibilityNewRoundedIcon />
</Avatar>
<ListItemContent>
<Typography level="title-md">Accessibility</Typography>
<Typography level="body-sm">
Toggle your accessibility settings
</Typography>
</ListItemContent>
</AccordionSummary>
<AccordionDetails>
<Stack spacing={1.5}>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<ZoomInRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Zoom</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<SpatialTrackingRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Audio Descriptions</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<SettingsVoiceRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Voice Control</FormLabel>
<Switch size="sm" />
</FormControl>
</Stack>
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,153 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails, {
accordionDetailsClasses,
} from '@mui/joy/AccordionDetails';
import AccordionSummary, {
accordionSummaryClasses,
} from '@mui/joy/AccordionSummary';
import Switch from '@mui/joy/Switch';
import Stack from '@mui/joy/Stack';
import Typography from '@mui/joy/Typography';
import Avatar from '@mui/joy/Avatar';
import FormControl from '@mui/joy/FormControl';
import FormLabel from '@mui/joy/FormLabel';
import ListItemContent from '@mui/joy/ListItemContent';
import AirplanemodeActiveRoundedIcon from '@mui/icons-material/AirplanemodeActiveRounded';
import WifiRoundedIcon from '@mui/icons-material/WifiRounded';
import BluetoothRoundedIcon from '@mui/icons-material/BluetoothRounded';
import TapAndPlayRoundedIcon from '@mui/icons-material/TapAndPlayRounded';
import EditNotificationsRoundedIcon from '@mui/icons-material/EditNotificationsRounded';
import AdUnitsRoundedIcon from '@mui/icons-material/AdUnitsRounded';
import MessageRoundedIcon from '@mui/icons-material/MessageRounded';
import EmailRoundedIcon from '@mui/icons-material/EmailRounded';
import AccessibilityNewRoundedIcon from '@mui/icons-material/AccessibilityNewRounded';
import ZoomInRoundedIcon from '@mui/icons-material/ZoomInRounded';
import SpatialTrackingRoundedIcon from '@mui/icons-material/SpatialTrackingRounded';
import SettingsVoiceRoundedIcon from '@mui/icons-material/SettingsVoiceRounded';
export default function AccordionFilter() {
return (
<AccordionGroup
variant="plain"
transition="0.2s"
sx={{
maxWidth: 400,
borderRadius: 'md',
[`& .${accordionDetailsClasses.content}.${accordionDetailsClasses.expanded}`]:
{
paddingBlock: '1rem',
},
[`& .${accordionSummaryClasses.button}`]: {
paddingBlock: '1rem',
},
}}
>
<Accordion>
<AccordionSummary>
<Avatar color="primary">
<TapAndPlayRoundedIcon />
</Avatar>
<ListItemContent>
<Typography level="title-md">Connections</Typography>
<Typography level="body-sm">
Activate or deactivate your connections
</Typography>
</ListItemContent>
</AccordionSummary>
<AccordionDetails>
<Stack spacing={1.5}>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<AirplanemodeActiveRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Airplane Mode</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<WifiRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Wi-Fi</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<BluetoothRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Bluetooth</FormLabel>
<Switch size="sm" />
</FormControl>
</Stack>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>
<Avatar color="success">
<EditNotificationsRoundedIcon />
</Avatar>
<ListItemContent>
<Typography level="title-md">Notifications</Typography>
<Typography level="body-sm">
Enable or disable your notifications
</Typography>
</ListItemContent>
</AccordionSummary>
<AccordionDetails>
<Stack spacing={1.5}>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<EmailRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>E-mail</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<MessageRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Messages</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<AdUnitsRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Push</FormLabel>
<Switch size="sm" />
</FormControl>
</Stack>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>
<Avatar color="danger">
<AccessibilityNewRoundedIcon />
</Avatar>
<ListItemContent>
<Typography level="title-md">Accessibility</Typography>
<Typography level="body-sm">
Toggle your accessibility settings
</Typography>
</ListItemContent>
</AccordionSummary>
<AccordionDetails>
<Stack spacing={1.5}>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<ZoomInRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Zoom</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<SpatialTrackingRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Audio Descriptions</FormLabel>
<Switch size="sm" />
</FormControl>
<FormControl orientation="horizontal" sx={{ gap: 1 }}>
<SettingsVoiceRoundedIcon fontSize="xl2" sx={{ mx: 1 }} />
<FormLabel>Voice Control</FormLabel>
<Switch size="sm" />
</FormControl>
</Stack>
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,45 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary, {
accordionSummaryClasses,
} from '@mui/joy/AccordionSummary';
import AddIcon from '@mui/icons-material/Add';
export default function AccordionIndicator() {
return (
<AccordionGroup
sx={{
maxWidth: 400,
[`& .${accordionSummaryClasses.indicator}`]: {
transition: '0.2s',
},
[`& [aria-expanded="true"] .${accordionSummaryClasses.indicator}`]: {
transform: 'rotate(45deg)',
},
}}
>
<Accordion>
<AccordionSummary indicator={<AddIcon />}>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary indicator={<AddIcon />}>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary indicator={<AddIcon />}>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,45 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary, {
accordionSummaryClasses,
} from '@mui/joy/AccordionSummary';
import AddIcon from '@mui/icons-material/Add';
export default function AccordionIndicator() {
return (
<AccordionGroup
sx={{
maxWidth: 400,
[`& .${accordionSummaryClasses.indicator}`]: {
transition: '0.2s',
},
[`& [aria-expanded="true"] .${accordionSummaryClasses.indicator}`]: {
transform: 'rotate(45deg)',
},
}}
>
<Accordion>
<AccordionSummary indicator={<AddIcon />}>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary indicator={<AddIcon />}>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary indicator={<AddIcon />}>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,32 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionNoDivider() {
return (
<AccordionGroup disableDivider sx={{ maxWidth: 400 }}>
<Accordion>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,32 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionNoDivider() {
return (
<AccordionGroup disableDivider sx={{ maxWidth: 400 }}>
<Accordion>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
import Button from '@mui/joy/Button';
import ToggleButtonGroup from '@mui/joy/ToggleButtonGroup';
import Stack from '@mui/joy/Stack';
export default function AccordionSizes() {
const [size, setSize] = React.useState('md');
return (
<Stack spacing={2} sx={{ maxWidth: 400, flex: 1 }}>
<ToggleButtonGroup
size="sm"
buttonFlex={1}
value={size}
onChange={(event, newValue) => setSize(newValue || size)}
>
<Button value="sm">Small</Button>
<Button value="md">Medium</Button>
<Button value="lg">Large</Button>
</ToggleButtonGroup>
<AccordionGroup size={size}>
<Accordion>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
</Stack>
);
}

View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import AccordionGroup, { AccordionGroupProps } from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
import Button from '@mui/joy/Button';
import ToggleButtonGroup from '@mui/joy/ToggleButtonGroup';
import Stack from '@mui/joy/Stack';
export default function AccordionSizes() {
const [size, setSize] = React.useState<AccordionGroupProps['size']>('md');
return (
<Stack spacing={2} sx={{ maxWidth: 400, flex: 1 }}>
<ToggleButtonGroup
size="sm"
buttonFlex={1}
value={size}
onChange={(event, newValue) => setSize(newValue || size)}
>
<Button value="sm">Small</Button>
<Button value="md">Medium</Button>
<Button value="lg">Large</Button>
</ToggleButtonGroup>
<AccordionGroup size={size}>
<Accordion>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
</Stack>
);
}

View File

@@ -0,0 +1,56 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion, { accordionClasses } from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionStylingExpansion() {
return (
<AccordionGroup
sx={(theme) => ({
maxWidth: 400,
[`& .${accordionClasses.root}`]: {
marginTop: '0.5rem',
transition: '0.2s ease',
'& button:not([aria-expanded="true"])': {
transition: '0.2s ease',
paddingBottom: '0.625rem',
},
'& button:hover': {
background: 'transparent',
},
},
[`& .${accordionClasses.root}.${accordionClasses.expanded}`]: {
bgcolor: 'background.level1',
borderRadius: 'md',
borderBottom: '1px solid',
borderColor: 'background.level2',
},
'& [aria-expanded="true"]': {
boxShadow: `inset 0 -1px 0 ${theme.vars.palette.divider}`,
},
})}
>
<Accordion>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,56 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion, { accordionClasses } from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
export default function AccordionStylingExpansion() {
return (
<AccordionGroup
sx={(theme) => ({
maxWidth: 400,
[`& .${accordionClasses.root}`]: {
marginTop: '0.5rem',
transition: '0.2s ease',
'& button:not([aria-expanded="true"])': {
transition: '0.2s ease',
paddingBottom: '0.625rem',
},
'& button:hover': {
background: 'transparent',
},
},
[`& .${accordionClasses.root}.${accordionClasses.expanded}`]: {
bgcolor: 'background.level1',
borderRadius: 'md',
borderBottom: '1px solid',
borderColor: 'background.level2',
},
'& [aria-expanded="true"]': {
boxShadow: `inset 0 -1px 0 ${theme.vars.palette.divider}`,
},
})}
>
<Accordion>
<AccordionSummary>First accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Second accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>Third accordion</AccordionSummary>
<AccordionDetails>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</AccordionDetails>
</Accordion>
</AccordionGroup>
);
}

View File

@@ -0,0 +1,85 @@
import * as React from 'react';
import Box from '@mui/joy/Box';
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
import RadioGroup from '@mui/joy/RadioGroup';
import Radio from '@mui/joy/Radio';
import Stack from '@mui/joy/Stack';
import Typography from '@mui/joy/Typography';
import { HighlightedCode } from '@mui/docs/HighlightedCode';
import { BrandingProvider } from '@mui/docs/branding';
export default function AccordionTransition() {
const [transition, setTransition] = React.useState('0.2s ease');
return (
<Stack spacing={2} sx={{ alignItems: 'center', flex: 1 }}>
<RadioGroup
orientation="horizontal"
value={transition}
onChange={(event) => {
setTransition(event.target.value);
}}
>
<Radio value="0.2s ease" label="Easing" />
<Radio value="mix" label="Mix" />
</RadioGroup>
<AccordionGroup
transition={
transition === 'mix'
? {
initial: '0.3s ease-out',
expanded: '0.2s ease',
}
: transition
}
sx={{ maxWidth: 400 }}
>
<Accordion>
<AccordionSummary>📖 How to animate the panel?</AccordionSummary>
<AccordionDetails>
<Typography>
The AccordionGroup supports the <code>transition</code> prop to
customize the animation of the panel. You can provide a <b>string</b>{' '}
value or an <b>object</b> to fine tune the animation at the initial and
expanded states.
</Typography>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>🤔 Does it work with dynamic height?</AccordionSummary>
<AccordionDetails>
<Typography>
Absolutely yes! an by the way, it is <b>pure CSS</b>.
</Typography>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary>🪄 What kind of magic this is?</AccordionSummary>
<AccordionDetails>
<Typography>
The panel is a <b>CSS Grid</b> which can be transitioned by the{' '}
<code>grid-template-rows</code> property.
</Typography>
</AccordionDetails>
</Accordion>
</AccordionGroup>
<Box sx={{ width: '100%' }}>
<BrandingProvider>
<HighlightedCode
code={`<AccordionGroup transition=${
transition === 'mix'
? `{{
initial: "0.3s ease-out",
expanded: "0.2s ease",
}}`
: `"${transition}"`
}>`}
language="jsx"
/>
</BrandingProvider>
</Box>
</Stack>
);
}

View File

@@ -0,0 +1,142 @@
import AccordionGroup from '@mui/joy/AccordionGroup';
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionSummary from '@mui/joy/AccordionSummary';
import JoyUsageDemo from 'docs/src/modules/components/JoyUsageDemo';
export default function AccordionUsage() {
return (
<JoyUsageDemo
componentName="AccordionGroup"
data={[
{
propName: 'variant',
knob: 'radio',
defaultValue: 'plain',
options: ['plain', 'outlined', 'soft', 'solid'],
},
{
propName: 'color',
knob: 'color',
defaultValue: 'neutral',
},
{
propName: 'size',
knob: 'radio',
options: ['sm', 'md', 'lg'],
defaultValue: 'md',
},
{
propName: 'disabled',
knob: 'switch',
defaultValue: false,
codeBlockDisplay: false,
},
{
propName: 'children',
defaultValue: `$children`,
},
]}
getCodeBlock={(code, props) =>
code.replace(
'$children',
`<Accordion${props.disabled ? ' disabled' : ''}${
props.variant === 'solid'
? ` variant=${props.variant} color=${props.color}`
: ''
}>
<AccordionSummary${
props.variant === 'solid'
? ` variant=${props.variant} color=${props.color}`
: ''
}>Title</AccordionSummary>
<AccordionDetails${
props.variant === 'solid'
? ` variant=${props.variant} color=${props.color}`
: ''
}>Content</AccordionDetails>
</Accordion>`,
)
}
renderDemo={({ disabled, ...props }) => (
<AccordionGroup
{...props}
sx={{ width: 300, maxWidth: '100%', alignSelf: 'flex-start', mb: 3 }}
>
<Accordion
{...(props.variant === 'solid' && {
variant: 'solid',
color: props.color,
})}
disabled={disabled}
>
<AccordionSummary
{...(props.variant === 'solid' && {
variant: 'solid',
color: props.color,
})}
>
First Header
</AccordionSummary>
<AccordionDetails
{...(props.variant === 'solid' && {
variant: 'solid',
color: props.color,
})}
>
Content of the first accordion.
</AccordionDetails>
</Accordion>
<Accordion
disabled={disabled}
{...(props.variant === 'solid' && {
variant: 'solid',
color: props.color,
})}
>
<AccordionSummary
{...(props.variant === 'solid' && {
variant: 'solid',
color: props.color,
})}
>
Second Header
</AccordionSummary>
<AccordionDetails
{...(props.variant === 'solid' && {
variant: 'solid',
color: props.color,
})}
>
Content of the second accordion.
</AccordionDetails>
</Accordion>
<Accordion
disabled={disabled}
{...(props.variant === 'solid' && {
variant: 'solid',
color: props.color,
})}
>
<AccordionSummary
{...(props.variant === 'solid' && {
variant: 'solid',
color: props.color,
})}
>
Third Header
</AccordionSummary>
<AccordionDetails
{...(props.variant === 'solid' && {
variant: 'solid',
color: props.color,
})}
>
Content of the third accordion.
</AccordionDetails>
</Accordion>
</AccordionGroup>
)}
/>
);
}

View File

@@ -0,0 +1,118 @@
---
productId: joy-ui
title: React Accordion component
components: Accordion, AccordionDetails, AccordionGroup, AccordionSummary
githubLabel: 'scope: accordion'
waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/accordion/
---
# Accordion
<p class="description">Accordions let users show and hide sections of related content on a page.</p>
{{"component": "@mui/docs/ComponentLinkHeader"}}
## Introduction
Joy UI provides four accordion-related components:
- [Accordion Group](#basic-usage) - a container that groups multiple accordions. It **does not** control the state of each accordion.
- [Accordion](#basic-usage) - a component that contains the expansion logic and send to AccordionSummary and AccordionDetails.
- [Accordion Summary](#basic-usage) - a header of the accordion which contain a button that triggers the expansion.
- [Accordion Details](#basic-usage) - a wrapper for the accordion details.
{{"demo": "AccordionUsage.js", "hideToolbar": true, "bg": "gradient"}}
## Basics
```jsx
import Accordion from '@mui/joy/Accordion';
import AccordionDetails from '@mui/joy/AccordionDetails';
import AccordionGroup from '@mui/joy/AccordionGroup';
import AccordionSummary from '@mui/joy/AccordionSummary';
```
{{"demo": "AccordionBasic.js"}}
## Customization
### Sizes
The AccordionGroup component comes in three sizes: `sm`, `md` (default), and `lg`.
{{"demo": "AccordionSizes.js"}}
:::info
To learn how to add custom sizes to the component, check out [Themed components—Extend sizes](/joy-ui/customization/themed-components/#extend-sizes).
:::
### Controlled accordion
Use the `expanded` prop to control the expansion state of the accordion and listen to the expansion event via `onChange` prop.
{{"demo": "AccordionControlled.js"}}
### Disabled
Use the `disabled` prop to disable the accordion trigger.
{{"demo": "AccordionDisabled.js"}}
:::info
Note: the `disabled` prop only disables the accordion trigger, not the accordion content.
:::
### Removing divider
Use `disableDivider` prop on the Accordion Group component to hide the divider between accordions.
{{"demo": "AccordionNoDivider.js"}}
:::info
**Good to know**: the reason that ListDivider can be used is because the accordion family reuses styles from the [List](/joy-ui/react-list/) family.
:::
### Animating the expansion
Use `transition` prop to animate the expansion. The value can be a **string** if you want the transition to be the same for initial and expanded states, or an **object** if you want to customize the transition for each state.
The object value can contain the following keys:
- `initial`: the transition when the accordion is closed
- `expanded`: the transition when the accordion is open
{{"demo": "AccordionTransition.js", "hideToolbar": true}}
### Indicator
Use `indicator` prop to customize the indicator of the accordion.
{{"demo": "AccordionIndicator.js"}}
### Styling on expansion
Use `sx` prop on the AccordionGroup to style all the accordions at once.
{{"demo": "AccordionStylingExpansion.js"}}
## Common examples
### Depth panel
This example shows how to customize the accordion to create lines and depth to make it look more realistic.
{{"demo": "AccordionDepthPanel.js"}}
### User settings
This example shows how to customize the accordion and craft diverse compositions using additional components.
{{"demo": "AccordionFilter.js"}}
## Accessibility
The built-in accessibility of the accordion follows [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).
- The accordion summary has a root slot (`div`) that can be changed, for example using `h3`, based on the hierarchy of the accordion.
- The accordion summary contains a button with `aria-expanded` and `aria-controls` attributes.
- The accordion details contains a div with `role="region"` and `aria-labelledby` attributes.

View File

@@ -0,0 +1,10 @@
import Box from '@mui/joy/Box';
import Alert from '@mui/joy/Alert';
export default function AlertBasic() {
return (
<Box sx={{ width: '100%' }}>
<Alert>This is a basic Alert.</Alert>
</Box>
);
}

View File

@@ -0,0 +1,10 @@
import Box from '@mui/joy/Box';
import Alert from '@mui/joy/Alert';
export default function AlertBasic() {
return (
<Box sx={{ width: '100%' }}>
<Alert>This is a basic Alert.</Alert>
</Box>
);
}

View File

@@ -0,0 +1 @@
<Alert>This is a basic Alert.</Alert>

View File

@@ -0,0 +1,63 @@
import * as React from 'react';
import Alert from '@mui/joy/Alert';
import Stack from '@mui/joy/Stack';
import Box from '@mui/joy/Box';
import Radio from '@mui/joy/Radio';
import RadioGroup from '@mui/joy/RadioGroup';
import Sheet from '@mui/joy/Sheet';
import Typography from '@mui/joy/Typography';
export default function AlertColors() {
const [variant, setVariant] = React.useState('solid');
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
}}
>
<Stack spacing={1} sx={{ width: '100%', maxWidth: 400 }}>
<Alert variant={variant} color="primary">
Primary
</Alert>
<Alert variant={variant} color="neutral">
Neutral
</Alert>
<Alert variant={variant} color="danger">
Danger
</Alert>
<Alert variant={variant} color="success">
Success
</Alert>
<Alert variant={variant} color="warning">
Warning
</Alert>
</Stack>
<Sheet sx={{ pl: 4, ml: 3, borderLeft: '1px solid', borderColor: 'divider' }}>
<Typography
level="body-sm"
id="variant-label"
textColor="text.primary"
sx={{ fontWeight: 'xl', mb: 1 }}
>
Variant:
</Typography>
<RadioGroup
size="sm"
aria-labelledby="variant-label"
name="variant"
value={variant}
onChange={(event) => setVariant(event.target.value)}
>
<Radio label="Solid" value="solid" />
<Radio label="Soft" value="soft" />
<Radio label="Outlined" value="outlined" />
<Radio label="Plain" value="plain" />
</RadioGroup>
</Sheet>
</Box>
);
}

View File

@@ -0,0 +1,63 @@
import * as React from 'react';
import Alert from '@mui/joy/Alert';
import Stack from '@mui/joy/Stack';
import Box from '@mui/joy/Box';
import Radio from '@mui/joy/Radio';
import RadioGroup from '@mui/joy/RadioGroup';
import Sheet from '@mui/joy/Sheet';
import { VariantProp } from '@mui/joy/styles';
import Typography from '@mui/joy/Typography';
export default function AlertColors() {
const [variant, setVariant] = React.useState<VariantProp>('solid');
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
}}
>
<Stack spacing={1} sx={{ width: '100%', maxWidth: 400 }}>
<Alert variant={variant} color="primary">
Primary
</Alert>
<Alert variant={variant} color="neutral">
Neutral
</Alert>
<Alert variant={variant} color="danger">
Danger
</Alert>
<Alert variant={variant} color="success">
Success
</Alert>
<Alert variant={variant} color="warning">
Warning
</Alert>
</Stack>
<Sheet sx={{ pl: 4, ml: 3, borderLeft: '1px solid', borderColor: 'divider' }}>
<Typography
level="body-sm"
id="variant-label"
textColor="text.primary"
sx={{ fontWeight: 'xl', mb: 1 }}
>
Variant:
</Typography>
<RadioGroup
size="sm"
aria-labelledby="variant-label"
name="variant"
value={variant}
onChange={(event) => setVariant(event.target.value as VariantProp)}
>
<Radio label="Solid" value="solid" />
<Radio label="Soft" value="soft" />
<Radio label="Outlined" value="outlined" />
<Radio label="Plain" value="plain" />
</RadioGroup>
</Sheet>
</Box>
);
}

View File

@@ -0,0 +1,97 @@
import Alert from '@mui/joy/Alert';
import AspectRatio from '@mui/joy/AspectRatio';
import IconButton from '@mui/joy/IconButton';
import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';
import CircularProgress from '@mui/joy/CircularProgress';
import LinearProgress from '@mui/joy/LinearProgress';
import Stack from '@mui/joy/Stack';
import Typography from '@mui/joy/Typography';
import Check from '@mui/icons-material/Check';
import Close from '@mui/icons-material/Close';
import Warning from '@mui/icons-material/Warning';
export default function AlertInvertedColors() {
return (
<Stack spacing={2} sx={{ maxWidth: 400 }}>
<Alert
size="lg"
color="success"
variant="solid"
invertedColors
startDecorator={
<AspectRatio
variant="solid"
ratio="1"
sx={{
minWidth: 40,
borderRadius: '50%',
boxShadow: '0 2px 12px 0 rgb(0 0 0/0.2)',
}}
>
<div>
<Check fontSize="xl2" />
</div>
</AspectRatio>
}
endDecorator={
<IconButton
variant="plain"
sx={{
'--IconButton-size': '32px',
transform: 'translate(0.5rem, -0.5rem)',
}}
>
<Close />
</IconButton>
}
sx={{ alignItems: 'flex-start', overflow: 'hidden' }}
>
<div>
<Typography level="title-lg">Success</Typography>
<Typography level="body-sm">
Success is walking from failure to failure with no loss of enthusiasm.
</Typography>
</div>
<LinearProgress
variant="solid"
color="success"
value={40}
sx={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
borderRadius: 0,
}}
/>
</Alert>
<Alert
variant="soft"
color="danger"
invertedColors
startDecorator={
<CircularProgress size="lg" color="danger">
<Warning />
</CircularProgress>
}
sx={{ alignItems: 'flex-start', gap: '1rem' }}
>
<Box sx={{ flex: 1 }}>
<Typography level="title-md">Lost connection</Typography>
<Typography level="body-md">
Please verify your network connection and try again.
</Typography>
<Box sx={{ mt: 2, display: 'flex', justifyContent: 'flex-end', gap: 1 }}>
<Button variant="outlined" size="sm">
Open network settings
</Button>
<Button variant="solid" size="sm">
Try again
</Button>
</Box>
</Box>
</Alert>
</Stack>
);
}

View File

@@ -0,0 +1,97 @@
import Alert from '@mui/joy/Alert';
import AspectRatio from '@mui/joy/AspectRatio';
import IconButton from '@mui/joy/IconButton';
import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';
import CircularProgress from '@mui/joy/CircularProgress';
import LinearProgress from '@mui/joy/LinearProgress';
import Stack from '@mui/joy/Stack';
import Typography from '@mui/joy/Typography';
import Check from '@mui/icons-material/Check';
import Close from '@mui/icons-material/Close';
import Warning from '@mui/icons-material/Warning';
export default function AlertInvertedColors() {
return (
<Stack spacing={2} sx={{ maxWidth: 400 }}>
<Alert
size="lg"
color="success"
variant="solid"
invertedColors
startDecorator={
<AspectRatio
variant="solid"
ratio="1"
sx={{
minWidth: 40,
borderRadius: '50%',
boxShadow: '0 2px 12px 0 rgb(0 0 0/0.2)',
}}
>
<div>
<Check fontSize="xl2" />
</div>
</AspectRatio>
}
endDecorator={
<IconButton
variant="plain"
sx={{
'--IconButton-size': '32px',
transform: 'translate(0.5rem, -0.5rem)',
}}
>
<Close />
</IconButton>
}
sx={{ alignItems: 'flex-start', overflow: 'hidden' }}
>
<div>
<Typography level="title-lg">Success</Typography>
<Typography level="body-sm">
Success is walking from failure to failure with no loss of enthusiasm.
</Typography>
</div>
<LinearProgress
variant="solid"
color="success"
value={40}
sx={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
borderRadius: 0,
}}
/>
</Alert>
<Alert
variant="soft"
color="danger"
invertedColors
startDecorator={
<CircularProgress size="lg" color="danger">
<Warning />
</CircularProgress>
}
sx={{ alignItems: 'flex-start', gap: '1rem' }}
>
<Box sx={{ flex: 1 }}>
<Typography level="title-md">Lost connection</Typography>
<Typography level="body-md">
Please verify your network connection and try again.
</Typography>
<Box sx={{ mt: 2, display: 'flex', justifyContent: 'flex-end', gap: 1 }}>
<Button variant="outlined" size="sm">
Open network settings
</Button>
<Button variant="solid" size="sm">
Try again
</Button>
</Box>
</Box>
</Alert>
</Stack>
);
}

View File

@@ -0,0 +1,12 @@
import Box from '@mui/joy/Box';
import Alert from '@mui/joy/Alert';
export default function AlertSizes() {
return (
<Box sx={{ display: 'flex', gap: 2, width: '100%', flexDirection: 'column' }}>
<Alert size="sm">This is a small Alert.</Alert>
<Alert size="md">This is a medium Alert.</Alert>
<Alert size="lg">This is a large Alert.</Alert>
</Box>
);
}

View File

@@ -0,0 +1,12 @@
import Box from '@mui/joy/Box';
import Alert from '@mui/joy/Alert';
export default function AlertSizes() {
return (
<Box sx={{ display: 'flex', gap: 2, width: '100%', flexDirection: 'column' }}>
<Alert size="sm">This is a small Alert.</Alert>
<Alert size="md">This is a medium Alert.</Alert>
<Alert size="lg">This is a large Alert.</Alert>
</Box>
);
}

View File

@@ -0,0 +1,3 @@
<Alert size="sm">This is a small Alert.</Alert>
<Alert size="md">This is a medium Alert.</Alert>
<Alert size="lg">This is a large Alert.</Alert>

View File

@@ -0,0 +1,32 @@
import Alert from '@mui/joy/Alert';
import JoyUsageDemo from 'docs/src/modules/components/JoyUsageDemo';
export default function AlertUsage() {
return (
<JoyUsageDemo
componentName="Alert"
data={[
{
propName: 'variant',
knob: 'radio',
defaultValue: 'soft',
options: ['plain', 'outlined', 'soft', 'solid'],
},
{
propName: 'color',
knob: 'color',
defaultValue: 'neutral',
},
{
propName: 'size',
knob: 'radio',
options: ['sm', 'md', 'lg'],
defaultValue: 'md',
},
]}
renderDemo={(props) => (
<Alert {...props}>This is a Joy UI Alert check it out!</Alert>
)}
/>
);
}

View File

@@ -0,0 +1,13 @@
import Box from '@mui/joy/Box';
import Alert from '@mui/joy/Alert';
export default function AlertVariants() {
return (
<Box sx={{ display: 'flex', gap: 2, width: '100%', flexDirection: 'column' }}>
<Alert variant="solid">This is an Alert using the solid variant.</Alert>
<Alert variant="soft">This is an Alert using the soft variant.</Alert>
<Alert variant="outlined">This is an Alert using the outlined variant.</Alert>
<Alert variant="plain">This is an Alert using the plain variant.</Alert>
</Box>
);
}

View File

@@ -0,0 +1,13 @@
import Box from '@mui/joy/Box';
import Alert from '@mui/joy/Alert';
export default function AlertVariants() {
return (
<Box sx={{ display: 'flex', gap: 2, width: '100%', flexDirection: 'column' }}>
<Alert variant="solid">This is an Alert using the solid variant.</Alert>
<Alert variant="soft">This is an Alert using the soft variant.</Alert>
<Alert variant="outlined">This is an Alert using the outlined variant.</Alert>
<Alert variant="plain">This is an Alert using the plain variant.</Alert>
</Box>
);
}

View File

@@ -0,0 +1,4 @@
<Alert variant="solid">This is an Alert using the solid variant.</Alert>
<Alert variant="soft">This is an Alert using the soft variant.</Alert>
<Alert variant="outlined">This is an Alert using the outlined variant.</Alert>
<Alert variant="plain">This is an Alert using the plain variant.</Alert>

Some files were not shown because too many files have changed in this diff Show More