@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,1096 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Integration patterns for Modus Web Components with Angular standalone wrapper components
|
|
3
|
+
globs: ["**/*.ts", "**/*.html", "**/*.scss", "**/*.css"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Modus Web Components Angular Wrappers
|
|
8
|
+
|
|
9
|
+
Comprehensive guide for Angular standalone wrapper components that wrap Modus Web Components, focusing on signal-based APIs, TypeScript interfaces, and Tailwind CSS integration.
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
1. [Overview](#overview)
|
|
14
|
+
2. [Wrapper Component Pattern](#wrapper-component-pattern)
|
|
15
|
+
3. [Signal-Based API](#signal-based-api)
|
|
16
|
+
4. [Component Structure](#component-structure)
|
|
17
|
+
5. [Icon Integration](#icon-integration)
|
|
18
|
+
6. [Styling Guidelines](#styling-guidelines)
|
|
19
|
+
7. [Best Practices](#best-practices)
|
|
20
|
+
- [Type Verification - Critical Rules](#6-type-verification---critical-rules)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Overview
|
|
25
|
+
|
|
26
|
+
### What Are These Components?
|
|
27
|
+
|
|
28
|
+
This application uses **Angular standalone wrapper components** that wrap Modus Web Components (`modus-wc-*`). These wrappers:
|
|
29
|
+
|
|
30
|
+
- Provide **Angular-native APIs** using signals (`input()`, `output()`)
|
|
31
|
+
- Offer **TypeScript type safety** with exported interfaces
|
|
32
|
+
- Maintain **consistency** across the application
|
|
33
|
+
- Enable **future flexibility** to swap implementations
|
|
34
|
+
- Follow **Angular 20+** best practices with modern control flow
|
|
35
|
+
|
|
36
|
+
### Available Wrapper Components
|
|
37
|
+
|
|
38
|
+
All wrapper components are located in `src/app/components/` and exported from `src/app/components/index.ts`:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import {
|
|
42
|
+
ModusButtonComponent,
|
|
43
|
+
ModusAlertComponent,
|
|
44
|
+
ModusIconComponent,
|
|
45
|
+
ModusTextInputComponent,
|
|
46
|
+
ModusSelectComponent,
|
|
47
|
+
// ... and many more
|
|
48
|
+
} from '@/app/components';
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Reference**: See `src/app/components/index.ts` for the complete list of available components.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Wrapper Component Pattern
|
|
56
|
+
|
|
57
|
+
### Standard Component Structure
|
|
58
|
+
|
|
59
|
+
All wrapper components follow this consistent pattern:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
|
|
63
|
+
import { CommonModule } from '@angular/common';
|
|
64
|
+
import { ModusWcButton } from '@trimble-oss/moduswebcomponents-angular';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Type definitions for component props
|
|
68
|
+
*/
|
|
69
|
+
export type ButtonColor = 'primary' | 'secondary' | 'tertiary' | 'warning' | 'danger';
|
|
70
|
+
export type ButtonVariant = 'filled' | 'outlined' | 'borderless';
|
|
71
|
+
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Props interface (optional but recommended for documentation)
|
|
75
|
+
*/
|
|
76
|
+
export interface ModusButtonProps {
|
|
77
|
+
color?: ButtonColor;
|
|
78
|
+
variant?: ButtonVariant;
|
|
79
|
+
size?: ButtonSize;
|
|
80
|
+
disabled?: boolean;
|
|
81
|
+
// ... other props
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@Component({
|
|
85
|
+
selector: 'modus-button', // ← Users invoke with <modus-button>, NOT <modus-wc-button>
|
|
86
|
+
standalone: true,
|
|
87
|
+
imports: [CommonModule, ModusWcButton],
|
|
88
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
89
|
+
template: `
|
|
90
|
+
<!-- Inside the wrapper, we use modus-wc-button -->
|
|
91
|
+
<modus-wc-button
|
|
92
|
+
[color]="color()"
|
|
93
|
+
[variant]="variant()"
|
|
94
|
+
[size]="size()"
|
|
95
|
+
[disabled]="disabled()"
|
|
96
|
+
(buttonClick)="handleButtonClick($event)"
|
|
97
|
+
>
|
|
98
|
+
<ng-content></ng-content>
|
|
99
|
+
</modus-wc-button>
|
|
100
|
+
`,
|
|
101
|
+
})
|
|
102
|
+
export class ModusButtonComponent {
|
|
103
|
+
// Signal-based inputs
|
|
104
|
+
readonly color = input<ButtonColor>('primary');
|
|
105
|
+
readonly variant = input<ButtonVariant>('filled');
|
|
106
|
+
readonly size = input<ButtonSize>('md');
|
|
107
|
+
readonly disabled = input<boolean>(false);
|
|
108
|
+
|
|
109
|
+
// Signal-based output
|
|
110
|
+
readonly buttonClick = output<MouseEvent | KeyboardEvent>();
|
|
111
|
+
|
|
112
|
+
// Event handler
|
|
113
|
+
handleButtonClick(event: CustomEvent<MouseEvent | KeyboardEvent>): void {
|
|
114
|
+
if (!this.disabled()) {
|
|
115
|
+
this.buttonClick.emit(event.detail);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Important**:
|
|
122
|
+
|
|
123
|
+
- **Users invoke wrapper components** with the wrapper selector: `<modus-button>`, `<modus-alert>`, `<modus-icon>`, etc.
|
|
124
|
+
- **Inside wrapper templates**, the underlying web component is used: `<modus-wc-button>`, `<modus-wc-alert>`, `<modus-wc-icon>`, etc.
|
|
125
|
+
- **Never use `<modus-wc-*>` directly** in application templates - always use the wrapper components.
|
|
126
|
+
|
|
127
|
+
### Key Characteristics
|
|
128
|
+
|
|
129
|
+
1. **Standalone Components**: All wrappers use `standalone: true`
|
|
130
|
+
2. **Signal-Based API**: Use `input()` and `output()` instead of `@Input()` and `@Output()`
|
|
131
|
+
3. **OnPush Change Detection**: Use `ChangeDetectionStrategy.OnPush` for performance
|
|
132
|
+
4. **Type Exports**: Export types and interfaces for reuse
|
|
133
|
+
5. **Direct Import**: Import Modus Web Components from `@trimble-oss/moduswebcomponents-angular`
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Signal-Based API
|
|
138
|
+
|
|
139
|
+
### Input Signals
|
|
140
|
+
|
|
141
|
+
Use `input()` for component properties:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// Required input
|
|
145
|
+
readonly alertTitle = input.required<string>();
|
|
146
|
+
|
|
147
|
+
// Optional input with default
|
|
148
|
+
readonly color = input<ButtonColor>('primary');
|
|
149
|
+
|
|
150
|
+
// Optional input without default
|
|
151
|
+
readonly icon = input<string | undefined>();
|
|
152
|
+
|
|
153
|
+
// Optional callback input
|
|
154
|
+
readonly onButtonClick = input<(() => void) | undefined>();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Output Signals
|
|
158
|
+
|
|
159
|
+
Use `output()` for component events:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Event output
|
|
163
|
+
readonly buttonClick = output<MouseEvent | KeyboardEvent>();
|
|
164
|
+
|
|
165
|
+
// Void event output
|
|
166
|
+
readonly dismiss = output<void>();
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Template Usage
|
|
170
|
+
|
|
171
|
+
```html
|
|
172
|
+
<!-- ✅ CORRECT: Use wrapper component selectors -->
|
|
173
|
+
<modus-button [color]="color()" [disabled]="disabled()" (buttonClick)="handleClick($event)">
|
|
174
|
+
Click Me
|
|
175
|
+
</modus-button>
|
|
176
|
+
|
|
177
|
+
<modus-alert alertTitle="Success" variant="success" [dismissible]="true" />
|
|
178
|
+
|
|
179
|
+
<modus-icon name="add" size="md" [decorative]="false" ariaLabel="Add item" />
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Important**:
|
|
183
|
+
|
|
184
|
+
- Always use **wrapper component selectors** (`<modus-button>`, `<modus-alert>`, etc.) in your templates
|
|
185
|
+
- Never use the underlying web component selectors (`<modus-wc-button>`, `<modus-wc-alert>`, etc.) directly
|
|
186
|
+
- Always call signal functions with `()` in templates: `color()`, `disabled()`, etc.
|
|
187
|
+
|
|
188
|
+
**Reference**: See `src/app/components/theme-demo.component.ts` for complete usage examples.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Component Structure
|
|
193
|
+
|
|
194
|
+
### File Organization
|
|
195
|
+
|
|
196
|
+
Each wrapper component follows this structure:
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
src/app/components/
|
|
200
|
+
├── modus-button.component.ts # Component implementation
|
|
201
|
+
├── modus-alert.component.ts # Another component
|
|
202
|
+
└── index.ts # Exports all components
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Component Template Pattern
|
|
206
|
+
|
|
207
|
+
All wrapper templates follow this pattern (inside the wrapper component):
|
|
208
|
+
|
|
209
|
+
1. **Wrap the Modus Web Component**: Use `<modus-wc-*>` internally
|
|
210
|
+
2. **Bind Signal Inputs**: Use `[prop]="signal()"` syntax
|
|
211
|
+
3. **Handle Events**: Use `(event)="handler($event)"`
|
|
212
|
+
4. **Project Content**: Use `<ng-content>` for slots
|
|
213
|
+
5. **Use Modern Control Flow**: Use `@if`, `@for`, `@switch` (Angular 17+)
|
|
214
|
+
|
|
215
|
+
### Usage Pattern: Application Templates vs. Wrapper Templates
|
|
216
|
+
|
|
217
|
+
**In Application Templates** (what you write in your components):
|
|
218
|
+
|
|
219
|
+
```html
|
|
220
|
+
<!-- ✅ CORRECT: Use wrapper component selectors -->
|
|
221
|
+
<modus-button
|
|
222
|
+
[color]="color()"
|
|
223
|
+
[size]="size()"
|
|
224
|
+
icon="add"
|
|
225
|
+
iconPosition="left"
|
|
226
|
+
(buttonClick)="handleClick($event)"
|
|
227
|
+
>
|
|
228
|
+
Click Me
|
|
229
|
+
</modus-button>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Inside Wrapper Component Templates** (internal implementation):
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
template: `
|
|
236
|
+
<!-- Inside wrapper: use modus-wc-button -->
|
|
237
|
+
<modus-wc-button
|
|
238
|
+
[color]="color()"
|
|
239
|
+
[size]="size()"
|
|
240
|
+
(buttonClick)="handleButtonClick($event)"
|
|
241
|
+
>
|
|
242
|
+
@if (icon() && iconPosition() === 'left') {
|
|
243
|
+
<i class="modus-icons mr-2">{{ icon() }}</i>
|
|
244
|
+
}
|
|
245
|
+
<ng-content></ng-content>
|
|
246
|
+
@if (icon() && iconPosition() === 'right') {
|
|
247
|
+
<i class="modus-icons ml-2">{{ icon() }}</i>
|
|
248
|
+
}
|
|
249
|
+
@if (icon() && iconPosition() === 'only') {
|
|
250
|
+
<i class="modus-icons">{{ icon() }}</i>
|
|
251
|
+
}
|
|
252
|
+
</modus-wc-button>
|
|
253
|
+
`,
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Key Distinction:**
|
|
257
|
+
|
|
258
|
+
- **Application code**: Always use `<modus-button>`, `<modus-alert>`, `<modus-icon>`, etc.
|
|
259
|
+
- **Wrapper implementation**: Uses `<modus-wc-button>`, `<modus-wc-alert>`, `<modus-wc-icon>`, etc.
|
|
260
|
+
|
|
261
|
+
**Reference**:
|
|
262
|
+
|
|
263
|
+
- See `src/app/components/modus-button.component.ts` for wrapper implementation
|
|
264
|
+
- See `src/app/components/theme-demo.component.ts` for application usage examples
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Icon Integration
|
|
269
|
+
|
|
270
|
+
### Using Modus Icons in Templates
|
|
271
|
+
|
|
272
|
+
Use the `<modus-icon>` wrapper component or direct `<i>` tags:
|
|
273
|
+
|
|
274
|
+
```html
|
|
275
|
+
<!-- Using modus-icon wrapper component -->
|
|
276
|
+
<modus-icon name="add" size="md" variant="outlined" [decorative]="false" ariaLabel="Add item" />
|
|
277
|
+
|
|
278
|
+
<!-- Using direct icon tag (for simple cases) -->
|
|
279
|
+
<i class="modus-icons mr-2">add</i>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Icon Spacing with Tailwind
|
|
283
|
+
|
|
284
|
+
Always use Tailwind classes for icon spacing, never inline styles:
|
|
285
|
+
|
|
286
|
+
```html
|
|
287
|
+
<!-- ✅ CORRECT: Use Tailwind classes -->
|
|
288
|
+
<i class="modus-icons mr-2">add</i>
|
|
289
|
+
<i class="modus-icons ml-2">chevron-right</i>
|
|
290
|
+
|
|
291
|
+
<!-- ❌ WRONG: Inline styles -->
|
|
292
|
+
<i class="modus-icons" [style.margin-right.px]="8">add</i>
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Icon Names
|
|
296
|
+
|
|
297
|
+
- Use **kebab-case** for icon names (e.g., `check_circle`, `user_add`)
|
|
298
|
+
- Refer to `data/modusIcons.ts` for all valid icon names
|
|
299
|
+
- Never use other icon libraries (Font Awesome, Material Icons, etc.)
|
|
300
|
+
|
|
301
|
+
**Reference**: See `src/app/components/modus-icon.component.ts` for the icon wrapper implementation.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Styling Guidelines
|
|
306
|
+
|
|
307
|
+
### Design System Colors
|
|
308
|
+
|
|
309
|
+
Always use design system Tailwind classes from `src/styles.css`:
|
|
310
|
+
|
|
311
|
+
```html
|
|
312
|
+
<!-- ✅ CORRECT: Design system colors -->
|
|
313
|
+
<div class="bg-background text-foreground">
|
|
314
|
+
<div class="bg-card text-card-foreground border-default">Content</div>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<!-- ❌ WRONG: Generic Tailwind colors -->
|
|
318
|
+
<div class="bg-blue-500 text-white">Content</div>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Border Utilities
|
|
322
|
+
|
|
323
|
+
Use custom border utilities for Tailwind v4 compatibility:
|
|
324
|
+
|
|
325
|
+
```html
|
|
326
|
+
<!-- ✅ CORRECT: Custom border utilities -->
|
|
327
|
+
<div class="border-default border-primary">
|
|
328
|
+
<div class="border-thick-success">
|
|
329
|
+
<div class="border-bottom-default">
|
|
330
|
+
<!-- ❌ WRONG: Tailwind border colors -->
|
|
331
|
+
<div class="border border-blue-500"></div>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Opacity Utilities
|
|
338
|
+
|
|
339
|
+
Use custom opacity utilities instead of Tailwind's `/80` syntax:
|
|
340
|
+
|
|
341
|
+
```html
|
|
342
|
+
<!-- ✅ CORRECT: Custom opacity utilities -->
|
|
343
|
+
<div class="text-foreground-80 bg-primary-20">
|
|
344
|
+
<!-- ❌ WRONG: Tailwind opacity syntax -->
|
|
345
|
+
<div class="text-foreground/80 bg-primary/20"></div>
|
|
346
|
+
</div>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Inline Styles
|
|
350
|
+
|
|
351
|
+
**Never use inline styles** except for truly dynamic values:
|
|
352
|
+
|
|
353
|
+
```html
|
|
354
|
+
<!-- ❌ WRONG: Static inline styles -->
|
|
355
|
+
<div style="padding: 1rem; margin: 0.5rem;">
|
|
356
|
+
<!-- ✅ CORRECT: Tailwind classes -->
|
|
357
|
+
<div class="p-4 m-2">
|
|
358
|
+
<!-- ✅ CORRECT: Dynamic values only -->
|
|
359
|
+
<div [style.width.px]="dynamicWidth"></div>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Reference**: See `src/styles.css` for all available design system utilities.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Best Practices
|
|
369
|
+
|
|
370
|
+
### 1. Component Patterns
|
|
371
|
+
|
|
372
|
+
- ✅ **Use signals** (`input()`, `output()`) for all component APIs
|
|
373
|
+
- ✅ **Export types and interfaces** for reuse across components
|
|
374
|
+
- ✅ **Use OnPush change detection** for performance
|
|
375
|
+
- ✅ **Keep components focused** - one responsibility per component
|
|
376
|
+
- ✅ **Document with JSDoc** - provide examples in component doc comments
|
|
377
|
+
|
|
378
|
+
### 2. Template Patterns
|
|
379
|
+
|
|
380
|
+
- ✅ **Use modern control flow** (`@if`, `@for`, `@switch`)
|
|
381
|
+
- ✅ **Bind to signals** with `()` syntax: `[prop]="signal()"`
|
|
382
|
+
- ✅ **Use Tailwind classes** for all styling
|
|
383
|
+
- ✅ **Project content** with `<ng-content>` for flexibility
|
|
384
|
+
- ❌ **Never use inline styles** (except dynamic values)
|
|
385
|
+
- ❌ **Never use old control flow** (`*ngIf`, `*ngFor`, etc.)
|
|
386
|
+
|
|
387
|
+
### 3. Event Handling
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// ✅ CORRECT: Handle events properly
|
|
391
|
+
handleButtonClick(event: CustomEvent<MouseEvent | KeyboardEvent>): void {
|
|
392
|
+
if (!this.disabled()) {
|
|
393
|
+
// Extract detail from CustomEvent
|
|
394
|
+
const actualEvent = event.detail;
|
|
395
|
+
this.buttonClick.emit(actualEvent);
|
|
396
|
+
|
|
397
|
+
// Call optional callback
|
|
398
|
+
const callback = this.onButtonClick();
|
|
399
|
+
if (callback) {
|
|
400
|
+
callback();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### 4. Accessibility
|
|
407
|
+
|
|
408
|
+
- ✅ **Always provide `ariaLabel`** for icon-only buttons or interactive elements without visible text
|
|
409
|
+
- ✅ **Use semantic HTML** where possible
|
|
410
|
+
- ✅ **Support keyboard navigation** - Modus components handle this, but verify in wrappers
|
|
411
|
+
- ✅ **Test with screen readers** for critical components
|
|
412
|
+
|
|
413
|
+
### 5. Type Safety
|
|
414
|
+
|
|
415
|
+
- ✅ **Export type definitions** for component props
|
|
416
|
+
- ✅ **Use TypeScript interfaces** for complex props
|
|
417
|
+
- ✅ **Type event outputs** correctly: `output<MouseEvent>()`
|
|
418
|
+
- ✅ **Use strict null checks** for optional props: `input<string | undefined>()`
|
|
419
|
+
|
|
420
|
+
### 6. Type Verification - Critical Rules
|
|
421
|
+
|
|
422
|
+
⚠️ **ALWAYS verify TypeScript types from `@trimble-oss/moduswebcomponents` package before using properties.**
|
|
423
|
+
|
|
424
|
+
**Common Pitfalls to Avoid**:
|
|
425
|
+
|
|
426
|
+
#### ❌ Don't Assume React Examples Match Angular Types
|
|
427
|
+
|
|
428
|
+
React examples may show properties that don't exist in TypeScript interfaces:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
// ❌ WRONG: Assumed IBreadcrumb has 'current' property
|
|
432
|
+
{ label: 'Home', url: '/', current: true } // ❌ Type error!
|
|
433
|
+
|
|
434
|
+
// ✅ CORRECT: IBreadcrumb only has 'label' and optional 'url'
|
|
435
|
+
{ label: 'Home', url: '/' } // ✅ Valid
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**Always verify the actual type**:
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
import type { IBreadcrumb } from '@trimble-oss/moduswebcomponents';
|
|
442
|
+
|
|
443
|
+
// IBreadcrumb interface:
|
|
444
|
+
interface IBreadcrumb {
|
|
445
|
+
label: string; // Required
|
|
446
|
+
url?: string; // Optional
|
|
447
|
+
// No 'current' property exists!
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
#### ❌ Don't Assume Multi-Select Changes Value Type
|
|
452
|
+
|
|
453
|
+
Even in multi-select mode, the `value` property remains a `string`, not `string[]`:
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
// ❌ WRONG: Assumed multi-select means value is string[]
|
|
457
|
+
<modus-autocomplete
|
|
458
|
+
[multiSelect]="true"
|
|
459
|
+
[value]="selectedItems()" // ❌ Type error! value expects string
|
|
460
|
+
/>
|
|
461
|
+
|
|
462
|
+
// ✅ CORRECT: Don't bind value for multi-select, let component manage state
|
|
463
|
+
<modus-autocomplete
|
|
464
|
+
[multiSelect]="true"
|
|
465
|
+
[items]="items()" // items with selected: true handle multi-select
|
|
466
|
+
(itemSelect)="handleSelection($event)"
|
|
467
|
+
/>
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
#### ❌ Don't Add Properties Not in Type Definitions
|
|
471
|
+
|
|
472
|
+
Check the actual interface before using properties:
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
// ❌ WRONG: Assumed IAutocompleteNoResults has 'severity'
|
|
476
|
+
const noResults = {
|
|
477
|
+
label: 'No results',
|
|
478
|
+
subLabel: 'Try again',
|
|
479
|
+
severity: 'info', // ❌ Property doesn't exist!
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
// ✅ CORRECT: Check actual interface
|
|
483
|
+
interface IAutocompleteNoResults {
|
|
484
|
+
label: string;
|
|
485
|
+
subLabel: string;
|
|
486
|
+
// No 'severity' property!
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
#### ✅ Verification Checklist
|
|
491
|
+
|
|
492
|
+
Before using any property or interface from Modus components:
|
|
493
|
+
|
|
494
|
+
1. **Check the actual TypeScript type**:
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
import type {
|
|
498
|
+
IBreadcrumb,
|
|
499
|
+
IAutocompleteItem,
|
|
500
|
+
IAutocompleteNoResults,
|
|
501
|
+
} from '@trimble-oss/moduswebcomponents';
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
2. **Inspect existing implementations** in the codebase:
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
// Look at src/app/components/theme-demo.component.ts for examples
|
|
508
|
+
breadcrumbItems: IBreadcrumb[] = [
|
|
509
|
+
{ label: 'Home', url: '/home' },
|
|
510
|
+
{ label: 'Components' }, // No 'current' property
|
|
511
|
+
];
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
3. **Read the wrapper component's interface** in `src/app/components/`:
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
// Check src/app/components/modus-breadcrumbs.component.ts
|
|
518
|
+
// to see the actual props interface
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
4. **Verify component documentation** - don't rely solely on React examples
|
|
522
|
+
|
|
523
|
+
**Key Principle**: The TypeScript compiler will catch these errors, but always verify types from the source package (`@trimble-oss/moduswebcomponents`) rather than assuming React examples match Angular types exactly.
|
|
524
|
+
|
|
525
|
+
### 7. Angular Signals - Common Pitfalls
|
|
526
|
+
|
|
527
|
+
⚠️ **Always pass values directly to `signal()`, not functions.**
|
|
528
|
+
|
|
529
|
+
#### ❌ Don't Use IIFE Pattern with `signal()`
|
|
530
|
+
|
|
531
|
+
The `signal()` function expects a value, not a function. Using an IIFE (Immediately Invoked Function Expression) pattern inside `signal()` will cause TypeScript parsing errors:
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
// ❌ WRONG: Passing a function to signal()
|
|
535
|
+
readonly minDate = signal<string>(() => {
|
|
536
|
+
const today = new Date();
|
|
537
|
+
return today.toISOString().split('T')[0];
|
|
538
|
+
});
|
|
539
|
+
// Error: Argument of type '() => string' is not assignable to parameter of type 'string'
|
|
540
|
+
|
|
541
|
+
// ❌ ALSO WRONG: IIFE syntax confuses the parser
|
|
542
|
+
readonly minDate = signal<string>(() => {
|
|
543
|
+
const today = new Date();
|
|
544
|
+
return today.toISOString().split('T')[0];
|
|
545
|
+
}()); // ← Still causes parsing errors!
|
|
546
|
+
// Error: Expected "=>" but found ")"
|
|
547
|
+
// Error: TS2345: Argument of type '() => string' is not assignable to parameter of type 'string'
|
|
548
|
+
|
|
549
|
+
// ✅ CORRECT: Use helper methods to compute values
|
|
550
|
+
private getTodayDate(): string {
|
|
551
|
+
const today = new Date();
|
|
552
|
+
return today.toISOString().split('T')[0];
|
|
553
|
+
}
|
|
554
|
+
readonly minDate = signal<string>(this.getTodayDate()); // ✅ Clean and readable
|
|
555
|
+
|
|
556
|
+
// ✅ ALSO CORRECT: Compute inline if simple
|
|
557
|
+
readonly today = signal<string>(new Date().toISOString().split('T')[0]);
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
#### ❌ Don't Omit Event Parameters
|
|
561
|
+
|
|
562
|
+
Event handlers must accept the event parameter even if you don't use it:
|
|
563
|
+
|
|
564
|
+
```typescript
|
|
565
|
+
// ❌ WRONG: Missing event parameter
|
|
566
|
+
handleCheckboxChange(): void {
|
|
567
|
+
this.featureEnabled.update((current) => !current);
|
|
568
|
+
}
|
|
569
|
+
// Error: Expected 1 arguments, but got 0
|
|
570
|
+
|
|
571
|
+
// ✅ CORRECT: Accept the event parameter
|
|
572
|
+
handleCheckboxChange(event: InputEvent): void {
|
|
573
|
+
this.featureEnabled.update((current) => !current);
|
|
574
|
+
// You can use the event if needed, or ignore it
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
#### ✅ Signal Initialization Patterns
|
|
579
|
+
|
|
580
|
+
**Pattern 1: Direct Value**
|
|
581
|
+
|
|
582
|
+
```typescript
|
|
583
|
+
readonly count = signal<number>(0);
|
|
584
|
+
readonly name = signal<string>('John');
|
|
585
|
+
readonly isActive = signal<boolean>(false);
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
**Pattern 2: Computed Initial Value (Helper Method)**
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
private getTodayDate(): string {
|
|
592
|
+
const date = new Date();
|
|
593
|
+
return date.toISOString().split('T')[0];
|
|
594
|
+
}
|
|
595
|
+
readonly today = signal<string>(this.getTodayDate()); // ✅ Use helper method
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
**Pattern 3: Helper Function**
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
private initializeDate(): string {
|
|
602
|
+
const today = new Date();
|
|
603
|
+
return today.toISOString().split('T')[0];
|
|
604
|
+
}
|
|
605
|
+
readonly minDate = signal<string>(this.initializeDate());
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**Pattern 4: Computed Signal (for derived values)**
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
readonly firstName = signal<string>('John');
|
|
612
|
+
readonly lastName = signal<string>('Doe');
|
|
613
|
+
readonly fullName = computed(() => `${this.firstName()} ${this.lastName()}`);
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### 8. Modus Divider Component - Critical Usage Rules
|
|
617
|
+
|
|
618
|
+
⚠️ **The divider component requires both property AND attribute bindings in the wrapper to work correctly.**
|
|
619
|
+
|
|
620
|
+
#### ✅ CORRECT: How to Use Divider Component
|
|
621
|
+
|
|
622
|
+
**Always use the Angular wrapper component (`modus-divider`), not the web component directly:**
|
|
623
|
+
|
|
624
|
+
```html
|
|
625
|
+
<!-- ✅ CORRECT: Use Angular wrapper with property bindings -->
|
|
626
|
+
<modus-divider [orientation]="'horizontal'" />
|
|
627
|
+
|
|
628
|
+
<!-- ✅ CORRECT: With color -->
|
|
629
|
+
<modus-divider [orientation]="'horizontal'" color="primary" />
|
|
630
|
+
|
|
631
|
+
<!-- ✅ CORRECT: With content -->
|
|
632
|
+
<modus-divider [orientation]="'horizontal'" content="OR" />
|
|
633
|
+
|
|
634
|
+
<!-- ✅ CORRECT: Vertical divider in flex container -->
|
|
635
|
+
<div class="flex items-stretch gap-4 min-h-[60px]">
|
|
636
|
+
<div>Left Content</div>
|
|
637
|
+
<modus-divider [orientation]="'vertical'" />
|
|
638
|
+
<div>Right Content</div>
|
|
639
|
+
</div>
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
#### ❌ INCORRECT: Don't Use Web Component Directly
|
|
643
|
+
|
|
644
|
+
**Never use `modus-wc-divider` directly in application code:**
|
|
645
|
+
|
|
646
|
+
```html
|
|
647
|
+
<!-- ❌ WRONG: Don't use web component directly -->
|
|
648
|
+
<modus-wc-divider orientation="horizontal"></modus-wc-divider>
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
**Why?**: The Angular wrapper (`modus-divider`) includes critical fixes for attribute binding that ensure the web component receives the correct attributes. Using the web component directly bypasses these fixes.
|
|
652
|
+
|
|
653
|
+
#### ✅ Horizontal Divider Requirements
|
|
654
|
+
|
|
655
|
+
Horizontal dividers work in any vertical layout:
|
|
656
|
+
|
|
657
|
+
```html
|
|
658
|
+
<!-- ✅ CORRECT: Horizontal divider in vertical flex container -->
|
|
659
|
+
<div class="flex flex-col gap-2">
|
|
660
|
+
<p>Content above</p>
|
|
661
|
+
<modus-divider [orientation]="'horizontal'" />
|
|
662
|
+
<p>Content below</p>
|
|
663
|
+
</div>
|
|
664
|
+
|
|
665
|
+
<!-- ✅ CORRECT: Horizontal divider with color -->
|
|
666
|
+
<modus-divider [orientation]="'horizontal'" color="primary" />
|
|
667
|
+
|
|
668
|
+
<!-- ✅ CORRECT: Horizontal divider with content -->
|
|
669
|
+
<modus-divider [orientation]="'horizontal'" content="OR" />
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
#### ✅ Vertical Divider Requirements
|
|
673
|
+
|
|
674
|
+
**Critical**: Vertical dividers need a flex container with `items-stretch`:
|
|
675
|
+
|
|
676
|
+
```html
|
|
677
|
+
<!-- ✅ CORRECT: Vertical divider with items-stretch -->
|
|
678
|
+
<div class="flex items-stretch gap-4 min-h-[60px]">
|
|
679
|
+
<div class="flex items-center">Left</div>
|
|
680
|
+
<modus-divider [orientation]="'vertical'" />
|
|
681
|
+
<div class="flex items-center">Right</div>
|
|
682
|
+
</div>
|
|
683
|
+
|
|
684
|
+
<!-- ✅ CORRECT: Vertical divider with explicit height -->
|
|
685
|
+
<div class="flex items-stretch gap-4 min-h-[120px]">
|
|
686
|
+
<div class="flex-1">Left Section</div>
|
|
687
|
+
<modus-divider [orientation]="'vertical'" />
|
|
688
|
+
<div class="flex-1">Right Section</div>
|
|
689
|
+
</div>
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
#### ❌ Vertical Divider Mistakes
|
|
693
|
+
|
|
694
|
+
```html
|
|
695
|
+
<!-- ❌ WRONG: Missing items-stretch -->
|
|
696
|
+
<div class="flex items-center gap-4">
|
|
697
|
+
<div>Left</div>
|
|
698
|
+
<modus-divider [orientation]="'vertical'" />
|
|
699
|
+
<div>Right</div>
|
|
700
|
+
</div>
|
|
701
|
+
<!-- Vertical divider won't be visible - needs items-stretch -->
|
|
702
|
+
|
|
703
|
+
<!-- ❌ WRONG: No explicit height -->
|
|
704
|
+
<div class="flex gap-4">
|
|
705
|
+
<div>Left</div>
|
|
706
|
+
<modus-divider [orientation]="'vertical'" />
|
|
707
|
+
<div>Right</div>
|
|
708
|
+
</div>
|
|
709
|
+
<!-- Vertical divider might not have height to render -->
|
|
710
|
+
|
|
711
|
+
<!-- ❌ WRONG: items-center prevents stretching -->
|
|
712
|
+
<div class="flex items-center gap-4 min-h-[60px]">
|
|
713
|
+
<div>Left</div>
|
|
714
|
+
<modus-divider [orientation]="'vertical'" />
|
|
715
|
+
<div>Right</div>
|
|
716
|
+
</div>
|
|
717
|
+
<!-- items-center doesn't allow divider to stretch -->
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
#### ✅ Key Rules for Divider Visibility
|
|
721
|
+
|
|
722
|
+
1. **Always use `modus-divider` (Angular wrapper), not `modus-wc-divider`**
|
|
723
|
+
2. **Horizontal dividers**: Work in any vertical layout - no special requirements
|
|
724
|
+
3. **Vertical dividers**: **MUST** use `items-stretch` in flex container:
|
|
725
|
+
- ✅ `class="flex items-stretch gap-4 min-h-[60px]"`
|
|
726
|
+
- ❌ `class="flex items-center gap-4"` (won't work)
|
|
727
|
+
4. **Vertical dividers**: **MUST** have explicit height on parent:
|
|
728
|
+
- ✅ `min-h-[60px]` or `min-h-[120px]`
|
|
729
|
+
- ❌ No height specified (divider might not render)
|
|
730
|
+
5. **Use property bindings**: `[orientation]="'horizontal'"` (not string attributes)
|
|
731
|
+
|
|
732
|
+
#### Component Properties
|
|
733
|
+
|
|
734
|
+
```typescript
|
|
735
|
+
// All properties use signal-based API
|
|
736
|
+
<modus-divider
|
|
737
|
+
[orientation]="'horizontal' | 'vertical'" // Required: specifies divider direction
|
|
738
|
+
[color]="'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'danger' | 'high-contrast'" // Optional
|
|
739
|
+
[content]="string" // Optional: text to display on divider
|
|
740
|
+
[position]="'start' | 'center' | 'end'" // Optional: where content appears (default: 'center')
|
|
741
|
+
[responsive]="boolean" // Optional: responsive behavior (default: true)
|
|
742
|
+
/>
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
#### Wrapper Component Implementation Details
|
|
746
|
+
|
|
747
|
+
**Why the wrapper needs both property and attribute bindings:**
|
|
748
|
+
|
|
749
|
+
The divider web component reads values from HTML attributes, not just properties. The wrapper component (`modus-divider`) includes both bindings to ensure attributes are set:
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
// In modus-divider.component.ts template:
|
|
753
|
+
<modus-wc-divider
|
|
754
|
+
[color]="color()" // Property binding
|
|
755
|
+
[orientation]="orientation()" // Property binding
|
|
756
|
+
[attr.color]="color()" // Attribute binding (critical!)
|
|
757
|
+
[attr.orientation]="orientation()" // Attribute binding (critical!)
|
|
758
|
+
/>
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
**This dual binding is essential** - without attribute bindings, the web component won't receive the values and dividers won't be visible.
|
|
762
|
+
|
|
763
|
+
#### Troubleshooting
|
|
764
|
+
|
|
765
|
+
**Problem**: Dividers not visible
|
|
766
|
+
|
|
767
|
+
- ✅ Check you're using `modus-divider` (wrapper), not `modus-wc-divider`
|
|
768
|
+
- ✅ For vertical: Ensure parent has `items-stretch` and explicit height (`min-h-[...]`)
|
|
769
|
+
- ✅ For horizontal: Works in any vertical layout
|
|
770
|
+
|
|
771
|
+
**Problem**: Vertical divider not stretching
|
|
772
|
+
|
|
773
|
+
- ✅ Use `items-stretch` (not `items-center`)
|
|
774
|
+
- ✅ Add `min-h-[60px]` or similar to parent container
|
|
775
|
+
- ✅ Ensure parent is a flex container (`flex`)
|
|
776
|
+
|
|
777
|
+
**Problem**: Divider with content not showing text
|
|
778
|
+
|
|
779
|
+
- ✅ Check `content` property is set: `[content]="'OR'"`
|
|
780
|
+
- ✅ Check `position` is set if needed: `[position]="'center'"`
|
|
781
|
+
|
|
782
|
+
### 9. Component Documentation
|
|
783
|
+
|
|
784
|
+
All wrapper components should include:
|
|
785
|
+
|
|
786
|
+
````typescript
|
|
787
|
+
/**
|
|
788
|
+
* Angular wrapper for the Modus Web Component [component name].
|
|
789
|
+
*
|
|
790
|
+
* Provides a typed, signal-driven API that wraps the underlying
|
|
791
|
+
* Modus Web Component while maintaining full compatibility.
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* ```html
|
|
795
|
+
* <modus-button color="primary" size="md">
|
|
796
|
+
* Click Me
|
|
797
|
+
* </modus-button>
|
|
798
|
+
* ```
|
|
799
|
+
*/
|
|
800
|
+
@Component({ ... })
|
|
801
|
+
export class ModusButtonComponent { ... }
|
|
802
|
+
````
|
|
803
|
+
|
|
804
|
+
---
|
|
805
|
+
|
|
806
|
+
## Real-World Examples
|
|
807
|
+
|
|
808
|
+
### Example 1: Simple Button Wrapper
|
|
809
|
+
|
|
810
|
+
**Wrapper Implementation**: `src/app/components/modus-button.component.ts`
|
|
811
|
+
|
|
812
|
+
```typescript
|
|
813
|
+
@Component({
|
|
814
|
+
selector: 'modus-button', // ← Application uses <modus-button>
|
|
815
|
+
standalone: true,
|
|
816
|
+
imports: [CommonModule, ModusWcButton],
|
|
817
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
818
|
+
template: `
|
|
819
|
+
<modus-wc-button <!-- ← Wrapper uses <modus-wc-button>
|
|
820
|
+
internally --> [color]="color()" [variant]="variant()" [size]="size()" [disabled]="disabled()"
|
|
821
|
+
(buttonClick)="handleButtonClick($event)" >
|
|
822
|
+
<ng-content></ng-content>
|
|
823
|
+
</modus-wc-button>
|
|
824
|
+
`,
|
|
825
|
+
})
|
|
826
|
+
export class ModusButtonComponent {
|
|
827
|
+
readonly color = input<ButtonColor>('primary');
|
|
828
|
+
readonly variant = input<ButtonVariant>('filled');
|
|
829
|
+
readonly size = input<ButtonSize>('md');
|
|
830
|
+
readonly disabled = input<boolean>(false);
|
|
831
|
+
readonly buttonClick = output<MouseEvent | KeyboardEvent>();
|
|
832
|
+
|
|
833
|
+
handleButtonClick(event: CustomEvent<MouseEvent | KeyboardEvent>): void {
|
|
834
|
+
if (!this.disabled()) {
|
|
835
|
+
this.buttonClick.emit(event.detail);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
**Application Usage** (in your component templates):
|
|
842
|
+
|
|
843
|
+
```html
|
|
844
|
+
<!-- ✅ CORRECT: Use the wrapper selector -->
|
|
845
|
+
<modus-button [color]="'primary'" [size]="'md'" (buttonClick)="handleClick($event)">
|
|
846
|
+
Click Me
|
|
847
|
+
</modus-button>
|
|
848
|
+
|
|
849
|
+
<!-- ❌ WRONG: Never use modus-wc-button directly -->
|
|
850
|
+
<modus-wc-button [color]="'primary'">Click Me</modus-wc-button>
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
**Reference**: See `src/app/components/theme-demo.component.ts` for real-world usage examples.
|
|
854
|
+
|
|
855
|
+
### Example 2: Alert with Content Projection
|
|
856
|
+
|
|
857
|
+
**Wrapper Implementation**: `src/app/components/modus-alert.component.ts`
|
|
858
|
+
|
|
859
|
+
**Application Usage**:
|
|
860
|
+
|
|
861
|
+
```html
|
|
862
|
+
<!-- ✅ CORRECT: Use <modus-alert> in your templates -->
|
|
863
|
+
<modus-alert
|
|
864
|
+
alertTitle="Success"
|
|
865
|
+
alertDescription="Changes saved successfully."
|
|
866
|
+
variant="success"
|
|
867
|
+
[dismissible]="true"
|
|
868
|
+
(dismiss)="handleDismiss()"
|
|
869
|
+
/>
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
```typescript
|
|
873
|
+
@Component({
|
|
874
|
+
selector: 'modus-alert',
|
|
875
|
+
standalone: true,
|
|
876
|
+
imports: [CommonModule, ModusWcAlert],
|
|
877
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
878
|
+
template: `
|
|
879
|
+
<modus-wc-alert
|
|
880
|
+
[alertTitle]="alertTitle()"
|
|
881
|
+
[variant]="variant()"
|
|
882
|
+
[dismissible]="dismissible()"
|
|
883
|
+
(dismissClick)="handleDismissClick()"
|
|
884
|
+
>
|
|
885
|
+
<ng-content select="[slot='content']" slot="content" />
|
|
886
|
+
<ng-content select="[slot='button']" slot="button" />
|
|
887
|
+
</modus-wc-alert>
|
|
888
|
+
`,
|
|
889
|
+
})
|
|
890
|
+
export class ModusAlertComponent {
|
|
891
|
+
readonly alertTitle = input.required<string>();
|
|
892
|
+
readonly variant = input<'error' | 'info' | 'success' | 'warning'>('info');
|
|
893
|
+
readonly dismissible = input<boolean>(false);
|
|
894
|
+
readonly dismiss = output<void>();
|
|
895
|
+
|
|
896
|
+
handleDismissClick(): void {
|
|
897
|
+
this.dismiss.emit();
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
### Example 3: Icon Component with Computed Properties
|
|
903
|
+
|
|
904
|
+
**Wrapper Implementation**: `src/app/components/modus-icon.component.ts`
|
|
905
|
+
|
|
906
|
+
**Application Usage**:
|
|
907
|
+
|
|
908
|
+
```html
|
|
909
|
+
<!-- ✅ CORRECT: Use <modus-icon> in your templates -->
|
|
910
|
+
<modus-icon name="add" size="md" variant="outlined" [decorative]="false" ariaLabel="Add item" />
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
```typescript
|
|
914
|
+
@Component({
|
|
915
|
+
selector: 'modus-icon',
|
|
916
|
+
standalone: true,
|
|
917
|
+
imports: [CommonModule, ModusWcIcon],
|
|
918
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
919
|
+
template: `
|
|
920
|
+
<modus-wc-icon
|
|
921
|
+
[name]="name()"
|
|
922
|
+
[size]="size()"
|
|
923
|
+
[decorative]="decorative()"
|
|
924
|
+
[attr.aria-label]="computedAriaLabel()"
|
|
925
|
+
/>
|
|
926
|
+
`,
|
|
927
|
+
})
|
|
928
|
+
export class ModusIconComponent {
|
|
929
|
+
readonly name = input.required<string>();
|
|
930
|
+
readonly size = input<ModusIconSize>('md');
|
|
931
|
+
readonly decorative = input<boolean>(true);
|
|
932
|
+
readonly ariaLabel = input<string | undefined>();
|
|
933
|
+
|
|
934
|
+
// Computed property for conditional aria-label
|
|
935
|
+
readonly computedAriaLabel = computed(() =>
|
|
936
|
+
this.decorative() ? undefined : this.ariaLabel() ?? `${this.name()} icon`
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
---
|
|
942
|
+
|
|
943
|
+
### Example 4: Navbar with Search Input Expansion
|
|
944
|
+
|
|
945
|
+
**Wrapper Implementation**: `src/app/components/modus-navbar.component.ts`
|
|
946
|
+
|
|
947
|
+
**Application Usage**: Managing navbar search input expansion requires proper state management.
|
|
948
|
+
|
|
949
|
+
**⚠️ Important: Navbar Placement**
|
|
950
|
+
The navbar **MUST** be placed in `app.html` (Angular component template), **NOT** in `index.html` (static HTML file). This is because:
|
|
951
|
+
|
|
952
|
+
- `index.html` is static HTML that only loads the Angular app via `<app-root></app-root>`
|
|
953
|
+
- Angular bindings (`[property]`, `(event)`), signals, and component state only work in component templates
|
|
954
|
+
- The navbar needs access to component properties, methods, and reactive state
|
|
955
|
+
- Placing it in `index.html` would break all Angular functionality (bindings, events, state management)
|
|
956
|
+
|
|
957
|
+
```html
|
|
958
|
+
<modus-navbar
|
|
959
|
+
[userCard]="userCard"
|
|
960
|
+
[searchInputOpen]="searchInputOpen()"
|
|
961
|
+
[visibility]="{
|
|
962
|
+
user: true,
|
|
963
|
+
search: true,
|
|
964
|
+
searchInput: true,
|
|
965
|
+
help: false,
|
|
966
|
+
apps: false,
|
|
967
|
+
notifications: false,
|
|
968
|
+
mainMenu: false
|
|
969
|
+
}"
|
|
970
|
+
(searchClick)="onSearchClick($event)"
|
|
971
|
+
(searchInputOpenChange)="onSearchInputOpenChange($event)"
|
|
972
|
+
(searchChange)="onSearchChange($event)"
|
|
973
|
+
>
|
|
974
|
+
</modus-navbar>
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
```typescript
|
|
978
|
+
export class App implements OnInit {
|
|
979
|
+
// User card data for navbar (required)
|
|
980
|
+
readonly userCard: INavbarUserCard = {
|
|
981
|
+
name: 'John Doe',
|
|
982
|
+
email: 'john.doe@example.com',
|
|
983
|
+
avatarSrc: 'https://via.placeholder.com/64',
|
|
984
|
+
avatarAlt: 'User avatar',
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
// Search input state management
|
|
988
|
+
readonly searchInputOpen = signal(false);
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Handle search button click - toggle search input manually
|
|
992
|
+
*/
|
|
993
|
+
onSearchClick(event: MouseEvent | KeyboardEvent): void {
|
|
994
|
+
// Toggle the search input state
|
|
995
|
+
this.searchInputOpen.set(!this.searchInputOpen());
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* Handle search input open state change - sync our state with navbar
|
|
1000
|
+
*/
|
|
1001
|
+
onSearchInputOpenChange(isOpen: boolean): void {
|
|
1002
|
+
// Keep state in sync (prevents conflicts if navbar manages state internally)
|
|
1003
|
+
if (this.searchInputOpen() !== isOpen) {
|
|
1004
|
+
this.searchInputOpen.set(isOpen);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* Handle search input value change
|
|
1010
|
+
*/
|
|
1011
|
+
onSearchChange(event: { value: string }): void {
|
|
1012
|
+
console.log('Search value:', event.value);
|
|
1013
|
+
// Implement search logic here
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
**Key Points for Navbar Search**:
|
|
1019
|
+
|
|
1020
|
+
1. **State Management**: Use a `signal` to track `searchInputOpen` state
|
|
1021
|
+
2. **Visibility Settings**: Set both `search: true` (search button) and `searchInput: true` (search input field) in visibility
|
|
1022
|
+
3. **Two-Way Binding**: Bind `[searchInputOpen]` to control state, and listen to `(searchInputOpenChange)` to sync
|
|
1023
|
+
4. **Toggle on Click**: Handle `(searchClick)` to toggle the state when search button is clicked
|
|
1024
|
+
5. **Sync Handler**: Handle `(searchInputOpenChange)` to keep state synchronized if the navbar manages state internally
|
|
1025
|
+
|
|
1026
|
+
**Why This Pattern**:
|
|
1027
|
+
|
|
1028
|
+
- The navbar requires explicit state management for the search input expansion
|
|
1029
|
+
- Binding `[searchInputOpen]` gives you control over the expansion state
|
|
1030
|
+
- Handling `(searchInputOpenChange)` ensures state stays synchronized
|
|
1031
|
+
- Setting `searchInput: true` in visibility makes the search input field visible when expanded
|
|
1032
|
+
|
|
1033
|
+
---
|
|
1034
|
+
|
|
1035
|
+
## Component List
|
|
1036
|
+
|
|
1037
|
+
All available wrapper components:
|
|
1038
|
+
|
|
1039
|
+
- `modus-accordion` - Accordion/collapsible sections
|
|
1040
|
+
- `modus-alert` - Alert/notification messages
|
|
1041
|
+
- `modus-autocomplete` - Autocomplete input
|
|
1042
|
+
- `modus-avatar` - User avatar display
|
|
1043
|
+
- `modus-badge` - Badge/counter component
|
|
1044
|
+
- `modus-breadcrumbs` - Navigation breadcrumbs
|
|
1045
|
+
- `modus-button` - Button with icon support
|
|
1046
|
+
- `modus-card` - Card container
|
|
1047
|
+
- `modus-checkbox` - Checkbox input
|
|
1048
|
+
- `modus-chip` - Chip/tag component
|
|
1049
|
+
- `modus-collapse` - Collapsible content
|
|
1050
|
+
- `modus-date` - Date picker input
|
|
1051
|
+
- `modus-divider` - Horizontal/vertical divider
|
|
1052
|
+
- `modus-dropdown-menu` - Dropdown menu
|
|
1053
|
+
- `modus-file-dropzone` - File upload dropzone
|
|
1054
|
+
- `modus-icon` - Icon wrapper component
|
|
1055
|
+
- `modus-loader` - Loading spinner
|
|
1056
|
+
- `modus-menu` / `modus-menu-item` - Menu components
|
|
1057
|
+
- `modus-modal` - Modal dialog
|
|
1058
|
+
- `modus-navbar` - Navigation bar
|
|
1059
|
+
- `modus-number-input` - Number input field
|
|
1060
|
+
- `modus-pagination` - Pagination controls
|
|
1061
|
+
- `modus-progress` - Progress bar
|
|
1062
|
+
- `modus-radio` - Radio button input
|
|
1063
|
+
- `modus-rating` - Star rating component
|
|
1064
|
+
- `modus-select` - Select/dropdown input
|
|
1065
|
+
- `modus-side-navigation` - Side navigation menu
|
|
1066
|
+
- `modus-skeleton` - Loading skeleton
|
|
1067
|
+
- `modus-slider` - Range slider
|
|
1068
|
+
- `modus-stepper` - Step indicator
|
|
1069
|
+
- `modus-switch` - Toggle switch
|
|
1070
|
+
- `modus-table` - Data table
|
|
1071
|
+
- `modus-tabs` - Tab navigation
|
|
1072
|
+
- `modus-text-input` - Text input field
|
|
1073
|
+
- `modus-textarea` - Textarea input
|
|
1074
|
+
- `modus-theme-switcher` - Theme switcher
|
|
1075
|
+
- `modus-time-input` - Time picker input
|
|
1076
|
+
- `modus-toast` - Toast notification
|
|
1077
|
+
- `modus-toolbar` - Toolbar component
|
|
1078
|
+
- `modus-tooltip` - Tooltip component
|
|
1079
|
+
- `modus-typography` - Typography component
|
|
1080
|
+
- `modus-utility-panel` - Utility panel
|
|
1081
|
+
|
|
1082
|
+
**Reference**: See `src/app/components/index.ts` for complete exports and `src/app/components/README.md` for detailed documentation.
|
|
1083
|
+
|
|
1084
|
+
---
|
|
1085
|
+
|
|
1086
|
+
## Reference Links
|
|
1087
|
+
|
|
1088
|
+
- **Modus Web Components**: [Documentation](https://trimble-oss.github.io/modus-wc-2.0/main/)
|
|
1089
|
+
- **Angular Signals**: [Angular Signals Guide](https://angular.io/guide/signals)
|
|
1090
|
+
- **Angular Standalone Components**: [Standalone Components Guide](https://angular.io/guide/standalone-components)
|
|
1091
|
+
- **Design System**: See `src/styles.css` and `.cursor/rules/angular-design-system.mdc`
|
|
1092
|
+
- **Component Source**: `src/app/components/`
|
|
1093
|
+
|
|
1094
|
+
---
|
|
1095
|
+
|
|
1096
|
+
**All wrapper components follow these patterns. When creating new wrappers or modifying existing ones, refer to this guide and existing component implementations.**
|