sdr_view_components 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +75 -0
  4. data/Rakefile +15 -0
  5. data/app/assets/stylesheets/styles.scss +118 -0
  6. data/app/components/base_component.rb +31 -0
  7. data/app/components/component_support/button_support.rb +14 -0
  8. data/app/components/component_support/css_classes.rb +16 -0
  9. data/app/components/component_support/file_hierarchy.rb +22 -0
  10. data/app/components/sdr_view_components/elements/alert_component.html.erb +14 -0
  11. data/app/components/sdr_view_components/elements/alert_component.rb +65 -0
  12. data/app/components/sdr_view_components/elements/banner_component.html.erb +20 -0
  13. data/app/components/sdr_view_components/elements/banner_component.rb +41 -0
  14. data/app/components/sdr_view_components/elements/breadcrumb_component.html.erb +7 -0
  15. data/app/components/sdr_view_components/elements/breadcrumb_component.rb +28 -0
  16. data/app/components/sdr_view_components/elements/breadcrumb_nav_component.html.erb +10 -0
  17. data/app/components/sdr_view_components/elements/breadcrumb_nav_component.rb +24 -0
  18. data/app/components/sdr_view_components/elements/button_component.rb +31 -0
  19. data/app/components/sdr_view_components/elements/button_link_component.rb +29 -0
  20. data/app/components/sdr_view_components/elements/navigation/dropdown_menu_component.html.erb +13 -0
  21. data/app/components/sdr_view_components/elements/navigation/dropdown_menu_component.rb +19 -0
  22. data/app/components/sdr_view_components/elements/navigation/nav_item_component.rb +25 -0
  23. data/app/components/sdr_view_components/elements/skip_links_component.html.erb +5 -0
  24. data/app/components/sdr_view_components/elements/skip_links_component.rb +8 -0
  25. data/app/components/sdr_view_components/elements/toast_component.html.erb +40 -0
  26. data/app/components/sdr_view_components/elements/toast_component.rb +35 -0
  27. data/app/components/sdr_view_components/elements/tooltip_component.html.erb +11 -0
  28. data/app/components/sdr_view_components/elements/tooltip_component.rb +39 -0
  29. data/app/components/sdr_view_components/forms/basic_checkbox_component.rb +22 -0
  30. data/app/components/sdr_view_components/forms/button_component.rb +42 -0
  31. data/app/components/sdr_view_components/forms/checkbox_component.html.erb +6 -0
  32. data/app/components/sdr_view_components/forms/checkbox_component.rb +23 -0
  33. data/app/components/sdr_view_components/forms/field_component.html.erb +6 -0
  34. data/app/components/sdr_view_components/forms/field_component.rb +74 -0
  35. data/app/components/sdr_view_components/forms/help_text_component.html.erb +3 -0
  36. data/app/components/sdr_view_components/forms/help_text_component.rb +23 -0
  37. data/app/components/sdr_view_components/forms/invalid_feedback_component.rb +43 -0
  38. data/app/components/sdr_view_components/forms/invalid_feedback_support.rb +21 -0
  39. data/app/components/sdr_view_components/forms/label_component.html.erb +6 -0
  40. data/app/components/sdr_view_components/forms/label_component.rb +33 -0
  41. data/app/components/sdr_view_components/forms/submit_component.html.erb +3 -0
  42. data/app/components/sdr_view_components/forms/submit_component.rb +25 -0
  43. data/app/components/sdr_view_components/forms/toggle_component.html.erb +10 -0
  44. data/app/components/sdr_view_components/forms/toggle_component.rb +24 -0
  45. data/app/components/sdr_view_components/forms/toggle_option_component.html.erb +2 -0
  46. data/app/components/sdr_view_components/forms/toggle_option_component.rb +26 -0
  47. data/app/components/sdr_view_components/structure/footer_component.html.erb +141 -0
  48. data/app/components/sdr_view_components/structure/footer_component.rb +9 -0
  49. data/app/components/sdr_view_components/structure/header_component.html.erb +65 -0
  50. data/app/components/sdr_view_components/structure/header_component.rb +63 -0
  51. data/app/components/sdr_view_components/structure/style_override_dark_component.html.erb +9 -0
  52. data/app/components/sdr_view_components/structure/style_override_dark_component.rb +22 -0
  53. data/app/components/sdr_view_components/structure/style_override_light_component.html.erb +6 -0
  54. data/app/components/sdr_view_components/structure/style_override_light_component.rb +9 -0
  55. data/config/routes.rb +4 -0
  56. data/lib/sdr_view_components/engine.rb +25 -0
  57. data/lib/sdr_view_components/version.rb +5 -0
  58. data/lib/sdr_view_components.rb +12 -0
  59. metadata +141 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 20f53928d5e6a766683fd1b7a819ab74c6e84f4de231645236f46be3b44c322b
4
+ data.tar.gz: 4d49980807bbe8b136182652220ddffa9d43eb001704a5f3deb81bd70c432069
5
+ SHA512:
6
+ metadata.gz: 836ba100594784e315565657e624e65cfceb433bb1af721628d2edaad0787cbab55c765b3e275b9f206eeec1adf79a2e89f74b619024abcd5486147c654a875c
7
+ data.tar.gz: 72c4838ad811a8ef3f5a7d4f16c33884b7699c31e11d0bc7d3c05ee4c6b5e6ecfb365f36ebaccb1c6f725853cf19948b41fcb173606d57dee348fb00e0f5d7ee
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Aaron Collier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ [![CircleCI](https://dl.circleci.com/status-badge/img/gh/sul-dlss/sdr_view_components/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/sul-dlss/sdr_view_components/tree/main)
2
+ [![codecov](https://codecov.io/github/sul-dlss/sdr_view_components/graph/badge.svg?token=VGFL92KY3E)](https://codecov.io/github/sul-dlss/sdr_view_components)
3
+
4
+ # SdrViewComponents
5
+
6
+ A rails gem to provide reusable view components used throughout the SDR applications and implement
7
+ component library assets.
8
+
9
+ # NOTE: Under Development
10
+
11
+ Until initial development is complete and this gem is published, it can only be used locally. Install by cloning the repository to your local system and then including it in your gemfile as:
12
+
13
+ ```
14
+ gem 'sdr_view_components', path: '../sdr_view_components'
15
+ ```
16
+
17
+ Or via github path
18
+
19
+ ## Requirements
20
+
21
+ This set of components relies on the component library stylesheets, add:
22
+
23
+ ```
24
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sul-dlss/component-library@v2025-09-11/styles/sul.css">
25
+ ```
26
+
27
+ with the most recent date tagged release to your `application.html.erb` layout file.
28
+
29
+ ## Usage
30
+
31
+ ### Add the SUL Header to your application
32
+
33
+ Supported header variations are `:dark`, `:light`, and `:white` (default is `:light`)
34
+
35
+ ```
36
+ <%= render SdrViewComponents::Structure::HeaderComponent.new(title: 'Test Header', subtitle: 'Test Subtitle', variant: :dark) do |header| %>
37
+ <% header.with_primary_navigation_link do %>
38
+ <%= render SdrViewComponents::Elements::Navigation::NavItemComponent.new(text: 'Home', path: '#') %>
39
+ <% end %>
40
+ <% header.with_primary_navigation_link do %>
41
+ <%= render SdrViewComponents::Elements::Navigation::DropdownMenuComponent.new(text: 'Logged in: amcollie-preview-dropdown') do |dropdown| %>
42
+ <% dropdown.with_link do %>
43
+ <%= link_to 'Logout', '/Shibboleth.sso/Logout', class: 'dropdown-item' %>
44
+ <% end %>
45
+ <% end %>
46
+ <%# ... all primary nav links %>
47
+ <% end %>
48
+ <% header.with_secondary_navigation_link do %>
49
+ <%= render SdrViewComponents::Elements::Navigation::NavItemComponent.new(text: 'Option', path: '/item1') %>
50
+ <%# ... all secondary nav links>
51
+ <% end %>
52
+ <% end %>
53
+ ```
54
+
55
+ The `:dark` variation supports providing an rgb value via the `rgb_color_str` param in order to override the default dark background, for example:
56
+
57
+ ```
58
+ <%= render SdrViewComponents::Structure::HeaderComponent.new(title: 'Test Header', subtitle: 'Test Subtitle', variant: :dark, rgb_color_str: '1, 104, 149') do |header| %>
59
+
60
+ ...
61
+
62
+ <% end %>
63
+ ```
64
+
65
+ ### General usage:
66
+
67
+ ```
68
+ <% render SdrViewComponent::....>
69
+ ```
70
+
71
+ ## Contributing
72
+ Contribution directions go here.
73
+
74
+ ## License
75
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
6
+ load 'rails/tasks/engine.rake'
7
+
8
+ require 'bundler/gem_tasks'
9
+ require 'rspec/core/rake_task'
10
+ require 'rubocop/rake_task'
11
+
12
+ RuboCop::RakeTask.new
13
+ RSpec::Core::RakeTask.new(:spec)
14
+
15
+ task default: %i[rubocop spec]
@@ -0,0 +1,118 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
16
+
17
+ // Header
18
+ .cardinal {
19
+ --bs-dark-rgb: var(--stanford-cardinal-rgb);
20
+ }
21
+
22
+ .container.without-progress-bar {
23
+ max-width: 960px;
24
+ }
25
+
26
+ .masthead {
27
+ color: white;
28
+
29
+ h1 {
30
+ font-size: 2.25rem;
31
+ }
32
+ }
33
+
34
+ // Submission form
35
+ .header-help {
36
+ padding-left: 42px;
37
+ }
38
+
39
+ .header-help p:last-of-type {
40
+ margin-bottom: 0;
41
+ }
42
+
43
+ .character-circle {
44
+ @extend .d-inline-flex;
45
+ @extend .align-items-center;
46
+ @extend .justify-content-center;
47
+ @extend .rounded-circle;
48
+ @extend .text-white;
49
+
50
+ width: 28px;
51
+ height: 28px;
52
+ font-size: 1rem;
53
+ }
54
+
55
+ .character-circle.character-circle-disabled {
56
+ background-color: var(--stanford-60-black);
57
+ }
58
+
59
+ .character-circle.character-circle-success {
60
+ @extend .bg-success;
61
+ }
62
+
63
+ .character-circle.character-circle-blank {
64
+ @extend .border;
65
+ @extend .border-1;
66
+ }
67
+
68
+ .character-circle.character-circle-check {
69
+ font-family: bootstrap-icons !important;
70
+ }
71
+
72
+ .badge.badge-in-progress {
73
+ background-color: var(--stanford-60-black);
74
+ }
75
+
76
+ .badge.badge-completed {
77
+ @extend .text-bg-success;
78
+ }
79
+
80
+ .btn-group-toggle {
81
+ // Provides visible indication when selected.
82
+ label.btn {
83
+ --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgb(var(--bs-btn-focus-shadow-rgb), .5);
84
+ }
85
+
86
+ input:checked + label {
87
+ @extend .btn-primary;
88
+ }
89
+
90
+ input:not(:checked) + label {
91
+ @extend .btn-outline-primary;
92
+ }
93
+
94
+ .btn-check:checked + .btn {
95
+ background-color: var(--stanford-digital-blue);
96
+ border-color: var(--stanford-digital-blue);
97
+ }
98
+
99
+ :not(.btn-check:checked) + .btn {
100
+ color: var(--stanford-digital-blue);
101
+ border-color: var(--stanford-digital-blue);
102
+ }
103
+ }
104
+
105
+ .card-step {
106
+ --bs-card-cap-padding-y: 1rem;
107
+ }
108
+
109
+ .banner.alert {
110
+ a.btn-primary {
111
+ color: white;
112
+ }
113
+ }
114
+
115
+ // Increase contrast for input borders
116
+ .form-control, .form-select, .form-check-input {
117
+ border-color: var(--stanford-60-black);
118
+ }
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Base component.
4
+ class BaseComponent < ViewComponent::Base
5
+ # Merge classes together.
6
+ #
7
+ # @param args [Array<String>, String] The classes to merge (array, classes, space separated classes).
8
+ # @return [String] The merged classes.
9
+ def merge_classes(*)
10
+ ComponentSupport::CssClasses.merge(*)
11
+ end
12
+
13
+ # Merge data-actions together.
14
+ #
15
+ # @param args [Array<String>, String] The actions to merge (array, classes, space separated classes).
16
+ # @return [String] The merged classes.
17
+ def merge_actions(*)
18
+ ComponentSupport::CssClasses.merge(*)
19
+ end
20
+
21
+ # Extract args with a given prefix.
22
+ #
23
+ # @param args [Hash] The args to extract from.
24
+ # @param prefix [String] The prefix to look for.
25
+ # @return [Hash] The extracted args with the prefix removed.
26
+ def args_for(args:, prefix:)
27
+ args.each_with_object({}) do |(key, value), h|
28
+ h[key.to_s.delete_prefix(prefix).to_sym] = value if key.to_s.start_with?(prefix)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComponentSupport
4
+ # General support for buttons.
5
+ class ButtonSupport
6
+ def self.classes(variant: nil, size: nil, classes: [], bordered: true)
7
+ ComponentSupport::CssClasses.merge('btn',
8
+ variant ? "btn-#{variant}" : nil,
9
+ size ? "btn-#{size}" : nil,
10
+ bordered ? nil : %w[border border-0],
11
+ classes)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComponentSupport
4
+ # Helper methods for CSS classes.
5
+ class CssClasses
6
+ # Merge classes together.
7
+ #
8
+ # @param args [Array<String>, String] The classes to merge (array, classes, space separated classes).
9
+ # @return [String] The merged classes.
10
+ def self.merge(*args)
11
+ args.map do |arg|
12
+ Array(arg&.split)
13
+ end.flatten.compact.uniq.join(' ').presence
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComponentSupport
4
+ # Support methods for working with file hierarchies.
5
+ class FileHierarchy
6
+ # Determine the difference between the last path parts and the current path parts.
7
+ # @param [Array<String>] last_path_parts the last path parts (e.g., ['dir1', 'dir2'])
8
+ # @param [Array<String>] path_parts the current path parts
9
+ # @return [Array<String>] path parts from the current path parts that differ
10
+ def self.path_parts_diff(last_path_parts:, path_parts:)
11
+ return [] if last_path_parts == path_parts
12
+
13
+ matching_index = 0
14
+ loop do
15
+ break if path_parts[matching_index] != last_path_parts[matching_index]
16
+
17
+ matching_index += 1
18
+ end
19
+ path_parts.slice(matching_index..)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ <%= tag.div role:, class: classes, data:, id: do %>
2
+ <% if icon? %>
3
+ <%= tag.div class: icon_classes, role: 'img', 'aria-label': "#{variant} alert icon" %>
4
+ <% end %>
5
+ <div class="text-body">
6
+ <% if title.present? %>
7
+ <div class="fw-semibold"><%= title %></div>
8
+ <% end %>
9
+ <%= content %>
10
+ </div>
11
+ <% if dismissible? %>
12
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
13
+ <% end %>
14
+ <% end %>
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrViewComponents
4
+ module Elements
5
+ # Component for rendering an alert.
6
+ class AlertComponent < BaseComponent
7
+ # NOTE: This component has been altered from the H3 version.
8
+
9
+ ICONS = {
10
+ danger: 'bi bi-exclamation-triangle-fill',
11
+ success: 'bi bi-check-circle-fill',
12
+ note: 'bi bi-exclamation-circle-fill',
13
+ info: 'bi bi-info-circle-fill',
14
+ warning: 'bi bi-exclamation-triangle-fill'
15
+ }.freeze
16
+
17
+ # Variants are :danger, :success, :note, :info, :warning, :input
18
+ # input is not part of the component library
19
+ def initialize(title: nil, variant: :info, dismissible: false, data: {}, classes: [], id: nil, # rubocop:disable Metrics/ParameterLists
20
+ role: 'alert')
21
+ raise ArgumentError, 'Invalid variant' unless %i[danger success note info warning
22
+ input].include?(variant.to_sym)
23
+
24
+ @title = title
25
+ @variant = variant.to_sym
26
+ @dismissible = dismissible
27
+ @data = data
28
+ @classes = classes
29
+ @id = id
30
+ @role = role
31
+ super()
32
+ end
33
+
34
+ attr_reader :title, :variant, :data, :id, :role
35
+
36
+ def classes
37
+ merge_classes(%w[alert d-flex shadow-sm align-items-center], variant_class, dismissible_class, @classes)
38
+ end
39
+
40
+ def variant_class
41
+ "alert-#{variant}"
42
+ end
43
+
44
+ def dismissible_class
45
+ 'alert-dismissible' if dismissible?
46
+ end
47
+
48
+ def dismissible?
49
+ @dismissible
50
+ end
51
+
52
+ def icon?
53
+ ICONS.key?(variant)
54
+ end
55
+
56
+ def icon_classes
57
+ merge_classes('fs-3 me-3 align-self-center d-flex justify-content-center', ICONS[variant])
58
+ end
59
+
60
+ def render?
61
+ title.present? || content.present?
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,20 @@
1
+ <%= tag.div role:, class: classes, 'aria-label': aria_label do %>
2
+ <%= tag.div class: icon_classes, role: 'img', 'aria-label': "#{variant} banner icon" %>
3
+ <div class="text-body">
4
+ <div class="d-flex">
5
+ <div class="p-3 flex-grow-1">
6
+ <%= header %>
7
+ <% if title %>
8
+ <div class="banner-header d-flex align-items-center justify-content-between mt-3 fw-bold">
9
+ <%= title %>
10
+ </div>
11
+ <% end %>
12
+ <% if body %>
13
+ <div class="banner-body">
14
+ <%= body %>
15
+ </div>
16
+ <% end %>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <% end %>
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrViewComponents
4
+ module Elements
5
+ # Banner component for displaying messages, welcomes, etc.
6
+ class BannerComponent < BaseComponent
7
+ renders_one :header
8
+ renders_one :body
9
+
10
+ ICONS = {
11
+ note: 'bi bi-exclamation-circle-fill',
12
+ success: 'bi bi-check-circle-fill',
13
+ warning: 'bi bi-exclamation-triangle-fill',
14
+ info: 'bi bi-info-circle-fill',
15
+ danger: 'bi bi-exclamation-triangle-fill'
16
+ }.freeze
17
+
18
+ def initialize(title: nil, variant: :note, classes: [], role: 'banner', aria_label: nil)
19
+ @title = title
20
+ @variant = variant.to_sym
21
+ @classes = classes
22
+ @role = role
23
+ @aria_label = aria_label
24
+ raise ArgumentError, "Unknown variant: #{variant}" unless ICONS.key?(variant)
25
+
26
+ super()
27
+ end
28
+
29
+ attr_reader :title, :variant, :role, :aria_label
30
+
31
+ def icon_classes
32
+ merge_classes('fs-3 me-3 align-self-center d-flex justify-content-center', ICONS[variant])
33
+ end
34
+
35
+ def classes
36
+ merge_classes('alert banner d-flex shadow-sm align-items-center p-3 mb-3 border-start', "alert-#{variant}",
37
+ @classes)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ <%= tag.li class: classes do %>
2
+ <% if link.present? %>
3
+ <%= link_to truncated_text, link, title: text %>
4
+ <% else %>
5
+ <%= truncated_text %>
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrViewComponents
4
+ module Elements
5
+ # Displays an item in the top breadcrumb navigation
6
+ class BreadcrumbComponent < BaseComponent
7
+ def initialize(text:, link: nil, active: false, truncate_length: 150)
8
+ @text = text
9
+ @link = link
10
+ @active = active
11
+ @truncate_length = truncate_length
12
+ super()
13
+ end
14
+
15
+ attr_reader :text, :link
16
+
17
+ def truncated_text
18
+ truncate(text, length: @truncate_length)
19
+ end
20
+
21
+ def classes
22
+ return 'breadcrumb-item active' if link.nil? || @active
23
+
24
+ 'breadcrumb-item'
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,10 @@
1
+ <% content_for(:title, page_title_from_breadcrumbs) # set the page's html title from the breadcrumb nav %>
2
+ <div class="breadcrumbs mb-5">
3
+ <nav id="breadcrumbs" aria-label="breadcrumb">
4
+ <ol class="breadcrumb">
5
+ <% breadcrumbs.each do |breadcrumb| %>
6
+ <%= breadcrumb %>
7
+ <% end %>
8
+ </ol>
9
+ </nav>
10
+ </div>
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrViewComponents
4
+ module Elements
5
+ # Displays the top breadcrumb navigation
6
+ class BreadcrumbNavComponent < BaseComponent
7
+ renders_many :breadcrumbs, types: {
8
+ default: {
9
+ renders: BreadcrumbComponent, as: :breadcrumb
10
+ },
11
+ title: {
12
+ renders: ->(**args) { BreadcrumbComponent.new(truncate_length: 75, **args) }, as: :title_breadcrumb
13
+ },
14
+ collection: {
15
+ renders: ->(**args) { BreadcrumbComponent.new(truncate_length: 50, **args) }, as: :collection_breadcrumb
16
+ }
17
+ }
18
+
19
+ def page_title_from_breadcrumbs
20
+ breadcrumbs.filter_map(&:truncated_text).unshift('SDR').join(' | ')
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrViewComponents
4
+ module Elements
5
+ # Generic button component
6
+ class ButtonComponent < BaseComponent
7
+ def initialize(label: nil, classes: [], variant: nil, size: nil, bordered: true, **options) # rubocop:disable Metrics/ParameterLists
8
+ @label = label
9
+ @classes = classes
10
+ @variant = variant
11
+ @size = size
12
+ @options = options
13
+ @bordered = bordered
14
+ super()
15
+ end
16
+
17
+ attr_reader :options, :label
18
+
19
+ def call
20
+ tag.button(
21
+ class: ComponentSupport::ButtonSupport.classes(variant: @variant, size: @size, classes: @classes,
22
+ bordered: @bordered),
23
+ type: 'button',
24
+ **options
25
+ ) do
26
+ label || content
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrViewComponents
4
+ module Elements
5
+ # Component for a button that is a link
6
+ class ButtonLinkComponent < BaseComponent
7
+ def initialize(link:, label: nil, variant: :primary, classes: [], bordered: true, size: nil, **options) # rubocop:disable Metrics/ParameterLists
8
+ @link = link
9
+ @label = label
10
+ @variant = variant
11
+ @options = options
12
+ @classes = classes
13
+ @bordered = bordered
14
+ @size = size
15
+ super()
16
+ end
17
+
18
+ attr_reader :link, :label
19
+
20
+ def call
21
+ link_to(link, class: ComponentSupport::ButtonSupport.classes(classes: @classes, variant: @variant,
22
+ bordered: @bordered, size: @size),
23
+ **@options) do
24
+ label || content
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ <li class="nav-item dropdown">
2
+ <button
3
+ class="nav-link logout-dropdown dropdown-toggle"
4
+ data-bs-toggle="dropdown"
5
+ aria-expanded="false">
6
+ <%= text %>
7
+ </button>
8
+ <ul class="dropdown-menu">
9
+ <%= items.map do |item| %>
10
+ <li><%= item %></li>
11
+ <% end %>
12
+ </ul>
13
+ </li>
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrViewComponents
4
+ module Elements
5
+ module Navigation
6
+ # Component for rendering a dropdown menu in the navigation bar.
7
+ class DropdownMenuComponent < BaseComponent
8
+ renders_many :items
9
+
10
+ def initialize(text:)
11
+ @text = text
12
+ super()
13
+ end
14
+
15
+ attr_reader :text
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrViewComponents
4
+ module Elements
5
+ module Navigation
6
+ # Component for navigation item link.
7
+ class NavItemComponent < BaseComponent
8
+ def initialize(text:, path:, data: {})
9
+ @text = text
10
+ @path = path
11
+ @data = data
12
+ super()
13
+ end
14
+
15
+ attr_reader :text, :path, :data
16
+
17
+ def call
18
+ tag.li class: 'nav-item' do
19
+ link_to text, path, class: 'nav-link', data:
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ <nav id="skip-link" class="visually-hidden-focusable" aria-label="Skip links">
2
+ <div class="container-xl">
3
+ <a class="d-inline-flex m-1 py-2 px-3" href="#main-container">Skip to main content</a>
4
+ </div>
5
+ </nav>