@julianoczkowski/create-trimble-app 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +191 -0
- package/bin/create-trimble-app.js +9 -0
- package/package.json +67 -0
- package/src/cli.js +134 -0
- package/src/frameworks.js +28 -0
- package/src/scaffold.js +209 -0
- package/src/utils/file.js +47 -0
- package/src/utils/git.js +140 -0
- package/src/utils/install.js +25 -0
- package/src/utils/logger.js +124 -0
- package/templates/angular/.cursor/commands/remove-dev-content.md +394 -0
- package/templates/angular/.cursor/mcp.json +13 -0
- package/templates/angular/.cursor/rules/modus-angular-20.mdc +82 -0
- package/templates/angular/.cursor/rules/modus-angular-accordion-state-management-short.mdc +45 -0
- package/templates/angular/.cursor/rules/modus-angular-accordion-state-management.mdc +322 -0
- package/templates/angular/.cursor/rules/modus-angular-best-practices.mdc +472 -0
- package/templates/angular/.cursor/rules/modus-angular-border-usage-short.mdc +48 -0
- package/templates/angular/.cursor/rules/modus-angular-border-usage.mdc +286 -0
- package/templates/angular/.cursor/rules/modus-angular-button-group-usage-short.mdc +47 -0
- package/templates/angular/.cursor/rules/modus-angular-button-group-usage.mdc +263 -0
- package/templates/angular/.cursor/rules/modus-angular-checkbox-value-inversion-short.mdc +36 -0
- package/templates/angular/.cursor/rules/modus-angular-checkbox-value-inversion.mdc +92 -0
- package/templates/angular/.cursor/rules/modus-angular-chrome-devtools-testing-short.mdc +34 -0
- package/templates/angular/.cursor/rules/modus-angular-chrome-devtools-testing.mdc +185 -0
- package/templates/angular/.cursor/rules/modus-angular-color-usage-short.mdc +56 -0
- package/templates/angular/.cursor/rules/modus-angular-color-usage.mdc +208 -0
- package/templates/angular/.cursor/rules/modus-angular-components-reference.mdc +114 -0
- package/templates/angular/.cursor/rules/modus-angular-design-system.mdc +273 -0
- package/templates/angular/.cursor/rules/modus-angular-development-workflow-short.mdc +43 -0
- package/templates/angular/.cursor/rules/modus-angular-development-workflow.mdc +145 -0
- package/templates/angular/.cursor/rules/modus-angular-essentials.mdc +272 -0
- package/templates/angular/.cursor/rules/modus-angular-forms-validation-short.mdc +56 -0
- package/templates/angular/.cursor/rules/modus-angular-forms-validation.mdc +124 -0
- package/templates/angular/.cursor/rules/modus-angular-icon-names.mdc +70 -0
- package/templates/angular/.cursor/rules/modus-angular-icons-short.mdc +40 -0
- package/templates/angular/.cursor/rules/modus-angular-icons.mdc +137 -0
- package/templates/angular/.cursor/rules/modus-angular-implementation-guides-short.mdc +36 -0
- package/templates/angular/.cursor/rules/modus-angular-implementation-guides.mdc +301 -0
- package/templates/angular/.cursor/rules/modus-angular-integration-short.mdc +60 -0
- package/templates/angular/.cursor/rules/modus-angular-integration.mdc +1096 -0
- package/templates/angular/.cursor/rules/modus-angular-master.mdc +164 -0
- package/templates/angular/.cursor/rules/modus-angular-modal-usage-short.mdc +51 -0
- package/templates/angular/.cursor/rules/modus-angular-modal-usage.mdc +115 -0
- package/templates/angular/.cursor/rules/modus-angular-navbar-usage-short.mdc +49 -0
- package/templates/angular/.cursor/rules/modus-angular-navbar-usage.mdc +146 -0
- package/templates/angular/.cursor/rules/modus-angular-no-emojis.mdc +66 -0
- package/templates/angular/.cursor/rules/modus-angular-opacity-utilities-short.mdc +43 -0
- package/templates/angular/.cursor/rules/modus-angular-opacity-utilities.mdc +160 -0
- package/templates/angular/.cursor/rules/modus-angular-select-vs-dropdown-menu-short.mdc +51 -0
- package/templates/angular/.cursor/rules/modus-angular-select-vs-dropdown-menu.mdc +83 -0
- package/templates/angular/.cursor/rules/modus-angular-semantic-html-short.mdc +36 -0
- package/templates/angular/.cursor/rules/modus-angular-semantic-html.mdc +81 -0
- package/templates/angular/.cursor/rules/modus-angular-side-navigation-usage-short.mdc +50 -0
- package/templates/angular/.cursor/rules/modus-angular-side-navigation-usage.mdc +136 -0
- package/templates/angular/.cursor/rules/modus-angular-table-usage-short.mdc +52 -0
- package/templates/angular/.cursor/rules/modus-angular-table-usage.mdc +151 -0
- package/templates/angular/.cursor/rules/modus-angular-tailwind-usage-short.mdc +44 -0
- package/templates/angular/.cursor/rules/modus-angular-tailwind-usage.mdc +242 -0
- package/templates/angular/.cursor/rules/modus-angular-themes-short.mdc +54 -0
- package/templates/angular/.cursor/rules/modus-angular-themes.mdc +222 -0
- package/templates/angular/.cursor/rules/modus-angular-utility-panel-usage-short.mdc +52 -0
- package/templates/angular/.cursor/rules/modus-angular-utility-panel-usage.mdc +130 -0
- package/templates/angular/.cursor/rules/ux/gestalt-laws-detailed.mdc +514 -0
- package/templates/angular/.cursor/rules/ux/ux-ui-foundations.mdc +235 -0
- package/templates/angular/.cursor/skills/run-lint-checks/SKILL.md +169 -0
- package/templates/angular/.editorconfig +17 -0
- package/templates/angular/.github/dependabot.yml +19 -0
- package/templates/angular/.github/workflows/a11y-check.yml +135 -0
- package/templates/angular/.github/workflows/ci.yml +44 -0
- package/templates/angular/.husky/pre-commit +32 -0
- package/templates/angular/.postcssrc.json +5 -0
- package/templates/angular/.vscode/extensions.json +4 -0
- package/templates/angular/.vscode/launch.json +20 -0
- package/templates/angular/.vscode/tasks.json +42 -0
- package/templates/angular/CLAUDE.md +148 -0
- package/templates/angular/README.md +92 -0
- package/templates/angular/amplify.yml +25 -0
- package/templates/angular/angular.json +106 -0
- package/templates/angular/data/modusIcons.ts +861 -0
- package/templates/angular/package-lock.json +11030 -0
- package/templates/angular/package.json +66 -0
- package/templates/angular/public/angular-icon.svg +12 -0
- package/templates/angular/public/favicon.ico +0 -0
- package/templates/angular/public/modus-icons.css +49 -0
- package/templates/angular/public/vite.svg +1 -0
- package/templates/angular/scripts/README.md +410 -0
- package/templates/angular/scripts/check-border-violations.js +352 -0
- package/templates/angular/scripts/check-icon-names.js +402 -0
- package/templates/angular/scripts/check-inline-styles.js +292 -0
- package/templates/angular/scripts/check-modus-colors.js +282 -0
- package/templates/angular/scripts/check-modus-icons.js +263 -0
- package/templates/angular/scripts/check-opacity-utilities.js +426 -0
- package/templates/angular/scripts/check-semantic-html.js +452 -0
- package/templates/angular/scripts/check-typescript.js +109 -0
- package/templates/angular/src/app/app.config.ts +29 -0
- package/templates/angular/src/app/app.css +0 -0
- package/templates/angular/src/app/app.html +4 -0
- package/templates/angular/src/app/app.routes.ts +351 -0
- package/templates/angular/src/app/app.spec.ts +27 -0
- package/templates/angular/src/app/app.ts +47 -0
- package/templates/angular/src/app/components/README.md +77 -0
- package/templates/angular/src/app/components/index.ts +53 -0
- package/templates/angular/src/app/components/modus-accordion.component.ts +50 -0
- package/templates/angular/src/app/components/modus-alert.component.ts +133 -0
- package/templates/angular/src/app/components/modus-autocomplete.component.ts +262 -0
- package/templates/angular/src/app/components/modus-avatar.component.ts +75 -0
- package/templates/angular/src/app/components/modus-badge.component.ts +84 -0
- package/templates/angular/src/app/components/modus-breadcrumbs.component.ts +65 -0
- package/templates/angular/src/app/components/modus-button-group.component.ts +82 -0
- package/templates/angular/src/app/components/modus-button.component.ts +292 -0
- package/templates/angular/src/app/components/modus-card.component.ts +73 -0
- package/templates/angular/src/app/components/modus-checkbox.component.ts +117 -0
- package/templates/angular/src/app/components/modus-chip.component.ts +97 -0
- package/templates/angular/src/app/components/modus-collapse.component.ts +118 -0
- package/templates/angular/src/app/components/modus-date.component.ts +165 -0
- package/templates/angular/src/app/components/modus-dropdown-menu.component.ts +108 -0
- package/templates/angular/src/app/components/modus-file-dropzone.component.ts +121 -0
- package/templates/angular/src/app/components/modus-handle.component.ts +96 -0
- package/templates/angular/src/app/components/modus-icon.component.ts +81 -0
- package/templates/angular/src/app/components/modus-input-feedback.component.ts +54 -0
- package/templates/angular/src/app/components/modus-input-label.component.ts +62 -0
- package/templates/angular/src/app/components/modus-loader.component.ts +48 -0
- package/templates/angular/src/app/components/modus-logo.component.ts +115 -0
- package/templates/angular/src/app/components/modus-menu-item.component.ts +116 -0
- package/templates/angular/src/app/components/modus-menu.component.ts +58 -0
- package/templates/angular/src/app/components/modus-modal.component.ts +70 -0
- package/templates/angular/src/app/components/modus-navbar.component.ts +303 -0
- package/templates/angular/src/app/components/modus-number-input.component.ts +174 -0
- package/templates/angular/src/app/components/modus-pagination.component.ts +74 -0
- package/templates/angular/src/app/components/modus-panel.component.ts +61 -0
- package/templates/angular/src/app/components/modus-progress.component.ts +62 -0
- package/templates/angular/src/app/components/modus-radio.component.ts +102 -0
- package/templates/angular/src/app/components/modus-rating.component.ts +80 -0
- package/templates/angular/src/app/components/modus-select.component.ts +131 -0
- package/templates/angular/src/app/components/modus-side-navigation.component.ts +90 -0
- package/templates/angular/src/app/components/modus-skeleton.component.ts +54 -0
- package/templates/angular/src/app/components/modus-slider.component.ts +132 -0
- package/templates/angular/src/app/components/modus-stepper.component.ts +65 -0
- package/templates/angular/src/app/components/modus-switch.component.ts +118 -0
- package/templates/angular/src/app/components/modus-table.component.ts +204 -0
- package/templates/angular/src/app/components/modus-tabs.component.ts +82 -0
- package/templates/angular/src/app/components/modus-text-input.component.ts +221 -0
- package/templates/angular/src/app/components/modus-textarea.component.ts +168 -0
- package/templates/angular/src/app/components/modus-theme-switcher.component.ts +45 -0
- package/templates/angular/src/app/components/modus-time-input.component.ts +172 -0
- package/templates/angular/src/app/components/modus-toast.component.ts +63 -0
- package/templates/angular/src/app/components/modus-toolbar.component.ts +43 -0
- package/templates/angular/src/app/components/modus-tooltip.component.ts +83 -0
- package/templates/angular/src/app/components/modus-typography.component.ts +79 -0
- package/templates/angular/src/app/components/modus-utility-panel.component.ts +275 -0
- package/templates/angular/src/app/components/theme-demo.component.ts +1242 -0
- package/templates/angular/src/app/data/component-demos.ts +360 -0
- package/templates/angular/src/app/data/modus-icons.ts +806 -0
- package/templates/angular/src/app/demos/accordion/accordion-demo.component.ts +212 -0
- package/templates/angular/src/app/demos/alert/alert-demo.component.ts +108 -0
- package/templates/angular/src/app/demos/autocomplete/autocomplete-demo.component.ts +174 -0
- package/templates/angular/src/app/demos/avatar/avatar-demo.component.ts +149 -0
- package/templates/angular/src/app/demos/badge/badge-demo.component.ts +148 -0
- package/templates/angular/src/app/demos/breadcrumbs/breadcrumbs-demo.component.ts +96 -0
- package/templates/angular/src/app/demos/button/button-demo.component.ts +256 -0
- package/templates/angular/src/app/demos/button-group/button-group-demo.component.ts +215 -0
- package/templates/angular/src/app/demos/card/card-demo.component.ts +180 -0
- package/templates/angular/src/app/demos/checkbox/checkbox-demo.component.ts +71 -0
- package/templates/angular/src/app/demos/chip/chip-demo.component.ts +183 -0
- package/templates/angular/src/app/demos/date/date-demo.component.ts +193 -0
- package/templates/angular/src/app/demos/dropdown/dropdown-demo.component.ts +196 -0
- package/templates/angular/src/app/demos/file-dropzone/file-dropzone-demo.component.ts +176 -0
- package/templates/angular/src/app/demos/handle/handle-demo.component.ts +265 -0
- package/templates/angular/src/app/demos/icon/icon-demo.component.ts +65 -0
- package/templates/angular/src/app/demos/input-feedback/input-feedback-demo.component.ts +189 -0
- package/templates/angular/src/app/demos/input-label/input-label-demo.component.ts +330 -0
- package/templates/angular/src/app/demos/loader/loader-demo.component.ts +143 -0
- package/templates/angular/src/app/demos/logo/logo-demo.component.ts +191 -0
- package/templates/angular/src/app/demos/menu/menu-demo.component.ts +72 -0
- package/templates/angular/src/app/demos/modal/modal-demo.component.ts +278 -0
- package/templates/angular/src/app/demos/navbar/navbar-demo.component.ts +135 -0
- package/templates/angular/src/app/demos/number-input/number-input-demo.component.ts +175 -0
- package/templates/angular/src/app/demos/pagination/pagination-demo.component.ts +134 -0
- package/templates/angular/src/app/demos/panel/panel-demo.component.ts +235 -0
- package/templates/angular/src/app/demos/progress/progress-demo.component.ts +169 -0
- package/templates/angular/src/app/demos/radio/radio-demo.component.ts +161 -0
- package/templates/angular/src/app/demos/rating/rating-demo.component.ts +97 -0
- package/templates/angular/src/app/demos/select/select-demo.component.ts +107 -0
- package/templates/angular/src/app/demos/shared/demo-example-clean.component.ts +41 -0
- package/templates/angular/src/app/demos/shared/demo-example.component.ts +42 -0
- package/templates/angular/src/app/demos/shared/demo-page.component.ts +97 -0
- package/templates/angular/src/app/demos/shared/index.ts +3 -0
- package/templates/angular/src/app/demos/side-navigation/side-navigation-demo.component.ts +524 -0
- package/templates/angular/src/app/demos/skeleton/skeleton-demo.component.ts +112 -0
- package/templates/angular/src/app/demos/slider/slider-demo.component.ts +76 -0
- package/templates/angular/src/app/demos/stepper/stepper-demo.component.ts +79 -0
- package/templates/angular/src/app/demos/switch/switch-demo.component.ts +113 -0
- package/templates/angular/src/app/demos/table/table-demo.component.ts +405 -0
- package/templates/angular/src/app/demos/tabs/tabs-demo.component.ts +318 -0
- package/templates/angular/src/app/demos/text-input/text-input-demo.component.ts +160 -0
- package/templates/angular/src/app/demos/textarea/textarea-demo.component.ts +95 -0
- package/templates/angular/src/app/demos/theme-switcher/theme-switcher-demo.component.ts +38 -0
- package/templates/angular/src/app/demos/time-input/time-input-demo.component.ts +130 -0
- package/templates/angular/src/app/demos/toast/toast-demo.component.ts +258 -0
- package/templates/angular/src/app/demos/toolbar/toolbar-demo.component.ts +54 -0
- package/templates/angular/src/app/demos/tooltip/tooltip-demo.component.ts +163 -0
- package/templates/angular/src/app/demos/utility-panel/utility-panel-demo.component.ts +197 -0
- package/templates/angular/src/app/dev/dev-config.ts +119 -0
- package/templates/angular/src/app/dev/dev-panel/dev-panel.component.ts +215 -0
- package/templates/angular/src/app/dev/dev-panel.service.ts +63 -0
- package/templates/angular/src/app/dev/index.ts +8 -0
- package/templates/angular/src/app/dev/theme-switcher-dropdown/theme-switcher-dropdown.component.ts +134 -0
- package/templates/angular/src/app/dev-pages/color-palette/color-palette.component.ts +229 -0
- package/templates/angular/src/app/dev-pages/components-gallery/components-gallery.component.ts +486 -0
- package/templates/angular/src/app/dev-pages/icons/icons.component.ts +149 -0
- package/templates/angular/src/app/pages/home/home.component.ts +251 -0
- package/templates/angular/src/app/services/README.md +57 -0
- package/templates/angular/src/app/services/theme.service.ts +163 -0
- package/templates/angular/src/environments/environment.development.ts +8 -0
- package/templates/angular/src/environments/environment.ts +8 -0
- package/templates/angular/src/index.html +14 -0
- package/templates/angular/src/main.ts +6 -0
- package/templates/angular/src/styles.css +1328 -0
- package/templates/angular/tsconfig.app.json +15 -0
- package/templates/angular/tsconfig.json +35 -0
- package/templates/angular/tsconfig.spec.json +14 -0
- package/templates/config.json +23 -0
- package/templates/react/.cursor/commands/remove-dev-content.md +311 -0
- package/templates/react/.cursor/mcp.json +13 -0
- package/templates/react/.cursor/rules/README.md +240 -0
- package/templates/react/.cursor/rules/border-usage-guidelines-short.mdc +22 -0
- package/templates/react/.cursor/rules/border-usage-guidelines.mdc +380 -0
- package/templates/react/.cursor/rules/chrome-devtools-testing-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/development-workflow-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/development-workflow-react.mdc +292 -0
- package/templates/react/.cursor/rules/implementation-guides-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/implementation-guides-react.mdc +446 -0
- package/templates/react/.cursor/rules/modus-accordion-state-management-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-accordion-state-management-react.mdc +445 -0
- package/templates/react/.cursor/rules/modus-button-group-usage-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-button-group-usage-react.mdc +117 -0
- package/templates/react/.cursor/rules/modus-checkbox-value-inversion-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-checkbox-value-inversion-react.mdc +492 -0
- package/templates/react/.cursor/rules/modus-color-usage-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-color-usage-react.mdc +420 -0
- package/templates/react/.cursor/rules/modus-components-reference.mdc +366 -0
- package/templates/react/.cursor/rules/modus-icon-names.mdc +63 -0
- package/templates/react/.cursor/rules/modus-icons-react-short.mdc +24 -0
- package/templates/react/.cursor/rules/modus-icons-react.mdc +402 -0
- package/templates/react/.cursor/rules/modus-modal-implementation-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-modal-implementation-react.mdc +831 -0
- package/templates/react/.cursor/rules/modus-navbar-side-navigation-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-navbar-side-navigation-react.mdc +247 -0
- package/templates/react/.cursor/rules/modus-no-emojis-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-opacity-utilities-react-short.mdc +70 -0
- package/templates/react/.cursor/rules/modus-opacity-utilities-react.mdc +208 -0
- package/templates/react/.cursor/rules/modus-react-best-practices-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-react-best-practices.mdc +508 -0
- package/templates/react/.cursor/rules/modus-react-essentials.mdc +209 -0
- package/templates/react/.cursor/rules/modus-react-integration-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-react-integration.mdc +509 -0
- package/templates/react/.cursor/rules/modus-react-key-warnings-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-react-key-warnings.mdc +805 -0
- package/templates/react/.cursor/rules/modus-react-master.mdc +160 -0
- package/templates/react/.cursor/rules/modus-select-vs-dropdown-menu-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-select-vs-dropdown-menu-react.mdc +442 -0
- package/templates/react/.cursor/rules/modus-semantic-html-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-semantic-html-react.mdc +427 -0
- package/templates/react/.cursor/rules/modus-tailwind-usage-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-tailwind-usage-react.mdc +642 -0
- package/templates/react/.cursor/rules/modus-themes-react-short.mdc +23 -0
- package/templates/react/.cursor/rules/modus-themes-react.mdc +506 -0
- package/templates/react/.cursor/rules/ux/gestalt-laws-detailed.mdc +456 -0
- package/templates/react/.cursor/rules/ux/ux-ui-foundations.mdc +211 -0
- package/templates/react/.cursor/skills/create-modus-form-component/SKILL.md +518 -0
- package/templates/react/.cursor/skills/create-modus-wrapper-component/SKILL.md +252 -0
- package/templates/react/.cursor/skills/fix-modus-component-event-issues/SKILL.md +345 -0
- package/templates/react/.cursor/skills/handle-modus-checkbox-value-bug/SKILL.md +202 -0
- package/templates/react/.cursor/skills/implement-modus-modal-with-refs/SKILL.md +386 -0
- package/templates/react/.cursor/skills/integrate-modus-icons/SKILL.md +300 -0
- package/templates/react/.cursor/skills/run-lint-checks/SKILL.md +235 -0
- package/templates/react/.cursor/skills/set-up-modus-event-listeners/SKILL.md +284 -0
- package/templates/react/.cursor/skills/style-modus-components-with-tailwind/SKILL.md +382 -0
- package/templates/react/.env.development +3 -0
- package/templates/react/.env.production +3 -0
- package/templates/react/.github/CODEOWNERS +28 -0
- package/templates/react/.github/ISSUE_TEMPLATE/bug_report.yml +176 -0
- package/templates/react/.github/ISSUE_TEMPLATE/config.yml +20 -0
- package/templates/react/.github/ISSUE_TEMPLATE/documentation.yml +115 -0
- package/templates/react/.github/ISSUE_TEMPLATE/feature_request.yml +171 -0
- package/templates/react/.github/ISSUE_TEMPLATE/question.yml +139 -0
- package/templates/react/.github/copilot-instructions.md +80 -0
- package/templates/react/.github/instructions/components.instructions.md +82 -0
- package/templates/react/.github/instructions/demos.instructions.md +82 -0
- package/templates/react/.github/instructions/pages.instructions.md +76 -0
- package/templates/react/.github/instructions/styles.instructions.md +77 -0
- package/templates/react/.github/instructions/typescript.instructions.md +101 -0
- package/templates/react/.github/pull_request_template.md +188 -0
- package/templates/react/.github/workflows/ci.yml +43 -0
- package/templates/react/.github/workflows/claude-code-review.yml +44 -0
- package/templates/react/.github/workflows/claude.yml +50 -0
- package/templates/react/.husky/pre-commit +28 -0
- package/templates/react/.vscode/extensions.json +8 -0
- package/templates/react/CLAUDE.md +119 -0
- package/templates/react/CODE_OF_CONDUCT.md +79 -0
- package/templates/react/CONTRIBUTING.md +65 -0
- package/templates/react/LICENSE +21 -0
- package/templates/react/README.md +728 -0
- package/templates/react/SECURITY.md +50 -0
- package/templates/react/eslint.config.js +23 -0
- package/templates/react/index.html +13 -0
- package/templates/react/package-lock.json +5209 -0
- package/templates/react/package.json +49 -0
- package/templates/react/postcss.config.js +6 -0
- package/templates/react/public/react.svg +1 -0
- package/templates/react/public/vite.svg +1 -0
- package/templates/react/readme_assets/getting_started_header.png +0 -0
- package/templates/react/readme_assets/hero.png +0 -0
- package/templates/react/readme_assets/modus_comp.png +0 -0
- package/templates/react/readme_assets/modus_figma_mcp.png +0 -0
- package/templates/react/readme_assets/teaser_comp.gif +0 -0
- package/templates/react/scripts/README.md +343 -0
- package/templates/react/scripts/check-border-violations.js +483 -0
- package/templates/react/scripts/check-icon-names.js +486 -0
- package/templates/react/scripts/check-inline-styles.js +364 -0
- package/templates/react/scripts/check-modus-colors.js +247 -0
- package/templates/react/scripts/check-modus-icons.js +256 -0
- package/templates/react/scripts/check-opacity-utilities.js +481 -0
- package/templates/react/scripts/check-semantic-html.js +476 -0
- package/templates/react/scripts/check-typescript.js +109 -0
- package/templates/react/src/App.css +42 -0
- package/templates/react/src/App.tsx +54 -0
- package/templates/react/src/assets/react.svg +1 -0
- package/templates/react/src/components/DemoExample.tsx +61 -0
- package/templates/react/src/components/DemoPage.tsx +81 -0
- package/templates/react/src/components/ModusAccordion.tsx +89 -0
- package/templates/react/src/components/ModusAlert.tsx +85 -0
- package/templates/react/src/components/ModusAutocomplete.tsx +207 -0
- package/templates/react/src/components/ModusAvatar.tsx +50 -0
- package/templates/react/src/components/ModusBadge.tsx +82 -0
- package/templates/react/src/components/ModusBreadcrumbs.tsx +75 -0
- package/templates/react/src/components/ModusButton.tsx +244 -0
- package/templates/react/src/components/ModusButtonGroup.tsx +91 -0
- package/templates/react/src/components/ModusCard.tsx +70 -0
- package/templates/react/src/components/ModusCheckbox.tsx +168 -0
- package/templates/react/src/components/ModusChip.tsx +93 -0
- package/templates/react/src/components/ModusDate.tsx +154 -0
- package/templates/react/src/components/ModusDropdownMenu.tsx +148 -0
- package/templates/react/src/components/ModusFileDropzone.tsx +140 -0
- package/templates/react/src/components/ModusHandle.tsx +101 -0
- package/templates/react/src/components/ModusIcon.tsx +49 -0
- package/templates/react/src/components/ModusInputFeedback.tsx +52 -0
- package/templates/react/src/components/ModusInputLabel.tsx +50 -0
- package/templates/react/src/components/ModusLoader.tsx +42 -0
- package/templates/react/src/components/ModusLogo.tsx +102 -0
- package/templates/react/src/components/ModusMenu.tsx +119 -0
- package/templates/react/src/components/ModusMenuItem.tsx +86 -0
- package/templates/react/src/components/ModusModal.tsx +145 -0
- package/templates/react/src/components/ModusNavbar.tsx +504 -0
- package/templates/react/src/components/ModusNumberInput.tsx +230 -0
- package/templates/react/src/components/ModusPagination.tsx +94 -0
- package/templates/react/src/components/ModusPanel.tsx +92 -0
- package/templates/react/src/components/ModusProgress.tsx +70 -0
- package/templates/react/src/components/ModusProvider.tsx +18 -0
- package/templates/react/src/components/ModusRadio.tsx +114 -0
- package/templates/react/src/components/ModusRating.tsx +108 -0
- package/templates/react/src/components/ModusSelect.tsx +171 -0
- package/templates/react/src/components/ModusSideNavigation.tsx +149 -0
- package/templates/react/src/components/ModusSkeleton.tsx +42 -0
- package/templates/react/src/components/ModusSlider.tsx +128 -0
- package/templates/react/src/components/ModusStepper.tsx +85 -0
- package/templates/react/src/components/ModusSwitch.tsx +130 -0
- package/templates/react/src/components/ModusTable.tsx +309 -0
- package/templates/react/src/components/ModusTabs.tsx +114 -0
- package/templates/react/src/components/ModusTextInput.tsx +179 -0
- package/templates/react/src/components/ModusTextarea.tsx +164 -0
- package/templates/react/src/components/ModusThemeSwitcher.tsx +58 -0
- package/templates/react/src/components/ModusTimeInput.tsx +176 -0
- package/templates/react/src/components/ModusToast.tsx +207 -0
- package/templates/react/src/components/ModusToolbar.tsx +70 -0
- package/templates/react/src/components/ModusTooltip.tsx +97 -0
- package/templates/react/src/components/ModusUtilityPanel.tsx +198 -0
- package/templates/react/src/components/ThemeSwitcherDropdown.tsx +117 -0
- package/templates/react/src/components/ThemeToggleSimple.tsx +157 -0
- package/templates/react/src/config/routes.ts +196 -0
- package/templates/react/src/contexts/ThemeContext.tsx +81 -0
- package/templates/react/src/contexts/ThemeContextData.tsx +89 -0
- package/templates/react/src/data/modusIcons.ts +865 -0
- package/templates/react/src/demos/accordion-demo/page.tsx +236 -0
- package/templates/react/src/demos/alert-demo/page.tsx +94 -0
- package/templates/react/src/demos/autocomplete-demo/page.tsx +166 -0
- package/templates/react/src/demos/avatar-demo/page.tsx +135 -0
- package/templates/react/src/demos/badge-demo/page.tsx +174 -0
- package/templates/react/src/demos/breadcrumbs-demo/page.tsx +88 -0
- package/templates/react/src/demos/button-demo/page.tsx +261 -0
- package/templates/react/src/demos/button-group-demo/page.tsx +231 -0
- package/templates/react/src/demos/card-demo/page.tsx +241 -0
- package/templates/react/src/demos/checkbox-demo/page.tsx +79 -0
- package/templates/react/src/demos/chip-demo/page.tsx +197 -0
- package/templates/react/src/demos/date-demo/page.tsx +179 -0
- package/templates/react/src/demos/dropdown-demo/page.tsx +150 -0
- package/templates/react/src/demos/file-dropzone-demo/page.tsx +186 -0
- package/templates/react/src/demos/handle-demo/page.tsx +313 -0
- package/templates/react/src/demos/icon-demo/page.tsx +72 -0
- package/templates/react/src/demos/input-feedback-demo/page.tsx +202 -0
- package/templates/react/src/demos/input-label-demo/page.tsx +392 -0
- package/templates/react/src/demos/loader-demo/page.tsx +138 -0
- package/templates/react/src/demos/logo-demo/page.tsx +292 -0
- package/templates/react/src/demos/menu-demo/page.tsx +70 -0
- package/templates/react/src/demos/modal-demo/page.tsx +332 -0
- package/templates/react/src/demos/navbar-demo/page.tsx +141 -0
- package/templates/react/src/demos/number-input-demo/page.tsx +180 -0
- package/templates/react/src/demos/pagination-demo/page.tsx +147 -0
- package/templates/react/src/demos/panel-demo/page.tsx +376 -0
- package/templates/react/src/demos/progress-demo/page.tsx +185 -0
- package/templates/react/src/demos/radio-demo/page.tsx +242 -0
- package/templates/react/src/demos/rating-demo/page.tsx +97 -0
- package/templates/react/src/demos/select-demo/page.tsx +111 -0
- package/templates/react/src/demos/side-navigation-demo/page.tsx +775 -0
- package/templates/react/src/demos/skeleton-demo/page.tsx +107 -0
- package/templates/react/src/demos/slider-demo/page.tsx +78 -0
- package/templates/react/src/demos/stepper-demo/page.tsx +86 -0
- package/templates/react/src/demos/switch-demo/page.tsx +146 -0
- package/templates/react/src/demos/table-demo/page.tsx +489 -0
- package/templates/react/src/demos/tabs-demo/page.tsx +187 -0
- package/templates/react/src/demos/text-input-demo/page.tsx +151 -0
- package/templates/react/src/demos/textarea-demo/page.tsx +73 -0
- package/templates/react/src/demos/theme-switcher-demo/page.tsx +26 -0
- package/templates/react/src/demos/time-input-demo/page.tsx +148 -0
- package/templates/react/src/demos/toast-demo/page.tsx +302 -0
- package/templates/react/src/demos/toolbar-demo/page.tsx +49 -0
- package/templates/react/src/demos/tooltip-demo/page.tsx +209 -0
- package/templates/react/src/demos/typography-test/page.tsx +28 -0
- package/templates/react/src/demos/utility-panel-demo/page.tsx +197 -0
- package/templates/react/src/dev/DevPanel.tsx +219 -0
- package/templates/react/src/dev/DevPanelContext.ts +14 -0
- package/templates/react/src/dev/DevPanelProvider.tsx +63 -0
- package/templates/react/src/dev/DevRoutes.tsx +98 -0
- package/templates/react/src/dev/config.ts +127 -0
- package/templates/react/src/dev/index.ts +8 -0
- package/templates/react/src/dev/useDevPanel.ts +17 -0
- package/templates/react/src/dev-pages/ColorPalettePage.tsx +347 -0
- package/templates/react/src/dev-pages/ComponentsGalleryPage.tsx +489 -0
- package/templates/react/src/dev-pages/IconsPage.tsx +137 -0
- package/templates/react/src/dev-pages/index.ts +3 -0
- package/templates/react/src/hooks/useTheme.ts +15 -0
- package/templates/react/src/index.css +635 -0
- package/templates/react/src/main.tsx +14 -0
- package/templates/react/src/pages/HomePage.tsx +283 -0
- package/templates/react/src/vite-env.d.ts +9 -0
- package/templates/react/tailwind.config.js +58 -0
- package/templates/react/tsconfig.app.json +27 -0
- package/templates/react/tsconfig.json +7 -0
- package/templates/react/tsconfig.node.json +25 -0
- package/templates/react/vite.config.ts +18 -0
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Critical React key prop patterns for Modus Web Components
|
|
3
|
+
globs: ["**/*.tsx"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Modus React Key Warnings - Essential Rules
|
|
8
|
+
|
|
9
|
+
## 🚨 CRITICAL: React Key Props with Modus Web Components
|
|
10
|
+
|
|
11
|
+
When using Modus Web Components in React, proper key handling is essential to prevent React warnings and ensure optimal performance.
|
|
12
|
+
|
|
13
|
+
## ❌ NEVER Do These
|
|
14
|
+
|
|
15
|
+
### 1. Missing Key Props on Mapped Components
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
// ❌ WRONG - No key prop
|
|
19
|
+
{
|
|
20
|
+
items.map((item) => (
|
|
21
|
+
<ModusWcMenuItem label={item.label} value={item.value} />
|
|
22
|
+
));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ❌ WRONG - Using index only as key
|
|
26
|
+
{
|
|
27
|
+
items.map((item, index) => (
|
|
28
|
+
<ModusWcMenuItem key={index} label={item.label} value={item.value} />
|
|
29
|
+
));
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Using Slots Instead of Props for Icons
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
// ❌ WRONG - Using slot content causes key issues
|
|
37
|
+
<ModusWcMenuItem label={item.label} value={item.value}>
|
|
38
|
+
{item.startIcon && (
|
|
39
|
+
<i className="modus-icons" slot="start-icon">
|
|
40
|
+
{item.startIcon}
|
|
41
|
+
</i>
|
|
42
|
+
)}
|
|
43
|
+
</ModusWcMenuItem>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 3. Wrapping Web Components in Divs
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
// ❌ WRONG - Div wrapper interferes with web component
|
|
50
|
+
{
|
|
51
|
+
items.map((item) => (
|
|
52
|
+
<div key={item.value}>
|
|
53
|
+
<ModusWcMenuItem label={item.label} value={item.value} />
|
|
54
|
+
</div>
|
|
55
|
+
));
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## ✅ ALWAYS Do These
|
|
60
|
+
|
|
61
|
+
### 1. Use Composite Keys for Web Components
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
// ✅ CORRECT - Composite key ensures uniqueness
|
|
65
|
+
{
|
|
66
|
+
items.map((item, index) => (
|
|
67
|
+
<ModusWcMenuItem
|
|
68
|
+
key={`${item.value}-${index}`}
|
|
69
|
+
label={item.label}
|
|
70
|
+
value={item.value}
|
|
71
|
+
start-icon={item.startIcon}
|
|
72
|
+
/>
|
|
73
|
+
));
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 2. Use Props Instead of Slots for Modus Components
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
// ✅ CORRECT - Use start-icon prop
|
|
81
|
+
<ModusWcMenuItem
|
|
82
|
+
key={`${item.value}-${index}`}
|
|
83
|
+
label={item.label}
|
|
84
|
+
value={item.value}
|
|
85
|
+
start-icon={item.startIcon}
|
|
86
|
+
sub-label={item.subLabel}
|
|
87
|
+
selected={item.selected}
|
|
88
|
+
disabled={item.disabled}
|
|
89
|
+
bordered={item.bordered}
|
|
90
|
+
/>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 3. Handle Dynamic Content Properly
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// ✅ CORRECT - Safe key generation
|
|
97
|
+
const generateKey = (item: MenuItem, index: number) => {
|
|
98
|
+
return item.value ? `${item.value}-${index}` : `item-${index}`;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
{
|
|
102
|
+
items.map((item, index) => (
|
|
103
|
+
<ModusWcMenuItem
|
|
104
|
+
key={generateKey(item, index)}
|
|
105
|
+
label={item.label}
|
|
106
|
+
value={item.value}
|
|
107
|
+
/>
|
|
108
|
+
));
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 🎯 Key Patterns for Different Modus Components
|
|
113
|
+
|
|
114
|
+
### ModusWcMenuItem
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
// ✅ CORRECT
|
|
118
|
+
{
|
|
119
|
+
menuItems.map((item, index) => (
|
|
120
|
+
<ModusWcMenuItem
|
|
121
|
+
key={`${item.value}-${index}`}
|
|
122
|
+
label={item.label}
|
|
123
|
+
value={item.value}
|
|
124
|
+
start-icon={item.startIcon}
|
|
125
|
+
sub-label={item.subLabel}
|
|
126
|
+
/>
|
|
127
|
+
));
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### ModusWcTabs
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
// ✅ CORRECT
|
|
135
|
+
{
|
|
136
|
+
panels.map((panel, index) => (
|
|
137
|
+
<div key={index} slot={`tab-${index}`}>
|
|
138
|
+
{panel}
|
|
139
|
+
</div>
|
|
140
|
+
));
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### ModusWcDropdownMenu
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
// ✅ CORRECT
|
|
148
|
+
{
|
|
149
|
+
menuItems.map((item, index) => (
|
|
150
|
+
<ModusWcMenuItem
|
|
151
|
+
key={`${item.value}-${index}`}
|
|
152
|
+
label={item.label}
|
|
153
|
+
value={item.value}
|
|
154
|
+
start-icon={item.startIcon}
|
|
155
|
+
/>
|
|
156
|
+
));
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## 🚨 Common Warning Messages
|
|
161
|
+
|
|
162
|
+
### React Key Warning
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
Each child in a list should have a unique "key" prop.
|
|
166
|
+
Check the render method of `ModusWcMenu2`. It was passed a child from ModusMenu.
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Solution**: Use composite keys and props instead of slots.
|
|
170
|
+
|
|
171
|
+
### Web Component Slot Issues
|
|
172
|
+
|
|
173
|
+
```text
|
|
174
|
+
Warning: React does not recognize the `slot` prop on a DOM element.
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Solution**: Use component props instead of slot attributes.
|
|
178
|
+
|
|
179
|
+
### Common Pitfalls and Solutions
|
|
180
|
+
|
|
181
|
+
#### ❌ Pitfall: Using Array Index as Key
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
// ❌ PROBLEMATIC - Can cause issues with reordering
|
|
185
|
+
{
|
|
186
|
+
items.map((item, index) => <ModusWcMenuItem key={index} {...item} />);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### ✅ Solution: Use Stable Identifiers
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
// ✅ CORRECT - Use unique, stable identifier
|
|
194
|
+
{
|
|
195
|
+
items.map((item) => <ModusWcMenuItem key={item.id} {...item} />);
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### ❌ Pitfall: Missing Event Cleanup
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
// ❌ PROBLEMATIC - Memory leaks
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
const element = ref.current;
|
|
205
|
+
element?.addEventListener("itemSelect", handler);
|
|
206
|
+
// Missing cleanup!
|
|
207
|
+
}, []);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### ✅ Solution: Proper Cleanup
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
// ✅ CORRECT - Clean up event listeners
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
const element = ref.current;
|
|
216
|
+
if (!element) return;
|
|
217
|
+
|
|
218
|
+
element.addEventListener("itemSelect", handler);
|
|
219
|
+
|
|
220
|
+
return () => {
|
|
221
|
+
element.removeEventListener("itemSelect", handler);
|
|
222
|
+
};
|
|
223
|
+
}, [handler]);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### ❌ Pitfall: Inconsistent Key Generation
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
// ❌ PROBLEMATIC - Keys can collide
|
|
230
|
+
{
|
|
231
|
+
items.map((item) => <ModusWcMenuItem key={item.value} {...item} />);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### ✅ Solution: Composite Keys
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
// ✅ CORRECT - Composite keys prevent collisions
|
|
239
|
+
{
|
|
240
|
+
items.map((item, index) => (
|
|
241
|
+
<ModusWcMenuItem key={`${item.value}-${index}`} {...item} />
|
|
242
|
+
));
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## 🔧 Debugging Steps
|
|
247
|
+
|
|
248
|
+
1. **Check Console**: Look for React key warnings
|
|
249
|
+
2. **Use Chrome DevTools**: Inspect component structure
|
|
250
|
+
3. **Verify Keys**: Ensure each mapped item has a unique key
|
|
251
|
+
4. **Test Props**: Use component props instead of slots when possible
|
|
252
|
+
|
|
253
|
+
## 📚 Reference Components
|
|
254
|
+
|
|
255
|
+
**Working Examples:**
|
|
256
|
+
|
|
257
|
+
- `ModusDropdownMenu.tsx` - Uses `start-icon={item.startIcon}` prop
|
|
258
|
+
- `ThemeSwitcherDropdown.tsx` - Proper key handling
|
|
259
|
+
- `ModusTabs.tsx` - Correct slot usage for tab panels
|
|
260
|
+
|
|
261
|
+
**Problematic Patterns:**
|
|
262
|
+
|
|
263
|
+
- Slot-based icon rendering
|
|
264
|
+
- Missing or duplicate keys
|
|
265
|
+
- Wrapping web components unnecessarily
|
|
266
|
+
|
|
267
|
+
## 🔗 Modus Web Components React Integration
|
|
268
|
+
|
|
269
|
+
### Official Documentation Patterns
|
|
270
|
+
|
|
271
|
+
Based on the [Modus Web Components React Documentation](https://trimble-oss.github.io/modus-wc-2.0/main/?path=/docs/documentation-frameworks-react--docs), follow these patterns:
|
|
272
|
+
|
|
273
|
+
#### ✅ Proper List Rendering
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
// ✅ CORRECT - Use unique identifiers as keys
|
|
277
|
+
{
|
|
278
|
+
items.map((item) => (
|
|
279
|
+
<ModusWcMenuItem
|
|
280
|
+
key={item.id}
|
|
281
|
+
label={item.label}
|
|
282
|
+
value={item.value}
|
|
283
|
+
start-icon={item.startIcon}
|
|
284
|
+
/>
|
|
285
|
+
));
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
#### ✅ Required Props Configuration
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
// ✅ CORRECT - Provide all required props
|
|
293
|
+
<ModusWcMenuItem
|
|
294
|
+
label="Menu Item"
|
|
295
|
+
value="menu-item"
|
|
296
|
+
start-icon="icon-name"
|
|
297
|
+
disabled={false}
|
|
298
|
+
selected={false}
|
|
299
|
+
/>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
#### ❌ Avoid Index-Based Keys for Dynamic Lists
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
// ❌ WRONG - Index can cause issues with reordering
|
|
306
|
+
{
|
|
307
|
+
items.map((item, index) => <ModusWcMenuItem key={index} {...item} />);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ✅ CORRECT - Use stable identifier
|
|
311
|
+
{
|
|
312
|
+
items.map((item) => <ModusWcMenuItem key={item.id} {...item} />);
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Key Integration Principles
|
|
317
|
+
|
|
318
|
+
1. **Stable Keys**: Use data-based identifiers, not array indices
|
|
319
|
+
2. **Complete Props**: Provide all required web component props
|
|
320
|
+
3. **Event Handling**: Use proper React event handlers for web component events
|
|
321
|
+
4. **Type Safety**: Leverage TypeScript interfaces for web component props
|
|
322
|
+
5. **Performance**: Avoid unnecessary re-renders with proper key usage
|
|
323
|
+
|
|
324
|
+
### TypeScript Integration Patterns
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
// ✅ CORRECT - Type-safe interface
|
|
328
|
+
interface MenuItem {
|
|
329
|
+
id: string;
|
|
330
|
+
label: string;
|
|
331
|
+
value: string;
|
|
332
|
+
startIcon?: string;
|
|
333
|
+
disabled?: boolean;
|
|
334
|
+
selected?: boolean;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ✅ CORRECT - Proper event handling
|
|
338
|
+
const handleItemSelect = (event: CustomEvent<{ value: string }>) => {
|
|
339
|
+
const selectedValue = event.detail.value;
|
|
340
|
+
// Handle selection
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// ✅ CORRECT - Type-safe component usage
|
|
344
|
+
{
|
|
345
|
+
menuItems.map((item: MenuItem) => (
|
|
346
|
+
<ModusWcMenuItem
|
|
347
|
+
key={item.id}
|
|
348
|
+
label={item.label}
|
|
349
|
+
value={item.value}
|
|
350
|
+
start-icon={item.startIcon}
|
|
351
|
+
disabled={item.disabled}
|
|
352
|
+
selected={item.selected}
|
|
353
|
+
onItemSelect={handleItemSelect}
|
|
354
|
+
/>
|
|
355
|
+
));
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Event Handling Best Practices
|
|
360
|
+
|
|
361
|
+
```tsx
|
|
362
|
+
// ✅ CORRECT - Proper event listener setup
|
|
363
|
+
useEffect(() => {
|
|
364
|
+
const menuElement = menuRef.current;
|
|
365
|
+
if (!menuElement) return;
|
|
366
|
+
|
|
367
|
+
const handleItemSelect = (event: CustomEvent<{ value: string }>) => {
|
|
368
|
+
onItemSelect?.(event.detail);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
menuElement.addEventListener("itemSelect", handleItemSelect);
|
|
372
|
+
|
|
373
|
+
return () => {
|
|
374
|
+
menuElement.removeEventListener("itemSelect", handleItemSelect);
|
|
375
|
+
};
|
|
376
|
+
}, [onItemSelect]);
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## 🎯 Key Rules Summary
|
|
380
|
+
|
|
381
|
+
1. **Always use composite keys**: `${value}-${index}`
|
|
382
|
+
2. **Use props over slots**: `start-icon={icon}` not `<i slot="start-icon">`
|
|
383
|
+
3. **Avoid div wrappers**: Let web components render directly
|
|
384
|
+
4. **Test in Chrome DevTools**: Verify no React warnings
|
|
385
|
+
5. **Follow Modus patterns**: Use established component patterns
|
|
386
|
+
6. **Use unique identifiers**: Never use array index as key when items can reorder
|
|
387
|
+
7. **Provide all required props**: Ensure web components have proper configuration
|
|
388
|
+
8. **Handle dynamic content safely**: Use stable identifiers for keys
|
|
389
|
+
|
|
390
|
+
## 🚀 Performance Benefits
|
|
391
|
+
|
|
392
|
+
- **Faster re-renders**: React can efficiently track component changes
|
|
393
|
+
- **No console warnings**: Clean development experience
|
|
394
|
+
- **Better debugging**: Clear component hierarchy
|
|
395
|
+
- **Optimal performance**: Proper React reconciliation
|
|
396
|
+
|
|
397
|
+
## 📝 Quick Checklist
|
|
398
|
+
|
|
399
|
+
- [ ] ✅ All mapped components have unique keys
|
|
400
|
+
- [ ] ✅ Using composite keys (`${value}-${index}`)
|
|
401
|
+
- [ ] ✅ Using props instead of slots for icons
|
|
402
|
+
- [ ] ✅ No unnecessary div wrappers
|
|
403
|
+
- [ ] ✅ No React warnings in console
|
|
404
|
+
- [ ] ✅ Components render correctly
|
|
405
|
+
- [ ] ✅ Following Modus Web Components patterns
|
|
406
|
+
|
|
407
|
+
# Modus React Key Warnings - Essential Rules
|
|
408
|
+
|
|
409
|
+
## 🚨 CRITICAL: React Key Props with Modus Web Components
|
|
410
|
+
|
|
411
|
+
When using Modus Web Components in React, proper key handling is essential to prevent React warnings and ensure optimal performance.
|
|
412
|
+
|
|
413
|
+
## ❌ NEVER Do These
|
|
414
|
+
|
|
415
|
+
### 1. Missing Key Props on Mapped Components
|
|
416
|
+
|
|
417
|
+
```tsx
|
|
418
|
+
// ❌ WRONG - No key prop
|
|
419
|
+
{
|
|
420
|
+
items.map((item) => (
|
|
421
|
+
<ModusWcMenuItem label={item.label} value={item.value} />
|
|
422
|
+
));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ❌ WRONG - Using index only as key
|
|
426
|
+
{
|
|
427
|
+
items.map((item, index) => (
|
|
428
|
+
<ModusWcMenuItem key={index} label={item.label} value={item.value} />
|
|
429
|
+
));
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### 2. Using Slots Instead of Props for Icons
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
// ❌ WRONG - Using slot content causes key issues
|
|
437
|
+
<ModusWcMenuItem label={item.label} value={item.value}>
|
|
438
|
+
{item.startIcon && (
|
|
439
|
+
<i className="modus-icons" slot="start-icon">
|
|
440
|
+
{item.startIcon}
|
|
441
|
+
</i>
|
|
442
|
+
)}
|
|
443
|
+
</ModusWcMenuItem>
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### 3. Wrapping Web Components in Divs
|
|
447
|
+
|
|
448
|
+
```tsx
|
|
449
|
+
// ❌ WRONG - Div wrapper interferes with web component
|
|
450
|
+
{
|
|
451
|
+
items.map((item) => (
|
|
452
|
+
<div key={item.value}>
|
|
453
|
+
<ModusWcMenuItem label={item.label} value={item.value} />
|
|
454
|
+
</div>
|
|
455
|
+
));
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## ✅ ALWAYS Do These
|
|
460
|
+
|
|
461
|
+
### 1. Use Composite Keys for Web Components
|
|
462
|
+
|
|
463
|
+
```tsx
|
|
464
|
+
// ✅ CORRECT - Composite key ensures uniqueness
|
|
465
|
+
{
|
|
466
|
+
items.map((item, index) => (
|
|
467
|
+
<ModusWcMenuItem
|
|
468
|
+
key={`${item.value}-${index}`}
|
|
469
|
+
label={item.label}
|
|
470
|
+
value={item.value}
|
|
471
|
+
start-icon={item.startIcon}
|
|
472
|
+
/>
|
|
473
|
+
));
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### 2. Use Props Instead of Slots for Modus Components
|
|
478
|
+
|
|
479
|
+
```tsx
|
|
480
|
+
// ✅ CORRECT - Use start-icon prop
|
|
481
|
+
<ModusWcMenuItem
|
|
482
|
+
key={`${item.value}-${index}`}
|
|
483
|
+
label={item.label}
|
|
484
|
+
value={item.value}
|
|
485
|
+
start-icon={item.startIcon}
|
|
486
|
+
sub-label={item.subLabel}
|
|
487
|
+
selected={item.selected}
|
|
488
|
+
disabled={item.disabled}
|
|
489
|
+
bordered={item.bordered}
|
|
490
|
+
/>
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### 3. Handle Dynamic Content Properly
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
// ✅ CORRECT - Safe key generation
|
|
497
|
+
const generateKey = (item: MenuItem, index: number) => {
|
|
498
|
+
return item.value ? `${item.value}-${index}` : `item-${index}`;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
{
|
|
502
|
+
items.map((item, index) => (
|
|
503
|
+
<ModusWcMenuItem
|
|
504
|
+
key={generateKey(item, index)}
|
|
505
|
+
label={item.label}
|
|
506
|
+
value={item.value}
|
|
507
|
+
/>
|
|
508
|
+
));
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
## 🎯 Key Patterns for Different Modus Components
|
|
513
|
+
|
|
514
|
+
### ModusWcMenuItem
|
|
515
|
+
|
|
516
|
+
```tsx
|
|
517
|
+
// ✅ CORRECT
|
|
518
|
+
{
|
|
519
|
+
menuItems.map((item, index) => (
|
|
520
|
+
<ModusWcMenuItem
|
|
521
|
+
key={`${item.value}-${index}`}
|
|
522
|
+
label={item.label}
|
|
523
|
+
value={item.value}
|
|
524
|
+
start-icon={item.startIcon}
|
|
525
|
+
sub-label={item.subLabel}
|
|
526
|
+
/>
|
|
527
|
+
));
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### ModusWcTabs
|
|
532
|
+
|
|
533
|
+
```tsx
|
|
534
|
+
// ✅ CORRECT
|
|
535
|
+
{
|
|
536
|
+
panels.map((panel, index) => (
|
|
537
|
+
<div key={index} slot={`tab-${index}`}>
|
|
538
|
+
{panel}
|
|
539
|
+
</div>
|
|
540
|
+
));
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### ModusWcDropdownMenu
|
|
545
|
+
|
|
546
|
+
```tsx
|
|
547
|
+
// ✅ CORRECT
|
|
548
|
+
{
|
|
549
|
+
menuItems.map((item, index) => (
|
|
550
|
+
<ModusWcMenuItem
|
|
551
|
+
key={`${item.value}-${index}`}
|
|
552
|
+
label={item.label}
|
|
553
|
+
value={item.value}
|
|
554
|
+
start-icon={item.startIcon}
|
|
555
|
+
/>
|
|
556
|
+
));
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
## 🚨 Common Warning Messages
|
|
561
|
+
|
|
562
|
+
### React Key Warning
|
|
563
|
+
|
|
564
|
+
```text
|
|
565
|
+
Each child in a list should have a unique "key" prop.
|
|
566
|
+
Check the render method of `ModusWcMenu2`. It was passed a child from ModusMenu.
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
**Solution**: Use composite keys and props instead of slots.
|
|
570
|
+
|
|
571
|
+
### Web Component Slot Issues
|
|
572
|
+
|
|
573
|
+
```text
|
|
574
|
+
Warning: React does not recognize the `slot` prop on a DOM element.
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Solution**: Use component props instead of slot attributes.
|
|
578
|
+
|
|
579
|
+
### Common Pitfalls and Solutions
|
|
580
|
+
|
|
581
|
+
#### ❌ Pitfall: Using Array Index as Key
|
|
582
|
+
|
|
583
|
+
```tsx
|
|
584
|
+
// ❌ PROBLEMATIC - Can cause issues with reordering
|
|
585
|
+
{
|
|
586
|
+
items.map((item, index) => <ModusWcMenuItem key={index} {...item} />);
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
#### ✅ Solution: Use Stable Identifiers
|
|
591
|
+
|
|
592
|
+
```tsx
|
|
593
|
+
// ✅ CORRECT - Use unique, stable identifier
|
|
594
|
+
{
|
|
595
|
+
items.map((item) => <ModusWcMenuItem key={item.id} {...item} />);
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
#### ❌ Pitfall: Missing Event Cleanup
|
|
600
|
+
|
|
601
|
+
```tsx
|
|
602
|
+
// ❌ PROBLEMATIC - Memory leaks
|
|
603
|
+
useEffect(() => {
|
|
604
|
+
const element = ref.current;
|
|
605
|
+
element?.addEventListener("itemSelect", handler);
|
|
606
|
+
// Missing cleanup!
|
|
607
|
+
}, []);
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
#### ✅ Solution: Proper Cleanup
|
|
611
|
+
|
|
612
|
+
```tsx
|
|
613
|
+
// ✅ CORRECT - Clean up event listeners
|
|
614
|
+
useEffect(() => {
|
|
615
|
+
const element = ref.current;
|
|
616
|
+
if (!element) return;
|
|
617
|
+
|
|
618
|
+
element.addEventListener("itemSelect", handler);
|
|
619
|
+
|
|
620
|
+
return () => {
|
|
621
|
+
element.removeEventListener("itemSelect", handler);
|
|
622
|
+
};
|
|
623
|
+
}, [handler]);
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
#### ❌ Pitfall: Inconsistent Key Generation
|
|
627
|
+
|
|
628
|
+
```tsx
|
|
629
|
+
// ❌ PROBLEMATIC - Keys can collide
|
|
630
|
+
{
|
|
631
|
+
items.map((item) => <ModusWcMenuItem key={item.value} {...item} />);
|
|
632
|
+
}
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
#### ✅ Solution: Composite Keys
|
|
636
|
+
|
|
637
|
+
```tsx
|
|
638
|
+
// ✅ CORRECT - Composite keys prevent collisions
|
|
639
|
+
{
|
|
640
|
+
items.map((item, index) => (
|
|
641
|
+
<ModusWcMenuItem key={`${item.value}-${index}`} {...item} />
|
|
642
|
+
));
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
## 🔧 Debugging Steps
|
|
647
|
+
|
|
648
|
+
1. **Check Console**: Look for React key warnings
|
|
649
|
+
2. **Use Chrome DevTools**: Inspect component structure
|
|
650
|
+
3. **Verify Keys**: Ensure each mapped item has a unique key
|
|
651
|
+
4. **Test Props**: Use component props instead of slots when possible
|
|
652
|
+
|
|
653
|
+
## 📚 Reference Components
|
|
654
|
+
|
|
655
|
+
**Working Examples:**
|
|
656
|
+
|
|
657
|
+
- `ModusDropdownMenu.tsx` - Uses `start-icon={item.startIcon}` prop
|
|
658
|
+
- `ThemeSwitcherDropdown.tsx` - Proper key handling
|
|
659
|
+
- `ModusTabs.tsx` - Correct slot usage for tab panels
|
|
660
|
+
|
|
661
|
+
**Problematic Patterns:**
|
|
662
|
+
|
|
663
|
+
- Slot-based icon rendering
|
|
664
|
+
- Missing or duplicate keys
|
|
665
|
+
- Wrapping web components unnecessarily
|
|
666
|
+
|
|
667
|
+
## 🔗 Modus Web Components React Integration
|
|
668
|
+
|
|
669
|
+
### Official Documentation Patterns
|
|
670
|
+
|
|
671
|
+
Based on the [Modus Web Components React Documentation](https://trimble-oss.github.io/modus-wc-2.0/main/?path=/docs/documentation-frameworks-react--docs), follow these patterns:
|
|
672
|
+
|
|
673
|
+
#### ✅ Proper List Rendering
|
|
674
|
+
|
|
675
|
+
```tsx
|
|
676
|
+
// ✅ CORRECT - Use unique identifiers as keys
|
|
677
|
+
{
|
|
678
|
+
items.map((item) => (
|
|
679
|
+
<ModusWcMenuItem
|
|
680
|
+
key={item.id}
|
|
681
|
+
label={item.label}
|
|
682
|
+
value={item.value}
|
|
683
|
+
start-icon={item.startIcon}
|
|
684
|
+
/>
|
|
685
|
+
));
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
#### ✅ Required Props Configuration
|
|
690
|
+
|
|
691
|
+
```tsx
|
|
692
|
+
// ✅ CORRECT - Provide all required props
|
|
693
|
+
<ModusWcMenuItem
|
|
694
|
+
label="Menu Item"
|
|
695
|
+
value="menu-item"
|
|
696
|
+
start-icon="icon-name"
|
|
697
|
+
disabled={false}
|
|
698
|
+
selected={false}
|
|
699
|
+
/>
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
#### ❌ Avoid Index-Based Keys for Dynamic Lists
|
|
703
|
+
|
|
704
|
+
```tsx
|
|
705
|
+
// ❌ WRONG - Index can cause issues with reordering
|
|
706
|
+
{
|
|
707
|
+
items.map((item, index) => <ModusWcMenuItem key={index} {...item} />);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// ✅ CORRECT - Use stable identifier
|
|
711
|
+
{
|
|
712
|
+
items.map((item) => <ModusWcMenuItem key={item.id} {...item} />);
|
|
713
|
+
}
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
### Key Integration Principles
|
|
717
|
+
|
|
718
|
+
1. **Stable Keys**: Use data-based identifiers, not array indices
|
|
719
|
+
2. **Complete Props**: Provide all required web component props
|
|
720
|
+
3. **Event Handling**: Use proper React event handlers for web component events
|
|
721
|
+
4. **Type Safety**: Leverage TypeScript interfaces for web component props
|
|
722
|
+
5. **Performance**: Avoid unnecessary re-renders with proper key usage
|
|
723
|
+
|
|
724
|
+
### TypeScript Integration Patterns
|
|
725
|
+
|
|
726
|
+
```tsx
|
|
727
|
+
// ✅ CORRECT - Type-safe interface
|
|
728
|
+
interface MenuItem {
|
|
729
|
+
id: string;
|
|
730
|
+
label: string;
|
|
731
|
+
value: string;
|
|
732
|
+
startIcon?: string;
|
|
733
|
+
disabled?: boolean;
|
|
734
|
+
selected?: boolean;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// ✅ CORRECT - Proper event handling
|
|
738
|
+
const handleItemSelect = (event: CustomEvent<{ value: string }>) => {
|
|
739
|
+
const selectedValue = event.detail.value;
|
|
740
|
+
// Handle selection
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
// ✅ CORRECT - Type-safe component usage
|
|
744
|
+
{
|
|
745
|
+
menuItems.map((item: MenuItem) => (
|
|
746
|
+
<ModusWcMenuItem
|
|
747
|
+
key={item.id}
|
|
748
|
+
label={item.label}
|
|
749
|
+
value={item.value}
|
|
750
|
+
start-icon={item.startIcon}
|
|
751
|
+
disabled={item.disabled}
|
|
752
|
+
selected={item.selected}
|
|
753
|
+
onItemSelect={handleItemSelect}
|
|
754
|
+
/>
|
|
755
|
+
));
|
|
756
|
+
}
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### Event Handling Best Practices
|
|
760
|
+
|
|
761
|
+
```tsx
|
|
762
|
+
// ✅ CORRECT - Proper event listener setup
|
|
763
|
+
useEffect(() => {
|
|
764
|
+
const menuElement = menuRef.current;
|
|
765
|
+
if (!menuElement) return;
|
|
766
|
+
|
|
767
|
+
const handleItemSelect = (event: CustomEvent<{ value: string }>) => {
|
|
768
|
+
onItemSelect?.(event.detail);
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
menuElement.addEventListener("itemSelect", handleItemSelect);
|
|
772
|
+
|
|
773
|
+
return () => {
|
|
774
|
+
menuElement.removeEventListener("itemSelect", handleItemSelect);
|
|
775
|
+
};
|
|
776
|
+
}, [onItemSelect]);
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
## 🎯 Key Rules Summary
|
|
780
|
+
|
|
781
|
+
1. **Always use composite keys**: `${value}-${index}`
|
|
782
|
+
2. **Use props over slots**: `start-icon={icon}` not `<i slot="start-icon">`
|
|
783
|
+
3. **Avoid div wrappers**: Let web components render directly
|
|
784
|
+
4. **Test in Chrome DevTools**: Verify no React warnings
|
|
785
|
+
5. **Follow Modus patterns**: Use established component patterns
|
|
786
|
+
6. **Use unique identifiers**: Never use array index as key when items can reorder
|
|
787
|
+
7. **Provide all required props**: Ensure web components have proper configuration
|
|
788
|
+
8. **Handle dynamic content safely**: Use stable identifiers for keys
|
|
789
|
+
|
|
790
|
+
## 🚀 Performance Benefits
|
|
791
|
+
|
|
792
|
+
- **Faster re-renders**: React can efficiently track component changes
|
|
793
|
+
- **No console warnings**: Clean development experience
|
|
794
|
+
- **Better debugging**: Clear component hierarchy
|
|
795
|
+
- **Optimal performance**: Proper React reconciliation
|
|
796
|
+
|
|
797
|
+
## 📝 Quick Checklist
|
|
798
|
+
|
|
799
|
+
- [ ] ✅ All mapped components have unique keys
|
|
800
|
+
- [ ] ✅ Using composite keys (`${value}-${index}`)
|
|
801
|
+
- [ ] ✅ Using props instead of slots for icons
|
|
802
|
+
- [ ] ✅ No unnecessary div wrappers
|
|
803
|
+
- [ ] ✅ No React warnings in console
|
|
804
|
+
- [ ] ✅ Components render correctly
|
|
805
|
+
- [ ] ✅ Following Modus Web Components patterns
|