@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,207 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useCallback } from "react";
|
|
4
|
+
import { ModusWcToast, ModusWcButton } from "@trimble-oss/moduswebcomponents-react";
|
|
5
|
+
import ModusAlert from "./ModusAlert";
|
|
6
|
+
|
|
7
|
+
export type ToastPosition =
|
|
8
|
+
| "top-start"
|
|
9
|
+
| "top-center"
|
|
10
|
+
| "top-end"
|
|
11
|
+
| "middle-start"
|
|
12
|
+
| "middle-center"
|
|
13
|
+
| "middle-end"
|
|
14
|
+
| "bottom-start"
|
|
15
|
+
| "bottom-center"
|
|
16
|
+
| "bottom-end";
|
|
17
|
+
|
|
18
|
+
export type ToastVariant = "info" | "success" | "warning" | "error";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Represents an action button for a toast.
|
|
22
|
+
*/
|
|
23
|
+
export interface ModusToastAction {
|
|
24
|
+
/** The label for the action button. */
|
|
25
|
+
label: string;
|
|
26
|
+
/** The color of the action button. */
|
|
27
|
+
color?: 'primary' | 'secondary' | 'tertiary' | 'warning' | 'danger';
|
|
28
|
+
/** The variant of the action button. */
|
|
29
|
+
variant?: 'filled' | 'outlined' | 'borderless';
|
|
30
|
+
/** The size of the action button. */
|
|
31
|
+
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
32
|
+
/** Whether to dismiss the toast when the action is clicked. */
|
|
33
|
+
dismissOnAction?: boolean;
|
|
34
|
+
/** A callback function to handle action clicks. */
|
|
35
|
+
onClick?: (toastId: string) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Represents a single toast item.
|
|
40
|
+
*/
|
|
41
|
+
export interface ModusToastItem {
|
|
42
|
+
/** A unique identifier for the toast. */
|
|
43
|
+
id: string;
|
|
44
|
+
/** The title of the toast. */
|
|
45
|
+
title: string;
|
|
46
|
+
/** The description of the toast. */
|
|
47
|
+
description?: string;
|
|
48
|
+
/** The variant of the toast. */
|
|
49
|
+
variant?: ToastVariant;
|
|
50
|
+
/** Whether the toast can be dismissed. */
|
|
51
|
+
dismissible?: boolean;
|
|
52
|
+
/** The delay in milliseconds before the toast is automatically dismissed. */
|
|
53
|
+
delay?: number | null;
|
|
54
|
+
/** The position of the toast. */
|
|
55
|
+
position?: ToastPosition;
|
|
56
|
+
/** A custom CSS class to apply to the toast. */
|
|
57
|
+
customClass?: string;
|
|
58
|
+
/** An action button to display in the toast. */
|
|
59
|
+
action?: ModusToastAction;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Props for the ModusToast component.
|
|
64
|
+
*/
|
|
65
|
+
interface ModusToastProps {
|
|
66
|
+
/** The toasts to display. */
|
|
67
|
+
toasts: ModusToastItem[];
|
|
68
|
+
/** The default position for toasts. */
|
|
69
|
+
defaultPosition?: ToastPosition;
|
|
70
|
+
/** The default delay in milliseconds before toasts are automatically dismissed. */
|
|
71
|
+
defaultDelay?: number | null;
|
|
72
|
+
/** A custom CSS class to apply to the toast container. */
|
|
73
|
+
customClass?: string;
|
|
74
|
+
/** A CSS class to apply to the toast container. */
|
|
75
|
+
className?: string;
|
|
76
|
+
/** A callback function to handle toast dismissals. */
|
|
77
|
+
onDismiss?: (toastId: string) => void;
|
|
78
|
+
/** A callback function to handle toast actions. */
|
|
79
|
+
onAction?: (toastId: string) => void;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Renders a Modus toast component.
|
|
84
|
+
* @param {ModusToastProps} props - The component props.
|
|
85
|
+
* @returns {JSX.Element} The rendered toast component.
|
|
86
|
+
*/
|
|
87
|
+
export default function ModusToast({
|
|
88
|
+
toasts,
|
|
89
|
+
defaultPosition = 'top-end',
|
|
90
|
+
defaultDelay,
|
|
91
|
+
customClass,
|
|
92
|
+
className = 'relative w-full pointer-events-none',
|
|
93
|
+
onDismiss,
|
|
94
|
+
onAction,
|
|
95
|
+
}: ModusToastProps) {
|
|
96
|
+
const timers = useRef<Map<string, number>>(new Map());
|
|
97
|
+
|
|
98
|
+
const handleDismiss = useCallback(
|
|
99
|
+
(toastId: string) => {
|
|
100
|
+
const timeoutId = timers.current.get(toastId);
|
|
101
|
+
if (timeoutId) {
|
|
102
|
+
window.clearTimeout(timeoutId);
|
|
103
|
+
timers.current.delete(toastId);
|
|
104
|
+
}
|
|
105
|
+
onDismiss?.(toastId);
|
|
106
|
+
},
|
|
107
|
+
[onDismiss]
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const handleActionClick = useCallback(
|
|
111
|
+
(toast: ModusToastItem) => {
|
|
112
|
+
toast.action?.onClick?.(toast.id);
|
|
113
|
+
onAction?.(toast.id);
|
|
114
|
+
|
|
115
|
+
if (toast.action?.dismissOnAction !== false) {
|
|
116
|
+
handleDismiss(toast.id);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
[handleDismiss, onAction]
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const resolveDelay = useCallback(
|
|
123
|
+
(toast: ModusToastItem) => {
|
|
124
|
+
if (toast.delay === null) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
if (typeof toast.delay === "number") {
|
|
128
|
+
return toast.delay;
|
|
129
|
+
}
|
|
130
|
+
if (typeof defaultDelay === "number") {
|
|
131
|
+
return defaultDelay;
|
|
132
|
+
}
|
|
133
|
+
return 4000;
|
|
134
|
+
},
|
|
135
|
+
[defaultDelay]
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
const activeToastIds = new Set(toasts.map((toast) => toast.id));
|
|
140
|
+
|
|
141
|
+
timers.current.forEach((timeoutId, storedId) => {
|
|
142
|
+
if (!activeToastIds.has(storedId)) {
|
|
143
|
+
window.clearTimeout(timeoutId);
|
|
144
|
+
timers.current.delete(storedId);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
toasts.forEach((toast) => {
|
|
149
|
+
const delayMs = resolveDelay(toast);
|
|
150
|
+
if (typeof delayMs === "number" && delayMs > 0 && !timers.current.has(toast.id)) {
|
|
151
|
+
const timeoutId = window.setTimeout(() => {
|
|
152
|
+
handleDismiss(toast.id);
|
|
153
|
+
}, delayMs);
|
|
154
|
+
timers.current.set(toast.id, timeoutId);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}, [toasts, resolveDelay, handleDismiss]);
|
|
158
|
+
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
const timersMap = timers.current;
|
|
161
|
+
return () => {
|
|
162
|
+
timersMap.forEach((timeoutId) => window.clearTimeout(timeoutId));
|
|
163
|
+
timersMap.clear();
|
|
164
|
+
};
|
|
165
|
+
}, []);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div className={className}>
|
|
169
|
+
{toasts.map((toast) => {
|
|
170
|
+
const resolvedDelay = resolveDelay(toast);
|
|
171
|
+
const toastDelay =
|
|
172
|
+
typeof resolvedDelay === "number" && resolvedDelay > 0 ? resolvedDelay : undefined;
|
|
173
|
+
const toastPosition = toast.position ?? defaultPosition;
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<div key={toast.id} className="pointer-events-auto">
|
|
177
|
+
<ModusWcToast
|
|
178
|
+
position={toastPosition}
|
|
179
|
+
delay={toastDelay}
|
|
180
|
+
custom-class={toast.customClass ?? customClass}
|
|
181
|
+
>
|
|
182
|
+
<ModusAlert
|
|
183
|
+
alertTitle={toast.title}
|
|
184
|
+
alertDescription={toast.description}
|
|
185
|
+
variant={toast.variant ?? "info"}
|
|
186
|
+
dismissible={toast.dismissible}
|
|
187
|
+
onDismissClick={() => handleDismiss(toast.id)}
|
|
188
|
+
>
|
|
189
|
+
{toast.action && (
|
|
190
|
+
<ModusWcButton
|
|
191
|
+
slot="button"
|
|
192
|
+
color={toast.action.color ?? "primary"}
|
|
193
|
+
variant={toast.action.variant ?? "filled"}
|
|
194
|
+
size={toast.action.size ?? "sm"}
|
|
195
|
+
onButtonClick={() => handleActionClick(toast)}
|
|
196
|
+
>
|
|
197
|
+
{toast.action.label}
|
|
198
|
+
</ModusWcButton>
|
|
199
|
+
)}
|
|
200
|
+
</ModusAlert>
|
|
201
|
+
</ModusWcToast>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
})}
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { forwardRef } from "react";
|
|
5
|
+
import { ModusWcToolbar } from "@trimble-oss/moduswebcomponents-react";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Props for the ModusToolbar component.
|
|
9
|
+
*/
|
|
10
|
+
export interface ModusToolbarProps {
|
|
11
|
+
/** The content to display at the start of the toolbar. */
|
|
12
|
+
startContent?: ReactNode;
|
|
13
|
+
/** The content to display in the center of the toolbar. */
|
|
14
|
+
centerContent?: ReactNode;
|
|
15
|
+
/** The content to display at the end of the toolbar. */
|
|
16
|
+
endContent?: ReactNode;
|
|
17
|
+
/** The main content of the toolbar. */
|
|
18
|
+
children?: ReactNode;
|
|
19
|
+
/** The ARIA label for the toolbar. */
|
|
20
|
+
ariaLabel?: string;
|
|
21
|
+
/** A custom CSS class to apply to the toolbar. */
|
|
22
|
+
customClass?: string;
|
|
23
|
+
/** A CSS class to apply to the toolbar. */
|
|
24
|
+
className?: string;
|
|
25
|
+
/** The ARIA role of the toolbar. */
|
|
26
|
+
role?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Renders a Modus toolbar component.
|
|
31
|
+
* @param {ModusToolbarProps} props - The component props.
|
|
32
|
+
* @param {React.Ref<HTMLModusWcToolbarElement>} ref - The ref object for the toolbar.
|
|
33
|
+
* @returns {JSX.Element} The rendered toolbar component.
|
|
34
|
+
*/
|
|
35
|
+
const ModusToolbar = forwardRef<HTMLModusWcToolbarElement, ModusToolbarProps>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
startContent,
|
|
39
|
+
centerContent,
|
|
40
|
+
endContent,
|
|
41
|
+
children,
|
|
42
|
+
ariaLabel,
|
|
43
|
+
customClass,
|
|
44
|
+
className,
|
|
45
|
+
role,
|
|
46
|
+
},
|
|
47
|
+
ref
|
|
48
|
+
) => {
|
|
49
|
+
const combinedClass =
|
|
50
|
+
[customClass, className].filter(Boolean).join(" ") || undefined;
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<ModusWcToolbar
|
|
54
|
+
ref={ref}
|
|
55
|
+
aria-label={ariaLabel}
|
|
56
|
+
role={role}
|
|
57
|
+
custom-class={combinedClass}
|
|
58
|
+
>
|
|
59
|
+
{startContent && <div slot="start">{startContent}</div>}
|
|
60
|
+
{centerContent && <div slot="center">{centerContent}</div>}
|
|
61
|
+
{endContent && <div slot="end">{endContent}</div>}
|
|
62
|
+
{children}
|
|
63
|
+
</ModusWcToolbar>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
ModusToolbar.displayName = "ModusToolbar";
|
|
69
|
+
|
|
70
|
+
export default ModusToolbar;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Children, forwardRef, useMemo } from "react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { ModusWcTooltip } from "@trimble-oss/moduswebcomponents-react";
|
|
4
|
+
|
|
5
|
+
export type TooltipPosition = "auto" | "top" | "bottom" | "left" | "right";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Props for the ModusTooltip component.
|
|
9
|
+
*/
|
|
10
|
+
export interface ModusTooltipProps {
|
|
11
|
+
/** The text to display in the tooltip. */
|
|
12
|
+
content: string;
|
|
13
|
+
/** The position of the tooltip. */
|
|
14
|
+
position?: TooltipPosition;
|
|
15
|
+
/** Whether the tooltip is disabled. */
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
/** Whether to force the tooltip to be open. */
|
|
18
|
+
forceOpen?: boolean;
|
|
19
|
+
/** A unique identifier for the tooltip. */
|
|
20
|
+
tooltipId?: string;
|
|
21
|
+
/** A custom CSS class to apply to the tooltip. */
|
|
22
|
+
customClass?: string;
|
|
23
|
+
/** A CSS class to apply to the tooltip. */
|
|
24
|
+
className?: string;
|
|
25
|
+
/** The content to display as the tooltip's target. */
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Renders a Modus tooltip component.
|
|
31
|
+
* @param {ModusTooltipProps} props - The component props.
|
|
32
|
+
* @param {React.Ref<HTMLModusWcTooltipElement>} ref - The ref object for the tooltip.
|
|
33
|
+
* @returns {JSX.Element} The rendered tooltip component.
|
|
34
|
+
*/
|
|
35
|
+
const ModusTooltip = forwardRef<HTMLModusWcTooltipElement, ModusTooltipProps>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
content,
|
|
39
|
+
position = "auto",
|
|
40
|
+
disabled = false,
|
|
41
|
+
forceOpen = false,
|
|
42
|
+
tooltipId,
|
|
43
|
+
customClass,
|
|
44
|
+
className,
|
|
45
|
+
children,
|
|
46
|
+
},
|
|
47
|
+
ref
|
|
48
|
+
) => {
|
|
49
|
+
/**
|
|
50
|
+
* Normalizes children to ensure proper tooltip behavior.
|
|
51
|
+
*
|
|
52
|
+
* This memoized function handles different child scenarios:
|
|
53
|
+
* - No children: Returns null
|
|
54
|
+
* - Single child: Returns the child as-is
|
|
55
|
+
* - Multiple children: Wraps them in a span with proper styling
|
|
56
|
+
*
|
|
57
|
+
* The normalization ensures that the tooltip can properly attach to
|
|
58
|
+
* the child element(s) and handle hover events correctly.
|
|
59
|
+
*
|
|
60
|
+
* @returns {ReactNode | null} The normalized child element(s)
|
|
61
|
+
* @private
|
|
62
|
+
*/
|
|
63
|
+
const normalizedChild = useMemo(() => {
|
|
64
|
+
const childArray = Children.toArray(children);
|
|
65
|
+
if (childArray.length === 0) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (childArray.length === 1) {
|
|
69
|
+
return childArray[0];
|
|
70
|
+
}
|
|
71
|
+
return (
|
|
72
|
+
<span className="inline-flex items-center gap-1">{childArray}</span>
|
|
73
|
+
);
|
|
74
|
+
}, [children]);
|
|
75
|
+
|
|
76
|
+
const combinedClass =
|
|
77
|
+
[customClass, className].filter(Boolean).join(" ") || undefined;
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<ModusWcTooltip
|
|
81
|
+
ref={ref}
|
|
82
|
+
content={content}
|
|
83
|
+
position={position}
|
|
84
|
+
disabled={disabled}
|
|
85
|
+
force-open={forceOpen}
|
|
86
|
+
tooltip-id={tooltipId}
|
|
87
|
+
custom-class={combinedClass}
|
|
88
|
+
>
|
|
89
|
+
{normalizedChild}
|
|
90
|
+
</ModusWcTooltip>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
ModusTooltip.displayName = "ModusTooltip";
|
|
96
|
+
|
|
97
|
+
export default ModusTooltip;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import type { CSSProperties } from "react";
|
|
3
|
+
import { ModusWcUtilityPanel } from "@trimble-oss/moduswebcomponents-react";
|
|
4
|
+
|
|
5
|
+
type UtilityPanelPosition = "left" | "right";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Props for the ModusUtilityPanel component.
|
|
9
|
+
*/
|
|
10
|
+
export interface ModusUtilityPanelProps {
|
|
11
|
+
/** Whether the utility panel is expanded. */
|
|
12
|
+
expanded?: boolean;
|
|
13
|
+
/** The position of the utility panel. */
|
|
14
|
+
position?: UtilityPanelPosition;
|
|
15
|
+
/** Whether the utility panel should push the content. */
|
|
16
|
+
pushContent?: boolean;
|
|
17
|
+
/** The width of the utility panel. */
|
|
18
|
+
panelWidth?: string;
|
|
19
|
+
/** A CSS class to apply to the utility panel. */
|
|
20
|
+
className?: string;
|
|
21
|
+
/** The main content of the utility panel. */
|
|
22
|
+
children?: React.ReactNode;
|
|
23
|
+
/** The content to display in the header slot. */
|
|
24
|
+
headerSlot?: React.ReactNode;
|
|
25
|
+
/** The text to display in the header. */
|
|
26
|
+
headerText?: string;
|
|
27
|
+
/** The content to display in the footer slot. */
|
|
28
|
+
footerSlot?: React.ReactNode;
|
|
29
|
+
/** The ARIA label for the utility panel. */
|
|
30
|
+
ariaLabel?: string;
|
|
31
|
+
/** The ARIA expanded state of the utility panel. */
|
|
32
|
+
ariaExpanded?: boolean;
|
|
33
|
+
/** A callback function to handle the panel opened event. */
|
|
34
|
+
onPanelOpened?: () => void;
|
|
35
|
+
/** A callback function to handle the panel closed event. */
|
|
36
|
+
onPanelClosed?: () => void;
|
|
37
|
+
/** A callback function to handle the toggle event. */
|
|
38
|
+
onToggle?: (collapsed: boolean) => void;
|
|
39
|
+
/** A CSS selector for the target element to push. */
|
|
40
|
+
targetSelector?: string;
|
|
41
|
+
/** The target element to push. */
|
|
42
|
+
targetElement?: HTMLElement | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Renders a Modus utility panel component.
|
|
47
|
+
* @param {ModusUtilityPanelProps} props - The component props.
|
|
48
|
+
* @returns {JSX.Element} The rendered utility panel component.
|
|
49
|
+
*/
|
|
50
|
+
export default function ModusUtilityPanel({
|
|
51
|
+
expanded = false,
|
|
52
|
+
position = "right",
|
|
53
|
+
pushContent = false,
|
|
54
|
+
panelWidth = "312px",
|
|
55
|
+
className,
|
|
56
|
+
children,
|
|
57
|
+
headerSlot,
|
|
58
|
+
headerText,
|
|
59
|
+
footerSlot,
|
|
60
|
+
ariaLabel,
|
|
61
|
+
ariaExpanded,
|
|
62
|
+
onPanelOpened,
|
|
63
|
+
onPanelClosed,
|
|
64
|
+
onToggle,
|
|
65
|
+
targetSelector,
|
|
66
|
+
targetElement,
|
|
67
|
+
}: ModusUtilityPanelProps) {
|
|
68
|
+
const panelRef = useRef<HTMLModusWcUtilityPanelElement>(null);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resolves the target element for content pushing functionality.
|
|
72
|
+
*
|
|
73
|
+
* This memoized function determines which element should be pushed when
|
|
74
|
+
* the utility panel is expanded. It checks for targetElement first,
|
|
75
|
+
* then falls back to querying the DOM using targetSelector.
|
|
76
|
+
*
|
|
77
|
+
* @returns {HTMLElement | undefined} The resolved target element or undefined
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
const resolvedTarget = useMemo(() => {
|
|
81
|
+
if (targetElement) return targetElement;
|
|
82
|
+
if (typeof document === "undefined") return undefined;
|
|
83
|
+
if (targetSelector) {
|
|
84
|
+
return document.querySelector<HTMLElement>(targetSelector) ?? undefined;
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}, [targetElement, targetSelector]);
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
return () => {
|
|
91
|
+
if (resolvedTarget) {
|
|
92
|
+
resolvedTarget.classList.remove(
|
|
93
|
+
"modus-wc-utility-panel-push-target-left"
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}, [resolvedTarget]);
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
const panel = panelRef.current;
|
|
100
|
+
if (!panel) return;
|
|
101
|
+
|
|
102
|
+
panel.expanded = expanded;
|
|
103
|
+
panel.pushContent = pushContent;
|
|
104
|
+
panel.targetElement = resolvedTarget;
|
|
105
|
+
|
|
106
|
+
if (resolvedTarget) {
|
|
107
|
+
if (position === "left" && pushContent) {
|
|
108
|
+
resolvedTarget.classList.add("modus-wc-utility-panel-push-target-left");
|
|
109
|
+
} else {
|
|
110
|
+
resolvedTarget.classList.remove(
|
|
111
|
+
"modus-wc-utility-panel-push-target-left"
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}, [expanded, pushContent, resolvedTarget, position]);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Handles the panel opened event and updates state accordingly.
|
|
119
|
+
*
|
|
120
|
+
* This callback is triggered when the utility panel opens and calls
|
|
121
|
+
* the appropriate event handlers while updating the toggle state.
|
|
122
|
+
*
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
const handlePanelOpened = useCallback(() => {
|
|
126
|
+
onPanelOpened?.();
|
|
127
|
+
onToggle?.(false);
|
|
128
|
+
}, [onPanelOpened, onToggle]);
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Handles the panel closed event and updates state accordingly.
|
|
132
|
+
*
|
|
133
|
+
* This callback is triggered when the utility panel closes and calls
|
|
134
|
+
* the appropriate event handlers while updating the toggle state.
|
|
135
|
+
*
|
|
136
|
+
* @private
|
|
137
|
+
*/
|
|
138
|
+
const handlePanelClosed = useCallback(() => {
|
|
139
|
+
onPanelClosed?.();
|
|
140
|
+
onToggle?.(true);
|
|
141
|
+
}, [onPanelClosed, onToggle]);
|
|
142
|
+
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
const panel = panelRef.current;
|
|
145
|
+
if (!panel) return;
|
|
146
|
+
|
|
147
|
+
panel.addEventListener("panelOpened", handlePanelOpened);
|
|
148
|
+
panel.addEventListener("panelClosed", handlePanelClosed);
|
|
149
|
+
|
|
150
|
+
return () => {
|
|
151
|
+
panel.removeEventListener("panelOpened", handlePanelOpened);
|
|
152
|
+
panel.removeEventListener("panelClosed", handlePanelClosed);
|
|
153
|
+
};
|
|
154
|
+
}, [handlePanelClosed, handlePanelOpened]);
|
|
155
|
+
|
|
156
|
+
const positionClass =
|
|
157
|
+
position === "left" ? "modus-utility-panel--left" : undefined;
|
|
158
|
+
|
|
159
|
+
const combinedClass =
|
|
160
|
+
[className, positionClass].filter(Boolean).join(" ") || undefined;
|
|
161
|
+
|
|
162
|
+
const style = useMemo<CSSProperties | undefined>(() => {
|
|
163
|
+
if (!panelWidth) return undefined;
|
|
164
|
+
return {
|
|
165
|
+
"--modus-wc-utility-panel-width": panelWidth,
|
|
166
|
+
} as CSSProperties;
|
|
167
|
+
}, [panelWidth]);
|
|
168
|
+
|
|
169
|
+
const renderedHeader =
|
|
170
|
+
headerSlot ??
|
|
171
|
+
(headerText ? (
|
|
172
|
+
<div className="text-xl font-bold text-foreground">{headerText}</div>
|
|
173
|
+
) : null);
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<ModusWcUtilityPanel
|
|
177
|
+
ref={panelRef}
|
|
178
|
+
expanded={expanded}
|
|
179
|
+
pushContent={pushContent}
|
|
180
|
+
className={combinedClass}
|
|
181
|
+
aria-label={ariaLabel}
|
|
182
|
+
aria-expanded={ariaExpanded}
|
|
183
|
+
style={style}
|
|
184
|
+
>
|
|
185
|
+
{renderedHeader && (
|
|
186
|
+
<div slot="header" className="w-full min-w-full max-w-full block">
|
|
187
|
+
{renderedHeader}
|
|
188
|
+
</div>
|
|
189
|
+
)}
|
|
190
|
+
<div slot="body">{children}</div>
|
|
191
|
+
{footerSlot && (
|
|
192
|
+
<div slot="footer" className="w-full min-w-full max-w-full block">
|
|
193
|
+
{footerSlot}
|
|
194
|
+
</div>
|
|
195
|
+
)}
|
|
196
|
+
</ModusWcUtilityPanel>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useTheme } from "../hooks/useTheme";
|
|
2
|
+
import { type Theme } from "../contexts/ThemeContextData";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import ModusDropdownMenu from "./ModusDropdownMenu";
|
|
5
|
+
import type { MenuItem } from "./ModusMenu";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Props for the ThemeSwitcherDropdown component.
|
|
9
|
+
*/
|
|
10
|
+
interface ThemeSwitcherDropdownProps {
|
|
11
|
+
/** A custom CSS class to apply to the component. */
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Renders the content of the theme switcher dropdown.
|
|
17
|
+
* @param {ThemeSwitcherDropdownProps} props - The component props.
|
|
18
|
+
* @returns {JSX.Element} The rendered dropdown content.
|
|
19
|
+
*/
|
|
20
|
+
function ThemeSwitcherDropdownContent({
|
|
21
|
+
className = '',
|
|
22
|
+
}: ThemeSwitcherDropdownProps) {
|
|
23
|
+
const { theme, setTheme } = useTheme();
|
|
24
|
+
|
|
25
|
+
const handleItemSelect = (event: CustomEvent<{ value: string }>) => {
|
|
26
|
+
const selectedValue = event.detail.value as Theme;
|
|
27
|
+
if (selectedValue && selectedValue !== theme) {
|
|
28
|
+
setTheme(selectedValue);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const themes: { value: Theme; label: string; description: string }[] = [
|
|
33
|
+
{
|
|
34
|
+
value: "modus-classic-light",
|
|
35
|
+
label: "Classic Light",
|
|
36
|
+
description: "Traditional light theme",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
value: "modus-classic-dark",
|
|
40
|
+
label: "Classic Dark",
|
|
41
|
+
description: "Traditional dark theme",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
value: "modus-modern-light",
|
|
45
|
+
label: "Modern Light",
|
|
46
|
+
description: "Contemporary light theme",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
value: "modus-modern-dark",
|
|
50
|
+
label: "Modern Dark",
|
|
51
|
+
description: "Contemporary dark theme",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
value: "connect-light",
|
|
55
|
+
label: "Connect Light",
|
|
56
|
+
description: "Connect light theme",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
value: "connect-dark",
|
|
60
|
+
label: "Connect Dark",
|
|
61
|
+
description: "Connect dark theme",
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const getCurrentThemeLabel = () => {
|
|
66
|
+
const currentTheme = themes.find((t) => t.value === theme);
|
|
67
|
+
return currentTheme ? currentTheme.label : "Theme";
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const menuItems: MenuItem[] = themes.map((themeOption) => ({
|
|
71
|
+
label: themeOption.label,
|
|
72
|
+
value: themeOption.value,
|
|
73
|
+
selected: theme === themeOption.value,
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div className={className}>
|
|
78
|
+
<ModusDropdownMenu
|
|
79
|
+
buttonVariant="filled"
|
|
80
|
+
menuPlacement="bottom-end"
|
|
81
|
+
menuItems={menuItems}
|
|
82
|
+
onItemSelect={handleItemSelect}
|
|
83
|
+
buttonContent={
|
|
84
|
+
<div className="flex items-center justify-between w-full min-w-[140px] px-3 py-2 gap-2">
|
|
85
|
+
<div className="flex-1 text-left text-md font-medium">
|
|
86
|
+
{getCurrentThemeLabel()}
|
|
87
|
+
</div>
|
|
88
|
+
<i className="modus-icons text-base flex-shrink-0">expand_more</i>
|
|
89
|
+
</div>
|
|
90
|
+
}
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default function ThemeSwitcherDropdown(
|
|
97
|
+
props: ThemeSwitcherDropdownProps
|
|
98
|
+
) {
|
|
99
|
+
const [mounted, setMounted] = useState(false);
|
|
100
|
+
|
|
101
|
+
// Prevent hydration mismatch
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
setMounted(true);
|
|
104
|
+
}, []);
|
|
105
|
+
|
|
106
|
+
if (!mounted) {
|
|
107
|
+
return (
|
|
108
|
+
<div className={props.className || ""}>
|
|
109
|
+
<div className="animate-pulse">
|
|
110
|
+
<div className="h-8 w-32 rounded bg-muted" />
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return <ThemeSwitcherDropdownContent {...props} />;
|
|
117
|
+
}
|