anchor_view_components 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +61 -0
  5. data/app/assets/builds/anchor-view-components.css +1 -0
  6. data/app/assets/stylesheets/anchor-view-components.tailwind.css +8 -0
  7. data/app/assets/stylesheets/components/button.css +29 -0
  8. data/app/assets/stylesheets/components/toast.css +21 -0
  9. data/app/components/anchor/action_menu_component.html.erb +19 -0
  10. data/app/components/anchor/action_menu_component.rb +23 -0
  11. data/app/components/anchor/action_menu_controller.ts +21 -0
  12. data/app/components/anchor/anchor_view_components.ts +13 -0
  13. data/app/components/anchor/autocomplete_component.en.yml +3 -0
  14. data/app/components/anchor/autocomplete_component.html.erb +36 -0
  15. data/app/components/anchor/autocomplete_component.rb +42 -0
  16. data/app/components/anchor/autocomplete_controller.ts +37 -0
  17. data/app/components/anchor/badge_component.html.erb +8 -0
  18. data/app/components/anchor/badge_component.rb +27 -0
  19. data/app/components/anchor/banner_component.html.erb +9 -0
  20. data/app/components/anchor/banner_component.rb +28 -0
  21. data/app/components/anchor/breadcrumbs/item_component.html.erb +16 -0
  22. data/app/components/anchor/breadcrumbs/item_component.rb +15 -0
  23. data/app/components/anchor/breadcrumbs_component.html.erb +10 -0
  24. data/app/components/anchor/breadcrumbs_component.rb +17 -0
  25. data/app/components/anchor/button_component.html.erb +22 -0
  26. data/app/components/anchor/button_component.rb +76 -0
  27. data/app/components/anchor/component.rb +21 -0
  28. data/app/components/anchor/dialog_component.html.erb +42 -0
  29. data/app/components/anchor/dialog_component.rb +25 -0
  30. data/app/components/anchor/dialog_controller.ts +15 -0
  31. data/app/components/anchor/icon_component.html.erb +6 -0
  32. data/app/components/anchor/icon_component.rb +9 -0
  33. data/app/components/anchor/loading_indicator_component.html.erb +6 -0
  34. data/app/components/anchor/loading_indicator_component.rb +23 -0
  35. data/app/components/anchor/panel/body_component.html.erb +8 -0
  36. data/app/components/anchor/panel/body_component.rb +6 -0
  37. data/app/components/anchor/panel/footer_component.html.erb +9 -0
  38. data/app/components/anchor/panel/footer_component.rb +17 -0
  39. data/app/components/anchor/panel/header_component.html.erb +9 -0
  40. data/app/components/anchor/panel/header_component.rb +15 -0
  41. data/app/components/anchor/panel_component.html.erb +8 -0
  42. data/app/components/anchor/panel_component.rb +20 -0
  43. data/app/components/anchor/prose_component.html.erb +3 -0
  44. data/app/components/anchor/prose_component.rb +9 -0
  45. data/app/components/anchor/tab_bar/tab_component.html.erb +11 -0
  46. data/app/components/anchor/tab_bar/tab_component.rb +12 -0
  47. data/app/components/anchor/tab_bar_component.html.erb +10 -0
  48. data/app/components/anchor/tab_bar_component.rb +17 -0
  49. data/app/components/anchor/text_component.html.erb +8 -0
  50. data/app/components/anchor/text_component.rb +43 -0
  51. data/app/components/anchor/toast_component.html.erb +28 -0
  52. data/app/components/anchor/toast_component.rb +32 -0
  53. data/app/components/anchor/toast_controller.ts +21 -0
  54. data/app/helpers/anchor/fetch_or_fallback_helper.rb +20 -0
  55. data/app/helpers/anchor/view_helper.rb +35 -0
  56. data/lib/anchor/view_components/engine.rb +23 -0
  57. data/lib/anchor/view_components/version.rb +5 -0
  58. data/lib/anchor/view_components.rb +8 -0
  59. data/previews/anchor/action_menu_component_preview.rb +22 -0
  60. data/previews/anchor/badge_component_preview.rb +40 -0
  61. data/previews/anchor/banner_component_preview.rb +41 -0
  62. data/previews/anchor/banner_preview/with_banner.html.erb +11 -0
  63. data/previews/anchor/breadcrumbs_component_preview.rb +15 -0
  64. data/previews/anchor/button_component_preview.rb +81 -0
  65. data/previews/anchor/dialog_component_preview.rb +19 -0
  66. data/previews/anchor/dialog_preview/with_footer.html.erb +16 -0
  67. data/previews/anchor/icon_component_preview.rb +7 -0
  68. data/previews/anchor/loading_indicator_component_preview.rb +26 -0
  69. data/previews/anchor/panel_component_preview.rb +48 -0
  70. data/previews/anchor/tab_bar_component_preview.rb +15 -0
  71. data/previews/anchor/table_preview/default.html.erb +35 -0
  72. data/previews/anchor/text_component_preview.rb +76 -0
  73. data/previews/anchor/toast_component_preview.rb +34 -0
  74. metadata +156 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4a1787b8586a125669bb568cb6b27c5220354508e979264e44937291012380c7
4
+ data.tar.gz: f324e897a30739899f50a581c0242d312394ee79007de490d0ceb57616d003bb
5
+ SHA512:
6
+ metadata.gz: d8752971ee67126aa612523a7ff44257a007c732df8d13361ee3fc701a551b451b4e2130b82eaba165b7663b862c3a22cd60239c5250c68dadbf9d5aad2fa5de
7
+ data.tar.gz: ce47a488e3b0207870ca973717bbf5be2dfd2555da5d822ec2363438c69ea7f9b4e5d42d1654ee846ad1bc84d5a4f96dd810581864414335cb25fcc3d14f05dc
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2023-07-27
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [year] [fullname]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Anchor ViewComponents
2
+
3
+ [ViewComponents] for Buoy’s Anchor Design System.
4
+
5
+ [ViewComponents]: https://github.com/github/view_component
6
+
7
+ ## Dependencies
8
+
9
+ - [Stimulus]
10
+
11
+ [Stimulus]: https://stimulus.hotwired.dev
12
+
13
+ ## Installation
14
+
15
+ 1. Add `anchor_view_components` to your Gemfile and install:
16
+
17
+ ```bash
18
+ bundle add anchor_view_components && bundle install
19
+ ```
20
+
21
+ 2. Add `@buoysoftware/anchor-view-components` to your package.json and install:
22
+
23
+ ```bash
24
+ yarn add @buoysoftware/anchor-view-components && yarn
25
+ ```
26
+
27
+ 3. In `config/application.rb`, add the following statement:
28
+
29
+ ```rb
30
+ require "anchor/view_components/engine"
31
+ ```
32
+
33
+ 4. Add Anchor’s CSS to your application layout:
34
+
35
+ ```erb
36
+ <%= stylesheet_link_tag "anchor-view-components" %>
37
+ ```
38
+
39
+ 5. Import Anchor’s Stimulus controllers and register them:
40
+
41
+ ```js
42
+ import { registerAnchorControllers } from "@buoysoftware/anchor-view-components";
43
+
44
+ registerAnchorControllers(application);
45
+ ```
46
+
47
+ 6. Optionally, if you need Anchor’s Tailwind configuration to use its colors,
48
+ fonts, and other design system values, add the following to your project’s
49
+ `tailwind.config.js`:
50
+
51
+ ```js
52
+ presets: [require("@buoysoftware/anchor-view-components/tailwind.config.js")]
53
+ ```
54
+
55
+ ## Development
56
+
57
+ TODO
58
+
59
+ ## License
60
+
61
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1 @@
1
+ /*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[popover]{position:fixed;z-index:2147483647;inset:0;padding:.25em;width:-moz-fit-content;width:fit-content;height:-moz-fit-content;height:fit-content;border:initial solid;border-color:initial;-o-border-image:initial;border-image:initial;background-color:canvas;color:initial;overflow:auto;margin:auto}[popover]:not(.\:popover-open){display:none}[popover]:is(dialog[open]){display:revert}@supports not (background-color:canvas){[popover]{background-color:#fff;color:#000}}@supports (width:-moz-fit-content){[popover]{width:-moz-fit-content;height:-moz-fit-content}}@supports not (inset:0){[popover]{top:0;left:0;right:0;bottom:0}}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.prose{color:#101112;max-width:65ch}.prose :where(p):not(:where([class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-left:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-left:1.625em}.prose :where(ol>li):not(:where([class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(hr):not(:where([class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-left-width:.25rem;border-left-color:var(--tw-prose-quote-borders);quotes:"\201C""\201D""\2018""\2019";margin-top:1.6em;margin-bottom:1.6em;padding-left:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(figure>*):not(:where([class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose :where(code):not(:where([class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding:.8571429em 1.1428571em}.prose :where(pre code):not(:where([class~=not-prose] *)){background-color:initial;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose] *)){width:100%;table-layout:auto;text-align:left;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-right:.5714286em;padding-bottom:.5714286em;padding-left:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose] *)){vertical-align:initial}.prose :where(tfoot):not(:where([class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose] *)){vertical-align:top}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:.875rem;line-height:1rem}.prose :where(video):not(:where([class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(figure):not(:where([class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(li):not(:where([class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose] *)){padding-left:.375em}.prose :where(ul>li):not(:where([class~=not-prose] *)){padding-left:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>:first-child):not(:where([class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>:last-child):not(:where([class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(hr+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose] *)){padding-left:0}.prose :where(thead th:last-child):not(:where([class~=not-prose] *)){padding-right:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose] *)){padding:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose] *)){padding-left:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose] *)){padding-right:0}.prose :where(.prose>:first-child):not(:where([class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose] *)){margin-bottom:0}.btn{display:inline-flex;height:2.5rem;align-items:center;gap:.5rem;border-radius:.25rem;border-width:1px;--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));padding-left:1.25rem;padding-right:1.25rem;font-size:.75rem;line-height:1rem;font-weight:600;text-decoration-line:none}.btn:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.btn-primary{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.btn-primary:hover{border-color:rgb(29 78 216/var(--tw-border-opacity));background-color:rgb(29 78 216/var(--tw-bg-opacity))}.btn-critical,.btn-primary:hover{--tw-border-opacity:1;--tw-bg-opacity:1}.btn-critical{border-color:rgb(191 47 29/var(--tw-border-opacity));background-color:rgb(191 47 29/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.btn-critical:hover{--tw-border-opacity:1;border-color:rgb(148 38 25/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(148 38 25/var(--tw-bg-opacity))}.btn-text{color:rgb(39 110 241/var(--tw-text-opacity))}.btn-text,.btn-text:hover{border-color:#0000;background-color:initial;--tw-text-opacity:1}.btn-text:hover{color:rgb(30 84 183/var(--tw-text-opacity))}.btn-small{height:1.75rem;padding-right:.75rem}.btn-small,.btn-with-starting-icon{padding-left:.75rem}.btn-with-ending-icon{padding-right:.75rem}.toast{align-items:center}.toast>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.toast{border-radius:.25rem;padding:1rem;--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.toast.\:popover-open{position:fixed;top:auto;bottom:2.5rem;margin-left:auto;margin-right:auto;display:flex;overflow:visible;border-width:0}.toast:popover-open{position:fixed;top:auto;bottom:2.5rem;margin-left:auto;margin-right:auto;display:flex;overflow:visible;border-width:0}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.bottom-0{bottom:0}.left-0{left:0}.z-10{z-index:10}.m-0{margin:0}.mx-2{margin-left:.5rem;margin-right:.5rem}.\!ml-10{margin-left:2.5rem!important}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.contents{display:contents}.hidden{display:none}.h-12{height:3rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.max-h-\[200px\]{max-height:200px}.w-12{width:3rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-\[36rem\]{width:36rem}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-4{gap:1rem}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-\[1\.5px\]{border-width:1.5px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-grey-100{--tw-border-opacity:1;border-color:rgb(16 17 18/var(--tw-border-opacity))}.border-subdued{--tw-border-opacity:1;border-color:rgb(186 190 195/var(--tw-border-opacity))}.border-b-transparent{border-bottom-color:#0000}.bg-critical-subdued{--tw-bg-opacity:1;background-color:rgb(255 222 217/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-green-700{--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity))}.bg-informational-subdued{--tw-bg-opacity:1;background-color:rgb(212 226 252/var(--tw-bg-opacity))}.bg-neutral{--tw-bg-opacity:1;background-color:rgb(235 237 240/var(--tw-bg-opacity))}.bg-red-700{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity))}.bg-success-subdued{--tw-bg-opacity:1;background-color:rgb(218 240 227/var(--tw-bg-opacity))}.bg-warning-subdued{--tw-bg-opacity:1;background-color:rgb(255 242 217/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-yellow-600{--tw-bg-opacity:1;background-color:rgb(202 138 4/var(--tw-bg-opacity))}.p-0{padding:0}.p-6{padding:1.5rem}.px-0{padding-left:0;padding-right:0}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-1{padding-bottom:.25rem}.pb-6{padding-bottom:1.5rem}.text-center{text-align:center}.text-2xl{font-size:1.5rem;line-height:1.75rem}.text-3xl{font-size:2rem;line-height:2.5rem}.text-4xl{font-size:2.5rem;line-height:3rem}.text-base{font-size:.875rem;line-height:1rem}.text-lg{font-size:1rem;line-height:1.25rem}.text-sm{font-size:.75rem;line-height:1rem}.text-xl{font-size:1.25rem;line-height:1.5rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-4{line-height:1rem}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-grey-50{--tw-text-opacity:1;color:rgb(156 161 165/var(--tw-text-opacity))}.text-primary{--tw-text-opacity:1;color:rgb(16 17 18/var(--tw-text-opacity))}.text-tertiary{--tw-text-opacity:1;color:rgb(156 161 165/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.opacity-70{opacity:.7}.shadow-\[inset_0_-1px\]{--tw-shadow:inset 0 -1px;--tw-shadow-colored:inset 0 -1px var(--tw-shadow-color)}.shadow-\[inset_0_-1px\],.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-gray-400{--tw-shadow-color:#9ca3af;--tw-shadow:var(--tw-shadow-colored)}.backdrop\:bg-grey-100\/50::backdrop{background-color:#10111280}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:inset-x-0:after{content:var(--tw-content);left:0;right:0}.after\:-bottom-1:after{content:var(--tw-content);bottom:-.25rem}.after\:block:after{content:var(--tw-content);display:block}.after\:h-1:after{content:var(--tw-content);height:.25rem}.after\:bg-blue-500:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.hover\:bg-grey-10:hover{--tw-bg-opacity:1;background-color:rgb(245 246 247/var(--tw-bg-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.hover\:text-grey-70:hover{--tw-text-opacity:1;color:rgb(98 102 106/var(--tw-text-opacity))}.group[aria-expanded][data-empty=true][data-loading=false] .group-aria-\[expanded\]\[data-empty\=\'true\'\]\[data-loading\=\'false\'\]\:block,.group[aria-expanded][data-loading=true] .group-aria-\[expanded\]\[data-loading\=\'true\'\]\:block{display:block}.group[data-empty=true] .group-data-\[empty\=\'true\'\]\:hidden,.group[data-loading=true] .group-data-\[loading\=\'true\'\]\:hidden{display:none}.popover-open\:block:popover-open{display:block}.\[\&\>\[aria-selected\=\'true\'\]\]\:bg-blue-20>[aria-selected=true]{--tw-bg-opacity:1;background-color:rgb(212 226 252/var(--tw-bg-opacity))}.\[\&\>li\]\:cursor-pointer>li{cursor:pointer}.\[\&\>li\]\:px-3>li{padding-left:.75rem;padding-right:.75rem}.\[\&\>li\]\:py-2>li{padding-top:.5rem;padding-bottom:.5rem}
@@ -0,0 +1,8 @@
1
+ @import "tailwindcss/base";
2
+ @import "tailwindcss/components";
3
+ @import "tailwindcss/utilities";
4
+
5
+ @import "@oddbird/popover-polyfill/dist/popover" layer(base);
6
+
7
+ @import "components/button" layer(components);
8
+ @import "components/toast" layer(components);
@@ -0,0 +1,29 @@
1
+ @layer components {
2
+ .btn {
3
+ @apply inline-flex items-center gap-2 rounded bg-white hover:bg-gray-100 border h-10 px-5 text-sm font-semibold no-underline;
4
+ }
5
+
6
+ .btn-primary {
7
+ @apply bg-blue-600 border-blue-600 hover:bg-blue-700 hover:border-blue-700 text-white;
8
+ }
9
+
10
+ .btn-critical {
11
+ @apply bg-red-60 border-red-60 hover:bg-red-70 hover:border-red-70 text-white;
12
+ }
13
+
14
+ .btn-text {
15
+ @apply bg-transparent border-transparent hover:bg-transparent hover:border-transparent text-blue-50 hover:text-blue-60;
16
+ }
17
+
18
+ .btn-small {
19
+ @apply h-7 px-3;
20
+ }
21
+
22
+ .btn-with-starting-icon {
23
+ @apply pl-3;
24
+ }
25
+
26
+ .btn-with-ending-icon {
27
+ @apply pr-3;
28
+ }
29
+ }
@@ -0,0 +1,21 @@
1
+ @layer components {
2
+ .toast {
3
+ @apply p-4 rounded items-center space-x-2 shadow-lg;
4
+ }
5
+
6
+ /*
7
+ Styling for polyfilled version of `:popover-open` pseudo-class
8
+ https://github.com/oddbird/popover-polyfill
9
+ */
10
+ .toast.\:popover-open {
11
+ @apply flex fixed top-auto bottom-10 mx-auto border-0 overflow-visible;
12
+ }
13
+
14
+ /*
15
+ Styling for standards version of `:popover-open` pseudo-class
16
+ https://developer.mozilla.org/en-US/docs/Web/CSS/:popover-open
17
+ */
18
+ .toast:popover-open {
19
+ @apply flex fixed top-auto bottom-10 mx-auto border-0 overflow-visible;
20
+ }
21
+ }
@@ -0,0 +1,19 @@
1
+ <%= tag.div(
2
+ class: "contents",
3
+ data: { controller: "action-menu" },
4
+ ) do %>
5
+ <%= show_button if show_button? %>
6
+
7
+ <%= tag.div(
8
+ class: "absolute left-0 w-max m-0 mt-1 px-0 py-1 bg-white rounded border border-subdued popover-open:block",
9
+ data: { action: "toggle->action-menu#positionPopover" },
10
+ id: @menu_id,
11
+ popover: "auto",
12
+ ) do %>
13
+ <ul>
14
+ <% items.each do |item| %>
15
+ <%= tag.li item, class: "px-3 py-1 hover:bg-grey-10" %>
16
+ <% end %>
17
+ </ul>
18
+ <% end %>
19
+ <% end %>
@@ -0,0 +1,23 @@
1
+ module Anchor
2
+ class ActionMenuComponent < Component
3
+ renders_one :show_button, -> do
4
+ ButtonComponent.new(
5
+ data: { action_menu_target: "button" },
6
+ popovertarget: @menu_id
7
+ )
8
+ end
9
+ renders_many :items
10
+
11
+ def initialize
12
+ @menu_id = self.class.generate_id
13
+
14
+ super
15
+ end
16
+
17
+ private
18
+
19
+ def render?
20
+ items.present?
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import { computePosition } from "@floating-ui/dom";
3
+
4
+ export default class extends Controller<HTMLDivElement> {
5
+ static targets = ["button"];
6
+
7
+ declare readonly buttonTarget: HTMLButtonElement;
8
+
9
+ positionPopover = (event: { target: HTMLDivElement }): void => {
10
+ const popover = event.target;
11
+
12
+ computePosition(this.buttonTarget, popover, {
13
+ placement: "bottom-start",
14
+ }).then(({ x, y }) => {
15
+ Object.assign(popover.style, {
16
+ left: `${x}px`,
17
+ top: `${y + 4}px`,
18
+ });
19
+ });
20
+ };
21
+ }
@@ -0,0 +1,13 @@
1
+ import "@oddbird/popover-polyfill";
2
+
3
+ import ActionMenuController from "./action_menu_controller";
4
+ import AutocompleteController from "./autocomplete_controller";
5
+ import DialogController from "./dialog_controller";
6
+ import ToastController from "./toast_controller";
7
+
8
+ export function registerAnchorControllers(application) {
9
+ application.register("action-menu", ActionMenuController);
10
+ application.register("autocomplete", AutocompleteController);
11
+ application.register("dialog", DialogController);
12
+ application.register("toast", ToastController);
13
+ }
@@ -0,0 +1,3 @@
1
+ en:
2
+ no_options: "No options"
3
+ loading: "Loading..."
@@ -0,0 +1,36 @@
1
+ <div
2
+ data-controller="autocomplete"
3
+ data-autocomplete-url-value="<%= src %>"
4
+ role="combobox"
5
+ class="group relative"
6
+ >
7
+ <%= form_builder.input(
8
+ "#{name}_search".to_sym,
9
+ input_html: {
10
+ data: {
11
+ autocomplete_target: "input",
12
+ autocomplete_min_length_value: 0,
13
+ action: "click->autocomplete#onInputClick",
14
+ }.merge(input_data),
15
+ readonly:,
16
+ }
17
+ ) %>
18
+ <div class="hidden">
19
+ <%= form_builder.input(
20
+ "#{name}_id".to_sym,
21
+ input_html: {
22
+ data: { autocomplete_target: "hidden" }
23
+ }) %>
24
+ </div>
25
+ <ul
26
+ data-autocomplete-target="results"
27
+ class="<%= list_box_classes %> group-data-[empty='true']:hidden group-data-[loading='true']:hidden [&>li]:py-2 [&>li]:px-3 [&>li]:cursor-pointer [&>[aria-selected='true']]:bg-blue-20"
28
+ >
29
+ </ul>
30
+ <div class="<%= list_box_classes %> text-center text-gray-500 hidden group-aria-[expanded][data-empty='true'][data-loading='false']:block">
31
+ <%= t(".no_options") %>
32
+ </div>
33
+ <div class="<%= list_box_classes %> text-center text-gray-500 hidden group-aria-[expanded][data-loading='true']:block">
34
+ <%= t(".loading") %>
35
+ </div>
36
+ </div>
@@ -0,0 +1,42 @@
1
+ module Anchor
2
+ class AutocompleteComponent < Component
3
+ LIST_BOX_CLASSES = %w(
4
+ absolute
5
+ bg-white
6
+ border
7
+ border-gray-300
8
+ max-h-[200px]
9
+ mt-2
10
+ overflow-y-auto
11
+ py-2
12
+ rounded-md
13
+ shadow-lg
14
+ w-full
15
+ z-10
16
+ ).join(" ").freeze
17
+
18
+ def initialize(
19
+ form_builder:,
20
+ name:,
21
+ src:,
22
+ readonly: false,
23
+ input_data: {}
24
+ )
25
+ @form_builder = form_builder
26
+ @name = name
27
+ @readonly = readonly
28
+ @input_data = input_data
29
+ @src = src
30
+
31
+ super
32
+ end
33
+
34
+ def list_box_classes
35
+ LIST_BOX_CLASSES
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :form_builder, :name, :readonly, :input_data, :src
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ import type { ControllerConstructor } from "@hotwired/stimulus"
2
+ import BaseAutocompleteController from "stimulus-autocomplete"
3
+
4
+ class AutocompleteController extends BaseAutocompleteController {
5
+ connect(): void {
6
+ super.connect()
7
+ this.element.addEventListener("loadstart", this.onLoadStart)
8
+ this.element.addEventListener("loadend", this.onLoadEnd)
9
+ }
10
+
11
+ disconnect(): void {
12
+ super.disconnect()
13
+ this.element.removeEventListener("loadstart", this.onLoadStart)
14
+ this.element.removeEventListener("loadend", this.onLoadEnd)
15
+ }
16
+
17
+ onInputClick = (): void => {
18
+ const query = this.inputTarget.value.trim()
19
+
20
+ if (query.length >= this.minLengthValue) {
21
+ this.fetchResults(query)
22
+ } else {
23
+ this.hideAndRemoveOptions()
24
+ }
25
+ }
26
+
27
+ onLoadStart = (): void => {
28
+ this.element.dataset.loading = true
29
+ }
30
+
31
+ onLoadEnd = (): void => {
32
+ this.element.dataset.loading = false
33
+ this.element.dataset.empty = this.resultsTarget.childElementCount === 0
34
+ }
35
+ }
36
+
37
+ export default AutocompleteController as unknown as ControllerConstructor
@@ -0,0 +1,8 @@
1
+ <%= tag.span(
2
+ class: class_names(
3
+ "inline-block px-2 py-0.5 rounded-full text-xs",
4
+ @variant,
5
+ ),
6
+ ) do %>
7
+ <%= content %>
8
+ <% end %>
@@ -0,0 +1,27 @@
1
+ module Anchor
2
+ class BadgeComponent < Component
3
+ VARIANT_DEFAULT = :neutral
4
+ VARIANT_MAPPINGS = {
5
+ VARIANT_DEFAULT => "bg-neutral text-base",
6
+ :informational => "bg-informational-subdued text-base",
7
+ :success => "bg-success-subdued text-base",
8
+ :critical => "bg-critical-subdued text-base",
9
+ :warning => "bg-warning-subdued text-base",
10
+ }.freeze
11
+ VARIANT_OPTIONS = VARIANT_MAPPINGS.keys
12
+
13
+ def initialize(variant: VARIANT_DEFAULT)
14
+ @variant = VARIANT_MAPPINGS[
15
+ fetch_or_fallback(VARIANT_OPTIONS, variant, VARIANT_DEFAULT)
16
+ ]
17
+
18
+ super
19
+ end
20
+
21
+ private
22
+
23
+ def render?
24
+ content.present?
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ <%= tag.div(
2
+ class: class_names(
3
+ "px-4 py-3 rounded",
4
+ @variant,
5
+ @classes,
6
+ ),
7
+ ) do %>
8
+ <%= content %>
9
+ <% end %>
@@ -0,0 +1,28 @@
1
+ module Anchor
2
+ class BannerComponent < Component
3
+ VARIANT_DEFAULT = :neutral
4
+ VARIANT_MAPPINGS = {
5
+ VARIANT_DEFAULT => "bg-neutral",
6
+ :informational => "bg-informational-subdued",
7
+ :success => "bg-success-subdued",
8
+ :critical => "bg-critical-subdued",
9
+ :warning => "bg-warning-subdued",
10
+ }.freeze
11
+ VARIANT_OPTIONS = VARIANT_MAPPINGS.keys
12
+
13
+ def initialize(variant: VARIANT_DEFAULT, classes: nil)
14
+ @variant = VARIANT_MAPPINGS[
15
+ fetch_or_fallback(VARIANT_OPTIONS, variant, VARIANT_DEFAULT)
16
+ ]
17
+ @classes = classes
18
+
19
+ super
20
+ end
21
+
22
+ private
23
+
24
+ def render?
25
+ content.present?
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ <%= tag.li(
2
+ aria: {
3
+ current: { page: @active }
4
+ },
5
+ ) do %>
6
+ <%= link_to(
7
+ @href,
8
+ class: "flex"
9
+ ) do %>
10
+ <% if show_separator %>
11
+ <span aria-hidden="true" class="mx-2 text-tertiary">/</span>
12
+ <% end %>
13
+
14
+ <%= content %>
15
+ <% end %>
16
+ <% end %>
@@ -0,0 +1,15 @@
1
+ module Anchor
2
+ module Breadcrumbs
3
+ class ItemComponent < Component
4
+ attr_accessor :active, :show_separator
5
+
6
+ def initialize(href:, active: false, show_separator: true)
7
+ @href = href
8
+ @active = active
9
+ @show_separator = show_separator
10
+
11
+ super
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ <%= tag.nav aria: { label: @label } do %>
2
+ <ol class="flex items-center">
3
+ <% items.first.show_separator = false %>
4
+ <% items.last.active = true %>
5
+
6
+ <% items.each do |item| %>
7
+ <%= item %>
8
+ <% end %>
9
+ </ol>
10
+ <% end %>
@@ -0,0 +1,17 @@
1
+ module Anchor
2
+ class BreadcrumbsComponent < Component
3
+ renders_many :items, Breadcrumbs::ItemComponent
4
+
5
+ def initialize(label: "Breadcrumb")
6
+ @label = label
7
+
8
+ super
9
+ end
10
+
11
+ private
12
+
13
+ def render?
14
+ items.present?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ <%= content_tag(
2
+ @tag,
3
+ type: @type,
4
+ href: @href,
5
+ data: @data,
6
+ form: @form,
7
+ popovertarget: @popovertarget,
8
+ popovertargetaction: @popovertargetaction,
9
+ class: class_names(
10
+ @size,
11
+ @variant,
12
+ @classes,
13
+ "btn-with-starting-icon" => starting_icon?,
14
+ "btn-with-ending-icon" => ending_icon?,
15
+ ),
16
+ ) do %>
17
+ <%= starting_icon if starting_icon? %>
18
+
19
+ <%= content %>
20
+
21
+ <%= ending_icon if ending_icon? %>
22
+ <% end %>
@@ -0,0 +1,76 @@
1
+ module Anchor
2
+ class ButtonComponent < Component
3
+ TAG_DEFAULT = :button
4
+ TAG_OPTIONS = [TAG_DEFAULT, :a].freeze
5
+
6
+ TYPE_DEFAULT = :button
7
+ TYPE_OPTIONS = [TYPE_DEFAULT, :reset, :submit].freeze
8
+
9
+ VARIANT_DEFAULT = :basic
10
+ VARIANT_MAPPINGS = {
11
+ VARIANT_DEFAULT => "btn",
12
+ :primary => "btn btn-primary",
13
+ :critical => "btn btn-critical",
14
+ :text => "btn btn-text",
15
+ }.freeze
16
+ VARIANT_OPTIONS = VARIANT_MAPPINGS.keys
17
+
18
+ SIZE_DEFAULT = nil
19
+ SIZE_MAPPINGS = {
20
+ small: "btn-small",
21
+ }.freeze
22
+ SIZE_OPTIONS = SIZE_MAPPINGS.keys << SIZE_DEFAULT
23
+
24
+ renders_one :starting_icon, ->(icon:) do
25
+ Anchor::IconComponent.new(
26
+ icon:,
27
+ classes: "w-5 h-5",
28
+ data: { testid: "button-starting-icon" }
29
+ )
30
+ end
31
+ renders_one :ending_icon, ->(icon:) do
32
+ Anchor::IconComponent.new(
33
+ icon:,
34
+ classes: "w-5 h-5",
35
+ data: { testid: "button-ending-icon" }
36
+ )
37
+ end
38
+
39
+ def initialize(
40
+ tag: TAG_DEFAULT,
41
+ type: TYPE_DEFAULT,
42
+ href: nil,
43
+ size: SIZE_DEFAULT,
44
+ variant: VARIANT_DEFAULT,
45
+ data: {},
46
+ form: nil,
47
+ popovertarget: nil,
48
+ popovertargetaction: nil,
49
+ **kwargs
50
+ )
51
+ @tag = fetch_or_fallback(TAG_OPTIONS, tag, TAG_DEFAULT)
52
+ if @tag == :button
53
+ @type = fetch_or_fallback(
54
+ TYPE_OPTIONS, type, TYPE_DEFAULT
55
+ )
56
+ end
57
+ @data = data
58
+ @href = href if @tag == :a
59
+ @size = SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, SIZE_DEFAULT)]
60
+ @variant = VARIANT_MAPPINGS[
61
+ fetch_or_fallback(VARIANT_OPTIONS, variant, VARIANT_DEFAULT)
62
+ ]
63
+ @form = form
64
+ @popovertarget = popovertarget
65
+ @popovertargetaction = popovertargetaction
66
+
67
+ super
68
+ end
69
+
70
+ private
71
+
72
+ def render?
73
+ content.present?
74
+ end
75
+ end
76
+ end