dsfr-view-components 0.0.1

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +214 -0
  3. data/Rakefile +22 -0
  4. data/app/components/dsfr_component/accordion_component/section_component.html.erb +11 -0
  5. data/app/components/dsfr_component/accordion_component/section_component.rb +39 -0
  6. data/app/components/dsfr_component/accordion_component.html.erb +5 -0
  7. data/app/components/dsfr_component/accordion_component.rb +33 -0
  8. data/app/components/dsfr_component/alert_component.rb +22 -0
  9. data/app/components/dsfr_component/back_link_component.rb +24 -0
  10. data/app/components/dsfr_component/base.rb +28 -0
  11. data/app/components/dsfr_component/breadcrumbs_component.html.erb +7 -0
  12. data/app/components/dsfr_component/breadcrumbs_component.rb +51 -0
  13. data/app/components/dsfr_component/cookie_banner_component/message_component.rb +62 -0
  14. data/app/components/dsfr_component/cookie_banner_component.rb +35 -0
  15. data/app/components/dsfr_component/details_component.rb +42 -0
  16. data/app/components/dsfr_component/footer_component.html.erb +49 -0
  17. data/app/components/dsfr_component/footer_component.rb +87 -0
  18. data/app/components/dsfr_component/header_component.html.erb +51 -0
  19. data/app/components/dsfr_component/header_component.rb +145 -0
  20. data/app/components/dsfr_component/inset_text_component.rb +28 -0
  21. data/app/components/dsfr_component/notification_banner_component.html.erb +14 -0
  22. data/app/components/dsfr_component/notification_banner_component.rb +93 -0
  23. data/app/components/dsfr_component/pagination_component/adjacent_page.rb +59 -0
  24. data/app/components/dsfr_component/pagination_component/item.rb +77 -0
  25. data/app/components/dsfr_component/pagination_component/next_page.rb +27 -0
  26. data/app/components/dsfr_component/pagination_component/previous_page.rb +21 -0
  27. data/app/components/dsfr_component/pagination_component.rb +129 -0
  28. data/app/components/dsfr_component/panel_component.rb +56 -0
  29. data/app/components/dsfr_component/phase_banner_component.html.erb +6 -0
  30. data/app/components/dsfr_component/phase_banner_component.rb +25 -0
  31. data/app/components/dsfr_component/section_break_component.rb +49 -0
  32. data/app/components/dsfr_component/start_button_component.rb +55 -0
  33. data/app/components/dsfr_component/summary_list_component/action_component.rb +42 -0
  34. data/app/components/dsfr_component/summary_list_component/key_component.rb +23 -0
  35. data/app/components/dsfr_component/summary_list_component/row_component.rb +57 -0
  36. data/app/components/dsfr_component/summary_list_component/value_component.rb +23 -0
  37. data/app/components/dsfr_component/summary_list_component.html.erb +5 -0
  38. data/app/components/dsfr_component/summary_list_component.rb +49 -0
  39. data/app/components/dsfr_component/tab_component.html.erb +11 -0
  40. data/app/components/dsfr_component/tab_component.rb +61 -0
  41. data/app/components/dsfr_component/table_component/body_component.html.erb +5 -0
  42. data/app/components/dsfr_component/table_component/body_component.rb +21 -0
  43. data/app/components/dsfr_component/table_component/caption_component.rb +37 -0
  44. data/app/components/dsfr_component/table_component/cell_component.rb +57 -0
  45. data/app/components/dsfr_component/table_component/head_component.html.erb +5 -0
  46. data/app/components/dsfr_component/table_component/head_component.rb +23 -0
  47. data/app/components/dsfr_component/table_component/row_component.html.erb +5 -0
  48. data/app/components/dsfr_component/table_component/row_component.rb +30 -0
  49. data/app/components/dsfr_component/table_component.html.erb +7 -0
  50. data/app/components/dsfr_component/table_component.rb +35 -0
  51. data/app/components/dsfr_component/tag_component.rb +44 -0
  52. data/app/components/dsfr_component/traits/custom_html_attributes.rb +7 -0
  53. data/app/components/dsfr_component/traits.rb +1 -0
  54. data/app/components/dsfr_component/warning_text_component.rb +37 -0
  55. data/app/components/dsfr_component.rb +1 -0
  56. data/app/helpers/dsfr_back_to_top_link_helper.rb +14 -0
  57. data/app/helpers/dsfr_components_helper.rb +33 -0
  58. data/app/helpers/dsfr_link_helper.rb +123 -0
  59. data/app/helpers/dsfr_skip_link_helper.rb +13 -0
  60. data/config/routes.rb +2 -0
  61. data/lib/dsfr/components/engine.rb +110 -0
  62. data/lib/dsfr/components/helpers/css_utilities.rb +22 -0
  63. data/lib/dsfr/components/version.rb +5 -0
  64. data/lib/dsfr/components.rb +6 -0
  65. data/lib/tasks/dsfr/components_tasks.rake +4 -0
  66. metadata +482 -0
@@ -0,0 +1,87 @@
1
+ class DsfrComponent::FooterComponent < DsfrComponent::Base
2
+ using HTMLAttributesUtils
3
+
4
+ renders_one :meta_html
5
+ renders_one :meta
6
+ renders_one :navigation
7
+
8
+ attr_reader :meta_items, :meta_text, :meta_items_title, :meta_licence, :copyright, :custom_container_classes
9
+
10
+ def initialize(
11
+ classes: [],
12
+ container_classes: [],
13
+ container_html_attributes: {},
14
+ copyright_text: config.default_footer_copyright_text,
15
+ copyright_url: config.default_footer_copyright_url,
16
+ html_attributes: {},
17
+ meta_items: {},
18
+ meta_items_title: "Support links",
19
+ meta_licence: nil,
20
+ meta_text: config.default_footer_component_meta_text,
21
+ meta_classes: [],
22
+ meta_html_attributes: {}
23
+ )
24
+ @meta_text = meta_text
25
+ @meta_items = build_meta_links(meta_items)
26
+ @meta_items_title = meta_items_title
27
+ @meta_licence = meta_licence
28
+ @custom_meta_classes = meta_classes
29
+ @custom_meta_html_attributes = meta_html_attributes
30
+ @copyright = build_copyright(copyright_text, copyright_url)
31
+ @custom_container_classes = container_classes
32
+ @custom_container_html_attributes = container_html_attributes
33
+
34
+ super(classes: classes, html_attributes: html_attributes)
35
+ end
36
+
37
+ private
38
+
39
+ def default_attributes
40
+ { class: %w(govuk-footer) }
41
+ end
42
+
43
+ def meta_content
44
+ meta_html || meta_text
45
+ end
46
+
47
+ def meta_classes
48
+ %w(govuk-footer__meta).append(@custom_meta_classes)
49
+ end
50
+
51
+ def meta_html_attributes
52
+ @custom_meta_html_attributes
53
+ end
54
+
55
+ def container_html_attributes
56
+ # FIXME: remove when we deprecate classes
57
+ #
58
+ # Once we drop classes this extra merging can be dropped along with the
59
+ # container_classes and meta_classes args
60
+ { class: %w(govuk-width-container) }.deep_merge_html_attributes(
61
+ @custom_container_html_attributes.merge(class: custom_container_classes)
62
+ )
63
+ end
64
+
65
+ def build_meta_links(links)
66
+ return [] if links.blank?
67
+
68
+ case links
69
+ when Array
70
+ links.map { |link| raw(link_to(link[:text], link[:href], class: %w(govuk-footer__link), **link.fetch(:attr, {}))) }
71
+ when Hash
72
+ links.map { |text, href| raw(link_to(text, href, class: %w(govuk-footer__link))) }
73
+ else
74
+ fail(ArgumentError, 'meta links must be a hash or array of hashes') unless links.is_a?(Hash)
75
+ end
76
+ end
77
+
78
+ def default_licence
79
+ link = link_to("Open Government Licence v3.0", "https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/", class: %w(govuk-footer__link))
80
+
81
+ raw(%(All content is available under the #{link}, except where otherwise stated))
82
+ end
83
+
84
+ def build_copyright(text, url)
85
+ link_to(text, url, class: %w(govuk-footer__link govuk-footer__copyright-logo))
86
+ end
87
+ end
@@ -0,0 +1,51 @@
1
+ <%= tag.header(role: 'banner', data: { module: 'govuk-header' }, **html_attributes) do %>
2
+ <%= tag.div(**container_html_attributes) do %>
3
+ <div class="govuk-header__logo">
4
+ <%= link_to(homepage_url, class: %w(govuk-header__link govuk-header__link--homepage)) do %>
5
+ <span class="govuk-header__logotype">
6
+ <% if custom_logo.present? %>
7
+ <%= custom_logo %>
8
+ <% else %>
9
+ <% if crown %>
10
+ <!--[if gt IE 8]><!-->
11
+ <svg aria-hidden="true" focusable="false" class="govuk-header__logotype-crown" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 132 97" height="30" width="36">
12
+ <path fill="currentColor" fill-rule="evenodd" d="M25 30.2c3.5 1.5 7.7-.2 9.1-3.7 1.5-3.6-.2-7.8-3.9-9.2-3.6-1.4-7.6.3-9.1 3.9-1.4 3.5.3 7.5 3.9 9zM9 39.5c3.6 1.5 7.8-.2 9.2-3.7 1.5-3.6-.2-7.8-3.9-9.1-3.6-1.5-7.6.2-9.1 3.8-1.4 3.5.3 7.5 3.8 9zM4.4 57.2c3.5 1.5 7.7-.2 9.1-3.8 1.5-3.6-.2-7.7-3.9-9.1-3.5-1.5-7.6.3-9.1 3.8-1.4 3.5.3 7.6 3.9 9.1zm38.3-21.4c3.5 1.5 7.7-.2 9.1-3.8 1.5-3.6-.2-7.7-3.9-9.1-3.6-1.5-7.6.3-9.1 3.8-1.3 3.6.4 7.7 3.9 9.1zm64.4-5.6c-3.6 1.5-7.8-.2-9.1-3.7-1.5-3.6.2-7.8 3.8-9.2 3.6-1.4 7.7.3 9.2 3.9 1.3 3.5-.4 7.5-3.9 9zm15.9 9.3c-3.6 1.5-7.7-.2-9.1-3.7-1.5-3.6.2-7.8 3.7-9.1 3.6-1.5 7.7.2 9.2 3.8 1.5 3.5-.3 7.5-3.8 9zm4.7 17.7c-3.6 1.5-7.8-.2-9.2-3.8-1.5-3.6.2-7.7 3.9-9.1 3.6-1.5 7.7.3 9.2 3.8 1.3 3.5-.4 7.6-3.9 9.1zM89.3 35.8c-3.6 1.5-7.8-.2-9.2-3.8-1.4-3.6.2-7.7 3.9-9.1 3.6-1.5 7.7.3 9.2 3.8 1.4 3.6-.3 7.7-3.9 9.1zM69.7 17.7l8.9 4.7V9.3l-8.9 2.8c-.2-.3-.5-.6-.9-.9L72.4 0H59.6l3.5 11.2c-.3.3-.6.5-.9.9l-8.8-2.8v13.1l8.8-4.7c.3.3.6.7.9.9l-5 15.4v.1c-.2.8-.4 1.6-.4 2.4 0 4.1 3.1 7.5 7 8.1h.2c.3 0 .7.1 1 .1.4 0 .7 0 1-.1h.2c4-.6 7.1-4.1 7.1-8.1 0-.8-.1-1.7-.4-2.4V34l-5.1-15.4c.4-.2.7-.6 1-.9zM66 92.8c16.9 0 32.8 1.1 47.1 3.2 4-16.9 8.9-26.7 14-33.5l-9.6-3.4c1 4.9 1.1 7.2 0 10.2-1.5-1.4-3-4.3-4.2-8.7L108.6 76c2.8-2 5-3.2 7.5-3.3-4.4 9.4-10 11.9-13.6 11.2-4.3-.8-6.3-4.6-5.6-7.9 1-4.7 5.7-5.9 8-.5 4.3-8.7-3-11.4-7.6-8.8 7.1-7.2 7.9-13.5 2.1-21.1-8 6.1-8.1 12.3-4.5 20.8-4.7-5.4-12.1-2.5-9.5 6.2 3.4-5.2 7.9-2 7.2 3.1-.6 4.3-6.4 7.8-13.5 7.2-10.3-.9-10.9-8-11.2-13.8 2.5-.5 7.1 1.8 11 7.3L80.2 60c-4.1 4.4-8 5.3-12.3 5.4 1.4-4.4 8-11.6 8-11.6H55.5s6.4 7.2 7.9 11.6c-4.2-.1-8-1-12.3-5.4l1.4 16.4c3.9-5.5 8.5-7.7 10.9-7.3-.3 5.8-.9 12.8-11.1 13.8-7.2.6-12.9-2.9-13.5-7.2-.7-5 3.8-8.3 7.1-3.1 2.7-8.7-4.6-11.6-9.4-6.2 3.7-8.5 3.6-14.7-4.6-20.8-5.8 7.6-5 13.9 2.2 21.1-4.7-2.6-11.9.1-7.7 8.8 2.3-5.5 7.1-4.2 8.1.5.7 3.3-1.3 7.1-5.7 7.9-3.5.7-9-1.8-13.5-11.2 2.5.1 4.7 1.3 7.5 3.3l-4.7-15.4c-1.2 4.4-2.7 7.2-4.3 8.7-1.1-3-.9-5.3 0-10.2l-9.5 3.4c5 6.9 9.9 16.7 14 33.5 14.8-2.1 30.8-3.2 47.7-3.2z"></path>
13
+ </svg>
14
+ <!--<![endif]-->
15
+ <% end %>
16
+
17
+ <% if crown_fallback_image_path.present? %>
18
+ <!--[if IE 8]>
19
+ <%= image_tag(crown_fallback_image_path, **crown_fallback_image_attributes) %>
20
+ <![endif]-->
21
+ <% end %>
22
+
23
+ <%= tag.span(logotype, class: "govuk-header__logotype-text") %>
24
+ <% end %>
25
+ </span>
26
+ <% if product_name.present? %>
27
+ <%= product_name %>
28
+ <% end %>
29
+ <% end %>
30
+ </div>
31
+
32
+ <% if service_name.present? || navigation_items.present? %>
33
+ <div class="govuk-header__content">
34
+ <% if service_name.present? %>
35
+ <%= link_to(service_name, service_url, class: %w(govuk-header__link govuk-header__service-name)) %>
36
+ <% end %>
37
+
38
+ <% if navigation_items.any? %>
39
+ <%= tag.nav(**navigation_html_attributes) do %>
40
+ <%= tag.button("Menu", type: "button", class: %w(govuk-header__menu-button govuk-js-header-toggle), hidden: true, aria: { controls: "navigation", label: menu_button_label }) %>
41
+ <ul id="navigation" class="govuk-header__navigation-list">
42
+ <% navigation_items.each do |item| %>
43
+ <%= item %>
44
+ <% end %>
45
+ </ul>
46
+ <% end %>
47
+ <% end %>
48
+ </div>
49
+ <% end %>
50
+ <% end %>
51
+ <% end %>
@@ -0,0 +1,145 @@
1
+ class DsfrComponent::HeaderComponent < DsfrComponent::Base
2
+ renders_many :navigation_items, "NavigationItem"
3
+ renders_one :custom_logo
4
+ renders_one :product_name, "ProductName"
5
+
6
+ attr_reader :logotype,
7
+ :crown,
8
+ :crown_fallback_image_path,
9
+ :homepage_url,
10
+ :service_name,
11
+ :service_url,
12
+ :menu_button_label,
13
+ :navigation_label,
14
+ :custom_navigation_classes,
15
+ :custom_container_classes
16
+
17
+ def initialize(classes: [],
18
+ html_attributes: {},
19
+ logotype: config.default_header_logotype,
20
+ crown: true,
21
+ crown_fallback_image_path: nil,
22
+ homepage_url: config.default_header_homepage_url,
23
+ menu_button_label: config.default_header_menu_button_label,
24
+ navigation_classes: [],
25
+ navigation_label: config.default_header_navigation_label,
26
+ service_name: config.default_header_service_name,
27
+ service_url: config.default_header_service_url,
28
+ container_classes: nil)
29
+
30
+ @logotype = logotype
31
+ @crown = crown
32
+ @crown_fallback_image_path = crown_fallback_image_path
33
+ @homepage_url = homepage_url
34
+ @service_name = service_name
35
+ @service_url = service_url
36
+ @menu_button_label = menu_button_label
37
+ @custom_navigation_classes = navigation_classes
38
+ @navigation_label = navigation_label
39
+ @custom_container_classes = container_classes
40
+
41
+ super(classes: classes, html_attributes: html_attributes)
42
+ end
43
+
44
+ private
45
+
46
+ def default_attributes
47
+ { class: %w(govuk-header) }
48
+ end
49
+
50
+ def navigation_html_attributes
51
+ nc = %w(govuk-header__navigation).append(custom_navigation_classes).compact
52
+
53
+ { class: nc, aria: { label: navigation_label } }
54
+ end
55
+
56
+ def container_html_attributes
57
+ { class: %w(govuk-header__container govuk-width-container).append(custom_container_classes).compact }
58
+ end
59
+
60
+ def crown_fallback_image_attributes
61
+ {
62
+ class: "govuk-header__logotype-crown-fallback-image",
63
+ width: "36",
64
+ height: "32",
65
+ }
66
+ end
67
+
68
+ class NavigationItem < DsfrComponent::Base
69
+ attr_reader :text, :href, :options, :active
70
+
71
+ def initialize(text:, href: nil, options: {}, active: nil, classes: [], html_attributes: {})
72
+ @text = text
73
+ @href = href
74
+ @options = options
75
+ @active_override = active
76
+
77
+ super(classes: classes, html_attributes: html_attributes)
78
+ end
79
+
80
+ def before_render
81
+ if active?
82
+ html_attributes[:class] << active_class
83
+ end
84
+ end
85
+
86
+ def active_class
87
+ %w(govuk-header__navigation-item--active) if active?
88
+ end
89
+
90
+ def link?
91
+ href.present?
92
+ end
93
+
94
+ def call
95
+ tag.li(**html_attributes) do
96
+ if link?
97
+ link_to(text, href, class: "govuk-header__link", **options)
98
+ else
99
+ text
100
+ end
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def active?
107
+ return @active_override unless @active_override.nil?
108
+ return false if href.blank?
109
+
110
+ current_page?(href)
111
+ end
112
+
113
+ def default_attributes
114
+ { class: %w(govuk-header__navigation-item) }
115
+ end
116
+ end
117
+
118
+ class ProductName < DsfrComponent::Base
119
+ attr_reader :name
120
+
121
+ def initialize(name: nil, html_attributes: {}, classes: [])
122
+ @name = name
123
+
124
+ super(classes: classes, html_attributes: html_attributes)
125
+ end
126
+
127
+ def render?
128
+ name.present? || content.present?
129
+ end
130
+
131
+ def call
132
+ if content.present?
133
+ tag.div(content, **html_attributes)
134
+ else
135
+ tag.span(name, **html_attributes)
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def default_attributes
142
+ { class: %w(govuk-header__product-name) }
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,28 @@
1
+ class DsfrComponent::InsetTextComponent < DsfrComponent::Base
2
+ attr_reader :text, :id
3
+
4
+ def initialize(text: nil, id: nil, classes: [], html_attributes: {})
5
+ @text = text
6
+ @id = id
7
+
8
+ super(classes: classes, html_attributes: html_attributes)
9
+ end
10
+
11
+ def call
12
+ tag.div(id: id, **html_attributes) { inset_text_content }
13
+ end
14
+
15
+ def render?
16
+ inset_text_content.present?
17
+ end
18
+
19
+ private
20
+
21
+ def inset_text_content
22
+ content.presence || text
23
+ end
24
+
25
+ def default_attributes
26
+ { class: %w(govuk-inset-text) }
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ <%= tag.div(**html_attributes) do %>
2
+ <div class="govuk-notification-banner__header">
3
+ <%= content_tag(title_tag, class: "govuk-notification-banner__title", id: title_id) do %>
4
+ <%= title_content %>
5
+ <% end %>
6
+ </div>
7
+ <div class="govuk-notification-banner__content">
8
+ <% headings.each do |heading| %>
9
+ <%= heading %>
10
+ <% end %>
11
+
12
+ <%= content || text %>
13
+ </div>
14
+ <% end %>
@@ -0,0 +1,93 @@
1
+ class DsfrComponent::NotificationBannerComponent < DsfrComponent::Base
2
+ attr_reader :title_text, :title_id, :text, :success, :title_heading_level, :disable_auto_focus, :role
3
+
4
+ renders_one :title_html
5
+ renders_many :headings, "Heading"
6
+
7
+ def initialize(
8
+ title_text: nil,
9
+ text: nil,
10
+ role: nil,
11
+ success: config.default_notification_title_success,
12
+ title_heading_level: config.default_notification_title_heading_level,
13
+ title_id: config.default_notification_banner_title_id,
14
+ disable_auto_focus: config.default_notification_disable_auto_focus,
15
+ classes: [],
16
+ html_attributes: {}
17
+ )
18
+ @title_text = title_text
19
+ @title_id = title_id
20
+ @text = text
21
+ @success = success
22
+ @role = role || default_role
23
+ @title_heading_level = title_heading_level
24
+ @disable_auto_focus = disable_auto_focus
25
+
26
+ super(classes: classes, html_attributes: html_attributes)
27
+ end
28
+
29
+ def render?
30
+ headings.any? || text.present? || content.present?
31
+ end
32
+
33
+ class Heading < DsfrComponent::Base
34
+ attr_reader :text, :link_href, :link_text
35
+
36
+ def initialize(text: nil, link_text: nil, link_href: nil, classes: [], html_attributes: {})
37
+ @text = text
38
+ @link_text = link_text
39
+ @link_href = link_href
40
+
41
+ super(classes: classes, html_attributes: html_attributes)
42
+ end
43
+
44
+ def call
45
+ tag.div(**html_attributes) do
46
+ if text.present?
47
+ safe_join([text, link].compact, " ")
48
+ else
49
+ content
50
+ end
51
+ end
52
+ end
53
+
54
+ def link
55
+ link_to(link_text, link_href, class: 'govuk-notification-banner__link') if link_text.present? && link_href.present?
56
+ end
57
+
58
+ def default_attributes
59
+ { class: %w(govuk-notification-banner__heading) }
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def default_attributes
66
+ {
67
+ class: class_names(
68
+ "govuk-notification-banner",
69
+ "govuk-notification-banner--success" => success
70
+ ),
71
+ data: {
72
+ "module" => "govuk-notification-banner",
73
+ "disable-auto-focus" => disable_auto_focus
74
+ },
75
+ role: role,
76
+ aria: { labelledby: title_id },
77
+ }
78
+ end
79
+
80
+ def title_content
81
+ title_html || title_text
82
+ end
83
+
84
+ def title_tag
85
+ fail "title_heading_level must be a number between 1 and 6" unless title_heading_level.is_a?(Integer) && title_heading_level.in?(1..6)
86
+
87
+ "h#{title_heading_level}"
88
+ end
89
+
90
+ def default_role
91
+ success ? "alert" : "region"
92
+ end
93
+ end
@@ -0,0 +1,59 @@
1
+ class DsfrComponent::PaginationComponent::AdjacentPage < DsfrComponent::Base
2
+ attr_reader :href, :label_text, :text, :suffix, :block_mode, :visually_hidden_text
3
+ alias_method :block_mode?, :block_mode
4
+
5
+ def initialize(href:, suffix:, text:, block_mode: true, label_text: nil, classes: [], html_attributes: {})
6
+ @href = href
7
+ @label_text = label_text
8
+ @text = text
9
+ @block_mode = block_mode
10
+ @suffix = suffix
11
+
12
+ super(html_attributes: html_attributes, classes: classes)
13
+ end
14
+
15
+ def call
16
+ tag.div(**html_attributes) do
17
+ tag.a(href: href, class: %w(fr-link govuk-pagination__link), rel: suffix) do
18
+ safe_join([body, divider, label_content])
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def default_attributes
26
+ { class: ["govuk-pagination__#{suffix}"] }
27
+ end
28
+
29
+ def body
30
+ [arrow, title_span]
31
+ end
32
+
33
+ def title_span
34
+ tag.span(text, class: title_classes)
35
+ end
36
+
37
+ def divider
38
+ return if label_text.blank?
39
+
40
+ tag.span(":", class: %w(govuk-visually-hidden))
41
+ end
42
+
43
+ def label_content
44
+ return if label_text.blank?
45
+
46
+ tag.span(label_text, class: label_classes)
47
+ end
48
+
49
+ def title_classes
50
+ class_names(
51
+ %(govuk-pagination__link-title),
52
+ %(govuk-pagination__link-title--decorated) => label_text.blank?
53
+ )
54
+ end
55
+
56
+ def label_classes
57
+ %w(govuk-pagination__link-label)
58
+ end
59
+ end
@@ -0,0 +1,77 @@
1
+ class DsfrComponent::PaginationComponent::Item < DsfrComponent::Base
2
+ attr_reader :number, :href, :visually_hidden_text, :mode
3
+
4
+ def initialize(number: nil, href: nil, current: false, ellipsis: false, from_pagy: false, visually_hidden_text: nil, classes: [], html_attributes: {})
5
+ @number = number
6
+ @href = href
7
+ @visually_hidden_text = visually_hidden_text
8
+
9
+ # We have three modes for rendering links:
10
+ #
11
+ # * link (a link to another page)
12
+ # * current (a link to the current page)
13
+ # * gap (an ellipsis symbol)
14
+ #
15
+ # Pagy sets these by object type:
16
+ # Integer = link
17
+ # String = current
18
+ # :gap = gap
19
+ #
20
+ # The original Nunjucks component has two boolean settings instead,
21
+ # ellipsis and current. When ellipsis is true all other arguments are
22
+ # ignored
23
+ @mode = from_pagy ? pagy_mode(number) : manual_mode(ellipsis, current)
24
+
25
+ super(classes: classes, html_attributes: html_attributes)
26
+ end
27
+
28
+ def call
29
+ case mode
30
+ when :link
31
+ link(current: false)
32
+ when :current
33
+ link(current: true)
34
+ when :gap
35
+ ellipsis_item
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def pagy_mode(number)
42
+ return :link if number.is_a?(Integer)
43
+ return :current if number.is_a?(String)
44
+
45
+ :gap
46
+ end
47
+
48
+ def manual_mode(ellipsis, current)
49
+ return :gap if ellipsis
50
+ return :current if current
51
+
52
+ :link
53
+ end
54
+
55
+ def link(current: false)
56
+ attributes = html_attributes.tap { |ha| ha[:class] << "govuk-pagination__item--current" if current }
57
+
58
+ tag.li(**attributes) do
59
+ tag.a(href: href, class: %w(fr-link govuk-pagination__link)) { number.to_s }
60
+ end
61
+ end
62
+
63
+ def ellipsis_item
64
+ tag.li("⋯", class: %w(govuk-pagination__item govuk-pagination__item--ellipses))
65
+ end
66
+
67
+ def default_attributes
68
+ {
69
+ class: %w(govuk-pagination__item),
70
+ aria: { label: aria_label }
71
+ }
72
+ end
73
+
74
+ def aria_label
75
+ visually_hidden_text || "Page #{number}"
76
+ end
77
+ end
@@ -0,0 +1,27 @@
1
+ class DsfrComponent::PaginationComponent::NextPage < DsfrComponent::PaginationComponent::AdjacentPage
2
+ def initialize(href:, text:, label_text: nil, block_mode: true, classes: [], html_attributes: {})
3
+ super(
4
+ suffix: "next",
5
+ text: text,
6
+ href: href,
7
+ label_text: label_text,
8
+ block_mode: block_mode,
9
+ classes: classes,
10
+ html_attributes: html_attributes
11
+ )
12
+ end
13
+
14
+ def body
15
+ return [arrow, title_span] if block_mode?
16
+
17
+ [title_span, arrow]
18
+ end
19
+
20
+ private
21
+
22
+ def arrow
23
+ tag.svg(class: "govuk-pagination__icon govuk-pagination__icon--next", xmlns: "http://www.w3.org/2000/svg", height: "13", width: "15", focusable: "false", viewBox: "0 0 15 13", aria: { hidden: "true" }) do
24
+ tag.path(d: "m8.107-0.0078125-1.4136 1.414 4.2926 4.293h-12.986v2h12.896l-4.1855 3.9766 1.377 1.4492 6.7441-6.4062-6.7246-6.7266z")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ class DsfrComponent::PaginationComponent::PreviousPage < DsfrComponent::PaginationComponent::AdjacentPage
2
+ def initialize(href:, text:, label_text: nil, block_mode: true, classes: [], html_attributes: {})
3
+ super(
4
+ suffix: "prev",
5
+ text: text,
6
+ href: href,
7
+ label_text: label_text,
8
+ block_mode: block_mode,
9
+ classes: classes,
10
+ html_attributes: html_attributes
11
+ )
12
+ end
13
+
14
+ private
15
+
16
+ def arrow
17
+ tag.svg(class: "govuk-pagination__icon govuk-pagination__icon--prev", xmlns: "http://www.w3.org/2000/svg", height: "13", width: "15", focusable: "false", viewBox: "0 0 15 13", aria: { hidden: "true" }) do
18
+ tag.path(d: "m6.5938-0.0078125-6.7266 6.7266 6.7441 6.4062 1.377-1.449-4.1856-3.9768h12.896v-2h-12.984l4.2931-4.293-1.414-1.414z")
19
+ end
20
+ end
21
+ end