@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.
Files changed (450) hide show
  1. package/README.md +191 -0
  2. package/bin/create-trimble-app.js +9 -0
  3. package/package.json +67 -0
  4. package/src/cli.js +134 -0
  5. package/src/frameworks.js +28 -0
  6. package/src/scaffold.js +209 -0
  7. package/src/utils/file.js +47 -0
  8. package/src/utils/git.js +140 -0
  9. package/src/utils/install.js +25 -0
  10. package/src/utils/logger.js +124 -0
  11. package/templates/angular/.cursor/commands/remove-dev-content.md +394 -0
  12. package/templates/angular/.cursor/mcp.json +13 -0
  13. package/templates/angular/.cursor/rules/modus-angular-20.mdc +82 -0
  14. package/templates/angular/.cursor/rules/modus-angular-accordion-state-management-short.mdc +45 -0
  15. package/templates/angular/.cursor/rules/modus-angular-accordion-state-management.mdc +322 -0
  16. package/templates/angular/.cursor/rules/modus-angular-best-practices.mdc +472 -0
  17. package/templates/angular/.cursor/rules/modus-angular-border-usage-short.mdc +48 -0
  18. package/templates/angular/.cursor/rules/modus-angular-border-usage.mdc +286 -0
  19. package/templates/angular/.cursor/rules/modus-angular-button-group-usage-short.mdc +47 -0
  20. package/templates/angular/.cursor/rules/modus-angular-button-group-usage.mdc +263 -0
  21. package/templates/angular/.cursor/rules/modus-angular-checkbox-value-inversion-short.mdc +36 -0
  22. package/templates/angular/.cursor/rules/modus-angular-checkbox-value-inversion.mdc +92 -0
  23. package/templates/angular/.cursor/rules/modus-angular-chrome-devtools-testing-short.mdc +34 -0
  24. package/templates/angular/.cursor/rules/modus-angular-chrome-devtools-testing.mdc +185 -0
  25. package/templates/angular/.cursor/rules/modus-angular-color-usage-short.mdc +56 -0
  26. package/templates/angular/.cursor/rules/modus-angular-color-usage.mdc +208 -0
  27. package/templates/angular/.cursor/rules/modus-angular-components-reference.mdc +114 -0
  28. package/templates/angular/.cursor/rules/modus-angular-design-system.mdc +273 -0
  29. package/templates/angular/.cursor/rules/modus-angular-development-workflow-short.mdc +43 -0
  30. package/templates/angular/.cursor/rules/modus-angular-development-workflow.mdc +145 -0
  31. package/templates/angular/.cursor/rules/modus-angular-essentials.mdc +272 -0
  32. package/templates/angular/.cursor/rules/modus-angular-forms-validation-short.mdc +56 -0
  33. package/templates/angular/.cursor/rules/modus-angular-forms-validation.mdc +124 -0
  34. package/templates/angular/.cursor/rules/modus-angular-icon-names.mdc +70 -0
  35. package/templates/angular/.cursor/rules/modus-angular-icons-short.mdc +40 -0
  36. package/templates/angular/.cursor/rules/modus-angular-icons.mdc +137 -0
  37. package/templates/angular/.cursor/rules/modus-angular-implementation-guides-short.mdc +36 -0
  38. package/templates/angular/.cursor/rules/modus-angular-implementation-guides.mdc +301 -0
  39. package/templates/angular/.cursor/rules/modus-angular-integration-short.mdc +60 -0
  40. package/templates/angular/.cursor/rules/modus-angular-integration.mdc +1096 -0
  41. package/templates/angular/.cursor/rules/modus-angular-master.mdc +164 -0
  42. package/templates/angular/.cursor/rules/modus-angular-modal-usage-short.mdc +51 -0
  43. package/templates/angular/.cursor/rules/modus-angular-modal-usage.mdc +115 -0
  44. package/templates/angular/.cursor/rules/modus-angular-navbar-usage-short.mdc +49 -0
  45. package/templates/angular/.cursor/rules/modus-angular-navbar-usage.mdc +146 -0
  46. package/templates/angular/.cursor/rules/modus-angular-no-emojis.mdc +66 -0
  47. package/templates/angular/.cursor/rules/modus-angular-opacity-utilities-short.mdc +43 -0
  48. package/templates/angular/.cursor/rules/modus-angular-opacity-utilities.mdc +160 -0
  49. package/templates/angular/.cursor/rules/modus-angular-select-vs-dropdown-menu-short.mdc +51 -0
  50. package/templates/angular/.cursor/rules/modus-angular-select-vs-dropdown-menu.mdc +83 -0
  51. package/templates/angular/.cursor/rules/modus-angular-semantic-html-short.mdc +36 -0
  52. package/templates/angular/.cursor/rules/modus-angular-semantic-html.mdc +81 -0
  53. package/templates/angular/.cursor/rules/modus-angular-side-navigation-usage-short.mdc +50 -0
  54. package/templates/angular/.cursor/rules/modus-angular-side-navigation-usage.mdc +136 -0
  55. package/templates/angular/.cursor/rules/modus-angular-table-usage-short.mdc +52 -0
  56. package/templates/angular/.cursor/rules/modus-angular-table-usage.mdc +151 -0
  57. package/templates/angular/.cursor/rules/modus-angular-tailwind-usage-short.mdc +44 -0
  58. package/templates/angular/.cursor/rules/modus-angular-tailwind-usage.mdc +242 -0
  59. package/templates/angular/.cursor/rules/modus-angular-themes-short.mdc +54 -0
  60. package/templates/angular/.cursor/rules/modus-angular-themes.mdc +222 -0
  61. package/templates/angular/.cursor/rules/modus-angular-utility-panel-usage-short.mdc +52 -0
  62. package/templates/angular/.cursor/rules/modus-angular-utility-panel-usage.mdc +130 -0
  63. package/templates/angular/.cursor/rules/ux/gestalt-laws-detailed.mdc +514 -0
  64. package/templates/angular/.cursor/rules/ux/ux-ui-foundations.mdc +235 -0
  65. package/templates/angular/.cursor/skills/run-lint-checks/SKILL.md +169 -0
  66. package/templates/angular/.editorconfig +17 -0
  67. package/templates/angular/.github/dependabot.yml +19 -0
  68. package/templates/angular/.github/workflows/a11y-check.yml +135 -0
  69. package/templates/angular/.github/workflows/ci.yml +44 -0
  70. package/templates/angular/.husky/pre-commit +32 -0
  71. package/templates/angular/.postcssrc.json +5 -0
  72. package/templates/angular/.vscode/extensions.json +4 -0
  73. package/templates/angular/.vscode/launch.json +20 -0
  74. package/templates/angular/.vscode/tasks.json +42 -0
  75. package/templates/angular/CLAUDE.md +148 -0
  76. package/templates/angular/README.md +92 -0
  77. package/templates/angular/amplify.yml +25 -0
  78. package/templates/angular/angular.json +106 -0
  79. package/templates/angular/data/modusIcons.ts +861 -0
  80. package/templates/angular/package-lock.json +11030 -0
  81. package/templates/angular/package.json +66 -0
  82. package/templates/angular/public/angular-icon.svg +12 -0
  83. package/templates/angular/public/favicon.ico +0 -0
  84. package/templates/angular/public/modus-icons.css +49 -0
  85. package/templates/angular/public/vite.svg +1 -0
  86. package/templates/angular/scripts/README.md +410 -0
  87. package/templates/angular/scripts/check-border-violations.js +352 -0
  88. package/templates/angular/scripts/check-icon-names.js +402 -0
  89. package/templates/angular/scripts/check-inline-styles.js +292 -0
  90. package/templates/angular/scripts/check-modus-colors.js +282 -0
  91. package/templates/angular/scripts/check-modus-icons.js +263 -0
  92. package/templates/angular/scripts/check-opacity-utilities.js +426 -0
  93. package/templates/angular/scripts/check-semantic-html.js +452 -0
  94. package/templates/angular/scripts/check-typescript.js +109 -0
  95. package/templates/angular/src/app/app.config.ts +29 -0
  96. package/templates/angular/src/app/app.css +0 -0
  97. package/templates/angular/src/app/app.html +4 -0
  98. package/templates/angular/src/app/app.routes.ts +351 -0
  99. package/templates/angular/src/app/app.spec.ts +27 -0
  100. package/templates/angular/src/app/app.ts +47 -0
  101. package/templates/angular/src/app/components/README.md +77 -0
  102. package/templates/angular/src/app/components/index.ts +53 -0
  103. package/templates/angular/src/app/components/modus-accordion.component.ts +50 -0
  104. package/templates/angular/src/app/components/modus-alert.component.ts +133 -0
  105. package/templates/angular/src/app/components/modus-autocomplete.component.ts +262 -0
  106. package/templates/angular/src/app/components/modus-avatar.component.ts +75 -0
  107. package/templates/angular/src/app/components/modus-badge.component.ts +84 -0
  108. package/templates/angular/src/app/components/modus-breadcrumbs.component.ts +65 -0
  109. package/templates/angular/src/app/components/modus-button-group.component.ts +82 -0
  110. package/templates/angular/src/app/components/modus-button.component.ts +292 -0
  111. package/templates/angular/src/app/components/modus-card.component.ts +73 -0
  112. package/templates/angular/src/app/components/modus-checkbox.component.ts +117 -0
  113. package/templates/angular/src/app/components/modus-chip.component.ts +97 -0
  114. package/templates/angular/src/app/components/modus-collapse.component.ts +118 -0
  115. package/templates/angular/src/app/components/modus-date.component.ts +165 -0
  116. package/templates/angular/src/app/components/modus-dropdown-menu.component.ts +108 -0
  117. package/templates/angular/src/app/components/modus-file-dropzone.component.ts +121 -0
  118. package/templates/angular/src/app/components/modus-handle.component.ts +96 -0
  119. package/templates/angular/src/app/components/modus-icon.component.ts +81 -0
  120. package/templates/angular/src/app/components/modus-input-feedback.component.ts +54 -0
  121. package/templates/angular/src/app/components/modus-input-label.component.ts +62 -0
  122. package/templates/angular/src/app/components/modus-loader.component.ts +48 -0
  123. package/templates/angular/src/app/components/modus-logo.component.ts +115 -0
  124. package/templates/angular/src/app/components/modus-menu-item.component.ts +116 -0
  125. package/templates/angular/src/app/components/modus-menu.component.ts +58 -0
  126. package/templates/angular/src/app/components/modus-modal.component.ts +70 -0
  127. package/templates/angular/src/app/components/modus-navbar.component.ts +303 -0
  128. package/templates/angular/src/app/components/modus-number-input.component.ts +174 -0
  129. package/templates/angular/src/app/components/modus-pagination.component.ts +74 -0
  130. package/templates/angular/src/app/components/modus-panel.component.ts +61 -0
  131. package/templates/angular/src/app/components/modus-progress.component.ts +62 -0
  132. package/templates/angular/src/app/components/modus-radio.component.ts +102 -0
  133. package/templates/angular/src/app/components/modus-rating.component.ts +80 -0
  134. package/templates/angular/src/app/components/modus-select.component.ts +131 -0
  135. package/templates/angular/src/app/components/modus-side-navigation.component.ts +90 -0
  136. package/templates/angular/src/app/components/modus-skeleton.component.ts +54 -0
  137. package/templates/angular/src/app/components/modus-slider.component.ts +132 -0
  138. package/templates/angular/src/app/components/modus-stepper.component.ts +65 -0
  139. package/templates/angular/src/app/components/modus-switch.component.ts +118 -0
  140. package/templates/angular/src/app/components/modus-table.component.ts +204 -0
  141. package/templates/angular/src/app/components/modus-tabs.component.ts +82 -0
  142. package/templates/angular/src/app/components/modus-text-input.component.ts +221 -0
  143. package/templates/angular/src/app/components/modus-textarea.component.ts +168 -0
  144. package/templates/angular/src/app/components/modus-theme-switcher.component.ts +45 -0
  145. package/templates/angular/src/app/components/modus-time-input.component.ts +172 -0
  146. package/templates/angular/src/app/components/modus-toast.component.ts +63 -0
  147. package/templates/angular/src/app/components/modus-toolbar.component.ts +43 -0
  148. package/templates/angular/src/app/components/modus-tooltip.component.ts +83 -0
  149. package/templates/angular/src/app/components/modus-typography.component.ts +79 -0
  150. package/templates/angular/src/app/components/modus-utility-panel.component.ts +275 -0
  151. package/templates/angular/src/app/components/theme-demo.component.ts +1242 -0
  152. package/templates/angular/src/app/data/component-demos.ts +360 -0
  153. package/templates/angular/src/app/data/modus-icons.ts +806 -0
  154. package/templates/angular/src/app/demos/accordion/accordion-demo.component.ts +212 -0
  155. package/templates/angular/src/app/demos/alert/alert-demo.component.ts +108 -0
  156. package/templates/angular/src/app/demos/autocomplete/autocomplete-demo.component.ts +174 -0
  157. package/templates/angular/src/app/demos/avatar/avatar-demo.component.ts +149 -0
  158. package/templates/angular/src/app/demos/badge/badge-demo.component.ts +148 -0
  159. package/templates/angular/src/app/demos/breadcrumbs/breadcrumbs-demo.component.ts +96 -0
  160. package/templates/angular/src/app/demos/button/button-demo.component.ts +256 -0
  161. package/templates/angular/src/app/demos/button-group/button-group-demo.component.ts +215 -0
  162. package/templates/angular/src/app/demos/card/card-demo.component.ts +180 -0
  163. package/templates/angular/src/app/demos/checkbox/checkbox-demo.component.ts +71 -0
  164. package/templates/angular/src/app/demos/chip/chip-demo.component.ts +183 -0
  165. package/templates/angular/src/app/demos/date/date-demo.component.ts +193 -0
  166. package/templates/angular/src/app/demos/dropdown/dropdown-demo.component.ts +196 -0
  167. package/templates/angular/src/app/demos/file-dropzone/file-dropzone-demo.component.ts +176 -0
  168. package/templates/angular/src/app/demos/handle/handle-demo.component.ts +265 -0
  169. package/templates/angular/src/app/demos/icon/icon-demo.component.ts +65 -0
  170. package/templates/angular/src/app/demos/input-feedback/input-feedback-demo.component.ts +189 -0
  171. package/templates/angular/src/app/demos/input-label/input-label-demo.component.ts +330 -0
  172. package/templates/angular/src/app/demos/loader/loader-demo.component.ts +143 -0
  173. package/templates/angular/src/app/demos/logo/logo-demo.component.ts +191 -0
  174. package/templates/angular/src/app/demos/menu/menu-demo.component.ts +72 -0
  175. package/templates/angular/src/app/demos/modal/modal-demo.component.ts +278 -0
  176. package/templates/angular/src/app/demos/navbar/navbar-demo.component.ts +135 -0
  177. package/templates/angular/src/app/demos/number-input/number-input-demo.component.ts +175 -0
  178. package/templates/angular/src/app/demos/pagination/pagination-demo.component.ts +134 -0
  179. package/templates/angular/src/app/demos/panel/panel-demo.component.ts +235 -0
  180. package/templates/angular/src/app/demos/progress/progress-demo.component.ts +169 -0
  181. package/templates/angular/src/app/demos/radio/radio-demo.component.ts +161 -0
  182. package/templates/angular/src/app/demos/rating/rating-demo.component.ts +97 -0
  183. package/templates/angular/src/app/demos/select/select-demo.component.ts +107 -0
  184. package/templates/angular/src/app/demos/shared/demo-example-clean.component.ts +41 -0
  185. package/templates/angular/src/app/demos/shared/demo-example.component.ts +42 -0
  186. package/templates/angular/src/app/demos/shared/demo-page.component.ts +97 -0
  187. package/templates/angular/src/app/demos/shared/index.ts +3 -0
  188. package/templates/angular/src/app/demos/side-navigation/side-navigation-demo.component.ts +524 -0
  189. package/templates/angular/src/app/demos/skeleton/skeleton-demo.component.ts +112 -0
  190. package/templates/angular/src/app/demos/slider/slider-demo.component.ts +76 -0
  191. package/templates/angular/src/app/demos/stepper/stepper-demo.component.ts +79 -0
  192. package/templates/angular/src/app/demos/switch/switch-demo.component.ts +113 -0
  193. package/templates/angular/src/app/demos/table/table-demo.component.ts +405 -0
  194. package/templates/angular/src/app/demos/tabs/tabs-demo.component.ts +318 -0
  195. package/templates/angular/src/app/demos/text-input/text-input-demo.component.ts +160 -0
  196. package/templates/angular/src/app/demos/textarea/textarea-demo.component.ts +95 -0
  197. package/templates/angular/src/app/demos/theme-switcher/theme-switcher-demo.component.ts +38 -0
  198. package/templates/angular/src/app/demos/time-input/time-input-demo.component.ts +130 -0
  199. package/templates/angular/src/app/demos/toast/toast-demo.component.ts +258 -0
  200. package/templates/angular/src/app/demos/toolbar/toolbar-demo.component.ts +54 -0
  201. package/templates/angular/src/app/demos/tooltip/tooltip-demo.component.ts +163 -0
  202. package/templates/angular/src/app/demos/utility-panel/utility-panel-demo.component.ts +197 -0
  203. package/templates/angular/src/app/dev/dev-config.ts +119 -0
  204. package/templates/angular/src/app/dev/dev-panel/dev-panel.component.ts +215 -0
  205. package/templates/angular/src/app/dev/dev-panel.service.ts +63 -0
  206. package/templates/angular/src/app/dev/index.ts +8 -0
  207. package/templates/angular/src/app/dev/theme-switcher-dropdown/theme-switcher-dropdown.component.ts +134 -0
  208. package/templates/angular/src/app/dev-pages/color-palette/color-palette.component.ts +229 -0
  209. package/templates/angular/src/app/dev-pages/components-gallery/components-gallery.component.ts +486 -0
  210. package/templates/angular/src/app/dev-pages/icons/icons.component.ts +149 -0
  211. package/templates/angular/src/app/pages/home/home.component.ts +251 -0
  212. package/templates/angular/src/app/services/README.md +57 -0
  213. package/templates/angular/src/app/services/theme.service.ts +163 -0
  214. package/templates/angular/src/environments/environment.development.ts +8 -0
  215. package/templates/angular/src/environments/environment.ts +8 -0
  216. package/templates/angular/src/index.html +14 -0
  217. package/templates/angular/src/main.ts +6 -0
  218. package/templates/angular/src/styles.css +1328 -0
  219. package/templates/angular/tsconfig.app.json +15 -0
  220. package/templates/angular/tsconfig.json +35 -0
  221. package/templates/angular/tsconfig.spec.json +14 -0
  222. package/templates/config.json +23 -0
  223. package/templates/react/.cursor/commands/remove-dev-content.md +311 -0
  224. package/templates/react/.cursor/mcp.json +13 -0
  225. package/templates/react/.cursor/rules/README.md +240 -0
  226. package/templates/react/.cursor/rules/border-usage-guidelines-short.mdc +22 -0
  227. package/templates/react/.cursor/rules/border-usage-guidelines.mdc +380 -0
  228. package/templates/react/.cursor/rules/chrome-devtools-testing-react-short.mdc +23 -0
  229. package/templates/react/.cursor/rules/development-workflow-react-short.mdc +23 -0
  230. package/templates/react/.cursor/rules/development-workflow-react.mdc +292 -0
  231. package/templates/react/.cursor/rules/implementation-guides-react-short.mdc +23 -0
  232. package/templates/react/.cursor/rules/implementation-guides-react.mdc +446 -0
  233. package/templates/react/.cursor/rules/modus-accordion-state-management-react-short.mdc +23 -0
  234. package/templates/react/.cursor/rules/modus-accordion-state-management-react.mdc +445 -0
  235. package/templates/react/.cursor/rules/modus-button-group-usage-react-short.mdc +23 -0
  236. package/templates/react/.cursor/rules/modus-button-group-usage-react.mdc +117 -0
  237. package/templates/react/.cursor/rules/modus-checkbox-value-inversion-react-short.mdc +23 -0
  238. package/templates/react/.cursor/rules/modus-checkbox-value-inversion-react.mdc +492 -0
  239. package/templates/react/.cursor/rules/modus-color-usage-react-short.mdc +23 -0
  240. package/templates/react/.cursor/rules/modus-color-usage-react.mdc +420 -0
  241. package/templates/react/.cursor/rules/modus-components-reference.mdc +366 -0
  242. package/templates/react/.cursor/rules/modus-icon-names.mdc +63 -0
  243. package/templates/react/.cursor/rules/modus-icons-react-short.mdc +24 -0
  244. package/templates/react/.cursor/rules/modus-icons-react.mdc +402 -0
  245. package/templates/react/.cursor/rules/modus-modal-implementation-react-short.mdc +23 -0
  246. package/templates/react/.cursor/rules/modus-modal-implementation-react.mdc +831 -0
  247. package/templates/react/.cursor/rules/modus-navbar-side-navigation-react-short.mdc +23 -0
  248. package/templates/react/.cursor/rules/modus-navbar-side-navigation-react.mdc +247 -0
  249. package/templates/react/.cursor/rules/modus-no-emojis-react-short.mdc +23 -0
  250. package/templates/react/.cursor/rules/modus-opacity-utilities-react-short.mdc +70 -0
  251. package/templates/react/.cursor/rules/modus-opacity-utilities-react.mdc +208 -0
  252. package/templates/react/.cursor/rules/modus-react-best-practices-short.mdc +23 -0
  253. package/templates/react/.cursor/rules/modus-react-best-practices.mdc +508 -0
  254. package/templates/react/.cursor/rules/modus-react-essentials.mdc +209 -0
  255. package/templates/react/.cursor/rules/modus-react-integration-short.mdc +23 -0
  256. package/templates/react/.cursor/rules/modus-react-integration.mdc +509 -0
  257. package/templates/react/.cursor/rules/modus-react-key-warnings-short.mdc +23 -0
  258. package/templates/react/.cursor/rules/modus-react-key-warnings.mdc +805 -0
  259. package/templates/react/.cursor/rules/modus-react-master.mdc +160 -0
  260. package/templates/react/.cursor/rules/modus-select-vs-dropdown-menu-react-short.mdc +23 -0
  261. package/templates/react/.cursor/rules/modus-select-vs-dropdown-menu-react.mdc +442 -0
  262. package/templates/react/.cursor/rules/modus-semantic-html-react-short.mdc +23 -0
  263. package/templates/react/.cursor/rules/modus-semantic-html-react.mdc +427 -0
  264. package/templates/react/.cursor/rules/modus-tailwind-usage-react-short.mdc +23 -0
  265. package/templates/react/.cursor/rules/modus-tailwind-usage-react.mdc +642 -0
  266. package/templates/react/.cursor/rules/modus-themes-react-short.mdc +23 -0
  267. package/templates/react/.cursor/rules/modus-themes-react.mdc +506 -0
  268. package/templates/react/.cursor/rules/ux/gestalt-laws-detailed.mdc +456 -0
  269. package/templates/react/.cursor/rules/ux/ux-ui-foundations.mdc +211 -0
  270. package/templates/react/.cursor/skills/create-modus-form-component/SKILL.md +518 -0
  271. package/templates/react/.cursor/skills/create-modus-wrapper-component/SKILL.md +252 -0
  272. package/templates/react/.cursor/skills/fix-modus-component-event-issues/SKILL.md +345 -0
  273. package/templates/react/.cursor/skills/handle-modus-checkbox-value-bug/SKILL.md +202 -0
  274. package/templates/react/.cursor/skills/implement-modus-modal-with-refs/SKILL.md +386 -0
  275. package/templates/react/.cursor/skills/integrate-modus-icons/SKILL.md +300 -0
  276. package/templates/react/.cursor/skills/run-lint-checks/SKILL.md +235 -0
  277. package/templates/react/.cursor/skills/set-up-modus-event-listeners/SKILL.md +284 -0
  278. package/templates/react/.cursor/skills/style-modus-components-with-tailwind/SKILL.md +382 -0
  279. package/templates/react/.env.development +3 -0
  280. package/templates/react/.env.production +3 -0
  281. package/templates/react/.github/CODEOWNERS +28 -0
  282. package/templates/react/.github/ISSUE_TEMPLATE/bug_report.yml +176 -0
  283. package/templates/react/.github/ISSUE_TEMPLATE/config.yml +20 -0
  284. package/templates/react/.github/ISSUE_TEMPLATE/documentation.yml +115 -0
  285. package/templates/react/.github/ISSUE_TEMPLATE/feature_request.yml +171 -0
  286. package/templates/react/.github/ISSUE_TEMPLATE/question.yml +139 -0
  287. package/templates/react/.github/copilot-instructions.md +80 -0
  288. package/templates/react/.github/instructions/components.instructions.md +82 -0
  289. package/templates/react/.github/instructions/demos.instructions.md +82 -0
  290. package/templates/react/.github/instructions/pages.instructions.md +76 -0
  291. package/templates/react/.github/instructions/styles.instructions.md +77 -0
  292. package/templates/react/.github/instructions/typescript.instructions.md +101 -0
  293. package/templates/react/.github/pull_request_template.md +188 -0
  294. package/templates/react/.github/workflows/ci.yml +43 -0
  295. package/templates/react/.github/workflows/claude-code-review.yml +44 -0
  296. package/templates/react/.github/workflows/claude.yml +50 -0
  297. package/templates/react/.husky/pre-commit +28 -0
  298. package/templates/react/.vscode/extensions.json +8 -0
  299. package/templates/react/CLAUDE.md +119 -0
  300. package/templates/react/CODE_OF_CONDUCT.md +79 -0
  301. package/templates/react/CONTRIBUTING.md +65 -0
  302. package/templates/react/LICENSE +21 -0
  303. package/templates/react/README.md +728 -0
  304. package/templates/react/SECURITY.md +50 -0
  305. package/templates/react/eslint.config.js +23 -0
  306. package/templates/react/index.html +13 -0
  307. package/templates/react/package-lock.json +5209 -0
  308. package/templates/react/package.json +49 -0
  309. package/templates/react/postcss.config.js +6 -0
  310. package/templates/react/public/react.svg +1 -0
  311. package/templates/react/public/vite.svg +1 -0
  312. package/templates/react/readme_assets/getting_started_header.png +0 -0
  313. package/templates/react/readme_assets/hero.png +0 -0
  314. package/templates/react/readme_assets/modus_comp.png +0 -0
  315. package/templates/react/readme_assets/modus_figma_mcp.png +0 -0
  316. package/templates/react/readme_assets/teaser_comp.gif +0 -0
  317. package/templates/react/scripts/README.md +343 -0
  318. package/templates/react/scripts/check-border-violations.js +483 -0
  319. package/templates/react/scripts/check-icon-names.js +486 -0
  320. package/templates/react/scripts/check-inline-styles.js +364 -0
  321. package/templates/react/scripts/check-modus-colors.js +247 -0
  322. package/templates/react/scripts/check-modus-icons.js +256 -0
  323. package/templates/react/scripts/check-opacity-utilities.js +481 -0
  324. package/templates/react/scripts/check-semantic-html.js +476 -0
  325. package/templates/react/scripts/check-typescript.js +109 -0
  326. package/templates/react/src/App.css +42 -0
  327. package/templates/react/src/App.tsx +54 -0
  328. package/templates/react/src/assets/react.svg +1 -0
  329. package/templates/react/src/components/DemoExample.tsx +61 -0
  330. package/templates/react/src/components/DemoPage.tsx +81 -0
  331. package/templates/react/src/components/ModusAccordion.tsx +89 -0
  332. package/templates/react/src/components/ModusAlert.tsx +85 -0
  333. package/templates/react/src/components/ModusAutocomplete.tsx +207 -0
  334. package/templates/react/src/components/ModusAvatar.tsx +50 -0
  335. package/templates/react/src/components/ModusBadge.tsx +82 -0
  336. package/templates/react/src/components/ModusBreadcrumbs.tsx +75 -0
  337. package/templates/react/src/components/ModusButton.tsx +244 -0
  338. package/templates/react/src/components/ModusButtonGroup.tsx +91 -0
  339. package/templates/react/src/components/ModusCard.tsx +70 -0
  340. package/templates/react/src/components/ModusCheckbox.tsx +168 -0
  341. package/templates/react/src/components/ModusChip.tsx +93 -0
  342. package/templates/react/src/components/ModusDate.tsx +154 -0
  343. package/templates/react/src/components/ModusDropdownMenu.tsx +148 -0
  344. package/templates/react/src/components/ModusFileDropzone.tsx +140 -0
  345. package/templates/react/src/components/ModusHandle.tsx +101 -0
  346. package/templates/react/src/components/ModusIcon.tsx +49 -0
  347. package/templates/react/src/components/ModusInputFeedback.tsx +52 -0
  348. package/templates/react/src/components/ModusInputLabel.tsx +50 -0
  349. package/templates/react/src/components/ModusLoader.tsx +42 -0
  350. package/templates/react/src/components/ModusLogo.tsx +102 -0
  351. package/templates/react/src/components/ModusMenu.tsx +119 -0
  352. package/templates/react/src/components/ModusMenuItem.tsx +86 -0
  353. package/templates/react/src/components/ModusModal.tsx +145 -0
  354. package/templates/react/src/components/ModusNavbar.tsx +504 -0
  355. package/templates/react/src/components/ModusNumberInput.tsx +230 -0
  356. package/templates/react/src/components/ModusPagination.tsx +94 -0
  357. package/templates/react/src/components/ModusPanel.tsx +92 -0
  358. package/templates/react/src/components/ModusProgress.tsx +70 -0
  359. package/templates/react/src/components/ModusProvider.tsx +18 -0
  360. package/templates/react/src/components/ModusRadio.tsx +114 -0
  361. package/templates/react/src/components/ModusRating.tsx +108 -0
  362. package/templates/react/src/components/ModusSelect.tsx +171 -0
  363. package/templates/react/src/components/ModusSideNavigation.tsx +149 -0
  364. package/templates/react/src/components/ModusSkeleton.tsx +42 -0
  365. package/templates/react/src/components/ModusSlider.tsx +128 -0
  366. package/templates/react/src/components/ModusStepper.tsx +85 -0
  367. package/templates/react/src/components/ModusSwitch.tsx +130 -0
  368. package/templates/react/src/components/ModusTable.tsx +309 -0
  369. package/templates/react/src/components/ModusTabs.tsx +114 -0
  370. package/templates/react/src/components/ModusTextInput.tsx +179 -0
  371. package/templates/react/src/components/ModusTextarea.tsx +164 -0
  372. package/templates/react/src/components/ModusThemeSwitcher.tsx +58 -0
  373. package/templates/react/src/components/ModusTimeInput.tsx +176 -0
  374. package/templates/react/src/components/ModusToast.tsx +207 -0
  375. package/templates/react/src/components/ModusToolbar.tsx +70 -0
  376. package/templates/react/src/components/ModusTooltip.tsx +97 -0
  377. package/templates/react/src/components/ModusUtilityPanel.tsx +198 -0
  378. package/templates/react/src/components/ThemeSwitcherDropdown.tsx +117 -0
  379. package/templates/react/src/components/ThemeToggleSimple.tsx +157 -0
  380. package/templates/react/src/config/routes.ts +196 -0
  381. package/templates/react/src/contexts/ThemeContext.tsx +81 -0
  382. package/templates/react/src/contexts/ThemeContextData.tsx +89 -0
  383. package/templates/react/src/data/modusIcons.ts +865 -0
  384. package/templates/react/src/demos/accordion-demo/page.tsx +236 -0
  385. package/templates/react/src/demos/alert-demo/page.tsx +94 -0
  386. package/templates/react/src/demos/autocomplete-demo/page.tsx +166 -0
  387. package/templates/react/src/demos/avatar-demo/page.tsx +135 -0
  388. package/templates/react/src/demos/badge-demo/page.tsx +174 -0
  389. package/templates/react/src/demos/breadcrumbs-demo/page.tsx +88 -0
  390. package/templates/react/src/demos/button-demo/page.tsx +261 -0
  391. package/templates/react/src/demos/button-group-demo/page.tsx +231 -0
  392. package/templates/react/src/demos/card-demo/page.tsx +241 -0
  393. package/templates/react/src/demos/checkbox-demo/page.tsx +79 -0
  394. package/templates/react/src/demos/chip-demo/page.tsx +197 -0
  395. package/templates/react/src/demos/date-demo/page.tsx +179 -0
  396. package/templates/react/src/demos/dropdown-demo/page.tsx +150 -0
  397. package/templates/react/src/demos/file-dropzone-demo/page.tsx +186 -0
  398. package/templates/react/src/demos/handle-demo/page.tsx +313 -0
  399. package/templates/react/src/demos/icon-demo/page.tsx +72 -0
  400. package/templates/react/src/demos/input-feedback-demo/page.tsx +202 -0
  401. package/templates/react/src/demos/input-label-demo/page.tsx +392 -0
  402. package/templates/react/src/demos/loader-demo/page.tsx +138 -0
  403. package/templates/react/src/demos/logo-demo/page.tsx +292 -0
  404. package/templates/react/src/demos/menu-demo/page.tsx +70 -0
  405. package/templates/react/src/demos/modal-demo/page.tsx +332 -0
  406. package/templates/react/src/demos/navbar-demo/page.tsx +141 -0
  407. package/templates/react/src/demos/number-input-demo/page.tsx +180 -0
  408. package/templates/react/src/demos/pagination-demo/page.tsx +147 -0
  409. package/templates/react/src/demos/panel-demo/page.tsx +376 -0
  410. package/templates/react/src/demos/progress-demo/page.tsx +185 -0
  411. package/templates/react/src/demos/radio-demo/page.tsx +242 -0
  412. package/templates/react/src/demos/rating-demo/page.tsx +97 -0
  413. package/templates/react/src/demos/select-demo/page.tsx +111 -0
  414. package/templates/react/src/demos/side-navigation-demo/page.tsx +775 -0
  415. package/templates/react/src/demos/skeleton-demo/page.tsx +107 -0
  416. package/templates/react/src/demos/slider-demo/page.tsx +78 -0
  417. package/templates/react/src/demos/stepper-demo/page.tsx +86 -0
  418. package/templates/react/src/demos/switch-demo/page.tsx +146 -0
  419. package/templates/react/src/demos/table-demo/page.tsx +489 -0
  420. package/templates/react/src/demos/tabs-demo/page.tsx +187 -0
  421. package/templates/react/src/demos/text-input-demo/page.tsx +151 -0
  422. package/templates/react/src/demos/textarea-demo/page.tsx +73 -0
  423. package/templates/react/src/demos/theme-switcher-demo/page.tsx +26 -0
  424. package/templates/react/src/demos/time-input-demo/page.tsx +148 -0
  425. package/templates/react/src/demos/toast-demo/page.tsx +302 -0
  426. package/templates/react/src/demos/toolbar-demo/page.tsx +49 -0
  427. package/templates/react/src/demos/tooltip-demo/page.tsx +209 -0
  428. package/templates/react/src/demos/typography-test/page.tsx +28 -0
  429. package/templates/react/src/demos/utility-panel-demo/page.tsx +197 -0
  430. package/templates/react/src/dev/DevPanel.tsx +219 -0
  431. package/templates/react/src/dev/DevPanelContext.ts +14 -0
  432. package/templates/react/src/dev/DevPanelProvider.tsx +63 -0
  433. package/templates/react/src/dev/DevRoutes.tsx +98 -0
  434. package/templates/react/src/dev/config.ts +127 -0
  435. package/templates/react/src/dev/index.ts +8 -0
  436. package/templates/react/src/dev/useDevPanel.ts +17 -0
  437. package/templates/react/src/dev-pages/ColorPalettePage.tsx +347 -0
  438. package/templates/react/src/dev-pages/ComponentsGalleryPage.tsx +489 -0
  439. package/templates/react/src/dev-pages/IconsPage.tsx +137 -0
  440. package/templates/react/src/dev-pages/index.ts +3 -0
  441. package/templates/react/src/hooks/useTheme.ts +15 -0
  442. package/templates/react/src/index.css +635 -0
  443. package/templates/react/src/main.tsx +14 -0
  444. package/templates/react/src/pages/HomePage.tsx +283 -0
  445. package/templates/react/src/vite-env.d.ts +9 -0
  446. package/templates/react/tailwind.config.js +58 -0
  447. package/templates/react/tsconfig.app.json +27 -0
  448. package/templates/react/tsconfig.json +7 -0
  449. package/templates/react/tsconfig.node.json +25 -0
  450. package/templates/react/vite.config.ts +18 -0
@@ -0,0 +1,831 @@
1
+ ---
2
+ description: Critical implementation patterns for ModusWcModal in React
3
+ globs: ["**/components/ModusModal*.tsx", "**/demos/**/page.tsx"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # ModusWcModal Implementation in React
8
+
9
+ ## 🚨 **CRITICAL: Modal Method Access Issue**
10
+
11
+ **Problem**: The `ModusWcModal` component's `showModal()` and `close()` methods are not directly available on the component reference in React.
12
+
13
+ **Root Cause**: Modus Web Components use shadow DOM, and the modal methods are on the inner native `<dialog>` element, not the component itself.
14
+
15
+ **Official Documentation**: According to the official Modus documentation, modals are controlled with direct method calls (`showModal()` and `close()`) on the modal element, not through React state management.
16
+
17
+ ## ❌ **Common Anti-Patterns**
18
+
19
+ ### ❌ **Direct Method Access (Won't Work)**
20
+
21
+ ```tsx
22
+ // ❌ WRONG: Direct method access on component
23
+ function ModalComponent() {
24
+ const modalRef = useRef<ModusWcModal>(null);
25
+
26
+ const openModal = () => {
27
+ if (modalRef.current) {
28
+ modalRef.current.showModal(); // ❌ This won't work
29
+ }
30
+ };
31
+
32
+ const closeModal = () => {
33
+ if (modalRef.current) {
34
+ modalRef.current.close(); // ❌ This won't work
35
+ }
36
+ };
37
+
38
+ return (
39
+ <div>
40
+ <button onClick={openModal}>Open Modal</button>
41
+ <ModusWcModal ref={modalRef}>
42
+ <div slot="header">Modal Header</div>
43
+ <div slot="body">Modal Body</div>
44
+ <div slot="footer">
45
+ <button onClick={closeModal}>Close</button>
46
+ </div>
47
+ </ModusWcModal>
48
+ </div>
49
+ );
50
+ }
51
+ ```
52
+
53
+ ### ❌ **Trying to Control Modal State from React**
54
+
55
+ ```tsx
56
+ // ❌ WRONG: Trying to control modal state from React
57
+ function ModalComponent() {
58
+ const [isOpen, setIsOpen] = useState(false);
59
+
60
+ return (
61
+ <ModusWcModal
62
+ open={isOpen} // ❌ This won't work as expected
63
+ onClose={() => setIsOpen(false)}
64
+ >
65
+ <div slot="header">Modal Header</div>
66
+ <div slot="body">Modal Body</div>
67
+ </ModusWcModal>
68
+ );
69
+ }
70
+ ```
71
+
72
+ ### ❌ **Using useEffect to Control Modal State (Critical Anti-Pattern)**
73
+
74
+ ```tsx
75
+ // ❌ WRONG: Using useEffect to control modal state
76
+ function ModusModal({ isOpen, onClose, ...props }) {
77
+ const modalRef = useRef<HTMLModusWcModalElement>(null);
78
+
79
+ // ❌ CRITICAL ANTI-PATTERN: Don't control modal state from React
80
+ useEffect(() => {
81
+ const modal = modalRef.current;
82
+ if (modal) {
83
+ if (isOpen) {
84
+ const dialogElement = modal.querySelector("dialog");
85
+ if (dialogElement) {
86
+ dialogElement.showModal();
87
+ }
88
+ } else {
89
+ const dialogElement = modal.querySelector("dialog");
90
+ if (dialogElement) {
91
+ dialogElement.close();
92
+ }
93
+ }
94
+ }
95
+ }, [isOpen]); // ❌ This violates the official pattern
96
+
97
+ return <ModusWcModal ref={modalRef} {...props} />;
98
+ }
99
+ ```
100
+
101
+ **Why This is Wrong**: The official Modus documentation states that modals should be controlled by direct method calls, not React state. This approach creates unnecessary re-renders and goes against the documented pattern.
102
+
103
+ ## ✅ **Correct Patterns**
104
+
105
+ ### ✅ **Recommended: React Wrapper Component with forwardRef**
106
+
107
+ The most maintainable approach is to create a React wrapper component that handles the dialog element access internally:
108
+
109
+ ```tsx
110
+ // ✅ CORRECT: React wrapper with forwardRef
111
+ import { ModusWcModal } from "@trimble-oss/moduswebcomponents-react";
112
+ import { useRef, forwardRef, useImperativeHandle } from "react";
113
+
114
+ export interface ModusModalRef {
115
+ openModal: () => void;
116
+ closeModal: () => void;
117
+ }
118
+
119
+ const ModusModal = forwardRef<ModusModalRef, ModusModalProps>(
120
+ (
121
+ {
122
+ modalId,
123
+ ariaLabel,
124
+ backdrop = "default",
125
+ position = "center",
126
+ fullscreen = false,
127
+ showFullscreenToggle = false,
128
+ showClose = true,
129
+ customClass,
130
+ header,
131
+ children,
132
+ footer,
133
+ onClose,
134
+ className,
135
+ },
136
+ ref
137
+ ) => {
138
+ const modalRef = useRef<HTMLModusWcModalElement>(null);
139
+
140
+ const openModal = () => {
141
+ if (modalRef.current) {
142
+ const dialog = modalRef.current.querySelector(
143
+ "dialog"
144
+ ) as HTMLDialogElement;
145
+ if (dialog) {
146
+ dialog.showModal();
147
+ }
148
+ }
149
+ };
150
+
151
+ const closeModal = () => {
152
+ if (modalRef.current) {
153
+ const dialog = modalRef.current.querySelector(
154
+ "dialog"
155
+ ) as HTMLDialogElement;
156
+ if (dialog) {
157
+ dialog.close();
158
+ }
159
+ }
160
+ };
161
+
162
+ useImperativeHandle(ref, () => ({
163
+ openModal,
164
+ closeModal,
165
+ }));
166
+
167
+ // Handle modal events
168
+ useEffect(() => {
169
+ const modal = modalRef.current;
170
+ if (modal) {
171
+ const handleClose = () => {
172
+ onClose?.();
173
+ };
174
+
175
+ const dialogElement = modal.querySelector("dialog");
176
+ if (dialogElement) {
177
+ dialogElement.addEventListener("close", handleClose);
178
+ return () => {
179
+ dialogElement.removeEventListener("close", handleClose);
180
+ };
181
+ }
182
+ }
183
+ }, [onClose]);
184
+
185
+ return (
186
+ <ModusWcModal
187
+ ref={modalRef}
188
+ modal-id={modalId}
189
+ aria-label={ariaLabel}
190
+ backdrop={backdrop}
191
+ position={position}
192
+ fullscreen={fullscreen}
193
+ show-fullscreen-toggle={showFullscreenToggle}
194
+ show-close={showClose}
195
+ custom-class={customClass || className}
196
+ >
197
+ {header && <div slot="header">{header}</div>}
198
+ <div slot="content">{children}</div>
199
+ {footer && <div slot="footer">{footer}</div>}
200
+ </ModusWcModal>
201
+ );
202
+ }
203
+ );
204
+
205
+ // Usage in parent component
206
+ function ParentComponent() {
207
+ const modalRef = useRef<ModusModalRef>(null);
208
+
209
+ const openModal = () => {
210
+ modalRef.current?.openModal();
211
+ };
212
+
213
+ const closeModal = () => {
214
+ modalRef.current?.closeModal();
215
+ };
216
+
217
+ return (
218
+ <div>
219
+ <button onClick={openModal}>Open Modal</button>
220
+ <ModusModal
221
+ ref={modalRef}
222
+ modalId="my-modal"
223
+ onClose={() => console.log("Modal closed")}
224
+ header={<div>Modal Title</div>}
225
+ footer={<div>Modal Actions</div>}
226
+ >
227
+ <div>Modal Content</div>
228
+ </ModusModal>
229
+ </div>
230
+ );
231
+ }
232
+ ```
233
+
234
+ ### ✅ **Access Inner Dialog Element (Direct Approach)**
235
+
236
+ ```tsx
237
+ // ✅ CORRECT: Access inner dialog element
238
+ function ModalComponent() {
239
+ const modalRef = useRef<ModusWcModal>(null);
240
+
241
+ const openModal = () => {
242
+ if (modalRef.current) {
243
+ // ✅ CORRECT: Access inner dialog element
244
+ const dialog = modalRef.current.querySelector(
245
+ "dialog"
246
+ ) as HTMLDialogElement;
247
+ if (dialog) {
248
+ dialog.showModal();
249
+ }
250
+ }
251
+ };
252
+
253
+ const closeModal = () => {
254
+ if (modalRef.current) {
255
+ // ✅ CORRECT: Access inner dialog element
256
+ const dialog = modalRef.current.querySelector(
257
+ "dialog"
258
+ ) as HTMLDialogElement;
259
+ if (dialog) {
260
+ dialog.close();
261
+ }
262
+ }
263
+ };
264
+
265
+ return (
266
+ <div>
267
+ <button onClick={openModal}>Open Modal</button>
268
+ <ModusWcModal ref={modalRef}>
269
+ <div slot="header">Modal Header</div>
270
+ <div slot="body">Modal Body</div>
271
+ <div slot="footer">
272
+ <button onClick={closeModal}>Close</button>
273
+ </div>
274
+ </ModusWcModal>
275
+ </div>
276
+ );
277
+ }
278
+ ```
279
+
280
+ ### ✅ **Reusable Modal Hook**
281
+
282
+ ```tsx
283
+ // ✅ CORRECT: Reusable modal hook
284
+ function useModal() {
285
+ const modalRef = useRef<ModusWcModal>(null);
286
+
287
+ const openModal = () => {
288
+ if (modalRef.current) {
289
+ const dialog = modalRef.current.querySelector(
290
+ "dialog"
291
+ ) as HTMLDialogElement;
292
+ if (dialog) {
293
+ dialog.showModal();
294
+ }
295
+ }
296
+ };
297
+
298
+ const closeModal = () => {
299
+ if (modalRef.current) {
300
+ const dialog = modalRef.current.querySelector(
301
+ "dialog"
302
+ ) as HTMLDialogElement;
303
+ if (dialog) {
304
+ dialog.close();
305
+ }
306
+ }
307
+ };
308
+
309
+ return {
310
+ modalRef,
311
+ openModal,
312
+ closeModal,
313
+ };
314
+ }
315
+
316
+ // ✅ CORRECT: Use the hook
317
+ function ModalComponent() {
318
+ const { modalRef, openModal, closeModal } = useModal();
319
+
320
+ return (
321
+ <div>
322
+ <button onClick={openModal}>Open Modal</button>
323
+ <ModusWcModal ref={modalRef}>
324
+ <div slot="header">Modal Header</div>
325
+ <div slot="body">Modal Body</div>
326
+ <div slot="footer">
327
+ <button onClick={closeModal}>Close</button>
328
+ </div>
329
+ </ModusWcModal>
330
+ </div>
331
+ );
332
+ }
333
+ ```
334
+
335
+ ### ✅ **Modal with React State Tracking**
336
+
337
+ ```tsx
338
+ // ✅ CORRECT: Track modal state without controlling it
339
+ function ModalComponent() {
340
+ const modalRef = useRef<ModusWcModal>(null);
341
+ const [isModalOpen, setIsModalOpen] = useState(false);
342
+
343
+ const openModal = () => {
344
+ if (modalRef.current) {
345
+ const dialog = modalRef.current.querySelector(
346
+ "dialog"
347
+ ) as HTMLDialogElement;
348
+ if (dialog) {
349
+ dialog.showModal();
350
+ setIsModalOpen(true); // ✅ CORRECT: Track state for UI updates
351
+ }
352
+ }
353
+ };
354
+
355
+ const closeModal = () => {
356
+ if (modalRef.current) {
357
+ const dialog = modalRef.current.querySelector(
358
+ "dialog"
359
+ ) as HTMLDialogElement;
360
+ if (dialog) {
361
+ dialog.close();
362
+ setIsModalOpen(false); // ✅ CORRECT: Track state for UI updates
363
+ }
364
+ }
365
+ };
366
+
367
+ // ✅ CORRECT: Listen to modal events
368
+ const handleModalClose = () => {
369
+ setIsModalOpen(false);
370
+ };
371
+
372
+ return (
373
+ <div>
374
+ <button onClick={openModal}>Open Modal</button>
375
+ {isModalOpen && <div>Modal is open</div>}
376
+
377
+ <ModusWcModal ref={modalRef} onClose={handleModalClose}>
378
+ <div slot="header">Modal Header</div>
379
+ <div slot="body">Modal Body</div>
380
+ <div slot="footer">
381
+ <button onClick={closeModal}>Close</button>
382
+ </div>
383
+ </ModusWcModal>
384
+ </div>
385
+ );
386
+ }
387
+ ```
388
+
389
+ ## 🎯 **Advanced Modal Patterns**
390
+
391
+ ### ✅ **Modal with Form Handling**
392
+
393
+ ```tsx
394
+ // ✅ CORRECT: Modal with form handling
395
+ function FormModal() {
396
+ const modalRef = useRef<ModusWcModal>(null);
397
+ const [formData, setFormData] = useState({ name: "", email: "" });
398
+
399
+ const openModal = () => {
400
+ if (modalRef.current) {
401
+ const dialog = modalRef.current.querySelector(
402
+ "dialog"
403
+ ) as HTMLDialogElement;
404
+ if (dialog) {
405
+ dialog.showModal();
406
+ }
407
+ }
408
+ };
409
+
410
+ const closeModal = () => {
411
+ if (modalRef.current) {
412
+ const dialog = modalRef.current.querySelector(
413
+ "dialog"
414
+ ) as HTMLDialogElement;
415
+ if (dialog) {
416
+ dialog.close();
417
+ }
418
+ }
419
+ };
420
+
421
+ const handleSubmit = (e: React.FormEvent) => {
422
+ e.preventDefault();
423
+ console.log("Form data:", formData);
424
+ closeModal();
425
+ };
426
+
427
+ return (
428
+ <div>
429
+ <button onClick={openModal}>Open Form Modal</button>
430
+
431
+ <ModusWcModal ref={modalRef}>
432
+ <div slot="header">User Form</div>
433
+ <div slot="body">
434
+ <form onSubmit={handleSubmit} className="space-y-4">
435
+ <div>
436
+ <label className="block text-sm font-medium text-foreground mb-1">
437
+ Name
438
+ </label>
439
+ <input
440
+ type="text"
441
+ value={formData.name}
442
+ onChange={(e) =>
443
+ setFormData((prev) => ({ ...prev, name: e.target.value }))
444
+ }
445
+ className="w-full px-3 py-2 border border-input rounded bg-input text-foreground"
446
+ required
447
+ />
448
+ </div>
449
+ <div>
450
+ <label className="block text-sm font-medium text-foreground mb-1">
451
+ Email
452
+ </label>
453
+ <input
454
+ type="email"
455
+ value={formData.email}
456
+ onChange={(e) =>
457
+ setFormData((prev) => ({ ...prev, email: e.target.value }))
458
+ }
459
+ className="w-full px-3 py-2 border border-input rounded bg-input text-foreground"
460
+ required
461
+ />
462
+ </div>
463
+ </form>
464
+ </div>
465
+ <div slot="footer" className="flex justify-end gap-2">
466
+ <button
467
+ type="button"
468
+ onClick={closeModal}
469
+ className="px-4 py-2 border border-border rounded text-foreground hover:bg-muted"
470
+ >
471
+ Cancel
472
+ </button>
473
+ <button
474
+ type="submit"
475
+ onClick={handleSubmit}
476
+ className="px-4 py-2 bg-primary text-primary-foreground rounded hover:bg-primary/90"
477
+ >
478
+ Save
479
+ </button>
480
+ </div>
481
+ </ModusWcModal>
482
+ </div>
483
+ );
484
+ }
485
+ ```
486
+
487
+ ### ✅ **Modal with Confirmation**
488
+
489
+ ```tsx
490
+ // ✅ CORRECT: Confirmation modal
491
+ function ConfirmationModal({
492
+ isOpen,
493
+ onConfirm,
494
+ onCancel,
495
+ title,
496
+ message,
497
+ }: ConfirmationModalProps) {
498
+ const modalRef = useRef<ModusWcModal>(null);
499
+
500
+ useEffect(() => {
501
+ if (isOpen && modalRef.current) {
502
+ const dialog = modalRef.current.querySelector(
503
+ "dialog"
504
+ ) as HTMLDialogElement;
505
+ if (dialog) {
506
+ dialog.showModal();
507
+ }
508
+ }
509
+ }, [isOpen]);
510
+
511
+ const handleConfirm = () => {
512
+ onConfirm();
513
+ if (modalRef.current) {
514
+ const dialog = modalRef.current.querySelector(
515
+ "dialog"
516
+ ) as HTMLDialogElement;
517
+ if (dialog) {
518
+ dialog.close();
519
+ }
520
+ }
521
+ };
522
+
523
+ const handleCancel = () => {
524
+ onCancel();
525
+ if (modalRef.current) {
526
+ const dialog = modalRef.current.querySelector(
527
+ "dialog"
528
+ ) as HTMLDialogElement;
529
+ if (dialog) {
530
+ dialog.close();
531
+ }
532
+ }
533
+ };
534
+
535
+ return (
536
+ <ModusWcModal ref={modalRef}>
537
+ <div slot="header">{title}</div>
538
+ <div slot="body">{message}</div>
539
+ <div slot="footer" className="flex justify-end gap-2">
540
+ <button
541
+ onClick={handleCancel}
542
+ className="px-4 py-2 border border-border rounded text-foreground hover:bg-muted"
543
+ >
544
+ Cancel
545
+ </button>
546
+ <button
547
+ onClick={handleConfirm}
548
+ className="px-4 py-2 bg-destructive text-destructive-foreground rounded hover:bg-destructive/90"
549
+ >
550
+ Confirm
551
+ </button>
552
+ </div>
553
+ </ModusWcModal>
554
+ );
555
+ }
556
+ ```
557
+
558
+ ### ✅ **Modal with Backdrop Click**
559
+
560
+ ```tsx
561
+ // ✅ CORRECT: Modal with backdrop click handling
562
+ function ModalWithBackdrop() {
563
+ const modalRef = useRef<ModusWcModal>(null);
564
+
565
+ const openModal = () => {
566
+ if (modalRef.current) {
567
+ const dialog = modalRef.current.querySelector(
568
+ "dialog"
569
+ ) as HTMLDialogElement;
570
+ if (dialog) {
571
+ dialog.showModal();
572
+
573
+ // ✅ CORRECT: Handle backdrop click
574
+ dialog.addEventListener("click", (e) => {
575
+ if (e.target === dialog) {
576
+ dialog.close();
577
+ }
578
+ });
579
+ }
580
+ }
581
+ };
582
+
583
+ const closeModal = () => {
584
+ if (modalRef.current) {
585
+ const dialog = modalRef.current.querySelector(
586
+ "dialog"
587
+ ) as HTMLDialogElement;
588
+ if (dialog) {
589
+ dialog.close();
590
+ }
591
+ }
592
+ };
593
+
594
+ return (
595
+ <div>
596
+ <button onClick={openModal}>Open Modal</button>
597
+ <ModusWcModal ref={modalRef}>
598
+ <div slot="header">Modal Header</div>
599
+ <div slot="body">Click outside to close</div>
600
+ <div slot="footer">
601
+ <button onClick={closeModal}>Close</button>
602
+ </div>
603
+ </ModusWcModal>
604
+ </div>
605
+ );
606
+ }
607
+ ```
608
+
609
+ ## 🚫 **What NOT to Do**
610
+
611
+ ### ❌ **Don't Try to Control Modal State from React**
612
+
613
+ ```tsx
614
+ // ❌ WRONG: Don't try to control modal state
615
+ const [isOpen, setIsOpen] = useState(false);
616
+
617
+ <ModusWcModal open={isOpen}> // ❌ This won't work
618
+ ```
619
+
620
+ ### ❌ **Don't Use Direct Method Access**
621
+
622
+ ```tsx
623
+ // ❌ WRONG: Don't use direct method access
624
+ modalRef.current.showModal(); // ❌ This won't work
625
+ modalRef.current.close(); // ❌ This won't work
626
+ ```
627
+
628
+ ### ❌ **Don't Ignore the Dialog Element**
629
+
630
+ ```tsx
631
+ // ❌ WRONG: Don't ignore the dialog element
632
+ const openModal = () => {
633
+ // This won't work
634
+ modalRef.current.showModal();
635
+ };
636
+ ```
637
+
638
+ ## 📚 **Official Modus Documentation Compliance**
639
+
640
+ ### **Key Findings from Official Documentation:**
641
+
642
+ 1. **No React State Control**: The official documentation shows modals controlled by direct method calls, not React state
643
+ 2. **Direct Method Access**: Use `element.showModal()` and `element.close()` directly
644
+ 3. **Proper Slot Structure**: Use `header`, `content`, and `footer` slots correctly
645
+ 4. **Event Handling**: Listen to native dialog events, not React synthetic events
646
+ 5. **No Built-in React Integration**: Modus Web Components are framework-agnostic
647
+
648
+ ### **Documentation Quote:**
649
+
650
+ > "Opening / closing: call `element.showModal()` to open and `element.close()` to dismiss; both return immediately and trigger `<dialog>` events."
651
+
652
+ ## 🎯 **Key Takeaways**
653
+
654
+ 1. **✅ Use forwardRef Pattern**: Create React wrapper components with `forwardRef` and `useImperativeHandle`
655
+ 2. **✅ Access Inner Dialog**: Use `querySelector('dialog')` to access the inner dialog element
656
+ 3. **✅ Use Dialog Methods**: Call `showModal()` and `close()` on the dialog element
657
+ 4. **❌ Don't Control from React State**: Never use `useEffect` to control modal visibility
658
+ 5. **✅ Track State Separately**: Use React state only for UI updates, not modal control
659
+ 6. **✅ Handle Events Properly**: Listen to modal events for state synchronization
660
+ 7. **✅ Follow Official Pattern**: Align with Modus documentation for best practices
661
+
662
+ ## 🔧 **Testing Modal Implementation**
663
+
664
+ ```tsx
665
+ // ✅ CORRECT: Test modal functionality
666
+ function ModalTest() {
667
+ const modalRef = useRef<ModusWcModal>(null);
668
+ const [testResults, setTestResults] = useState<string[]>([]);
669
+
670
+ const testModal = () => {
671
+ if (modalRef.current) {
672
+ const dialog = modalRef.current.querySelector(
673
+ "dialog"
674
+ ) as HTMLDialogElement;
675
+ if (dialog) {
676
+ dialog.showModal();
677
+ setTestResults((prev) => [...prev, "Modal opened successfully"]);
678
+
679
+ setTimeout(() => {
680
+ dialog.close();
681
+ setTestResults((prev) => [...prev, "Modal closed successfully"]);
682
+ }, 1000);
683
+ }
684
+ }
685
+ };
686
+
687
+ return (
688
+ <div>
689
+ <button onClick={testModal}>Test Modal</button>
690
+ <div>
691
+ <h3>Test Results:</h3>
692
+ {testResults.map((result, index) => (
693
+ <div key={index}>{result}</div>
694
+ ))}
695
+ </div>
696
+ <ModusWcModal ref={modalRef}>
697
+ <div slot="header">Test Modal</div>
698
+ <div slot="body">This is a test modal</div>
699
+ <div slot="footer">
700
+ <button
701
+ onClick={() => {
702
+ const dialog = modalRef.current?.querySelector(
703
+ "dialog"
704
+ ) as HTMLDialogElement;
705
+ dialog?.close();
706
+ }}
707
+ >
708
+ Close
709
+ </button>
710
+ </div>
711
+ </ModusWcModal>
712
+ </div>
713
+ );
714
+ }
715
+ ```
716
+
717
+ ## 🎉 **Successfully Implemented Pattern**
718
+
719
+ ### **Working Implementation Example:**
720
+
721
+ ```tsx
722
+ // ✅ SUCCESSFUL: Our working implementation
723
+ import { ModusWcModal } from "@trimble-oss/moduswebcomponents-react";
724
+ import { useRef, forwardRef, useImperativeHandle } from "react";
725
+
726
+ export interface ModusModalRef {
727
+ openModal: () => void;
728
+ closeModal: () => void;
729
+ }
730
+
731
+ const ModusModal = forwardRef<ModusModalRef, ModusModalProps>(
732
+ (
733
+ {
734
+ modalId,
735
+ ariaLabel,
736
+ backdrop = "default",
737
+ position = "center",
738
+ fullscreen = false,
739
+ showFullscreenToggle = false,
740
+ showClose = true,
741
+ customClass,
742
+ header,
743
+ children,
744
+ footer,
745
+ onClose,
746
+ className,
747
+ },
748
+ ref
749
+ ) => {
750
+ const modalRef = useRef<HTMLModusWcModalElement>(null);
751
+
752
+ const openModal = () => {
753
+ if (modalRef.current) {
754
+ const dialog = modalRef.current.querySelector(
755
+ "dialog"
756
+ ) as HTMLDialogElement;
757
+ if (dialog) {
758
+ dialog.showModal();
759
+ }
760
+ }
761
+ };
762
+
763
+ const closeModal = () => {
764
+ if (modalRef.current) {
765
+ const dialog = modalRef.current.querySelector(
766
+ "dialog"
767
+ ) as HTMLDialogElement;
768
+ if (dialog) {
769
+ dialog.close();
770
+ }
771
+ }
772
+ };
773
+
774
+ useImperativeHandle(ref, () => ({
775
+ openModal,
776
+ closeModal,
777
+ }));
778
+
779
+ return (
780
+ <ModusWcModal
781
+ ref={modalRef}
782
+ modal-id={modalId}
783
+ aria-label={ariaLabel}
784
+ backdrop={backdrop}
785
+ position={position}
786
+ fullscreen={fullscreen}
787
+ show-fullscreen-toggle={showFullscreenToggle}
788
+ show-close={showClose}
789
+ custom-class={customClass || className}
790
+ >
791
+ {header && <div slot="header">{header}</div>}
792
+ <div slot="content">{children}</div>
793
+ {footer && <div slot="footer">{footer}</div>}
794
+ </ModusWcModal>
795
+ );
796
+ }
797
+ );
798
+
799
+ // Usage in parent component
800
+ function ParentComponent() {
801
+ const modalRef = useRef<ModusModalRef>(null);
802
+
803
+ return (
804
+ <div>
805
+ <button onClick={() => modalRef.current?.openModal()}>Open Modal</button>
806
+ <ModusModal
807
+ ref={modalRef}
808
+ modalId="my-modal"
809
+ onClose={() => console.log("Modal closed")}
810
+ header={<div>Modal Title</div>}
811
+ footer={<div>Modal Actions</div>}
812
+ >
813
+ <div>Modal Content</div>
814
+ </ModusModal>
815
+ </div>
816
+ );
817
+ }
818
+ ```
819
+
820
+ ### **Benefits of This Pattern:**
821
+
822
+ - ✅ **Follows Official Documentation**: Aligns with Modus documentation exactly
823
+ - ✅ **No React State Control**: Uses direct method calls as documented
824
+ - ✅ **Type Safety**: Full TypeScript support with proper interfaces
825
+ - ✅ **Reusable**: Can be used across the application consistently
826
+ - ✅ **Performance**: No unnecessary re-renders from state changes
827
+ - ✅ **Maintainable**: Clear separation of concerns
828
+
829
+ ---
830
+
831
+ **Remember: ModusWcModal methods are on the inner dialog element, not the component itself. Always use `querySelector('dialog')` to access the native dialog element and call methods on it. This ensures proper modal functionality in React applications and aligns with the official Modus documentation.**