shoelace-rails-ui 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (332) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/CHANGELOG.md +5 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/Gemfile +12 -0
  6. data/Gemfile.lock +42 -0
  7. data/LICENSE.txt +21 -0
  8. data/README-real.md +24 -0
  9. data/README.md +43 -0
  10. data/Rakefile +16 -0
  11. data/bin/console +15 -0
  12. data/bin/setup +8 -0
  13. data/lib/shoelace/rails/ui/version.rb +9 -0
  14. data/lib/shoelace/rails/ui.rb +12 -0
  15. data/regenerate.rb +48 -0
  16. data/remote-src/.DS_Store +0 -0
  17. data/remote-src/2.0.0.beta64/.DS_Store +0 -0
  18. data/remote-src/2.0.0.beta64/shoelace-next/.editorconfig +15 -0
  19. data/remote-src/2.0.0.beta64/shoelace-next/.github/CODE_OF_CONDUCT.md +44 -0
  20. data/remote-src/2.0.0.beta64/shoelace-next/.github/FUNDING.yml +1 -0
  21. data/remote-src/2.0.0.beta64/shoelace-next/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
  22. data/remote-src/2.0.0.beta64/shoelace-next/.github/ISSUE_TEMPLATE/config.yml +4 -0
  23. data/remote-src/2.0.0.beta64/shoelace-next/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  24. data/remote-src/2.0.0.beta64/shoelace-next/.github/SECURITY.md +7 -0
  25. data/remote-src/2.0.0.beta64/shoelace-next/.github/workflows/node.js.yml +30 -0
  26. data/remote-src/2.0.0.beta64/shoelace-next/.github/workflows/release.yml +17 -0
  27. data/remote-src/2.0.0.beta64/shoelace-next/.gitignore +8 -0
  28. data/remote-src/2.0.0.beta64/shoelace-next/.husky/pre-commit +4 -0
  29. data/remote-src/2.0.0.beta64/shoelace-next/.prettierignore +12 -0
  30. data/remote-src/2.0.0.beta64/shoelace-next/.vscode/extensions.json +9 -0
  31. data/remote-src/2.0.0.beta64/shoelace-next/.vscode/settings.json +4 -0
  32. data/remote-src/2.0.0.beta64/shoelace-next/CONTRIBUTING.md +5 -0
  33. data/remote-src/2.0.0.beta64/shoelace-next/LICENSE.md +7 -0
  34. data/remote-src/2.0.0.beta64/shoelace-next/README.md +94 -0
  35. data/remote-src/2.0.0.beta64/shoelace-next/cspell.json +110 -0
  36. data/remote-src/2.0.0.beta64/shoelace-next/custom-elements-manifest.config.js +104 -0
  37. data/remote-src/2.0.0.beta64/shoelace-next/docs/404.md +5 -0
  38. data/remote-src/2.0.0.beta64/shoelace-next/docs/_sidebar.md +89 -0
  39. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/icons/sprite.svg +1 -0
  40. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/chrome.png +0 -0
  41. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/edge.png +0 -0
  42. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/firefox.png +0 -0
  43. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/logo.svg +7 -0
  44. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/og-image.png +0 -0
  45. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/opera.png +0 -0
  46. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/safari.png +0 -0
  47. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/shoe.svg +134 -0
  48. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/tie.webp +0 -0
  49. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/touch-icon.png +0 -0
  50. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/twitter-card.png +0 -0
  51. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/undraw-content-team.svg +80 -0
  52. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/undraw-not-found.svg +1 -0
  53. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/walk.gif +0 -0
  54. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/images/wordmark.svg +7 -0
  55. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/plugins/code-block/code-block.css +228 -0
  56. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/plugins/code-block/code-block.js +376 -0
  57. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/plugins/metadata/metadata.js +537 -0
  58. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/plugins/scroll-position/scroll-position.js +24 -0
  59. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/plugins/search/lunr.min.js +1311 -0
  60. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/plugins/search/search.css +180 -0
  61. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/plugins/search/search.js +293 -0
  62. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/plugins/theme-picker/theme-picker.css +29 -0
  63. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/plugins/theme-picker/theme-picker.js +83 -0
  64. data/remote-src/2.0.0.beta64/shoelace-next/docs/assets/styles/docs.css +642 -0
  65. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/alert.md +441 -0
  66. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/animated-image.md +128 -0
  67. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/animation.md +351 -0
  68. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/avatar.md +201 -0
  69. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/badge.md +191 -0
  70. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/breadcrumb-item.md +35 -0
  71. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/breadcrumb.md +266 -0
  72. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/button-group.md +470 -0
  73. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/button.md +453 -0
  74. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/card.md +311 -0
  75. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/checkbox.md +71 -0
  76. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/color-picker.md +101 -0
  77. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/details.md +89 -0
  78. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/dialog.md +265 -0
  79. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/divider.md +152 -0
  80. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/drawer.md +452 -0
  81. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/dropdown.md +389 -0
  82. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/format-bytes.md +126 -0
  83. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/format-date.md +120 -0
  84. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/format-number.md +124 -0
  85. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/icon-button.md +160 -0
  86. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/icon.md +711 -0
  87. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/image-comparer.md +51 -0
  88. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/include.md +47 -0
  89. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/input.md +283 -0
  90. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/menu-item.md +255 -0
  91. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/menu-label.md +52 -0
  92. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/menu.md +49 -0
  93. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/mutation-observer.md +196 -0
  94. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/progress-bar.md +144 -0
  95. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/progress-ring.md +178 -0
  96. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/qr-code.md +167 -0
  97. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/radio-group.md +52 -0
  98. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/radio.md +59 -0
  99. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/range.md +180 -0
  100. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/rating.md +153 -0
  101. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/relative-time.md +105 -0
  102. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/resize-observer.md +68 -0
  103. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/responsive-media.md +73 -0
  104. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/select.md +457 -0
  105. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/skeleton.md +439 -0
  106. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/spinner.md +86 -0
  107. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/split-panel.md +671 -0
  108. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/switch.md +77 -0
  109. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/tab-group.md +359 -0
  110. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/tab-panel.md +41 -0
  111. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/tab.md +29 -0
  112. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/tag.md +142 -0
  113. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/textarea.md +184 -0
  114. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/tooltip.md +410 -0
  115. data/remote-src/2.0.0.beta64/shoelace-next/docs/components/visually-hidden.md +47 -0
  116. data/remote-src/2.0.0.beta64/shoelace-next/docs/frameworks/angular.md +46 -0
  117. data/remote-src/2.0.0.beta64/shoelace-next/docs/frameworks/react.md +156 -0
  118. data/remote-src/2.0.0.beta64/shoelace-next/docs/frameworks/vue.md +92 -0
  119. data/remote-src/2.0.0.beta64/shoelace-next/docs/getting-started/customizing.md +153 -0
  120. data/remote-src/2.0.0.beta64/shoelace-next/docs/getting-started/form-controls.md +336 -0
  121. data/remote-src/2.0.0.beta64/shoelace-next/docs/getting-started/installation.md +137 -0
  122. data/remote-src/2.0.0.beta64/shoelace-next/docs/getting-started/localization.md +140 -0
  123. data/remote-src/2.0.0.beta64/shoelace-next/docs/getting-started/overview.md +131 -0
  124. data/remote-src/2.0.0.beta64/shoelace-next/docs/getting-started/themes.md +139 -0
  125. data/remote-src/2.0.0.beta64/shoelace-next/docs/getting-started/usage.md +173 -0
  126. data/remote-src/2.0.0.beta64/shoelace-next/docs/index.html +98 -0
  127. data/remote-src/2.0.0.beta64/shoelace-next/docs/resources/accessibility.md +18 -0
  128. data/remote-src/2.0.0.beta64/shoelace-next/docs/resources/changelog.md +925 -0
  129. data/remote-src/2.0.0.beta64/shoelace-next/docs/resources/community.md +53 -0
  130. data/remote-src/2.0.0.beta64/shoelace-next/docs/resources/contributing.md +261 -0
  131. data/remote-src/2.0.0.beta64/shoelace-next/docs/tokens/border-radius.md +12 -0
  132. data/remote-src/2.0.0.beta64/shoelace-next/docs/tokens/color.md +439 -0
  133. data/remote-src/2.0.0.beta64/shoelace-next/docs/tokens/elevation.md +11 -0
  134. data/remote-src/2.0.0.beta64/shoelace-next/docs/tokens/spacing.md +16 -0
  135. data/remote-src/2.0.0.beta64/shoelace-next/docs/tokens/transition.md +11 -0
  136. data/remote-src/2.0.0.beta64/shoelace-next/docs/tokens/typography.md +58 -0
  137. data/remote-src/2.0.0.beta64/shoelace-next/docs/tokens/z-index.md +11 -0
  138. data/remote-src/2.0.0.beta64/shoelace-next/docs/tutorials/integrating-with-laravel.md +117 -0
  139. data/remote-src/2.0.0.beta64/shoelace-next/docs/tutorials/integrating-with-nextjs.md +137 -0
  140. data/remote-src/2.0.0.beta64/shoelace-next/docs/tutorials/integrating-with-rails.md +103 -0
  141. data/remote-src/2.0.0.beta64/shoelace-next/lint-staged.config.js +4 -0
  142. data/remote-src/2.0.0.beta64/shoelace-next/package-lock.json +9534 -0
  143. data/remote-src/2.0.0.beta64/shoelace-next/package.json +93 -0
  144. data/remote-src/2.0.0.beta64/shoelace-next/prettier.config.cjs +17 -0
  145. data/remote-src/2.0.0.beta64/shoelace-next/scripts/build.js +158 -0
  146. data/remote-src/2.0.0.beta64/shoelace-next/scripts/make-css.js +42 -0
  147. data/remote-src/2.0.0.beta64/shoelace-next/scripts/make-icons.js +71 -0
  148. data/remote-src/2.0.0.beta64/shoelace-next/scripts/make-metadata.js +11 -0
  149. data/remote-src/2.0.0.beta64/shoelace-next/scripts/make-react.js +63 -0
  150. data/remote-src/2.0.0.beta64/shoelace-next/scripts/make-search.js +100 -0
  151. data/remote-src/2.0.0.beta64/shoelace-next/scripts/make-vscode-data.js +58 -0
  152. data/remote-src/2.0.0.beta64/shoelace-next/scripts/plop/plopfile.js +67 -0
  153. data/remote-src/2.0.0.beta64/shoelace-next/scripts/plop/templates/component/component.hbs +44 -0
  154. data/remote-src/2.0.0.beta64/shoelace-next/scripts/plop/templates/component/docs.hbs +21 -0
  155. data/remote-src/2.0.0.beta64/shoelace-next/scripts/plop/templates/component/styles.hbs +10 -0
  156. data/remote-src/2.0.0.beta64/shoelace-next/scripts/plop/templates/component/tests.hbs +13 -0
  157. data/remote-src/2.0.0.beta64/shoelace-next/scripts/shared.js +18 -0
  158. data/remote-src/2.0.0.beta64/shoelace-next/src/components/alert/alert.styles.ts +95 -0
  159. data/remote-src/2.0.0.beta64/shoelace-next/src/components/alert/alert.test.ts +93 -0
  160. data/remote-src/2.0.0.beta64/shoelace-next/src/components/alert/alert.ts +238 -0
  161. data/remote-src/2.0.0.beta64/shoelace-next/src/components/animated-image/animated-image.styles.ts +52 -0
  162. data/remote-src/2.0.0.beta64/shoelace-next/src/components/animated-image/animated-image.test.ts +13 -0
  163. data/remote-src/2.0.0.beta64/shoelace-next/src/components/animated-image/animated-image.ts +120 -0
  164. data/remote-src/2.0.0.beta64/shoelace-next/src/components/animation/animation.styles.ts +10 -0
  165. data/remote-src/2.0.0.beta64/shoelace-next/src/components/animation/animation.ts +222 -0
  166. data/remote-src/2.0.0.beta64/shoelace-next/src/components/animation/animations.ts +15 -0
  167. data/remote-src/2.0.0.beta64/shoelace-next/src/components/avatar/avatar.styles.ts +66 -0
  168. data/remote-src/2.0.0.beta64/shoelace-next/src/components/avatar/avatar.test.ts +111 -0
  169. data/remote-src/2.0.0.beta64/shoelace-next/src/components/avatar/avatar.ts +83 -0
  170. data/remote-src/2.0.0.beta64/shoelace-next/src/components/badge/badge.styles.ts +94 -0
  171. data/remote-src/2.0.0.beta64/shoelace-next/src/components/badge/badge.test.ts +77 -0
  172. data/remote-src/2.0.0.beta64/shoelace-next/src/components/badge/badge.ts +53 -0
  173. data/remote-src/2.0.0.beta64/shoelace-next/src/components/breadcrumb/breadcrumb.styles.ts +12 -0
  174. data/remote-src/2.0.0.beta64/shoelace-next/src/components/breadcrumb/breadcrumb.test.ts +104 -0
  175. data/remote-src/2.0.0.beta64/shoelace-next/src/components/breadcrumb/breadcrumb.ts +82 -0
  176. data/remote-src/2.0.0.beta64/shoelace-next/src/components/breadcrumb-item/breadcrumb-item.styles.ts +85 -0
  177. data/remote-src/2.0.0.beta64/shoelace-next/src/components/breadcrumb-item/breadcrumb-item.test.ts +160 -0
  178. data/remote-src/2.0.0.beta64/shoelace-next/src/components/breadcrumb-item/breadcrumb-item.ts +92 -0
  179. data/remote-src/2.0.0.beta64/shoelace-next/src/components/button/button.styles.ts +637 -0
  180. data/remote-src/2.0.0.beta64/shoelace-next/src/components/button/button.ts +209 -0
  181. data/remote-src/2.0.0.beta64/shoelace-next/src/components/button-group/button-group.styles.ts +15 -0
  182. data/remote-src/2.0.0.beta64/shoelace-next/src/components/button-group/button-group.ts +84 -0
  183. data/remote-src/2.0.0.beta64/shoelace-next/src/components/card/card.styles.ts +62 -0
  184. data/remote-src/2.0.0.beta64/shoelace-next/src/components/card/card.test.ts +139 -0
  185. data/remote-src/2.0.0.beta64/shoelace-next/src/components/card/card.ts +68 -0
  186. data/remote-src/2.0.0.beta64/shoelace-next/src/components/checkbox/checkbox.styles.ts +108 -0
  187. data/remote-src/2.0.0.beta64/shoelace-next/src/components/checkbox/checkbox.test.ts +47 -0
  188. data/remote-src/2.0.0.beta64/shoelace-next/src/components/checkbox/checkbox.ts +203 -0
  189. data/remote-src/2.0.0.beta64/shoelace-next/src/components/color-picker/color-picker.styles.ts +326 -0
  190. data/remote-src/2.0.0.beta64/shoelace-next/src/components/color-picker/color-picker.test.ts +43 -0
  191. data/remote-src/2.0.0.beta64/shoelace-next/src/components/color-picker/color-picker.ts +853 -0
  192. data/remote-src/2.0.0.beta64/shoelace-next/src/components/details/details.styles.ts +73 -0
  193. data/remote-src/2.0.0.beta64/shoelace-next/src/components/details/details.test.ts +153 -0
  194. data/remote-src/2.0.0.beta64/shoelace-next/src/components/details/details.ts +192 -0
  195. data/remote-src/2.0.0.beta64/shoelace-next/src/components/dialog/dialog.styles.ts +106 -0
  196. data/remote-src/2.0.0.beta64/shoelace-next/src/components/dialog/dialog.test.ts +135 -0
  197. data/remote-src/2.0.0.beta64/shoelace-next/src/components/dialog/dialog.ts +296 -0
  198. data/remote-src/2.0.0.beta64/shoelace-next/src/components/divider/divider.styles.ts +25 -0
  199. data/remote-src/2.0.0.beta64/shoelace-next/src/components/divider/divider.ts +34 -0
  200. data/remote-src/2.0.0.beta64/shoelace-next/src/components/drawer/drawer.styles.ts +142 -0
  201. data/remote-src/2.0.0.beta64/shoelace-next/src/components/drawer/drawer.test.ts +135 -0
  202. data/remote-src/2.0.0.beta64/shoelace-next/src/components/drawer/drawer.ts +377 -0
  203. data/remote-src/2.0.0.beta64/shoelace-next/src/components/dropdown/dropdown.styles.ts +58 -0
  204. data/remote-src/2.0.0.beta64/shoelace-next/src/components/dropdown/dropdown.test.ts +147 -0
  205. data/remote-src/2.0.0.beta64/shoelace-next/src/components/dropdown/dropdown.ts +459 -0
  206. data/remote-src/2.0.0.beta64/shoelace-next/src/components/format-bytes/format-bytes.ts +35 -0
  207. data/remote-src/2.0.0.beta64/shoelace-next/src/components/format-date/format-date.ts +81 -0
  208. data/remote-src/2.0.0.beta64/shoelace-next/src/components/format-number/format-number.ts +69 -0
  209. data/remote-src/2.0.0.beta64/shoelace-next/src/components/icon/icon.styles.ts +21 -0
  210. data/remote-src/2.0.0.beta64/shoelace-next/src/components/icon/icon.ts +131 -0
  211. data/remote-src/2.0.0.beta64/shoelace-next/src/components/icon/library.default.ts +9 -0
  212. data/remote-src/2.0.0.beta64/shoelace-next/src/components/icon/library.system.ts +97 -0
  213. data/remote-src/2.0.0.beta64/shoelace-next/src/components/icon/library.ts +49 -0
  214. data/remote-src/2.0.0.beta64/shoelace-next/src/components/icon/request.ts +36 -0
  215. data/remote-src/2.0.0.beta64/shoelace-next/src/components/icon-button/icon-button.styles.ts +48 -0
  216. data/remote-src/2.0.0.beta64/shoelace-next/src/components/icon-button/icon-button.ts +100 -0
  217. data/remote-src/2.0.0.beta64/shoelace-next/src/components/image-comparer/image-comparer.styles.ts +77 -0
  218. data/remote-src/2.0.0.beta64/shoelace-next/src/components/image-comparer/image-comparer.ts +147 -0
  219. data/remote-src/2.0.0.beta64/shoelace-next/src/components/include/include.styles.ts +10 -0
  220. data/remote-src/2.0.0.beta64/shoelace-next/src/components/include/include.test.ts +62 -0
  221. data/remote-src/2.0.0.beta64/shoelace-next/src/components/include/include.ts +80 -0
  222. data/remote-src/2.0.0.beta64/shoelace-next/src/components/include/request.ts +23 -0
  223. data/remote-src/2.0.0.beta64/shoelace-next/src/components/input/input.styles.ts +282 -0
  224. data/remote-src/2.0.0.beta64/shoelace-next/src/components/input/input.test.ts +35 -0
  225. data/remote-src/2.0.0.beta64/shoelace-next/src/components/input/input.ts +410 -0
  226. data/remote-src/2.0.0.beta64/shoelace-next/src/components/menu/menu.styles.ts +18 -0
  227. data/remote-src/2.0.0.beta64/shoelace-next/src/components/menu/menu.ts +205 -0
  228. data/remote-src/2.0.0.beta64/shoelace-next/src/components/menu-item/menu-item.styles.ts +84 -0
  229. data/remote-src/2.0.0.beta64/shoelace-next/src/components/menu-item/menu-item.ts +92 -0
  230. data/remote-src/2.0.0.beta64/shoelace-next/src/components/menu-label/menu-label.styles.ts +21 -0
  231. data/remote-src/2.0.0.beta64/shoelace-next/src/components/menu-label/menu-label.ts +30 -0
  232. data/remote-src/2.0.0.beta64/shoelace-next/src/components/mutation-observer/mutation-observer.styles.ts +10 -0
  233. data/remote-src/2.0.0.beta64/shoelace-next/src/components/mutation-observer/mutation-observer.test.ts +13 -0
  234. data/remote-src/2.0.0.beta64/shoelace-next/src/components/mutation-observer/mutation-observer.ts +118 -0
  235. data/remote-src/2.0.0.beta64/shoelace-next/src/components/progress-bar/progress-bar.styles.ts +57 -0
  236. data/remote-src/2.0.0.beta64/shoelace-next/src/components/progress-bar/progress-bar.test.ts +89 -0
  237. data/remote-src/2.0.0.beta64/shoelace-next/src/components/progress-bar/progress-bar.ts +74 -0
  238. data/remote-src/2.0.0.beta64/shoelace-next/src/components/progress-ring/progress-ring.styles.ts +66 -0
  239. data/remote-src/2.0.0.beta64/shoelace-next/src/components/progress-ring/progress-ring.test.ts +68 -0
  240. data/remote-src/2.0.0.beta64/shoelace-next/src/components/progress-ring/progress-ring.ts +84 -0
  241. data/remote-src/2.0.0.beta64/shoelace-next/src/components/qr-code/qr-code.styles.ts +22 -0
  242. data/remote-src/2.0.0.beta64/shoelace-next/src/components/qr-code/qr-code.ts +90 -0
  243. data/remote-src/2.0.0.beta64/shoelace-next/src/components/radio/radio.styles.ts +107 -0
  244. data/remote-src/2.0.0.beta64/shoelace-next/src/components/radio/radio.test.ts +65 -0
  245. data/remote-src/2.0.0.beta64/shoelace-next/src/components/radio/radio.ts +226 -0
  246. data/remote-src/2.0.0.beta64/shoelace-next/src/components/radio-group/radio-group.styles.ts +47 -0
  247. data/remote-src/2.0.0.beta64/shoelace-next/src/components/radio-group/radio-group.ts +66 -0
  248. data/remote-src/2.0.0.beta64/shoelace-next/src/components/range/range.styles.ts +192 -0
  249. data/remote-src/2.0.0.beta64/shoelace-next/src/components/range/range.ts +273 -0
  250. data/remote-src/2.0.0.beta64/shoelace-next/src/components/rating/rating.styles.ts +79 -0
  251. data/remote-src/2.0.0.beta64/shoelace-next/src/components/rating/rating.ts +249 -0
  252. data/remote-src/2.0.0.beta64/shoelace-next/src/components/relative-time/relative-time.ts +116 -0
  253. data/remote-src/2.0.0.beta64/shoelace-next/src/components/resize-observer/resize-observer.styles.ts +10 -0
  254. data/remote-src/2.0.0.beta64/shoelace-next/src/components/resize-observer/resize-observer.ts +87 -0
  255. data/remote-src/2.0.0.beta64/shoelace-next/src/components/responsive-media/responsive-media.styles.ts +36 -0
  256. data/remote-src/2.0.0.beta64/shoelace-next/src/components/responsive-media/responsive-media.ts +50 -0
  257. data/remote-src/2.0.0.beta64/shoelace-next/src/components/select/select.styles.ts +331 -0
  258. data/remote-src/2.0.0.beta64/shoelace-next/src/components/select/select.test.ts +56 -0
  259. data/remote-src/2.0.0.beta64/shoelace-next/src/components/select/select.ts +560 -0
  260. data/remote-src/2.0.0.beta64/shoelace-next/src/components/skeleton/skeleton.styles.ts +60 -0
  261. data/remote-src/2.0.0.beta64/shoelace-next/src/components/skeleton/skeleton.ts +46 -0
  262. data/remote-src/2.0.0.beta64/shoelace-next/src/components/spinner/spinner.styles.ts +56 -0
  263. data/remote-src/2.0.0.beta64/shoelace-next/src/components/spinner/spinner.test.ts +24 -0
  264. data/remote-src/2.0.0.beta64/shoelace-next/src/components/spinner/spinner.ts +34 -0
  265. data/remote-src/2.0.0.beta64/shoelace-next/src/components/split-panel/split-panel.styles.ts +76 -0
  266. data/remote-src/2.0.0.beta64/shoelace-next/src/components/split-panel/split-panel.test.ts +13 -0
  267. data/remote-src/2.0.0.beta64/shoelace-next/src/components/split-panel/split-panel.ts +272 -0
  268. data/remote-src/2.0.0.beta64/shoelace-next/src/components/switch/switch.styles.ts +135 -0
  269. data/remote-src/2.0.0.beta64/shoelace-next/src/components/switch/switch.test.ts +64 -0
  270. data/remote-src/2.0.0.beta64/shoelace-next/src/components/switch/switch.ts +187 -0
  271. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tab/tab.styles.ts +61 -0
  272. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tab/tab.ts +104 -0
  273. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tab-group/tab-group.styles.ts +209 -0
  274. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tab-group/tab-group.ts +409 -0
  275. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tab-panel/tab-panel.styles.ts +17 -0
  276. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tab-panel/tab-panel.ts +49 -0
  277. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tag/tag.styles.ts +110 -0
  278. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tag/tag.ts +92 -0
  279. data/remote-src/2.0.0.beta64/shoelace-next/src/components/textarea/textarea.styles.ts +167 -0
  280. data/remote-src/2.0.0.beta64/shoelace-next/src/components/textarea/textarea.test.ts +35 -0
  281. data/remote-src/2.0.0.beta64/shoelace-next/src/components/textarea/textarea.ts +339 -0
  282. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tooltip/tooltip.styles.ts +131 -0
  283. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tooltip/tooltip.test.ts +117 -0
  284. data/remote-src/2.0.0.beta64/shoelace-next/src/components/tooltip/tooltip.ts +355 -0
  285. data/remote-src/2.0.0.beta64/shoelace-next/src/components/visually-hidden/visually-hidden.styles.ts +18 -0
  286. data/remote-src/2.0.0.beta64/shoelace-next/src/components/visually-hidden/visually-hidden.test.ts +39 -0
  287. data/remote-src/2.0.0.beta64/shoelace-next/src/components/visually-hidden/visually-hidden.ts +24 -0
  288. data/remote-src/2.0.0.beta64/shoelace-next/src/declaration.d.ts +8 -0
  289. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/animate.ts +70 -0
  290. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/event.ts +35 -0
  291. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/focus-visible.ts +26 -0
  292. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/form-control.ts +203 -0
  293. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/formdata-event-polyfill.ts +93 -0
  294. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/math.ts +8 -0
  295. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/modal.ts +53 -0
  296. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/number.ts +32 -0
  297. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/offset.ts +13 -0
  298. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/scroll.ts +57 -0
  299. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/slot.ts +91 -0
  300. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/string.ts +3 -0
  301. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/support.ts +15 -0
  302. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/tabbable.ts +81 -0
  303. data/remote-src/2.0.0.beta64/shoelace-next/src/internal/watch.ts +40 -0
  304. data/remote-src/2.0.0.beta64/shoelace-next/src/shoelace.ts +58 -0
  305. data/remote-src/2.0.0.beta64/shoelace-next/src/styles/component.styles.ts +23 -0
  306. data/remote-src/2.0.0.beta64/shoelace-next/src/styles/form-control.styles.ts +52 -0
  307. data/remote-src/2.0.0.beta64/shoelace-next/src/styles/utility.styles.ts +23 -0
  308. data/remote-src/2.0.0.beta64/shoelace-next/src/themes/dark.styles.ts +524 -0
  309. data/remote-src/2.0.0.beta64/shoelace-next/src/themes/light.styles.ts +524 -0
  310. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/de-ch.ts +21 -0
  311. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/de.ts +21 -0
  312. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/en.ts +21 -0
  313. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/es.ts +21 -0
  314. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/fr.ts +21 -0
  315. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/he.ts +21 -0
  316. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/ja.ts +21 -0
  317. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/nl.ts +21 -0
  318. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/pl.ts +21 -0
  319. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/pt.ts +21 -0
  320. data/remote-src/2.0.0.beta64/shoelace-next/src/translations/ru.ts +21 -0
  321. data/remote-src/2.0.0.beta64/shoelace-next/src/utilities/animation-registry.ts +59 -0
  322. data/remote-src/2.0.0.beta64/shoelace-next/src/utilities/animation.ts +1 -0
  323. data/remote-src/2.0.0.beta64/shoelace-next/src/utilities/base-path.ts +43 -0
  324. data/remote-src/2.0.0.beta64/shoelace-next/src/utilities/form.ts +22 -0
  325. data/remote-src/2.0.0.beta64/shoelace-next/src/utilities/icon-library.ts +1 -0
  326. data/remote-src/2.0.0.beta64/shoelace-next/src/utilities/localize.ts +5 -0
  327. data/remote-src/2.0.0.beta64/shoelace-next/tsconfig.json +35 -0
  328. data/remote-src/2.0.0.beta64/shoelace-next/vercel.json +4 -0
  329. data/remote-src/2.0.0.beta64/shoelace-next/web-test-runner.config.js +14 -0
  330. data/shoelace-rails-ui.gemspec +39 -0
  331. data/shoelace-rails-ui.rb +0 -0
  332. metadata +377 -0
@@ -0,0 +1,203 @@
1
+ import { html, ReactiveController, ReactiveControllerHost, TemplateResult } from 'lit';
2
+ import { classMap } from 'lit/directives/class-map.js';
3
+ import { ifDefined } from 'lit/directives/if-defined.js';
4
+ import './formdata-event-polyfill';
5
+
6
+ export interface FormSubmitControllerOptions {
7
+ /** A function that returns the form containing the form control. */
8
+ form: (input: unknown) => HTMLFormElement;
9
+ /** A function that returns the form control's name, which will be submitted with the form data. */
10
+ name: (input: unknown) => string;
11
+ /** A function that returns the form control's current value. */
12
+ value: (input: unknown) => any;
13
+ /** A function that returns the form control's current disabled state. If disabled, the value won't be submitted. */
14
+ disabled: (input: unknown) => boolean;
15
+ /**
16
+ * A function that maps to the form control's reportValidity() function. When the control is invalid, this will
17
+ * prevent submission and trigger the browser's constraint violation warning.
18
+ */
19
+ reportValidity: (input: unknown) => boolean;
20
+ }
21
+
22
+ export class FormSubmitController implements ReactiveController {
23
+ host?: ReactiveControllerHost & Element;
24
+ form?: HTMLFormElement;
25
+ options?: FormSubmitControllerOptions;
26
+
27
+ constructor(host: ReactiveControllerHost & Element, options?: FormSubmitControllerOptions) {
28
+ (this.host = host).addController(this);
29
+ this.options = Object.assign(
30
+ {
31
+ form: (input: HTMLInputElement) => input.closest('form'),
32
+ name: (input: HTMLInputElement) => input.name,
33
+ value: (input: HTMLInputElement) => input.value,
34
+ disabled: (input: HTMLInputElement) => input.disabled,
35
+ reportValidity: (input: HTMLInputElement) => {
36
+ return typeof input.reportValidity === 'function' ? input.reportValidity() : true;
37
+ }
38
+ },
39
+ options
40
+ );
41
+ this.handleFormData = this.handleFormData.bind(this);
42
+ this.handleFormSubmit = this.handleFormSubmit.bind(this);
43
+ }
44
+
45
+ hostConnected() {
46
+ this.form = this.options?.form(this.host);
47
+
48
+ if (this.form) {
49
+ this.form.addEventListener('formdata', this.handleFormData);
50
+ this.form.addEventListener('submit', this.handleFormSubmit);
51
+ }
52
+ }
53
+
54
+ hostDisconnected() {
55
+ if (this.form) {
56
+ this.form.removeEventListener('formdata', this.handleFormData);
57
+ this.form.removeEventListener('submit', this.handleFormSubmit);
58
+ this.form = undefined;
59
+ }
60
+ }
61
+
62
+ handleFormData(event: FormDataEvent) {
63
+ const disabled = this.options?.disabled(this.host);
64
+ const name = this.options?.name(this.host);
65
+ const value = this.options?.value(this.host);
66
+
67
+ if (!disabled && name && value !== undefined) {
68
+ if (Array.isArray(value)) {
69
+ value.map(val => event.formData.append(name, val));
70
+ } else {
71
+ event.formData.append(name, value);
72
+ }
73
+ }
74
+ }
75
+
76
+ handleFormSubmit(event: Event) {
77
+ const form = this.form;
78
+ const disabled = this.options?.disabled(this.host);
79
+ const reportValidity = this.options?.reportValidity;
80
+
81
+ if (form && !form.noValidate && !disabled && reportValidity && !reportValidity(this.host)) {
82
+ event.preventDefault();
83
+ event.stopImmediatePropagation();
84
+ }
85
+ }
86
+
87
+ submit() {
88
+ // Calling form.submit() seems to bypass the submit event and constraint validation. Instead, we can inject a
89
+ // native submit button into the form, click it, then remove it to simulate a standard form submission.
90
+ const button = document.createElement('button');
91
+ if (this.form) {
92
+ button.type = 'submit';
93
+ button.style.position = 'absolute';
94
+ button.style.width = '0';
95
+ button.style.height = '0';
96
+ button.style.clip = 'rect(0 0 0 0)';
97
+ button.style.clipPath = 'inset(50%)';
98
+ button.style.overflow = 'hidden';
99
+ button.style.whiteSpace = 'nowrap';
100
+ this.form.append(button);
101
+ button.click();
102
+ button.remove();
103
+ }
104
+ }
105
+ }
106
+
107
+ export const renderFormControl = (
108
+ props: {
109
+ /** The input id, used to map the input to the label */
110
+ inputId: string;
111
+
112
+ /** The size of the form control */
113
+ size: 'small' | 'medium' | 'large';
114
+
115
+ /** The label id, used to map the label to the input */
116
+ labelId?: string;
117
+
118
+ /** The label text (if the label slot isn't used) */
119
+ label?: string;
120
+
121
+ /** Whether or not a label slot has been provided. */
122
+ hasLabelSlot?: boolean;
123
+
124
+ /** The help text id, used to map the input to the help text */
125
+ helpTextId?: string;
126
+
127
+ /** The help text (if the help-text slot isn't used) */
128
+ helpText?: string;
129
+
130
+ /** Whether or not a help text slot has been provided. */
131
+ hasHelpTextSlot?: boolean;
132
+
133
+ /** A function that gets called when the label is clicked. */
134
+ onLabelClick?: (event: MouseEvent) => void;
135
+ },
136
+ input: TemplateResult
137
+ ) => {
138
+ const hasLabel = props.label ? true : !!props.hasLabelSlot;
139
+ const hasHelpText = props.helpText ? true : !!props.hasHelpTextSlot;
140
+
141
+ return html`
142
+ <div
143
+ part="form-control"
144
+ class=${classMap({
145
+ 'form-control': true,
146
+ 'form-control--small': props.size === 'small',
147
+ 'form-control--medium': props.size === 'medium',
148
+ 'form-control--large': props.size === 'large',
149
+ 'form-control--has-label': hasLabel,
150
+ 'form-control--has-help-text': hasHelpText
151
+ })}
152
+ >
153
+ <label
154
+ part="label"
155
+ id=${ifDefined(props.labelId)}
156
+ class="form-control__label"
157
+ for=${props.inputId}
158
+ aria-hidden=${hasLabel ? 'false' : 'true'}
159
+ @click=${(event: MouseEvent) => (props.onLabelClick ? props.onLabelClick(event) : null)}
160
+ >
161
+ <slot name="label">${props.label}</slot>
162
+ </label>
163
+
164
+ <div class="form-control__input">${html`${input}`}</div>
165
+
166
+ <div
167
+ part="help-text"
168
+ id=${ifDefined(props.helpTextId)}
169
+ class="form-control__help-text"
170
+ aria-hidden=${hasHelpText ? 'false' : 'true'}
171
+ >
172
+ <slot name="help-text">${props.helpText}</slot>
173
+ </div>
174
+ </div>
175
+ `;
176
+ };
177
+
178
+ export function getLabelledBy(props: {
179
+ /** The label id, used to map the label to the input */
180
+ labelId: string;
181
+
182
+ /** The label text (if the label slot isn't used) */
183
+ label: string;
184
+
185
+ /** Whether or not a label slot has been provided. */
186
+ hasLabelSlot: boolean;
187
+
188
+ /** The help text id, used to map the input to the help text */
189
+ helpTextId: string;
190
+
191
+ /** The help text (if the help-text slot isn't used) */
192
+ helpText: string;
193
+
194
+ /** Whether or not a help text slot has been provided. */
195
+ hasHelpTextSlot: boolean;
196
+ }) {
197
+ const labelledBy = [
198
+ props.label || props.hasLabelSlot ? props.labelId : '',
199
+ props.helpText || props.hasHelpTextSlot ? props.helpTextId : ''
200
+ ].filter(val => val);
201
+
202
+ return labelledBy.join(' ') || undefined;
203
+ }
@@ -0,0 +1,93 @@
1
+ //
2
+ // Polyfills the formdata event in unsupportive browsers. This is a partial polyfill to support appending custom element
3
+ // form data on submit. The formdata event landed in Safari until 15.1, which is slighly too new to rely on. All other
4
+ // browsers have great support.
5
+ //
6
+ // https://caniuse.com/mdn-api_htmlformelement_formdata_event
7
+ //
8
+ // Original code derived from: https://gist.github.com/WickyNilliams/eb6a44075356ee504dd9491c5a3ab0be
9
+ //
10
+ // Copyright (c) 2021 Nick Williams (https://wicky.nillia.ms)
11
+ //
12
+ // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13
+ // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15
+ //
16
+ class FormDataEventPolyfill extends Event {
17
+ formData: FormData;
18
+
19
+ constructor(formData: FormData) {
20
+ super('formdata');
21
+ this.formData = formData;
22
+ }
23
+ }
24
+
25
+ class FormDataPolyfill extends FormData {
26
+ private form: HTMLFormElement;
27
+
28
+ constructor(form: HTMLFormElement) {
29
+ super(form);
30
+ this.form = form;
31
+ form.dispatchEvent(new FormDataEventPolyfill(this));
32
+ }
33
+
34
+ append(name: string, value: any) {
35
+ let input = this.form.elements[name as any] as HTMLInputElement;
36
+
37
+ if (!input) {
38
+ input = document.createElement('input');
39
+ input.type = 'hidden';
40
+ input.name = name;
41
+ this.form.appendChild(input);
42
+ }
43
+
44
+ if (this.has(name)) {
45
+ const entries = this.getAll(name);
46
+ const index = entries.indexOf(input.value);
47
+
48
+ if (index !== -1) {
49
+ entries.splice(index, 1);
50
+ }
51
+
52
+ entries.push(value);
53
+ this.set(name, entries as any);
54
+ } else {
55
+ super.append(name, value);
56
+ }
57
+
58
+ input.value = value;
59
+ }
60
+ }
61
+
62
+ function supportsFormDataEvent() {
63
+ const form = document.createElement('form');
64
+ let isSupported = false;
65
+
66
+ document.body.append(form);
67
+
68
+ form.addEventListener('submit', event => {
69
+ new FormData(event.target as HTMLFormElement);
70
+ event.preventDefault();
71
+ });
72
+
73
+ form.addEventListener('formdata', () => (isSupported = true));
74
+ form.dispatchEvent(new Event('submit', { cancelable: true }));
75
+ form.remove();
76
+
77
+ return isSupported;
78
+ }
79
+
80
+ function polyfillFormData() {
81
+ if (!window.FormData || supportsFormDataEvent()) {
82
+ return;
83
+ }
84
+
85
+ window.FormData = FormDataPolyfill;
86
+ window.addEventListener('submit', event => {
87
+ if (!event.defaultPrevented) {
88
+ new FormData(event.target as HTMLFormElement);
89
+ }
90
+ });
91
+ }
92
+
93
+ polyfillFormData();
@@ -0,0 +1,8 @@
1
+ //
2
+ // Ensures a number stays within a minimum and maximum value
3
+ //
4
+ export function clamp(value: number, min: number, max: number) {
5
+ if (value < min) return min;
6
+ if (value > max) return max;
7
+ return value;
8
+ }
@@ -0,0 +1,53 @@
1
+ import { getTabbableBoundary } from '../internal/tabbable';
2
+
3
+ let activeModals: HTMLElement[] = [];
4
+
5
+ export default class Modal {
6
+ element: HTMLElement;
7
+ tabDirection: 'forward' | 'backward' = 'forward';
8
+
9
+ constructor(element: HTMLElement) {
10
+ this.element = element;
11
+ this.handleFocusIn = this.handleFocusIn.bind(this);
12
+ this.handleKeyDown = this.handleKeyDown.bind(this);
13
+ }
14
+
15
+ activate() {
16
+ activeModals.push(this.element);
17
+ document.addEventListener('focusin', this.handleFocusIn);
18
+ document.addEventListener('keydown', this.handleKeyDown);
19
+ }
20
+
21
+ deactivate() {
22
+ activeModals = activeModals.filter(modal => modal !== this.element);
23
+ document.removeEventListener('focusin', this.handleFocusIn);
24
+ document.removeEventListener('keydown', this.handleKeyDown);
25
+ }
26
+
27
+ isActive() {
28
+ // The "active" modal is always the most recent one shown
29
+ return activeModals[activeModals.length - 1] === this.element;
30
+ }
31
+
32
+ handleFocusIn(event: Event) {
33
+ const path = event.composedPath();
34
+
35
+ // Trap focus so it doesn't go out of the modal's boundary
36
+ if (this.isActive() && !path.includes(this.element)) {
37
+ const { start, end } = getTabbableBoundary(this.element);
38
+ const target = this.tabDirection === 'forward' ? start : end;
39
+
40
+ if (typeof target?.focus === 'function') {
41
+ target.focus({ preventScroll: true });
42
+ }
43
+ }
44
+ }
45
+
46
+ handleKeyDown(event: KeyboardEvent) {
47
+ // Quick hack to determine tab direction
48
+ if (event.key === 'Tab' && event.shiftKey) {
49
+ this.tabDirection = 'backward';
50
+ setTimeout(() => (this.tabDirection = 'forward'));
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,32 @@
1
+ //
2
+ // Formats a number to a human-readable string of bytes or bits such as "100 MB"
3
+ //
4
+ export function formatBytes(bytes: number, options: FormatBytesOptions) {
5
+ options = Object.assign(
6
+ {
7
+ unit: 'bytes',
8
+ formatter: (number: number) => number.toLocaleString()
9
+ },
10
+ options
11
+ );
12
+
13
+ const byteUnits = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
14
+ const bitUnits = ['b', 'kbit', 'Mbit', 'Gbit', 'Tbit', 'Pbit', 'Ebit', 'Zbit', 'Ybit']; // cspell:disable-line
15
+ const units = options.unit === 'bytes' ? byteUnits : bitUnits;
16
+ const isNegative = bytes < 0;
17
+
18
+ bytes = Math.abs(bytes);
19
+ if (bytes === 0) return '0 B';
20
+
21
+ const i = Math.min(Math.floor(Math.log10(bytes) / 3), units.length - 1);
22
+ const num = Number((bytes / Math.pow(1000, i)).toPrecision(3));
23
+ const numString = options.formatter ? options.formatter(num) : num;
24
+ const prefix = isNegative ? '-' : '';
25
+
26
+ return `${prefix}${numString} ${units[i]}`;
27
+ }
28
+
29
+ interface FormatBytesOptions {
30
+ unit?: 'bytes' | 'bits';
31
+ formatter?: (number: number) => string;
32
+ }
@@ -0,0 +1,13 @@
1
+ //
2
+ // Returns an element's offset relative to its parent. Similar to element.offsetTop and element.offsetLeft, except the
3
+ // parent doesn't have to be positioned relative or absolute.
4
+ //
5
+ // NOTE: This was created to work around what appears to be a bug in Chrome where a slotted element's offsetParent
6
+ // seems to ignore elements inside the surrounding shadow DOM: https://bugs.chromium.org/p/chromium/issues/detail?id=920069
7
+ //
8
+ export function getOffset(element: HTMLElement, parent: HTMLElement) {
9
+ return {
10
+ top: Math.round(element.getBoundingClientRect().top - parent.getBoundingClientRect().top),
11
+ left: Math.round(element.getBoundingClientRect().left - parent.getBoundingClientRect().left)
12
+ };
13
+ }
@@ -0,0 +1,57 @@
1
+ import { getOffset } from './offset';
2
+
3
+ const locks = new Set();
4
+
5
+ //
6
+ // Prevents body scrolling. Keeps track of which elements requested a lock so multiple levels of locking are possible
7
+ // without premature unlocking.
8
+ //
9
+ export function lockBodyScrolling(lockingEl: HTMLElement) {
10
+ locks.add(lockingEl);
11
+ document.body.classList.add('sl-scroll-lock');
12
+ }
13
+
14
+ //
15
+ // Unlocks body scrolling. Scrolling will only be unlocked once all elements that requested a lock call this method.
16
+ //
17
+ export function unlockBodyScrolling(lockingEl: HTMLElement) {
18
+ locks.delete(lockingEl);
19
+
20
+ if (locks.size === 0) {
21
+ document.body.classList.remove('sl-scroll-lock');
22
+ }
23
+ }
24
+
25
+ //
26
+ // Scrolls an element into view of its container. If the element is already in view, nothing will happen.
27
+ //
28
+ export function scrollIntoView(
29
+ element: HTMLElement,
30
+ container: HTMLElement,
31
+ direction: 'horizontal' | 'vertical' | 'both' = 'vertical',
32
+ behavior: 'smooth' | 'auto' = 'smooth'
33
+ ) {
34
+ const offset = getOffset(element, container);
35
+ const offsetTop = offset.top + container.scrollTop;
36
+ const offsetLeft = offset.left + container.scrollLeft;
37
+ const minX = container.scrollLeft;
38
+ const maxX = container.scrollLeft + container.offsetWidth;
39
+ const minY = container.scrollTop;
40
+ const maxY = container.scrollTop + container.offsetHeight;
41
+
42
+ if (direction === 'horizontal' || direction === 'both') {
43
+ if (offsetLeft < minX) {
44
+ container.scrollTo({ left: offsetLeft, behavior });
45
+ } else if (offsetLeft + element.clientWidth > maxX) {
46
+ container.scrollTo({ left: offsetLeft - container.offsetWidth + element.clientWidth, behavior });
47
+ }
48
+ }
49
+
50
+ if (direction === 'vertical' || direction === 'both') {
51
+ if (offsetTop < minY) {
52
+ container.scrollTo({ top: offsetTop, behavior });
53
+ } else if (offsetTop + element.clientHeight > maxY) {
54
+ container.scrollTo({ top: offsetTop - container.offsetHeight + element.clientHeight, behavior });
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,91 @@
1
+ import { ReactiveController, ReactiveControllerHost } from 'lit';
2
+
3
+ export class HasSlotController implements ReactiveController {
4
+ host: ReactiveControllerHost & Element;
5
+ slotNames: string[] = [];
6
+
7
+ constructor(host: ReactiveControllerHost & Element, ...slotNames: string[]) {
8
+ (this.host = host).addController(this);
9
+ this.slotNames = slotNames;
10
+ this.handleSlotChange = this.handleSlotChange.bind(this);
11
+ }
12
+
13
+ private hasDefaultSlot() {
14
+ return [...this.host.childNodes].some(node => {
15
+ if (node.nodeType === node.TEXT_NODE && node.textContent!.trim() !== '') {
16
+ return true;
17
+ }
18
+
19
+ if (node.nodeType === node.ELEMENT_NODE) {
20
+ const el = node as HTMLElement;
21
+ if (!el.hasAttribute('slot')) {
22
+ return true;
23
+ }
24
+ }
25
+
26
+ return false;
27
+ });
28
+ }
29
+
30
+ private hasNamedSlot(name: string) {
31
+ return this.host.querySelector(`:scope > [slot="${name}"]`) !== null;
32
+ }
33
+
34
+ test(slotName: string) {
35
+ return slotName === '[default]' ? this.hasDefaultSlot() : this.hasNamedSlot(slotName);
36
+ }
37
+
38
+ hostConnected() {
39
+ this.host.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
40
+ }
41
+
42
+ hostDisconnected() {
43
+ this.host.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
44
+ }
45
+
46
+ handleSlotChange(event: Event) {
47
+ const slot = event.target as HTMLSlotElement;
48
+
49
+ if ((this.slotNames.includes('[default]') && !slot.name) || (slot.name && this.slotNames?.includes(slot.name))) {
50
+ this.host.requestUpdate();
51
+ }
52
+ }
53
+ }
54
+
55
+ //
56
+ // Given a slot, this function iterates over all of its assigned element and text nodes and returns the concatenated
57
+ // HTML as a string. This is useful because we can't use slot.innerHTML as an alternative.
58
+ //
59
+ export function getInnerHTML(slot: HTMLSlotElement): string {
60
+ const nodes = slot.assignedNodes({ flatten: true });
61
+ let html = '';
62
+
63
+ [...nodes].map(node => {
64
+ if (node.nodeType === Node.ELEMENT_NODE) {
65
+ html += (node as HTMLElement).outerHTML;
66
+ }
67
+
68
+ if (node.nodeType === Node.TEXT_NODE) {
69
+ html += node.textContent;
70
+ }
71
+ });
72
+
73
+ return html;
74
+ }
75
+
76
+ //
77
+ // Given a slot, this function iterates over all of its assigned text nodes and returns the concatenated text as a
78
+ // string. This is useful because we can't use slot.textContent as an alternative.
79
+ //
80
+ export function getTextContent(slot: HTMLSlotElement): string {
81
+ const nodes = slot ? slot.assignedNodes({ flatten: true }) : [];
82
+ let text = '';
83
+
84
+ [...nodes].map(node => {
85
+ if (node.nodeType === Node.TEXT_NODE) {
86
+ text += node.textContent;
87
+ }
88
+ });
89
+
90
+ return text;
91
+ }
@@ -0,0 +1,3 @@
1
+ export function uppercaseFirstLetter(string: string) {
2
+ return string.charAt(0).toUpperCase() + string.slice(1);
3
+ }
@@ -0,0 +1,15 @@
1
+ //
2
+ // Determines if the browser supports focus({ preventScroll })
3
+ //
4
+ export function isPreventScrollSupported() {
5
+ let supported = false;
6
+
7
+ document.createElement('div').focus({
8
+ get preventScroll() {
9
+ supported = true;
10
+ return false;
11
+ }
12
+ });
13
+
14
+ return supported;
15
+ }
@@ -0,0 +1,81 @@
1
+ // Determines if the specified element is tabbable using heuristics inspired by https://github.com/focus-trap/tabbable
2
+ function isTabbable(el: HTMLElement) {
3
+ const tag = el.tagName.toLowerCase();
4
+
5
+ // Elements with a -1 tab index are not tabbable
6
+ if (el.getAttribute('tabindex') === '-1') {
7
+ return false;
8
+ }
9
+
10
+ // Elements with a disabled attribute are not tabbable
11
+ if (el.hasAttribute('disabled')) {
12
+ return false;
13
+ }
14
+
15
+ // Elements with aria-disabled are not tabbable
16
+ if (el.hasAttribute('aria-disabled') && el.getAttribute('aria-disabled') !== 'false') {
17
+ return false;
18
+ }
19
+
20
+ // Radios without a checked attribute are not tabbable
21
+ if (tag === 'input' && el.getAttribute('type') === 'radio' && !el.hasAttribute('checked')) {
22
+ return false;
23
+ }
24
+
25
+ // Elements that are hidden have no offsetParent and are not tabbable
26
+ if (!el.offsetParent) {
27
+ return false;
28
+ }
29
+
30
+ // Elements without visibility are not tabbable
31
+ if (window.getComputedStyle(el).visibility === 'hidden') {
32
+ return false;
33
+ }
34
+
35
+ // Audio and video elements with the controls attribute are tabbable
36
+ if ((tag === 'audio' || tag === 'video') && el.hasAttribute('controls')) {
37
+ return true;
38
+ }
39
+
40
+ // Elements with a tabindex other than -1 are tabbable
41
+ if (el.hasAttribute('tabindex')) {
42
+ return true;
43
+ }
44
+
45
+ // Elements with a contenteditable attribute are tabbable
46
+ if (el.hasAttribute('contenteditable') && el.getAttribute('contenteditable') !== 'false') {
47
+ return true;
48
+ }
49
+
50
+ // At this point, the following elements are considered tabbable
51
+ return ['button', 'input', 'select', 'textarea', 'a', 'audio', 'video', 'summary'].includes(tag);
52
+ }
53
+
54
+ //
55
+ // Returns the first and last bounding elements that are tabbable. This is more performant than checking every single
56
+ // element because it short-circuits after finding the first and last ones.
57
+ //
58
+ export function getTabbableBoundary(root: HTMLElement | ShadowRoot) {
59
+ const allElements: HTMLElement[] = [];
60
+
61
+ function walk(el: HTMLElement | ShadowRoot) {
62
+ if (el instanceof HTMLElement) {
63
+ allElements.push(el);
64
+
65
+ if (el.shadowRoot && el.shadowRoot.mode === 'open') {
66
+ walk(el.shadowRoot);
67
+ }
68
+ }
69
+
70
+ [...el.querySelectorAll('*')].map((e: HTMLElement) => walk(e));
71
+ }
72
+
73
+ // Collect all elements including the root
74
+ walk(root);
75
+
76
+ // Find the first and last tabbable elements
77
+ const start = allElements.find(el => isTabbable(el)) || null;
78
+ const end = allElements.reverse().find(el => isTabbable(el)) || null;
79
+
80
+ return { start, end };
81
+ }