brut 0.0.20 → 0.0.22

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 (728) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +24 -3
  3. data/.nvim.lua +1 -0
  4. data/Dockerfile.dx +12 -3
  5. data/Gemfile.lock +9 -7
  6. data/README.md +0 -7
  7. data/Rakefile +6 -4
  8. data/bin/dev +20 -0
  9. data/bin/docs +27 -0
  10. data/bin/setup +47 -1
  11. data/brut-css/.nvim.lua +1 -0
  12. data/brut-css/README.md +28 -0
  13. data/brut-css/bin/build +31 -0
  14. data/brut-css/bin/dev +1 -0
  15. data/brut-css/bin/docs +15 -0
  16. data/brut-css/bin/setup +5 -0
  17. data/brut-css/config/media-queries-all.css +15 -0
  18. data/brut-css/config/media-queries-minimal.css +5 -0
  19. data/brut-css/config/postcss.config.cjs +7 -0
  20. data/brut-css/config/pseudo-classes-all.css +9 -0
  21. data/brut-css/dx +1 -0
  22. data/brut-css/package-lock.json +3217 -0
  23. data/brut-css/package.json +36 -0
  24. data/brut-css/src/css/appearance.css +145 -0
  25. data/brut-css/src/css/border.css +522 -0
  26. data/brut-css/src/css/colors.css +3502 -0
  27. data/brut-css/src/css/dimensions.css +548 -0
  28. data/brut-css/src/css/flex.css +179 -0
  29. data/brut-css/src/css/index.css +13 -0
  30. data/brut-css/src/css/layout.css +120 -0
  31. data/brut-css/src/css/list.css +41 -0
  32. data/brut-css/src/css/positioning.css +354 -0
  33. data/brut-css/src/css/properties/colors.css +455 -0
  34. data/brut-css/src/css/properties/index.css +3 -0
  35. data/brut-css/src/css/properties/spacing.css +140 -0
  36. data/brut-css/src/css/properties/typography.css +224 -0
  37. data/brut-css/src/css/reset.css +107 -0
  38. data/brut-css/src/css/spacing.css +585 -0
  39. data/brut-css/src/css/typography.css +519 -0
  40. data/brut-css/src/css/utils.css +104 -0
  41. data/brut-css/src/docs/1_getting-started/1_overview.md +46 -0
  42. data/brut-css/src/docs/1_getting-started/2_installation.md +25 -0
  43. data/brut-css/src/docs/1_getting-started/3_core-concepts.md +75 -0
  44. data/brut-css/src/docs/1_getting-started/4_simple-example.md +132 -0
  45. data/brut-css/src/docs/1_getting-started/page.html.ejs +10 -0
  46. data/brut-css/src/docs/2_properties/page.html.ejs +71 -0
  47. data/brut-css/src/docs/3_classes/color-demo.html.ejs +31 -0
  48. data/brut-css/src/docs/3_classes/page.html.ejs +87 -0
  49. data/brut-css/src/docs/4_customization/1_design-system.md +36 -0
  50. data/brut-css/src/docs/4_customization/2_breakpoints.md +75 -0
  51. data/brut-css/src/docs/4_customization/3_pseudo-classes.md +74 -0
  52. data/brut-css/src/docs/4_customization/4_advanced-configuration.md +40 -0
  53. data/brut-css/src/docs/4_customization/page.html.ejs +10 -0
  54. data/brut-css/src/docs/docs.css +98 -0
  55. data/brut-css/src/docs/includes/body-and-header.html.ejs +30 -0
  56. data/brut-css/src/docs/includes/footer-and-rest.html.ejs +9 -0
  57. data/brut-css/src/docs/includes/head.html.ejs +5 -0
  58. data/brut-css/src/docs/includes/nav.html.ejs +10 -0
  59. data/brut-css/src/docs/index.html.ejs +32 -0
  60. data/brut-css/src/docs/prism-twilight.min.css +1 -0
  61. data/brut-css/src/js/Logger.js +71 -0
  62. data/brut-css/src/js/build.js +111 -0
  63. data/brut-css/src/js/cli/CLIArgError.js +7 -0
  64. data/brut-css/src/js/cli/Debug.js +27 -0
  65. data/brut-css/src/js/cli/DocsDir.js +16 -0
  66. data/brut-css/src/js/cli/DocsTemplateSourceDir.js +16 -0
  67. data/brut-css/src/js/cli/InputFile.js +31 -0
  68. data/brut-css/src/js/cli/MediaQueryConfigFile.js +10 -0
  69. data/brut-css/src/js/cli/OutputFile.js +22 -0
  70. data/brut-css/src/js/cli/ParsedArg.js +17 -0
  71. data/brut-css/src/js/cli/PathToBrutCSSRoot.js +19 -0
  72. data/brut-css/src/js/cli/PseudoClassConfigFile.js +11 -0
  73. data/brut-css/src/js/cli.js +108 -0
  74. data/brut-css/src/js/docGenerator.js +467 -0
  75. data/brut-css/src/js/mediaQueryConfigParser.js +98 -0
  76. data/brut-css/src/js/post-css-plugins/addMediaQueriesPlugin.js +49 -0
  77. data/brut-css/src/js/post-css-plugins/addPseudoClassesPlugin.js +42 -0
  78. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Category.js +9 -0
  79. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/DocState.js +185 -0
  80. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Documentable.js +8 -0
  81. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Group.js +7 -0
  82. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/ParsedComment.js +73 -0
  83. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Property.js +9 -0
  84. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyCategory.js +4 -0
  85. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyGroup.js +8 -0
  86. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Rule.js +12 -0
  87. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleCategory.js +4 -0
  88. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleGroup.js +8 -0
  89. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeRef.js +5 -0
  90. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeURL.js +9 -0
  91. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin.js +49 -0
  92. data/brut-css/src/js/post-css-plugins/generateRootCustomPropertiesPlugin.js +45 -0
  93. data/brut-css/src/js/pseudoClassConfigParser.js +145 -0
  94. data/brut-js/.projections.json +10 -0
  95. data/brut-js/README.md +118 -0
  96. data/brut-js/bin/build +10 -0
  97. data/brut-js/bin/ci +5 -0
  98. data/brut-js/bin/setup +5 -0
  99. data/brut-js/docs/README.md +8 -0
  100. data/brut-js/docs/jsdoc-plugins/customElementTag.js +8 -0
  101. data/brut-js/docs/jsdoc-theme/publish.js +692 -0
  102. data/brut-js/docs/jsdoc-theme/static/scripts/linenumber.js +25 -0
  103. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/Apache-License-2.0.txt +202 -0
  104. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/lang-css.js +2 -0
  105. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/prettify.js +28 -0
  106. data/brut-js/docs/jsdoc-theme/static/styles/jsdoc-default.css +327 -0
  107. data/brut-js/docs/jsdoc-theme/static/styles/prettify-jsdoc.css +111 -0
  108. data/brut-js/docs/jsdoc-theme/static/styles/prettify-tomorrow.css +132 -0
  109. data/brut-js/docs/jsdoc-theme/tmpl/augments.tmpl +10 -0
  110. data/brut-js/docs/jsdoc-theme/tmpl/container.tmpl +199 -0
  111. data/brut-js/docs/jsdoc-theme/tmpl/details.tmpl +143 -0
  112. data/brut-js/docs/jsdoc-theme/tmpl/example.tmpl +2 -0
  113. data/brut-js/docs/jsdoc-theme/tmpl/examples.tmpl +13 -0
  114. data/brut-js/docs/jsdoc-theme/tmpl/exceptions.tmpl +32 -0
  115. data/brut-js/docs/jsdoc-theme/tmpl/layout.tmpl +38 -0
  116. data/brut-js/docs/jsdoc-theme/tmpl/mainpage.tmpl +14 -0
  117. data/brut-js/docs/jsdoc-theme/tmpl/members.tmpl +38 -0
  118. data/brut-js/docs/jsdoc-theme/tmpl/method.tmpl +131 -0
  119. data/brut-js/docs/jsdoc-theme/tmpl/modifies.tmpl +14 -0
  120. data/brut-js/docs/jsdoc-theme/tmpl/params.tmpl +131 -0
  121. data/brut-js/docs/jsdoc-theme/tmpl/properties.tmpl +108 -0
  122. data/brut-js/docs/jsdoc-theme/tmpl/returns.tmpl +19 -0
  123. data/brut-js/docs/jsdoc-theme/tmpl/source.tmpl +8 -0
  124. data/brut-js/docs/jsdoc-theme/tmpl/tutorial.tmpl +19 -0
  125. data/brut-js/docs/jsdoc-theme/tmpl/type.tmpl +7 -0
  126. data/brut-js/docs/jsdoc.config.json +23 -0
  127. data/brut-js/docs/package-lock.json +343 -0
  128. data/brut-js/docs/package.json +7 -0
  129. data/brut-js/package-lock.json +2171 -0
  130. data/brut-js/package.json +32 -0
  131. data/brut-js/specs/AjaxSubmit.spec.js +256 -0
  132. data/brut-js/specs/Autosubmit.spec.js +127 -0
  133. data/brut-js/specs/ConfirmSubmit.spec.js +193 -0
  134. data/brut-js/specs/ConstraintViolationMessage.spec.js +33 -0
  135. data/brut-js/specs/ConstraintViolationMessages.spec.js +29 -0
  136. data/brut-js/specs/CopyToClipboard.spec.js +35 -0
  137. data/brut-js/specs/Form.spec.js +181 -0
  138. data/brut-js/specs/I18nTranslation.spec.js +19 -0
  139. data/brut-js/specs/LocaleDetection.spec.js +22 -0
  140. data/brut-js/specs/Message.spec.js +15 -0
  141. data/brut-js/specs/SpecHelper.js +23 -0
  142. data/brut-js/specs/Tabs.spec.js +41 -0
  143. data/brut-js/specs/config/asset_metadata.json +7 -0
  144. data/brut-js/src/AjaxSubmit.js +384 -0
  145. data/brut-js/src/Autosubmit.js +63 -0
  146. data/brut-js/src/BaseCustomElement.js +261 -0
  147. data/brut-js/src/ConfirmSubmit.js +116 -0
  148. data/brut-js/src/ConfirmationDialog.js +143 -0
  149. data/brut-js/src/ConstraintViolationMessage.js +125 -0
  150. data/brut-js/src/ConstraintViolationMessages.js +98 -0
  151. data/brut-js/src/CopyToClipboard.js +96 -0
  152. data/brut-js/src/Form.js +151 -0
  153. data/brut-js/src/I18nTranslation.js +61 -0
  154. data/brut-js/src/LocaleDetection.js +117 -0
  155. data/brut-js/src/Logger.js +90 -0
  156. data/brut-js/src/Message.js +56 -0
  157. data/brut-js/src/RichString.js +113 -0
  158. data/brut-js/src/Tabs.js +168 -0
  159. data/brut-js/src/Tracing.js +247 -0
  160. data/brut-js/src/appForTestingOnly.js +15 -0
  161. data/brut-js/src/index.js +130 -0
  162. data/brut-js/src/testing/AssetMetadata.js +35 -0
  163. data/brut-js/src/testing/AssetMetadataLoader.js +25 -0
  164. data/brut-js/src/testing/CustomElementTest.js +235 -0
  165. data/brut-js/src/testing/DOMCreator.js +45 -0
  166. data/brut-js/src/testing/index.js +48 -0
  167. data/brutrb.com/.vitepress/config.mjs +106 -0
  168. data/brutrb.com/.vitepress/plugins/jsdocLinker.js +34 -0
  169. data/brutrb.com/.vitepress/plugins/rdocLinker.js +18 -0
  170. data/brutrb.com/.vitepress/theme/custom.css +7 -0
  171. data/brutrb.com/.vitepress/theme/index.js +18 -0
  172. data/brutrb.com/.vitepress/theme/style.css +149 -0
  173. data/brutrb.com/ai.md +68 -0
  174. data/brutrb.com/assets.md +138 -0
  175. data/brutrb.com/bin/build +5 -0
  176. data/brutrb.com/bin/deploy +7 -0
  177. data/brutrb.com/bin/dev +5 -0
  178. data/brutrb.com/bin/setup +5 -0
  179. data/brutrb.com/brut-js.md +117 -0
  180. data/brutrb.com/business-logic.md +55 -0
  181. data/brutrb.com/cli.md +278 -0
  182. data/brutrb.com/components.md +243 -0
  183. data/brutrb.com/configuration.md +257 -0
  184. data/brutrb.com/css.md +103 -0
  185. data/brutrb.com/custom-element-tests.md +149 -0
  186. data/brutrb.com/database-access.md +201 -0
  187. data/brutrb.com/database-schema.md +312 -0
  188. data/brutrb.com/deployment.md +66 -0
  189. data/brutrb.com/dev-environment.md +179 -0
  190. data/brutrb.com/doc-conventions.md +39 -0
  191. data/brutrb.com/end-to-end-tests.md +174 -0
  192. data/brutrb.com/flash-and-session.md +224 -0
  193. data/brutrb.com/forms.md +866 -0
  194. data/brutrb.com/getting-started.md +66 -0
  195. data/brutrb.com/handlers.md +153 -0
  196. data/brutrb.com/hooks.md +178 -0
  197. data/brutrb.com/i18n.md +188 -0
  198. data/brutrb.com/images/Makefile +10 -0
  199. data/brutrb.com/images/dev-env-overview.dot +54 -0
  200. data/brutrb.com/images/dev-env-overview.png +0 -0
  201. data/brutrb.com/images/dev-env-protocol.dot +37 -0
  202. data/brutrb.com/images/dev-env-protocol.png +0 -0
  203. data/brutrb.com/images/logo-300.png +0 -0
  204. data/brutrb.com/images/logo.png +0 -0
  205. data/brutrb.com/images/overview.graffle +0 -0
  206. data/brutrb.com/images/overview.png +0 -0
  207. data/brutrb.com/images/spa.dot +19 -0
  208. data/brutrb.com/images/spa.png +0 -0
  209. data/brutrb.com/images/workspace-protocol.dot +44 -0
  210. data/brutrb.com/images/workspace-protocol.png +0 -0
  211. data/brutrb.com/index.md +36 -0
  212. data/brutrb.com/instrumentation.md +183 -0
  213. data/brutrb.com/javascript.md +122 -0
  214. data/brutrb.com/jobs.md +14 -0
  215. data/{doc-src → brutrb.com}/keyword-injection.md +122 -68
  216. data/brutrb.com/markdown-examples.md +85 -0
  217. data/brutrb.com/middleware.md +80 -0
  218. data/brutrb.com/not-released.md +5 -0
  219. data/brutrb.com/overview.md +404 -0
  220. data/brutrb.com/package-lock.json +2404 -0
  221. data/brutrb.com/package.json +11 -0
  222. data/brutrb.com/pages.md +378 -0
  223. data/brutrb.com/public/images/logo-300.png +0 -0
  224. data/brutrb.com/public/images/logo.png +0 -0
  225. data/brutrb.com/routes.md +215 -0
  226. data/brutrb.com/security.md +105 -0
  227. data/brutrb.com/seed-data.md +63 -0
  228. data/brutrb.com/space-time-continuum.md +85 -0
  229. data/brutrb.com/tutorial.md +3 -0
  230. data/brutrb.com/unit-tests.md +148 -0
  231. data/docker-compose.dx.yml +6 -3
  232. data/docs/404.html +21 -0
  233. data/docs/CNAME +1 -0
  234. data/docs/ai.html +24 -0
  235. data/docs/api/Brut/BackEnd/SeedData.html +493 -0
  236. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +214 -0
  237. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +125 -0
  238. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +125 -0
  239. data/docs/api/Brut/BackEnd/Sidekiq.html +125 -0
  240. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +414 -0
  241. data/docs/api/Brut/BackEnd/Validators.html +128 -0
  242. data/docs/api/Brut/BackEnd.html +132 -0
  243. data/docs/api/Brut/CLI/App.html +1576 -0
  244. data/docs/api/Brut/CLI/AppRunner.html +491 -0
  245. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +264 -0
  246. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +306 -0
  247. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +262 -0
  248. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +314 -0
  249. data/docs/api/Brut/CLI/Apps/BuildAssets.html +183 -0
  250. data/docs/api/Brut/CLI/Apps/DB/Create.html +365 -0
  251. data/docs/api/Brut/CLI/Apps/DB/Drop.html +357 -0
  252. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +383 -0
  253. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +335 -0
  254. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +329 -0
  255. data/docs/api/Brut/CLI/Apps/DB/Seed.html +347 -0
  256. data/docs/api/Brut/CLI/Apps/DB/Status.html +383 -0
  257. data/docs/api/Brut/CLI/Apps/DB.html +183 -0
  258. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +303 -0
  259. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +512 -0
  260. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +398 -0
  261. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +374 -0
  262. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +410 -0
  263. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +262 -0
  264. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +303 -0
  265. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +480 -0
  266. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +450 -0
  267. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +380 -0
  268. data/docs/api/Brut/CLI/Apps/Scaffold.html +253 -0
  269. data/docs/api/Brut/CLI/Apps/Test/Audit.html +464 -0
  270. data/docs/api/Brut/CLI/Apps/Test/E2e.html +407 -0
  271. data/docs/api/Brut/CLI/Apps/Test/JS.html +262 -0
  272. data/docs/api/Brut/CLI/Apps/Test/Run.html +578 -0
  273. data/docs/api/Brut/CLI/Apps/Test.html +253 -0
  274. data/docs/api/Brut/CLI/Apps.html +125 -0
  275. data/docs/api/Brut/CLI/Command.html +2342 -0
  276. data/docs/api/Brut/CLI/Error.html +139 -0
  277. data/docs/api/Brut/CLI/ExecutionResults/Result.html +664 -0
  278. data/docs/api/Brut/CLI/ExecutionResults.html +675 -0
  279. data/docs/api/Brut/CLI/Executor.html +430 -0
  280. data/docs/api/Brut/CLI/InvalidOption.html +245 -0
  281. data/docs/api/Brut/CLI/Options.html +753 -0
  282. data/docs/api/Brut/CLI/Output.html +699 -0
  283. data/docs/api/Brut/CLI/SystemExecError.html +451 -0
  284. data/docs/api/Brut/CLI.html +263 -0
  285. data/docs/api/Brut/FactoryBot.html +225 -0
  286. data/docs/api/Brut/Framework/App.html +1097 -0
  287. data/docs/api/Brut/Framework/Config.html +1045 -0
  288. data/docs/api/Brut/Framework/Container.html +1379 -0
  289. data/docs/api/Brut/Framework/Error.html +140 -0
  290. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +144 -0
  291. data/docs/api/Brut/Framework/Errors/Bug.html +234 -0
  292. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +257 -0
  293. data/docs/api/Brut/Framework/Errors/MissingParameter.html +273 -0
  294. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +471 -0
  295. data/docs/api/Brut/Framework/Errors/NotFound.html +308 -0
  296. data/docs/api/Brut/Framework/Errors/NotImplemented.html +234 -0
  297. data/docs/api/Brut/Framework/Errors.html +328 -0
  298. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +392 -0
  299. data/docs/api/Brut/Framework/MCP.html +861 -0
  300. data/docs/api/Brut/Framework/ProjectEnvironment.html +648 -0
  301. data/docs/api/Brut/Framework.html +129 -0
  302. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +317 -0
  303. data/docs/api/Brut/FrontEnd/Component/Helpers.html +326 -0
  304. data/docs/api/Brut/FrontEnd/Component.html +365 -0
  305. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +470 -0
  306. data/docs/api/Brut/FrontEnd/Components/FormTag.html +518 -0
  307. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +317 -0
  308. data/docs/api/Brut/FrontEnd/Components/Input.html +195 -0
  309. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +339 -0
  310. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +660 -0
  311. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +417 -0
  312. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +918 -0
  313. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +651 -0
  314. data/docs/api/Brut/FrontEnd/Components/Inputs.html +125 -0
  315. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +367 -0
  316. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +336 -0
  317. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +655 -0
  318. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +352 -0
  319. data/docs/api/Brut/FrontEnd/Components.html +135 -0
  320. data/docs/api/Brut/FrontEnd/Download.html +467 -0
  321. data/docs/api/Brut/FrontEnd/Flash.html +1150 -0
  322. data/docs/api/Brut/FrontEnd/Form.html +1157 -0
  323. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +634 -0
  324. data/docs/api/Brut/FrontEnd/Forms/Input.html +615 -0
  325. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +547 -0
  326. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1318 -0
  327. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +609 -0
  328. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +587 -0
  329. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +613 -0
  330. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +582 -0
  331. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +609 -0
  332. data/docs/api/Brut/FrontEnd/Forms.html +127 -0
  333. data/docs/api/Brut/FrontEnd/GenericResponse.html +377 -0
  334. data/docs/api/Brut/FrontEnd/Handler.html +442 -0
  335. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +318 -0
  336. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +336 -0
  337. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +399 -0
  338. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +354 -0
  339. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +151 -0
  340. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +315 -0
  341. data/docs/api/Brut/FrontEnd/Handlers.html +125 -0
  342. data/docs/api/Brut/FrontEnd/HandlingResults.html +339 -0
  343. data/docs/api/Brut/FrontEnd/HttpMethod.html +661 -0
  344. data/docs/api/Brut/FrontEnd/HttpStatus.html +496 -0
  345. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +284 -0
  346. data/docs/api/Brut/FrontEnd/Layout.html +318 -0
  347. data/docs/api/Brut/FrontEnd/Middleware.html +135 -0
  348. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +288 -0
  349. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +292 -0
  350. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +324 -0
  351. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +372 -0
  352. data/docs/api/Brut/FrontEnd/Middlewares.html +125 -0
  353. data/docs/api/Brut/FrontEnd/Page.html +773 -0
  354. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +797 -0
  355. data/docs/api/Brut/FrontEnd/Pages.html +125 -0
  356. data/docs/api/Brut/FrontEnd/RequestContext.html +1312 -0
  357. data/docs/api/Brut/FrontEnd/RouteHook.html +424 -0
  358. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +242 -0
  359. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +249 -0
  360. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +264 -0
  361. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +261 -0
  362. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +284 -0
  363. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +252 -0
  364. data/docs/api/Brut/FrontEnd/RouteHooks.html +115 -0
  365. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +227 -0
  366. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +305 -0
  367. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +324 -0
  368. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +319 -0
  369. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +315 -0
  370. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +315 -0
  371. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +327 -0
  372. data/docs/api/Brut/FrontEnd/Routing/Route.html +761 -0
  373. data/docs/api/Brut/FrontEnd/Routing.html +927 -0
  374. data/docs/api/Brut/FrontEnd/Session.html +1195 -0
  375. data/docs/api/Brut/FrontEnd.html +134 -0
  376. data/docs/api/Brut/I18n/BaseMethods.html +931 -0
  377. data/docs/api/Brut/I18n/ForBackEnd.html +302 -0
  378. data/docs/api/Brut/I18n/ForCLI.html +302 -0
  379. data/docs/api/Brut/I18n/ForHTML.html +296 -0
  380. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +316 -0
  381. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +930 -0
  382. data/docs/api/Brut/I18n.html +127 -0
  383. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +435 -0
  384. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +286 -0
  385. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +302 -0
  386. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +864 -0
  387. data/docs/api/Brut/Instrumentation.html +126 -0
  388. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +532 -0
  389. data/docs/api/Brut/SinatraHelpers.html +281 -0
  390. data/docs/api/Brut/SpecSupport/ClockSupport.html +383 -0
  391. data/docs/api/Brut/SpecSupport/ComponentSupport.html +502 -0
  392. data/docs/api/Brut/SpecSupport/E2ETestServer.html +503 -0
  393. data/docs/api/Brut/SpecSupport/E2eSupport.html +142 -0
  394. data/docs/api/Brut/SpecSupport/EnhancedNode.html +403 -0
  395. data/docs/api/Brut/SpecSupport/FlashSupport.html +278 -0
  396. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +401 -0
  397. data/docs/api/Brut/SpecSupport/GeneralSupport.html +195 -0
  398. data/docs/api/Brut/SpecSupport/HandlerSupport.html +160 -0
  399. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +553 -0
  400. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +439 -0
  401. data/docs/api/Brut/SpecSupport/Matchers.html +125 -0
  402. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +335 -0
  403. data/docs/api/Brut/SpecSupport/RSpecSetup.html +602 -0
  404. data/docs/api/Brut/SpecSupport/SessionSupport.html +196 -0
  405. data/docs/api/Brut/SpecSupport.html +129 -0
  406. data/docs/api/Brut.html +225 -0
  407. data/docs/api/Clock.html +603 -0
  408. data/docs/api/RichString.html +968 -0
  409. data/docs/api/SemanticLogger/Appender/Async.html +219 -0
  410. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +115 -0
  411. data/docs/api/Sequel/Extensions/BrutMigrations.html +533 -0
  412. data/docs/api/Sequel/Extensions.html +117 -0
  413. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +105 -0
  414. data/docs/api/Sequel/Plugins/CreatedAt.html +125 -0
  415. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +207 -0
  416. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +186 -0
  417. data/docs/api/Sequel/Plugins/ExternalId.html +218 -0
  418. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +202 -0
  419. data/docs/api/Sequel/Plugins/FindBang.html +125 -0
  420. data/docs/api/Sequel/Plugins.html +117 -0
  421. data/docs/api/Sequel.html +117 -0
  422. data/docs/api/_index.html +1553 -0
  423. data/docs/api/class_list.html +54 -0
  424. data/docs/api/css/common.css +1 -0
  425. data/docs/api/css/full_list.css +58 -0
  426. data/docs/api/css/style.css +503 -0
  427. data/docs/api/file.README.html +127 -0
  428. data/docs/api/file_list.html +59 -0
  429. data/docs/api/frames.html +22 -0
  430. data/docs/api/index.html +127 -0
  431. data/docs/api/js/app.js +344 -0
  432. data/docs/api/js/full_list.js +242 -0
  433. data/docs/api/js/jquery.js +4 -0
  434. data/docs/api/method_list.html +3998 -0
  435. data/docs/api/top-level-namespace.html +112 -0
  436. data/docs/assets/ai.md.tZrjP9im.js +1 -0
  437. data/docs/assets/ai.md.tZrjP9im.lean.js +1 -0
  438. data/docs/assets/app.D_yaTITQ.js +1 -0
  439. data/docs/assets/assets.md.D3wunzLx.js +19 -0
  440. data/docs/assets/assets.md.D3wunzLx.lean.js +1 -0
  441. data/docs/assets/brut-js.md.o2DAO2s2.js +12 -0
  442. data/docs/assets/brut-js.md.o2DAO2s2.lean.js +1 -0
  443. data/docs/assets/business-logic.md.BY4hGy0m.js +1 -0
  444. data/docs/assets/business-logic.md.BY4hGy0m.lean.js +1 -0
  445. data/docs/assets/chunks/@localSearchIndexroot.BsN5i0Fi.js +1 -0
  446. data/docs/assets/chunks/VPLocalSearchBox.B2-ZzyTY.js +8 -0
  447. data/docs/assets/chunks/framework.1L-BeKqY.js +18 -0
  448. data/docs/assets/chunks/theme.CfGFVRvE.js +2 -0
  449. data/docs/assets/cli.md.RmeA2b0i.js +127 -0
  450. data/docs/assets/cli.md.RmeA2b0i.lean.js +1 -0
  451. data/docs/assets/components.md.eCttGlN-.js +104 -0
  452. data/docs/assets/components.md.eCttGlN-.lean.js +1 -0
  453. data/docs/assets/configuration.md.BRriU0cL.js +78 -0
  454. data/docs/assets/configuration.md.BRriU0cL.lean.js +1 -0
  455. data/docs/assets/css.md.DJgj2clw.js +21 -0
  456. data/docs/assets/css.md.DJgj2clw.lean.js +1 -0
  457. data/docs/assets/custom-element-tests.md.BrYJQEl3.js +69 -0
  458. data/docs/assets/custom-element-tests.md.BrYJQEl3.lean.js +1 -0
  459. data/docs/assets/database-access.md.C7l-Vuvb.js +63 -0
  460. data/docs/assets/database-access.md.C7l-Vuvb.lean.js +1 -0
  461. data/docs/assets/database-schema.md.BUjR0VS1.js +63 -0
  462. data/docs/assets/database-schema.md.BUjR0VS1.lean.js +1 -0
  463. data/docs/assets/deployment.md.Dbka4OTr.js +1 -0
  464. data/docs/assets/deployment.md.Dbka4OTr.lean.js +1 -0
  465. data/docs/assets/dev-env-overview.Gj7NWM8-.png +0 -0
  466. data/docs/assets/dev-env-protocol.DysDAtnz.png +0 -0
  467. data/docs/assets/dev-environment.md.BNc8AYiK.js +11 -0
  468. data/docs/assets/dev-environment.md.BNc8AYiK.lean.js +1 -0
  469. data/docs/assets/doc-conventions.md.DCfRXXi-.js +1 -0
  470. data/docs/assets/doc-conventions.md.DCfRXXi-.lean.js +1 -0
  471. data/docs/assets/end-to-end-tests.md.yfQHC0b5.js +26 -0
  472. data/docs/assets/end-to-end-tests.md.yfQHC0b5.lean.js +1 -0
  473. data/docs/assets/flash-and-session.md.BXY8RvT0.js +93 -0
  474. data/docs/assets/flash-and-session.md.BXY8RvT0.lean.js +1 -0
  475. data/docs/assets/forms.md.CBTYQ_Cz.js +379 -0
  476. data/docs/assets/forms.md.CBTYQ_Cz.lean.js +1 -0
  477. data/docs/assets/getting-started.md.Bz2s1Vjb.js +2 -0
  478. data/docs/assets/getting-started.md.Bz2s1Vjb.lean.js +1 -0
  479. data/docs/assets/handlers.md.089DVD3v.js +69 -0
  480. data/docs/assets/handlers.md.089DVD3v.lean.js +1 -0
  481. data/docs/assets/hooks.md.C4-moMny.js +80 -0
  482. data/docs/assets/hooks.md.C4-moMny.lean.js +1 -0
  483. data/docs/assets/i18n.md.Do9i1qWl.js +23 -0
  484. data/docs/assets/i18n.md.Do9i1qWl.lean.js +1 -0
  485. data/docs/assets/index.md.B28EwVpq.js +1 -0
  486. data/docs/assets/index.md.B28EwVpq.lean.js +1 -0
  487. data/docs/assets/instrumentation.md.CL6ax7nT.js +35 -0
  488. data/docs/assets/instrumentation.md.CL6ax7nT.lean.js +1 -0
  489. data/docs/assets/javascript.md.GWbhRS51.js +31 -0
  490. data/docs/assets/javascript.md.GWbhRS51.lean.js +1 -0
  491. data/docs/assets/jobs.md.S-2amAYp.js +1 -0
  492. data/docs/assets/jobs.md.S-2amAYp.lean.js +1 -0
  493. data/docs/assets/keyword-injection.md.Dt2tKREs.js +25 -0
  494. data/docs/assets/keyword-injection.md.Dt2tKREs.lean.js +1 -0
  495. data/docs/assets/markdown-examples.md.CCFEQO44.js +33 -0
  496. data/docs/assets/markdown-examples.md.CCFEQO44.lean.js +1 -0
  497. data/docs/assets/middleware.md.Czz_UlJN.js +20 -0
  498. data/docs/assets/middleware.md.Czz_UlJN.lean.js +1 -0
  499. data/docs/assets/not-released.md.BBy28McC.js +1 -0
  500. data/docs/assets/not-released.md.BBy28McC.lean.js +1 -0
  501. data/docs/assets/overview.Da81cB9R.png +0 -0
  502. data/docs/assets/overview.md.CDalkuxV.js +133 -0
  503. data/docs/assets/overview.md.CDalkuxV.lean.js +1 -0
  504. data/docs/assets/pages.md.BE3kfOc5.js +122 -0
  505. data/docs/assets/pages.md.BE3kfOc5.lean.js +1 -0
  506. data/docs/assets/routes.md.BMM7peut.js +29 -0
  507. data/docs/assets/routes.md.BMM7peut.lean.js +1 -0
  508. data/docs/assets/security.md.C668yXCi.js +1 -0
  509. data/docs/assets/security.md.C668yXCi.lean.js +1 -0
  510. data/docs/assets/seed-data.md.BvFZlqIk.js +14 -0
  511. data/docs/assets/seed-data.md.BvFZlqIk.lean.js +1 -0
  512. data/docs/assets/spa.qejUdp-5.png +0 -0
  513. data/docs/assets/space-time-continuum.md.KPUIKysQ.js +1 -0
  514. data/docs/assets/space-time-continuum.md.KPUIKysQ.lean.js +1 -0
  515. data/docs/assets/style.D73IYGCX.css +1 -0
  516. data/docs/assets/tutorial.md.BnoGjrdK.js +1 -0
  517. data/docs/assets/tutorial.md.BnoGjrdK.lean.js +1 -0
  518. data/docs/assets/unit-tests.md.DUGrnLj5.js +13 -0
  519. data/docs/assets/unit-tests.md.DUGrnLj5.lean.js +1 -0
  520. data/docs/assets/workspace-protocol.C0gXsoDb.png +0 -0
  521. data/docs/assets.html +42 -0
  522. data/docs/brut-css/brut.css +1 -0
  523. data/docs/brut-css/brut.max.css +22372 -0
  524. data/docs/brut-css/classes/appearances.html +783 -0
  525. data/docs/brut-css/classes/background-colors.html +3529 -0
  526. data/docs/brut-css/classes/border-colors.html +3529 -0
  527. data/docs/brut-css/classes/borders.html +2293 -0
  528. data/docs/brut-css/classes/dimensions.html +2581 -0
  529. data/docs/brut-css/classes/flex.html +917 -0
  530. data/docs/brut-css/classes/foreground-colors.html +3261 -0
  531. data/docs/brut-css/classes/junk-drawer.html +431 -0
  532. data/docs/brut-css/classes/layout.html +668 -0
  533. data/docs/brut-css/classes/lists.html +331 -0
  534. data/docs/brut-css/classes/positioning.html +1751 -0
  535. data/docs/brut-css/classes/spacings.html +2633 -0
  536. data/docs/brut-css/classes/typography.html +2206 -0
  537. data/docs/brut-css/customization/advanced-configuration.html +204 -0
  538. data/docs/brut-css/customization/breakpoints.html +227 -0
  539. data/docs/brut-css/customization/design-system.html +197 -0
  540. data/docs/brut-css/customization/pseudo-classes.html +228 -0
  541. data/docs/brut-css/docs.css +98 -0
  542. data/docs/brut-css/getting-started/core-concepts.html +234 -0
  543. data/docs/brut-css/getting-started/installation.html +190 -0
  544. data/docs/brut-css/getting-started/overview.html +210 -0
  545. data/docs/brut-css/getting-started/simple-example.html +285 -0
  546. data/docs/brut-css/index.html +193 -0
  547. data/docs/brut-css/prism-twilight.min.css +1 -0
  548. data/docs/brut-css/properties/colors.html +1548 -0
  549. data/docs/brut-css/properties/spacings.html +614 -0
  550. data/docs/brut-css/properties/typography.html +777 -0
  551. data/docs/brut-js/api/AjaxSubmit.html +374 -0
  552. data/docs/brut-js/api/AjaxSubmit.js.html +435 -0
  553. data/docs/brut-js/api/Autosubmit.html +192 -0
  554. data/docs/brut-js/api/Autosubmit.js.html +114 -0
  555. data/docs/brut-js/api/BaseCustomElement.html +1091 -0
  556. data/docs/brut-js/api/BaseCustomElement.js.html +312 -0
  557. data/docs/brut-js/api/BrutCustomElements.html +172 -0
  558. data/docs/brut-js/api/BufferedLogger.html +173 -0
  559. data/docs/brut-js/api/ConfirmSubmit.html +278 -0
  560. data/docs/brut-js/api/ConfirmSubmit.js.html +167 -0
  561. data/docs/brut-js/api/ConfirmationDialog.html +425 -0
  562. data/docs/brut-js/api/ConfirmationDialog.js.html +194 -0
  563. data/docs/brut-js/api/ConstraintViolationMessage.html +448 -0
  564. data/docs/brut-js/api/ConstraintViolationMessage.js.html +176 -0
  565. data/docs/brut-js/api/ConstraintViolationMessages.html +590 -0
  566. data/docs/brut-js/api/ConstraintViolationMessages.js.html +149 -0
  567. data/docs/brut-js/api/CopyToClipboard.html +345 -0
  568. data/docs/brut-js/api/CopyToClipboard.js.html +147 -0
  569. data/docs/brut-js/api/Form.html +294 -0
  570. data/docs/brut-js/api/Form.js.html +202 -0
  571. data/docs/brut-js/api/I18nTranslation.html +409 -0
  572. data/docs/brut-js/api/I18nTranslation.js.html +112 -0
  573. data/docs/brut-js/api/LocaleDetection.html +312 -0
  574. data/docs/brut-js/api/LocaleDetection.js.html +168 -0
  575. data/docs/brut-js/api/Logger.html +702 -0
  576. data/docs/brut-js/api/Logger.js.html +141 -0
  577. data/docs/brut-js/api/Message.html +238 -0
  578. data/docs/brut-js/api/Message.js.html +107 -0
  579. data/docs/brut-js/api/PrefixedLogger.html +369 -0
  580. data/docs/brut-js/api/RichString.html +1049 -0
  581. data/docs/brut-js/api/RichString.js.html +164 -0
  582. data/docs/brut-js/api/Tabs.html +295 -0
  583. data/docs/brut-js/api/Tabs.js.html +219 -0
  584. data/docs/brut-js/api/Tracing.html +277 -0
  585. data/docs/brut-js/api/Tracing.js.html +298 -0
  586. data/docs/brut-js/api/external-CustomElementRegistry.html +140 -0
  587. data/docs/brut-js/api/external-Performance.html +138 -0
  588. data/docs/brut-js/api/external-Promise.html +138 -0
  589. data/docs/brut-js/api/external-ValidityState.html +138 -0
  590. data/docs/brut-js/api/external-Window.html +233 -0
  591. data/docs/brut-js/api/external-fetch.html +138 -0
  592. data/docs/brut-js/api/global.html +400 -0
  593. data/docs/brut-js/api/index.html +168 -0
  594. data/docs/brut-js/api/index.js.html +181 -0
  595. data/docs/brut-js/api/module-testing.html +383 -0
  596. data/docs/brut-js/api/scripts/linenumber.js +25 -0
  597. data/docs/brut-js/api/scripts/prettify/Apache-License-2.0.txt +202 -0
  598. data/docs/brut-js/api/scripts/prettify/lang-css.js +2 -0
  599. data/docs/brut-js/api/scripts/prettify/prettify.js +28 -0
  600. data/docs/brut-js/api/styles/jsdoc-default.css +327 -0
  601. data/docs/brut-js/api/styles/prettify-jsdoc.css +111 -0
  602. data/docs/brut-js/api/styles/prettify-tomorrow.css +132 -0
  603. data/docs/brut-js/api/testing.AssetMetadata.html +172 -0
  604. data/docs/brut-js/api/testing.AssetMetadataLoader.html +171 -0
  605. data/docs/brut-js/api/testing.CustomElementTest.html +679 -0
  606. data/docs/brut-js/api/testing.DOMCreator.html +171 -0
  607. data/docs/brut-js/api/testing_AssetMetadata.js.html +86 -0
  608. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +76 -0
  609. data/docs/brut-js/api/testing_CustomElementTest.js.html +286 -0
  610. data/docs/brut-js/api/testing_DOMCreator.js.html +96 -0
  611. data/docs/brut-js/api/testing_index.js.html +99 -0
  612. data/docs/brut-js.html +35 -0
  613. data/docs/business-logic.html +24 -0
  614. data/docs/cli.html +150 -0
  615. data/docs/components.html +127 -0
  616. data/docs/configuration.html +101 -0
  617. data/docs/css.html +44 -0
  618. data/docs/custom-element-tests.html +92 -0
  619. data/docs/database-access.html +86 -0
  620. data/docs/database-schema.html +86 -0
  621. data/docs/deployment.html +24 -0
  622. data/docs/dev-environment.html +34 -0
  623. data/docs/doc-conventions.html +24 -0
  624. data/docs/end-to-end-tests.html +49 -0
  625. data/docs/flash-and-session.html +116 -0
  626. data/docs/forms.html +402 -0
  627. data/docs/getting-started.html +25 -0
  628. data/docs/handlers.html +92 -0
  629. data/docs/hashmap.json +1 -0
  630. data/docs/hooks.html +103 -0
  631. data/docs/i18n.html +46 -0
  632. data/docs/images/logo-300.png +0 -0
  633. data/docs/images/logo.png +0 -0
  634. data/docs/index.html +24 -0
  635. data/docs/instrumentation.html +58 -0
  636. data/docs/javascript.html +54 -0
  637. data/docs/jobs.html +24 -0
  638. data/docs/keyword-injection.html +48 -0
  639. data/docs/markdown-examples.html +56 -0
  640. data/docs/middleware.html +43 -0
  641. data/docs/not-released.html +24 -0
  642. data/docs/overview.html +156 -0
  643. data/docs/pages.html +145 -0
  644. data/docs/routes.html +52 -0
  645. data/docs/security.html +24 -0
  646. data/docs/seed-data.html +37 -0
  647. data/docs/space-time-continuum.html +24 -0
  648. data/docs/tutorial.html +24 -0
  649. data/docs/unit-tests.html +36 -0
  650. data/docs/vp-icons.css +1 -0
  651. data/lib/brut/back_end/seed_data.rb +19 -2
  652. data/lib/brut/back_end/sidekiq/middlewares/server.rb +2 -1
  653. data/lib/brut/back_end/sidekiq/middlewares.rb +2 -1
  654. data/lib/brut/back_end/sidekiq.rb +2 -1
  655. data/lib/brut/back_end/validator.rb +5 -1
  656. data/lib/brut/back_end.rb +4 -2
  657. data/lib/brut/cli/app_runner.rb +1 -1
  658. data/lib/brut/cli/apps/test.rb +5 -0
  659. data/lib/brut/cli.rb +4 -3
  660. data/lib/brut/factory_bot.rb +0 -5
  661. data/lib/brut/framework/app.rb +70 -5
  662. data/lib/brut/framework/config.rb +5 -3
  663. data/lib/brut/framework/container.rb +3 -2
  664. data/lib/brut/framework/errors.rb +12 -4
  665. data/lib/brut/framework/mcp.rb +58 -1
  666. data/lib/brut/framework/project_environment.rb +6 -2
  667. data/lib/brut/framework.rb +1 -1
  668. data/lib/brut/front_end/component.rb +69 -71
  669. data/lib/brut/front_end/components/constraint_violations.rb +1 -4
  670. data/lib/brut/front_end/components/form_tag.rb +1 -1
  671. data/lib/brut/front_end/components/input.rb +3 -3
  672. data/lib/brut/front_end/components/inputs/csrf_token.rb +1 -1
  673. data/lib/brut/front_end/components/inputs/{text_field.rb → input_tag.rb} +7 -9
  674. data/lib/brut/front_end/components/inputs/radio_button.rb +1 -1
  675. data/lib/brut/front_end/components/inputs/select_tag_with_options.rb +187 -0
  676. data/lib/brut/front_end/components/inputs/{textarea.rb → textarea_tag.rb} +2 -2
  677. data/lib/brut/front_end/components/time_tag.rb +2 -1
  678. data/lib/brut/front_end/form.rb +4 -4
  679. data/lib/brut/front_end/forms/input.rb +2 -1
  680. data/lib/brut/front_end/forms/input_definition.rb +5 -2
  681. data/lib/brut/front_end/forms/radio_button_group_input.rb +2 -1
  682. data/lib/brut/front_end/forms/radio_button_group_input_definition.rb +2 -2
  683. data/lib/brut/front_end/forms/select_input.rb +2 -4
  684. data/lib/brut/front_end/forms/select_input_definition.rb +2 -2
  685. data/lib/brut/front_end/handler.rb +28 -26
  686. data/lib/brut/front_end/handlers/csp_reporting_handler.rb +5 -2
  687. data/lib/brut/front_end/handlers/instrumentation_handler.rb +8 -4
  688. data/lib/brut/front_end/handlers/locale_detection_handler.rb +9 -5
  689. data/lib/brut/front_end/handlers/missing_handler.rb +5 -2
  690. data/lib/brut/front_end/layout.rb +16 -0
  691. data/lib/brut/front_end/page.rb +52 -29
  692. data/lib/brut/front_end/request_context.rb +3 -2
  693. data/lib/brut/front_end/routing.rb +5 -1
  694. data/lib/brut/front_end.rb +4 -13
  695. data/lib/brut/i18n/base_methods.rb +167 -79
  696. data/lib/brut/i18n/for_back_end.rb +4 -0
  697. data/lib/brut/i18n/for_cli.rb +4 -0
  698. data/lib/brut/i18n/for_html.rb +32 -4
  699. data/lib/brut/i18n/http_accept_language.rb +47 -0
  700. data/lib/brut/instrumentation/open_telemetry.rb +36 -1
  701. data/lib/brut/instrumentation.rb +3 -5
  702. data/lib/brut/sinatra_helpers.rb +11 -3
  703. data/lib/brut/spec_support/component_support.rb +30 -16
  704. data/lib/brut/spec_support/e2e_support.rb +1 -1
  705. data/lib/brut/spec_support/e2e_test_server.rb +3 -0
  706. data/lib/brut/spec_support/general_support.rb +3 -0
  707. data/lib/brut/spec_support/handler_support.rb +6 -1
  708. data/lib/brut/spec_support/matcher.rb +1 -0
  709. data/lib/brut/spec_support/matchers/be_page_for.rb +1 -0
  710. data/lib/brut/spec_support/matchers/have_html_attribute.rb +1 -0
  711. data/lib/brut/spec_support/matchers/have_i18n_string.rb +2 -5
  712. data/lib/brut/spec_support/matchers/have_link_to.rb +1 -0
  713. data/lib/brut/spec_support/matchers/have_redirected_to.rb +1 -0
  714. data/lib/brut/spec_support/matchers/have_rendered.rb +1 -0
  715. data/lib/brut/spec_support/matchers/have_returned_rack_response.rb +44 -0
  716. data/lib/brut/spec_support.rb +1 -1
  717. data/lib/brut/version.rb +1 -1
  718. data/lib/brut.rb +5 -4
  719. data/lib/sequel/extensions/brut_migrations.rb +1 -1
  720. metadata +648 -13
  721. data/doc-src/architecture.md +0 -102
  722. data/doc-src/assets.md +0 -98
  723. data/doc-src/forms.md +0 -214
  724. data/doc-src/handlers.md +0 -83
  725. data/doc-src/javascript.md +0 -265
  726. data/doc-src/pages.md +0 -210
  727. data/doc-src/route-hooks.md +0 -59
  728. data/lib/brut/front_end/components/inputs/select.rb +0 -117
@@ -0,0 +1,261 @@
1
+ import Logger from "./Logger"
2
+ import RichString from "./RichString"
3
+
4
+ /** Base class for Custom Elements that provides a few quality-of-life enhancements.
5
+ *
6
+ * Custom elements that use this base class instead of `HTMLElement` get the following features:
7
+ *
8
+ * * `connectedCallback` and `attributeChangedCallback` call into a central `update` method where the
9
+ * class can centralilize its logic
10
+ * * Instead of implementing `attributeChangedCallback` and checking the name, per-attribute
11
+ * callbacks can be implemented that are called when an observed attribute is changed. See {@link
12
+ * attributeChangedCallback}.
13
+ * * Support for defining the element by declaring a tag name
14
+ * * Opt-in debugging support to allow verbose logging of mistaken use of the element that can be turned
15
+ * off for production use.
16
+ *
17
+ * How to use this class:
18
+ *
19
+ * 1. Your custom element should extend this class via `extends BaseCustomElement`
20
+ * 2. Create a static property called `tagName` that will be your element's tag name. Remember that all tag names must have a dash in them.
21
+ * 3. Create a static property called `observedAttributes` that is an array of attribute names your element supports. This is part of the HTML spec and not specific to this base class.
22
+ * 4. If you include the attribute `show-warnings` in your list of `observedAttributes`, you will have enhanced debugging abilities.
23
+ * 5. For each attribute *other* than `show-warnings`, implement a callback to receive notifications on the attribute's changes. See {@link attributeChangedCallback} for more info.
24
+ * 6. Implement `update` to execute whatever logic the component needs. `update` will be called multiple times and thus should be relatively idempotent. Specifically, it will be called after any attribute has changed, and it will be called as part of the standard `connectedCallback`.
25
+ * 7. To use your component, call the static {@link define} method.
26
+ *
27
+ * Debugging
28
+ *
29
+ * Custom Elements have to work under a variety of degenerate cirucmstances. Further, if you are building
30
+ * elements that wrap and enhance conventional elements, it can be easy to make a mistake, for example intending
31
+ * to wrap a `FORM`, but wrapping only an `INPUT`.
32
+ *
33
+ * To help debug these situations, you are encouraged to use `this.logger.warn(...)` to emit warnings when
34
+ * potentially incorrect use of your component is detected. By default, these warnings will not be shown. This
35
+ * provides your users with a drama-free console. During development, however, you can add the `show-warnings`
36
+ * attribute to your element. If that is set, warnings *are* shown in the console.
37
+ *
38
+ * `show-warnings` can be given a value, in which case that value if used to prefix all warnings the element emits.
39
+ * This can be useful to know which use of an element is causing problems. If you don't give any value
40
+ * to `show-warnings`, the element's `id` will be used as the prefix. If the element has no `id`, you will
41
+ * still see warnings, but without a prefix. This could make it hard to know where the warnings are coming from.
42
+ *
43
+ * @example
44
+ * // Replaces all span elements inside the component with
45
+ * // an upper-cased value of the attribute 'some-attribute'
46
+ * class MyComponent extends BaseCustomElement {
47
+ * static tagName = "my-component"
48
+ * static observedAttributes = [
49
+ * "show-warnings",
50
+ * "some-attribute",
51
+ * ]
52
+ *
53
+ * someAttributeChangedCallback({newValue}) {
54
+ * this.someAttribute = newValue ? newValue.toUpperCase() : null
55
+ * }
56
+ *
57
+ * update() {
58
+ * const spans = this.querySelectorAll("span")
59
+ * if (spans.length == 0) {
60
+ * this.logger.warn("Did not find any <span> elements - element won't do anything")
61
+ * }
62
+ * spans.forEach( (element) => {
63
+ * element.textContent = this.someAttribute
64
+ * })
65
+ * }
66
+ * }
67
+ * docment.addEventListener("DOMContentLoaded", () => {
68
+ * MyComponent.define()
69
+ * })
70
+ *
71
+ * // Then, in your HTML
72
+ * <my-component some-attribute="hello there">
73
+ * <span></span>
74
+ * <div></div>
75
+ * <span></span>
76
+ * </my-component>
77
+ *
78
+ * // The browser will effectively produce this HTML:
79
+ * <my-component some-attribute="hello there">
80
+ * <span>HELLO THERE</span>
81
+ * <div></div>
82
+ * <span>HELLO THERE</span>
83
+ * </my-component>
84
+ *
85
+ * // If JavaScript (or browser dev tools) changed some-attribute
86
+ * // to be "goodbye then", the markup will change to look like so:
87
+ * <my-component some-attribute="goodby then">
88
+ * <span>GOODBYE THEN</span>
89
+ * <div></div>
90
+ * <span>GOODBYE THEN</span>
91
+ * </my-component>
92
+ */
93
+ class BaseCustomElement extends HTMLElement {
94
+
95
+ /** A {@link Logger} you can use to write warning messages. By default, these
96
+ * messages are not shown in the console. If you put `show-warnings` as an attribute on your
97
+ * element, warnings sent to this logger *are* shown.
98
+ */
99
+ logger = Logger.forPrefix(null)
100
+
101
+ #_connectedCallbackCalled = false
102
+ #_disconnectedCallbackCalled = false
103
+
104
+ constructor() {
105
+ super()
106
+ this.logger = Logger.forPrefix(null)
107
+ }
108
+
109
+ /** You must call this to define the custom element. This is bascially
110
+ * a wrapper around `customElements.define`. It is recommended that you call
111
+ * this inside a `DOMContentLoaded` event, or after the page's HTML has been processed.
112
+ *
113
+ * @see external:CustomElementRegistry
114
+ */
115
+ static define() {
116
+ if (!this.tagName) {
117
+ throw `To use BaseCustomElement, you must define the static member tagName to return your custom tag's name`
118
+ }
119
+ customElements.define(this.tagName, this)
120
+ }
121
+
122
+ showWarningsChangedCallback({oldValue,newValue}) {
123
+ let oldLogger
124
+ if (!oldValue && newValue) {
125
+ oldLogger = this.logger
126
+ }
127
+ let prefix = newValue == "" ? this.id : newValue
128
+ if (!prefix) {
129
+ prefix = "UNKNOWN COMPONENT"
130
+ }
131
+ this.logger = Logger.forPrefix(prefix)
132
+ if (oldLogger) {
133
+ this.logger.dump(oldLogger)
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Overrides the standard callback to allow subclasses to have a slightly easier API when responding
139
+ * to attribute changes. You can override this to use the custom element callback directly. Note that if
140
+ * you do, `show-warnings` will not have any affect and you probably don't need to bother using
141
+ * this class as your base class.
142
+ *
143
+ * This method will locate a per-attribute method and call that.
144
+ * Attribute names are assumed to be in kebab-case and are translated to camelCase to create a method name.
145
+ * That method is `«attributeInCamelCase»ChangedCallback`, so if your attribute is `hex-code`,
146
+ * a method named `hexCodeChangedCallback` in invoked. If no such method is defined, a
147
+ * warning is logged in the console, regardless of the `show-warnings` attribute.
148
+ *
149
+ * The method is invoked with `{oldValue,newValue,newValueAsBoolean}` - i.e. an object and not positional parameters. This
150
+ * means your implementation can omit any parameters it doesn't care about. `newValueAsBoolean` is not part of
151
+ * the custom element spec, but is provided as an unambiguous way to know if a boolean attribute was set or not. This is
152
+ * because if the value is set, it is likely to be the empty string, which is considered false by JavaScript. Cool.
153
+ *
154
+ * The return value of the method is ignored.
155
+ *
156
+ * After your method is called, if there is a method named `update`, it is called with no arguments.
157
+ *
158
+ * What this allows you to do is separate how you manage your element's attributes from how your logic
159
+ * is managed. For complex elements that take a lot of attributes, this can simplify your element's code without straying too far from the spec.
160
+ *
161
+ * @example
162
+ *
163
+ * // If your element accepts the attribute `warning-message` that will be trimmed of whitespace
164
+ * // then placed into all `H1` tags inside the element, you can manage that like so:
165
+ * class MyElement extends BaseCustomElement {
166
+ * static tagName = "my-element"
167
+ * static observedAttributes = [
168
+ * "warning-message",
169
+ * ]
170
+ *
171
+ * // called by attributeChangedCallback when warning-message's value changes
172
+ * warningMessageChangedCallback({newValue}) {
173
+ * this.warningMessage = (newValue || "").trim()
174
+ * }
175
+ *
176
+ * // called after attributeChangedCallback calls warningMessageChangedCallback
177
+ * update() {
178
+ * this.querySelectorAll("h1").forEach( (e) => e.textContent = this.warningMessage )
179
+ * }
180
+ * }
181
+ *
182
+ *
183
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements}
184
+ *
185
+ */
186
+ attributeChangedCallback(name,oldValue,newValue) {
187
+ const callbackName = `${new RichString(name).camelize()}ChangedCallback`
188
+ if (this[callbackName]) {
189
+ const newValueAsBoolean = newValue !== null
190
+ this[callbackName]({oldValue,newValue,newValueAsBoolean})
191
+ }
192
+ else if (this.constructor.observedAttributes.indexOf(name) != -1) {
193
+ console.warn("Observing %s but no method named %s was found to handle it",name,callbackName)
194
+ }
195
+ this.__update()
196
+ }
197
+
198
+ /** Overrides the custom element callback to set internal flags allowing you to know if your
199
+ * element has been disconnected. When an element is disconnected, `update` is not called.
200
+ *
201
+ * If you want to add your own logic during disconnection, override {@link onDisconnected}.
202
+ *
203
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements}
204
+ */
205
+ disconnectedCallback() {
206
+ this.#_disconnectedCallbackCalled = true
207
+ this.onDisconnected()
208
+ }
209
+
210
+ /** Override this to add logic when `disconnectedCallback` is called by the browser. This will
211
+ * not be called if you overrode `disconnectedCallback`.
212
+ */
213
+ onDisconnected() {}
214
+
215
+ /** Overrides the custom element callback to set internal flags allowing you to know if your
216
+ * element has been connected. `update` is still called for elements that have not yet connected, however
217
+ * in practice your element will be connected before any codepath that calls `update` is called.
218
+ *
219
+ * To add logic when your element is connected, override {@link onConnected}
220
+ *
221
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements}
222
+ * @see BaseCustomElement#connectedCallbackCalled
223
+ */
224
+ connectedCallback() {
225
+ this.#_connectedCallbackCalled = true
226
+ this.onConnected()
227
+ this.__update()
228
+ }
229
+
230
+ /** Override this to add logic when `connectedCallback` is called by the browser. This will
231
+ * not be called if you overrode `connectedCallback`
232
+ */
233
+ onConnected() {}
234
+
235
+ /** Returns true if this element is connected and the connected callback has been called.
236
+ * This is different from `Node#isConnected`, which can return true before `connectedCallback` is called.
237
+ */
238
+ get connectedCallbackCalled() { return !!this.#_connectedCallbackCalled }
239
+
240
+ /** Override this to perform whatever logic your element must perform.
241
+ * Because changes to your element's attributes can happen at any time and in any order,
242
+ * you will want to consolidate all logic into one method—this one. You will also
243
+ * want to make sure that this method is idempotent and fault-tolerant. It will be called multiple times.
244
+ *
245
+ * It is called by {@link BaseCustomElement#attributeChangedCallback|attributeChangedCallback} and {@link BaseCustomElement#connectedCallback|connectedCallback}, however
246
+ * it will *not* be called after the elment has been disconnected.
247
+ *
248
+ * That means that any event listeners, rendering, content manipulation, or other behavior should happen hear
249
+ * and it *must* be idempotent. In particular, any event listeners you attach must be done with care. Using
250
+ * anonymous functions could result in duplicate listeners.
251
+ */
252
+ update() {}
253
+
254
+ __update() {
255
+ if (this.#_disconnectedCallbackCalled) {
256
+ return
257
+ }
258
+ this.update()
259
+ }
260
+ }
261
+ export default BaseCustomElement
@@ -0,0 +1,116 @@
1
+ import BaseCustomElement from "./BaseCustomElement"
2
+ import RichString from "./RichString"
3
+ import ConfirmationDialog from "./ConfirmationDialog"
4
+
5
+ /** Confirms form submissions with the user before allowing the form to be submitted. This can be applied per submit
6
+ * button and only affects the behavior of `button` or `input type=submit`, and *only* if no other click handler
7
+ * has been put onto those buttons. Note that this does not affect links (`a` elements).
8
+ *
9
+ * This can ask for confirmation with {@link external:Window#confirm} or a
10
+ * `brut-confirmation-dialog`. What it will use depends on several factors, all of which
11
+ * are geared toward doing the right thing. Note that setting `show-warnings` will elucidate the reasons
12
+ * this component does what it does.
13
+ *
14
+ * * If `dialog` is set:
15
+ * - If that id is on a `<brut-confirmation-dialog>` that is used.
16
+ * - If not, `window.confirm` is used.
17
+ * * If `dialog` is not set:
18
+ * - If there is exactly one `<brut-confirmation-dialog>` on the page, this is used.
19
+ * - If there is more than one, or no `<brut-confirmation-dialog>`s, `window.confirm` is used.
20
+ *
21
+ * @see ConfirmationDialog
22
+ *
23
+ * @property {string} message - the message to show that asks for confirmation. It should be written such that
24
+ * "OK" is grammatically correct for confirmation and "Cancel" is for aborting.
25
+ * @property {string} dialog - optional ID of the `brut-confirmation-dialog` to use instead of `window.confirm`.
26
+ * If there is no such dialog or the id references the wrong element type,
27
+ * `window.confirm` will be used. Setting `show-warnings` will generate a warning for this.
28
+ *
29
+ * @customElement brut-confirm-submit
30
+ */
31
+ class ConfirmSubmit extends BaseCustomElement {
32
+ static tagName = "brut-confirm-submit"
33
+
34
+ static observedAttributes = [
35
+ "message",
36
+ "dialog",
37
+ "show-warnings",
38
+ ]
39
+
40
+ #message = new RichString("")
41
+ #confirming = false
42
+ #dialogId = null
43
+
44
+ messageChangedCallback({newValue}) {
45
+ this.#message = new RichString(newValue || "")
46
+ }
47
+
48
+ dialogChangedCallback({newValue}) {
49
+ this.#dialogId = RichString.fromString(newValue)
50
+ }
51
+
52
+ constructor() {
53
+ super()
54
+ this.onClick = (event) => {
55
+ if (this.#confirming) {
56
+ this.#confirming = false
57
+ return
58
+ }
59
+ if (this.#message.isBlank()) {
60
+ this.logger.warn("No message provided, so cannot confirm")
61
+ return
62
+ }
63
+ const dialog = this.#findDialog()
64
+ if (dialog) {
65
+ event.preventDefault()
66
+ dialog.setAttribute("message",this.#message.toString())
67
+ const buttonLabel = event.target.getAttribute("aria-label") || event.target.textContent
68
+ dialog.setAttribute("confirm-label",buttonLabel)
69
+ this.#confirming = true
70
+ dialog.showModal((confirm) => {
71
+ if (confirm) {
72
+ event.target.click()
73
+ }
74
+ else {
75
+ this.#confirming = false
76
+ }
77
+ })
78
+ }
79
+ else {
80
+ const result = window.confirm(this.#message)
81
+ if (!result) {
82
+ event.preventDefault()
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ #findDialog() {
89
+ if (this.#dialogId) {
90
+ const dialog = document.getElementById(this.#dialogId)
91
+ if (dialog) {
92
+ if (dialog.tagName.toLowerCase() != ConfirmationDialog.tagName) {
93
+ throw `${this.#dialogId} is the id of a '${dialog.tagName}', not '${ConfirmationDialog.tagName}'`
94
+ }
95
+ return dialog
96
+ }
97
+ this.logger.warn(`No dialog with id ${this.#dialogId} - using window.confirm as a fallback`)
98
+ return null
99
+ }
100
+ const dialogs = document.querySelectorAll(ConfirmationDialog.tagName)
101
+ if (dialogs.length == 1) {
102
+ return dialogs[0]
103
+ }
104
+ if (dialogs.length == 0) {
105
+ this.logger.warn(`No '${ConfirmationDialog.tagName}' found in document - using window.confirm as a fallback`)
106
+ return null
107
+ }
108
+ throw `Found ${dialogs.length} '${ConfirmationDialog.tagName}' elements. Not sure which to use. Remove all but one or specify the 'dialog' attribute on this element to specify which one to use`
109
+ }
110
+
111
+ update() {
112
+ this.querySelectorAll("button").forEach( (button) => button.addEventListener("click", this.onClick) )
113
+ this.querySelectorAll("input[type=submit]").forEach( (button) => button.addEventListener("click", this.onClick) )
114
+ }
115
+ }
116
+ export default ConfirmSubmit
@@ -0,0 +1,143 @@
1
+ import BaseCustomElement from "./BaseCustomElement"
2
+ import RichString from "./RichString"
3
+
4
+ /**
5
+ * Enhances a `DIALOG` element to allow it to be used for confirmation. Mostly useful
6
+ * with {@link Confirm}.
7
+ *
8
+ * This element must:
9
+ *
10
+ * * Wrap a `<DIALOG>` element
11
+ * * Contain an `<H1>` where the message will go
12
+ * * Have a `<BUTTON>` with value `ok` that will be considered the confirmation button.
13
+ * * Have a `<BUTTON>` with value `cancel` that will be considered the denial button.
14
+ *
15
+ * Omitting these will cause this element to not work properly. Set `show-warnings` to see
16
+ * warnings on this.
17
+ *
18
+ * @property {string} message - the message to use to ask for confirmation
19
+ * @property {string} confirm-label - the label to use for the "OK" or "Confirm" button
20
+ *
21
+ * @example <caption>Minimal Example</caption>
22
+ * <brut-confirmation-dialog message="This cannot be undone" confirm-label="DOIT">
23
+ * <dialog>
24
+ * <h1></h1>
25
+ * <button value="ok"></button>
26
+ * <button value="cancel">Nevermind</button>
27
+ * </dialog>
28
+ * </brut-confirmation-dialog>
29
+ *
30
+ * @customElement brut-confirmation-dialog
31
+ */
32
+ class ConfirmationDialog extends BaseCustomElement {
33
+ static tagName = "brut-confirmation-dialog"
34
+ static observedAttributes = [
35
+ "message",
36
+ "confirm-label",
37
+ "show-warnings"
38
+ ]
39
+
40
+ #onClose = () => {}
41
+ #message = new RichString("")
42
+ #confirmLabel = new RichString("OK")
43
+
44
+ constructor() {
45
+ super()
46
+ this.okListener = (event) => {
47
+ this.#closeDialog()
48
+ this.#onClose(true)
49
+ }
50
+ this.cancelListener = (event) => {
51
+ this.#closeDialog()
52
+ this.#onClose(false)
53
+ }
54
+ }
55
+
56
+ messageChangedCallback({newValue}) {
57
+ this.#message = RichString.fromString(newValue)
58
+ }
59
+
60
+ confirmLabelChangedCallback({newValue}) {
61
+ this.#confirmLabel = RichString.fromString(newValue)
62
+ }
63
+
64
+ /**
65
+ * Call this to show the dialog. When the dialog is closed, `onClose` is called with the result.
66
+ *
67
+ * @param {function} onClose - a function called with either `true` or `false`, if the dialog was confirmed or
68
+ * denied, respectively.
69
+ *
70
+ * @example
71
+ * dialog.showModal( (confirmed) => {
72
+ * if (confirmed) {
73
+ * form.submit()
74
+ * }
75
+ * else {
76
+ * // do nothing
77
+ * }
78
+ * })
79
+ */
80
+ showModal(onClose) {
81
+ const dialog = this.#dialog
82
+ if (dialog) {
83
+ this.#onClose = onClose || (() => {})
84
+ dialog.showModal()
85
+ }
86
+ else {
87
+ this.logger.warn("No <dialog> found to show")
88
+ }
89
+ }
90
+
91
+ get #dialog() {
92
+ return this.querySelector("dialog")
93
+ }
94
+
95
+ #closeDialog() {
96
+ const dialog = this.#dialog
97
+ if (dialog) {
98
+ dialog.close()
99
+ }
100
+ }
101
+
102
+ update() {
103
+ const dialog = this.#dialog
104
+ if (!dialog) {
105
+ this.logger.warn("Could not find a <dialog> - this custom element won't do anything")
106
+ return
107
+ }
108
+ this.#setMessage(dialog)
109
+ this.#setupButtons()
110
+ }
111
+
112
+ #setMessage(dialog) {
113
+ const h1 = dialog.querySelector("h1")
114
+ if (h1) {
115
+ if (this.#message.isBlank()) {
116
+ h1.textContent = null
117
+ }
118
+ else {
119
+ h1.textContent = this.#message.toString()
120
+ }
121
+ }
122
+ else {
123
+ this.logger.warn("Dialog had no <h1>, so nowhere to put the message")
124
+ }
125
+ }
126
+
127
+ #setupButtons() {
128
+ const okButton = this.querySelector("button[value='ok']")
129
+ const cancelButton = this.querySelector("button[value='cancel']")
130
+
131
+ if (!okButton || !cancelButton) {
132
+ if (!okButton) { this.logger.warn("no <button value='ok'> which is required for this dialog to work") }
133
+ if (!cancelButton) { this.logger.warn("no <button value='cancel'> which is required for this dialog to work") }
134
+ return
135
+ }
136
+
137
+ okButton.textContent = this.#confirmLabel
138
+
139
+ okButton.addEventListener("click" , this.okListener)
140
+ cancelButton.addEventListener("click" , this.cancelListener)
141
+ }
142
+ }
143
+ export default ConfirmationDialog
@@ -0,0 +1,125 @@
1
+ import BaseCustomElement from "./BaseCustomElement"
2
+ import RichString from "./RichString"
3
+ import I18nTranslation from "./I18nTranslation"
4
+
5
+ /** Like {@link Message} but specific to constraint violations of input fields. This accepts the name
6
+ * of an input field via `input-name`, which can be used to locate the field's localized name.
7
+ *
8
+ * Here is how the field's name is determined:
9
+ *
10
+ * 1. It will look for a `<brut-i18n-translation>` element with the `key` `cv.fe.fieldNames.«input-name»`.
11
+ * 2. If that's not found, it will attempt to use "this field" by locating a `<brut-i18n-translation>` element with the `key`
12
+ * `cv.this_field` (the underscore being what is used on Brut's server side).
13
+ * 3. If that is not found, it will use the literaly string "this field" and emit a console warning.
14
+ *
15
+ * @property {string} key - the i18n translation key to use. It must map to the `key` of a `<brut-i18n-translation>` on the page or
16
+ * the element will not render any text.
17
+ * @property {string} input-name - the name of the input, used to insert into the message, e.g. "Title is required".
18
+ * @property {boolean} server-side if true, this indicates the element contains constraint violation messages
19
+ * from the server. Currently doesn't affect this element's behavior, however
20
+ * AjaxSubmit will use it to locate where it should insert server-side errors.
21
+ *
22
+ * @see I18nTranslation
23
+ * @see ConstraintViolationMessages
24
+ * @see Message
25
+ *
26
+ * @customElement brut-cv
27
+ */
28
+ class ConstraintViolationMessage extends BaseCustomElement {
29
+ static tagName = "brut-cv"
30
+
31
+ static observedAttributes = [
32
+ "show-warnings",
33
+ "key",
34
+ "input-name",
35
+ "server-side",
36
+ ]
37
+
38
+ static createElement(document,attributes) {
39
+ const element = document.createElement(ConstraintViolationMessage.tagName)
40
+ element.setAttribute("key",this.i18nKey("fe", attributes.key))
41
+ element.setAttribute("input-name",attributes["input-name"])
42
+ if (Object.hasOwn(attributes,"show-warnings")) {
43
+ element.setAttribute("show-warnings",attributes["show-warnings"])
44
+ }
45
+ return element
46
+ }
47
+
48
+ static markServerSide(element) {
49
+ if (element.tagName.toLowerCase() == this.tagName) {
50
+ element.setAttribute("server-side", true)
51
+ }
52
+ }
53
+
54
+ static clientSideSelector() {
55
+ return `${this.tagName}:not([server-side])`
56
+ }
57
+
58
+ static serverSideSelector() {
59
+ return `${this.tagName}[server-side]`
60
+ }
61
+
62
+ /** Returns the I18N key used for front-end constraint violations. This is useful
63
+ * if you need to construct a key and want to follow Brut's conventions on how they
64
+ * are managed.
65
+ *
66
+ * @param {...String} keyPath - parts of the path of the key after the namespace that Brut manages.
67
+ */
68
+ static i18nKey(...keyPath) {
69
+ const path = [ "cv" ]
70
+ return path.concat(keyPath).join(".")
71
+ }
72
+
73
+ #key = null
74
+ #inputNameKey = null
75
+ #thisFieldKey = this.#i18nKey("this_field")
76
+
77
+ keyChangedCallback({newValue}) {
78
+ this.#key = newValue
79
+ }
80
+
81
+ inputNameChangedCallback({newValue}) {
82
+ this.#inputNameKey = this.#i18nKey("fe", "fieldNames", newValue)
83
+ }
84
+
85
+ serverSideChangedCallback({newValueAsBoolean}) {
86
+ // attribute listed for documentation purposes only
87
+ }
88
+
89
+ update() {
90
+ if (!this.#key) {
91
+ this.logger.info("No key attribute, so can't do anything")
92
+ return
93
+ }
94
+
95
+ const selector = `${I18nTranslation.tagName}[key='${this.#key}']`
96
+ const translation = document.querySelector(selector)
97
+ if (!translation) {
98
+ this.logger.info("Could not find translation based on selector '%s'",selector)
99
+ return
100
+ }
101
+
102
+ const fieldNameSelector = `${I18nTranslation.tagName}[key='${this.#inputNameKey}']`
103
+ const thisFieldSelector = `${I18nTranslation.tagName}[key='${this.#thisFieldKey}']`
104
+
105
+ let fieldNameTranslation = document.querySelector(fieldNameSelector)
106
+ if (!fieldNameTranslation) {
107
+ this.logger.info("Could not find translation for input/field name based on selector '%s'. Will try 'this field' fallback",fieldNameSelector)
108
+ fieldNameTranslation = document.querySelector(thisFieldSelector)
109
+ if (!fieldNameTranslation) {
110
+ this.logger.info("Could not find translation for 'this field' fallback key, based on selector '%s'",thisFieldSelector)
111
+ }
112
+ }
113
+
114
+ const fieldName = fieldNameTranslation ? fieldNameTranslation.translation() : "this field"
115
+ this.textContent = RichString.fromString(translation.translation({ field: fieldName })).capitalize().toString()
116
+ }
117
+
118
+ /** Helper that calls the static version */
119
+ #i18nKey(...keyPath) {
120
+ return this.constructor.i18nKey(...keyPath)
121
+ }
122
+
123
+
124
+ }
125
+ export default ConstraintViolationMessage