@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,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: handle-modus-checkbox-value-bug
|
|
3
|
+
description: Apply the critical checkbox value inversion workaround when working with ModusCheckbox components
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Handle Modus Checkbox Value Bug
|
|
7
|
+
|
|
8
|
+
Apply the critical value inversion workaround for ModusWcCheckbox components.
|
|
9
|
+
|
|
10
|
+
## Critical Bug
|
|
11
|
+
|
|
12
|
+
The `ModusWcCheckbox` web component has a **value inversion bug** where the `value` property returns the **opposite** of the actual checked state:
|
|
13
|
+
|
|
14
|
+
- When checkbox is **checked**: `target.value` returns `false`
|
|
15
|
+
- When checkbox is **unchecked**: `target.value` returns `true`
|
|
16
|
+
|
|
17
|
+
This is the **opposite** of what developers expect.
|
|
18
|
+
|
|
19
|
+
## When to Use
|
|
20
|
+
|
|
21
|
+
Use this skill when:
|
|
22
|
+
- Implementing checkbox functionality with ModusCheckbox
|
|
23
|
+
- Fixing checkbox-related bugs
|
|
24
|
+
- Handling checkbox value changes
|
|
25
|
+
- Creating forms with checkboxes
|
|
26
|
+
|
|
27
|
+
## Reference Implementation
|
|
28
|
+
|
|
29
|
+
See `src/components/ModusCheckbox.tsx:116-130` for the complete workaround implementation.
|
|
30
|
+
|
|
31
|
+
## The Fix
|
|
32
|
+
|
|
33
|
+
Always invert the value when reading from checkbox events:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
const handleValueChange = (event: Event) => {
|
|
37
|
+
const customEvent = event as CustomEvent<InputEvent>;
|
|
38
|
+
|
|
39
|
+
// 🚨 CRITICAL: Handle the value inversion bug
|
|
40
|
+
const rawValue = (customEvent.target as HTMLModusWcCheckboxElement).value;
|
|
41
|
+
const actualValue = !rawValue; // ✅ CORRECT: Invert the value
|
|
42
|
+
|
|
43
|
+
// Use actualValue for your logic
|
|
44
|
+
setChecked(actualValue);
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Complete Pattern
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { useEffect, useRef } from "react";
|
|
52
|
+
import { ModusWcCheckbox } from "@trimble-oss/moduswebcomponents-react";
|
|
53
|
+
|
|
54
|
+
export default function ModusCheckbox({
|
|
55
|
+
value = false,
|
|
56
|
+
onValueChange,
|
|
57
|
+
}: {
|
|
58
|
+
value?: boolean;
|
|
59
|
+
onValueChange?: (event: CustomEvent<boolean>) => void;
|
|
60
|
+
}) {
|
|
61
|
+
const checkboxRef = useRef<HTMLModusWcCheckboxElement>(null);
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const checkbox = checkboxRef.current;
|
|
65
|
+
if (!checkbox) return;
|
|
66
|
+
|
|
67
|
+
const handleValueChange = (event: Event) => {
|
|
68
|
+
const customEvent = event as CustomEvent<InputEvent>;
|
|
69
|
+
|
|
70
|
+
// 🚨 CRITICAL BUG WORKAROUND: The ModusWcCheckbox component has a value
|
|
71
|
+
// inversion bug where the `value` property returns the opposite of the
|
|
72
|
+
// actual checked state. This function corrects this by inverting the
|
|
73
|
+
// raw value before passing it to the parent component.
|
|
74
|
+
const rawValue = (customEvent.target as HTMLModusWcCheckboxElement).value;
|
|
75
|
+
const actualValue = !rawValue; // ✅ CORRECT: Invert the value
|
|
76
|
+
|
|
77
|
+
// Create a new event with the corrected value
|
|
78
|
+
const correctedEvent = new CustomEvent("valueChange", {
|
|
79
|
+
detail: actualValue,
|
|
80
|
+
bubbles: true,
|
|
81
|
+
cancelable: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
onValueChange?.(correctedEvent);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (onValueChange) {
|
|
88
|
+
checkbox.addEventListener("inputChange", handleValueChange);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return () => {
|
|
92
|
+
if (onValueChange) {
|
|
93
|
+
checkbox.removeEventListener("inputChange", handleValueChange);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}, [onValueChange]);
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<ModusWcCheckbox
|
|
100
|
+
ref={checkboxRef}
|
|
101
|
+
value={value}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Usage in Forms
|
|
108
|
+
|
|
109
|
+
When using checkboxes in forms:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
function MyForm() {
|
|
113
|
+
const [agreeToTerms, setAgreeToTerms] = useState(false);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<ModusCheckbox
|
|
117
|
+
value={agreeToTerms}
|
|
118
|
+
onValueChange={(event) => {
|
|
119
|
+
// event.detail is already corrected (inverted)
|
|
120
|
+
setAgreeToTerms(event.detail);
|
|
121
|
+
}}
|
|
122
|
+
label="I agree to the terms and conditions"
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Multiple Checkboxes
|
|
129
|
+
|
|
130
|
+
When handling multiple checkboxes:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
function CheckboxGroup() {
|
|
134
|
+
const [checkboxes, setCheckboxes] = useState({
|
|
135
|
+
option1: false,
|
|
136
|
+
option2: true,
|
|
137
|
+
option3: false,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const handleCheckboxChange = (key: string, event: CustomEvent<boolean>) => {
|
|
141
|
+
// event.detail is already corrected
|
|
142
|
+
setCheckboxes(prev => ({
|
|
143
|
+
...prev,
|
|
144
|
+
[key]: event.detail,
|
|
145
|
+
}));
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<>
|
|
150
|
+
<ModusCheckbox
|
|
151
|
+
value={checkboxes.option1}
|
|
152
|
+
onValueChange={(e) => handleCheckboxChange('option1', e)}
|
|
153
|
+
label="Option 1"
|
|
154
|
+
/>
|
|
155
|
+
<ModusCheckbox
|
|
156
|
+
value={checkboxes.option2}
|
|
157
|
+
onValueChange={(e) => handleCheckboxChange('option2', e)}
|
|
158
|
+
label="Option 2"
|
|
159
|
+
/>
|
|
160
|
+
<ModusCheckbox
|
|
161
|
+
value={checkboxes.option3}
|
|
162
|
+
onValueChange={(e) => handleCheckboxChange('option3', e)}
|
|
163
|
+
label="Option 3"
|
|
164
|
+
/>
|
|
165
|
+
</>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Important Notes
|
|
171
|
+
|
|
172
|
+
1. **The bug is in `target.value`**: The inversion happens when reading `event.target.value`, not in `event.detail`
|
|
173
|
+
2. **Always invert**: Never trust the raw value - always invert it
|
|
174
|
+
3. **Wrapper handles it**: The `ModusCheckbox` wrapper component already handles this, so if you're using the wrapper, you don't need to invert again
|
|
175
|
+
4. **Document the bug**: Add comments explaining the workaround
|
|
176
|
+
|
|
177
|
+
## Testing
|
|
178
|
+
|
|
179
|
+
To verify the fix works:
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
// Test that checked state matches expected value
|
|
183
|
+
<ModusCheckbox
|
|
184
|
+
value={isChecked}
|
|
185
|
+
onValueChange={(event) => {
|
|
186
|
+
console.log('Raw value (inverted):', event.detail);
|
|
187
|
+
console.log('Expected checked:', isChecked);
|
|
188
|
+
// These should match after inversion
|
|
189
|
+
}}
|
|
190
|
+
/>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Common Mistakes
|
|
194
|
+
|
|
195
|
+
1. **Forgetting to invert**: Using raw value directly
|
|
196
|
+
2. **Double inversion**: Inverting when using the wrapper component (it already handles it)
|
|
197
|
+
3. **Wrong event**: Using `inputChange` instead of reading `target.value`
|
|
198
|
+
|
|
199
|
+
## Related Files
|
|
200
|
+
|
|
201
|
+
- `src/components/ModusCheckbox.tsx` - Complete wrapper implementation
|
|
202
|
+
- `.cursor/rules/modus-checkbox-value-inversion-react.mdc` - Detailed documentation
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: implement-modus-modal-with-refs
|
|
3
|
+
description: Create Modus modals using the forwardRef + useImperativeHandle pattern for programmatic control
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Implement Modus Modal with Refs
|
|
7
|
+
|
|
8
|
+
Create Modus modal components using the `forwardRef` + `useImperativeHandle` pattern for programmatic control.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
Use this skill when:
|
|
13
|
+
- Creating modal dialogs that need to be opened/closed programmatically
|
|
14
|
+
- You need to control modal state from parent components
|
|
15
|
+
- Building modals with custom triggers (not just button clicks)
|
|
16
|
+
|
|
17
|
+
## Pattern Overview
|
|
18
|
+
|
|
19
|
+
Modus modals require:
|
|
20
|
+
1. **`forwardRef`** to expose methods to parent components
|
|
21
|
+
2. **`useImperativeHandle`** to define the API (`openModal`, `closeModal`)
|
|
22
|
+
3. **`querySelector("dialog")`** to access the native dialog element
|
|
23
|
+
4. **Event listeners** for dialog close events
|
|
24
|
+
|
|
25
|
+
## Reference Implementation
|
|
26
|
+
|
|
27
|
+
See `src/components/ModusModal.tsx` for the complete implementation.
|
|
28
|
+
|
|
29
|
+
## Complete Template
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { ModusWcModal } from "@trimble-oss/moduswebcomponents-react";
|
|
33
|
+
import type { ReactNode } from "react";
|
|
34
|
+
import { useRef, useEffect, forwardRef, useImperativeHandle } from "react";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Props for the ModusModal component.
|
|
38
|
+
*/
|
|
39
|
+
interface ModusModalProps {
|
|
40
|
+
/** A unique identifier for the modal. */
|
|
41
|
+
modalId: string;
|
|
42
|
+
/** The ARIA label for the modal. */
|
|
43
|
+
ariaLabel?: string;
|
|
44
|
+
|
|
45
|
+
/** The type of backdrop for the modal. */
|
|
46
|
+
backdrop?: 'default' | 'static';
|
|
47
|
+
/** The position of the modal. */
|
|
48
|
+
position?: 'top' | 'center' | 'bottom';
|
|
49
|
+
/** Whether the modal should be fullscreen. */
|
|
50
|
+
fullscreen?: boolean;
|
|
51
|
+
/** Whether to show the fullscreen toggle button. */
|
|
52
|
+
showFullscreenToggle?: boolean;
|
|
53
|
+
/** Whether to show the close button. */
|
|
54
|
+
showClose?: boolean;
|
|
55
|
+
/** A custom CSS class to apply to the modal. */
|
|
56
|
+
customClass?: string;
|
|
57
|
+
|
|
58
|
+
/** The header content of the modal. */
|
|
59
|
+
header?: ReactNode;
|
|
60
|
+
/** The main content of the modal. */
|
|
61
|
+
children: ReactNode;
|
|
62
|
+
/** The footer content of the modal. */
|
|
63
|
+
footer?: ReactNode;
|
|
64
|
+
|
|
65
|
+
/** A callback function to handle the close event. */
|
|
66
|
+
onClose?: () => void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A ref object for the ModusModal component.
|
|
71
|
+
*/
|
|
72
|
+
export interface ModusModalRef {
|
|
73
|
+
/** Opens the modal. */
|
|
74
|
+
openModal: () => void;
|
|
75
|
+
/** Closes the modal. */
|
|
76
|
+
closeModal: () => void;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Renders a Modus modal component.
|
|
81
|
+
* @param {ModusModalProps} props - The component props.
|
|
82
|
+
* @param {React.Ref<ModusModalRef>} ref - The ref object for the modal.
|
|
83
|
+
* @returns {JSX.Element} The rendered modal component.
|
|
84
|
+
*/
|
|
85
|
+
const ModusModal = forwardRef<ModusModalRef, ModusModalProps>(
|
|
86
|
+
(
|
|
87
|
+
{
|
|
88
|
+
modalId,
|
|
89
|
+
ariaLabel,
|
|
90
|
+
backdrop = 'default',
|
|
91
|
+
position = 'center',
|
|
92
|
+
fullscreen = false,
|
|
93
|
+
showFullscreenToggle = false,
|
|
94
|
+
showClose = true,
|
|
95
|
+
customClass,
|
|
96
|
+
header,
|
|
97
|
+
children,
|
|
98
|
+
footer,
|
|
99
|
+
onClose,
|
|
100
|
+
},
|
|
101
|
+
ref
|
|
102
|
+
) => {
|
|
103
|
+
const modalRef = useRef<HTMLModusWcModalElement>(null);
|
|
104
|
+
|
|
105
|
+
const openModal = () => {
|
|
106
|
+
if (modalRef.current) {
|
|
107
|
+
const dialog = modalRef.current.querySelector(
|
|
108
|
+
"dialog"
|
|
109
|
+
) as HTMLDialogElement;
|
|
110
|
+
if (dialog) {
|
|
111
|
+
dialog.showModal();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const closeModal = () => {
|
|
117
|
+
if (modalRef.current) {
|
|
118
|
+
const dialog = modalRef.current.querySelector(
|
|
119
|
+
"dialog"
|
|
120
|
+
) as HTMLDialogElement;
|
|
121
|
+
if (dialog) {
|
|
122
|
+
dialog.close();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
useImperativeHandle(ref, () => ({
|
|
128
|
+
openModal,
|
|
129
|
+
closeModal,
|
|
130
|
+
}));
|
|
131
|
+
|
|
132
|
+
// Handle modal events
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
const modal = modalRef.current;
|
|
135
|
+
if (modal) {
|
|
136
|
+
const handleClose = () => {
|
|
137
|
+
onClose?.();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const dialogElement = modal.querySelector("dialog");
|
|
141
|
+
if (dialogElement) {
|
|
142
|
+
dialogElement.addEventListener("close", handleClose);
|
|
143
|
+
return () => {
|
|
144
|
+
dialogElement.removeEventListener("close", handleClose);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}, [onClose]);
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<ModusWcModal
|
|
152
|
+
ref={modalRef}
|
|
153
|
+
modal-id={modalId}
|
|
154
|
+
aria-label={ariaLabel}
|
|
155
|
+
backdrop={backdrop}
|
|
156
|
+
position={position}
|
|
157
|
+
fullscreen={fullscreen}
|
|
158
|
+
show-fullscreen-toggle={showFullscreenToggle}
|
|
159
|
+
show-close={showClose}
|
|
160
|
+
custom-class={customClass}
|
|
161
|
+
>
|
|
162
|
+
{header && <div slot="header">{header}</div>}
|
|
163
|
+
<div slot="content">{children}</div>
|
|
164
|
+
{footer && <div slot="footer">{footer}</div>}
|
|
165
|
+
</ModusWcModal>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
ModusModal.displayName = "ModusModal";
|
|
171
|
+
|
|
172
|
+
export default ModusModal;
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Usage Pattern
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import { useRef } from "react";
|
|
179
|
+
import ModusButton from "./components/ModusButton";
|
|
180
|
+
import ModusModal, { type ModusModalRef } from "./components/ModusModal";
|
|
181
|
+
|
|
182
|
+
function MyComponent() {
|
|
183
|
+
const modalRef = useRef<ModusModalRef>(null);
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<>
|
|
187
|
+
<ModusButton
|
|
188
|
+
onButtonClick={() => {
|
|
189
|
+
modalRef.current?.openModal();
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
Open Modal
|
|
193
|
+
</ModusButton>
|
|
194
|
+
|
|
195
|
+
<ModusModal
|
|
196
|
+
ref={modalRef}
|
|
197
|
+
modalId="my-modal"
|
|
198
|
+
ariaLabel="Example modal"
|
|
199
|
+
onClose={() => {
|
|
200
|
+
console.log("Modal closed");
|
|
201
|
+
}}
|
|
202
|
+
header={
|
|
203
|
+
<div className="text-xl font-semibold text-foreground">
|
|
204
|
+
Modal Title
|
|
205
|
+
</div>
|
|
206
|
+
}
|
|
207
|
+
footer={
|
|
208
|
+
<div className="flex gap-2">
|
|
209
|
+
<ModusButton
|
|
210
|
+
variant="borderless"
|
|
211
|
+
onButtonClick={() => {
|
|
212
|
+
modalRef.current?.closeModal();
|
|
213
|
+
}}
|
|
214
|
+
>
|
|
215
|
+
Cancel
|
|
216
|
+
</ModusButton>
|
|
217
|
+
<ModusButton
|
|
218
|
+
onButtonClick={() => {
|
|
219
|
+
modalRef.current?.closeModal();
|
|
220
|
+
}}
|
|
221
|
+
>
|
|
222
|
+
Confirm
|
|
223
|
+
</ModusButton>
|
|
224
|
+
</div>
|
|
225
|
+
}
|
|
226
|
+
>
|
|
227
|
+
<div className="text-sm text-foreground opacity-80">
|
|
228
|
+
Modal content goes here.
|
|
229
|
+
</div>
|
|
230
|
+
</ModusModal>
|
|
231
|
+
</>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Key Patterns
|
|
237
|
+
|
|
238
|
+
### 1. forwardRef Pattern
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
const ModusModal = forwardRef<ModusModalRef, ModusModalProps>(
|
|
242
|
+
(props, ref) => {
|
|
243
|
+
// Component implementation
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 2. useImperativeHandle
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
useImperativeHandle(ref, () => ({
|
|
252
|
+
openModal,
|
|
253
|
+
closeModal,
|
|
254
|
+
}));
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### 3. Dialog Access
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
const openModal = () => {
|
|
261
|
+
if (modalRef.current) {
|
|
262
|
+
const dialog = modalRef.current.querySelector(
|
|
263
|
+
"dialog"
|
|
264
|
+
) as HTMLDialogElement;
|
|
265
|
+
if (dialog) {
|
|
266
|
+
dialog.showModal();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Critical**: Always use `querySelector("dialog")` - don't try to access dialog methods directly on the web component.
|
|
273
|
+
|
|
274
|
+
### 4. Slot Structure
|
|
275
|
+
|
|
276
|
+
Modus modals use slots for content:
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
<ModusWcModal>
|
|
280
|
+
{header && <div slot="header">{header}</div>}
|
|
281
|
+
<div slot="content">{children}</div>
|
|
282
|
+
{footer && <div slot="footer">{footer}</div>}
|
|
283
|
+
</ModusWcModal>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 5. Event Handling
|
|
287
|
+
|
|
288
|
+
Listen to the native dialog `close` event:
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
const modal = modalRef.current;
|
|
293
|
+
if (modal) {
|
|
294
|
+
const dialogElement = modal.querySelector("dialog");
|
|
295
|
+
if (dialogElement) {
|
|
296
|
+
dialogElement.addEventListener("close", handleClose);
|
|
297
|
+
return () => {
|
|
298
|
+
dialogElement.removeEventListener("close", handleClose);
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}, [onClose]);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Modal Variants
|
|
306
|
+
|
|
307
|
+
### Centered Modal (Default)
|
|
308
|
+
|
|
309
|
+
```tsx
|
|
310
|
+
<ModusModal
|
|
311
|
+
ref={modalRef}
|
|
312
|
+
modalId="centered-modal"
|
|
313
|
+
position="center"
|
|
314
|
+
// ... other props
|
|
315
|
+
/>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Top Positioned Modal
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
<ModusModal
|
|
322
|
+
ref={modalRef}
|
|
323
|
+
modalId="top-modal"
|
|
324
|
+
position="top"
|
|
325
|
+
// ... other props
|
|
326
|
+
/>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Bottom Positioned Modal
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
<ModusModal
|
|
333
|
+
ref={modalRef}
|
|
334
|
+
modalId="bottom-modal"
|
|
335
|
+
position="bottom"
|
|
336
|
+
// ... other props
|
|
337
|
+
/>
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Fullscreen Modal
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
<ModusModal
|
|
344
|
+
ref={modalRef}
|
|
345
|
+
modalId="fullscreen-modal"
|
|
346
|
+
fullscreen={true}
|
|
347
|
+
showFullscreenToggle={true}
|
|
348
|
+
// ... other props
|
|
349
|
+
/>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Static Backdrop Modal
|
|
353
|
+
|
|
354
|
+
```tsx
|
|
355
|
+
<ModusModal
|
|
356
|
+
ref={modalRef}
|
|
357
|
+
modalId="static-modal"
|
|
358
|
+
backdrop="static"
|
|
359
|
+
// ... other props
|
|
360
|
+
/>
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Common Mistakes
|
|
364
|
+
|
|
365
|
+
1. **Missing displayName**: Always set `displayName` for forwardRef components
|
|
366
|
+
2. **Wrong dialog access**: Use `querySelector("dialog")`, not direct property access
|
|
367
|
+
3. **Missing null checks**: Always check `modalRef.current` before use
|
|
368
|
+
4. **Wrong event**: Listen to `close` event on dialog element, not web component
|
|
369
|
+
5. **Forgetting cleanup**: Always remove event listeners in cleanup function
|
|
370
|
+
|
|
371
|
+
## Testing Checklist
|
|
372
|
+
|
|
373
|
+
- [ ] Modal opens when `openModal()` is called
|
|
374
|
+
- [ ] Modal closes when `closeModal()` is called
|
|
375
|
+
- [ ] `onClose` callback fires when modal closes
|
|
376
|
+
- [ ] ESC key closes modal (if backdrop is default)
|
|
377
|
+
- [ ] Clicking outside closes modal (if backdrop is default)
|
|
378
|
+
- [ ] Static backdrop prevents outside clicks from closing
|
|
379
|
+
- [ ] Slots render correctly (header, content, footer)
|
|
380
|
+
- [ ] ARIA label is set for accessibility
|
|
381
|
+
|
|
382
|
+
## Related Files
|
|
383
|
+
|
|
384
|
+
- `src/components/ModusModal.tsx` - Complete implementation
|
|
385
|
+
- `src/demos/modal-demo/page.tsx` - Usage examples
|
|
386
|
+
- `.cursor/rules/modus-modal-implementation-react.mdc` - Detailed documentation
|