ariadne_view_components 0.0.11-x64-mingw32 → 0.0.13-x64-mingw32

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/ariadne.d.ts +1 -0
  3. data/app/assets/javascripts/ariadne_view_components.js +2 -2
  4. data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
  5. data/app/assets/javascripts/tab-container-component.d.ts +1 -0
  6. data/app/assets/javascripts/tab-nav-component.d.ts +9 -0
  7. data/app/components/ariadne/ariadne.ts +3 -0
  8. data/app/components/ariadne/body_component.rb +1 -1
  9. data/app/components/ariadne/button_component.rb +2 -2
  10. data/app/components/ariadne/comment_component.html.erb +2 -6
  11. data/app/components/ariadne/comment_component.rb +1 -1
  12. data/app/components/ariadne/component.rb +3 -5
  13. data/app/components/ariadne/container_component.rb +1 -1
  14. data/app/components/ariadne/counter_component.rb +1 -1
  15. data/app/components/ariadne/flash_component.rb +1 -1
  16. data/app/components/ariadne/flex_component.rb +1 -1
  17. data/app/components/ariadne/footer_component.rb +1 -1
  18. data/app/components/ariadne/grid_component.html.erb +2 -2
  19. data/app/components/ariadne/grid_component.rb +10 -8
  20. data/app/components/ariadne/header_component.rb +1 -1
  21. data/app/components/ariadne/heroicon_component.html.erb +1 -1
  22. data/app/components/ariadne/heroicon_component.rb +1 -2
  23. data/app/components/ariadne/list_component.html.erb +3 -5
  24. data/app/components/ariadne/list_component.rb +5 -5
  25. data/app/components/ariadne/main_component.rb +1 -1
  26. data/app/components/ariadne/narrow_container_component.rb +1 -1
  27. data/app/components/ariadne/panel_bar_component.rb +2 -2
  28. data/app/components/ariadne/pill_component.rb +1 -1
  29. data/app/components/ariadne/rich_text_area_component.html.erb +1 -1
  30. data/app/components/ariadne/rich_text_area_component.rb +1 -1
  31. data/app/components/ariadne/slideover_component.rb +2 -2
  32. data/app/components/ariadne/tab-container-component.ts +24 -0
  33. data/app/components/ariadne/tab-nav-component.ts +34 -0
  34. data/app/components/ariadne/tab_component.html.erb +2 -6
  35. data/app/components/ariadne/tab_component.rb +77 -18
  36. data/app/components/ariadne/tab_container_component.erb +12 -0
  37. data/app/components/ariadne/tab_container_component.rb +61 -0
  38. data/app/components/ariadne/tab_nav_component.html.erb +7 -0
  39. data/app/components/ariadne/tab_nav_component.rb +72 -0
  40. data/app/components/ariadne/table_component.html.erb +17 -0
  41. data/app/components/ariadne/table_component.rb +281 -0
  42. data/app/components/ariadne/time_ago_component.rb +1 -1
  43. data/app/components/ariadne/timeline_component.rb +1 -1
  44. data/app/lib/ariadne/fetch_or_fallback_helper.rb +11 -3
  45. data/app/lib/ariadne/icon_helper.rb +17 -13
  46. data/lib/ariadne/view_components/commands.rb +1 -1
  47. data/lib/ariadne/view_components/constants.rb +2 -2
  48. data/lib/ariadne/view_components/statuses.rb +2 -2
  49. data/lib/ariadne/view_components/version.rb +1 -1
  50. data/lib/rubocop/config/default.yml +1 -1
  51. data/lib/tasks/docs.rake +5 -96
  52. data/static/arguments.yml +51 -15
  53. data/static/audited_at.json +9 -1
  54. data/static/classes.yml +157 -269
  55. data/static/constants.json +55 -15
  56. data/static/statuses.json +9 -1
  57. data/tailwind.config.js +11 -26
  58. metadata +13 -10
  59. data/app/components/ariadne/tab_bar_component.html.erb +0 -3
  60. data/app/components/ariadne/tab_bar_component.rb +0 -45
  61. data/app/lib/ariadne/join_style_arguments_helper.rb +0 -14
  62. data/app/lib/ariadne/tab_nav_helper.rb +0 -35
  63. data/app/lib/ariadne/tabbed_component_helper.rb +0 -39
  64. data/app/lib/ariadne/test_selector_helper.rb +0 -20
  65. data/app/lib/ariadne/underline_nav_helper.rb +0 -44
@@ -0,0 +1 @@
1
+ import '@github/tab-container-element';
@@ -0,0 +1,9 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class TabNavComponent extends Controller {
3
+ static targets: string[];
4
+ readonly tabTargets: [HTMLAnchorElement];
5
+ SELECTED_CLASSES: string[];
6
+ UNSELECTED_CLASSES: string[];
7
+ connect(): void;
8
+ toggle(event: Event): void;
9
+ }
@@ -6,8 +6,10 @@ import ClipboardCopyComponent from './clipboard-copy-component'
6
6
  import CommentComponent from './comment-component'
7
7
  import RichTextAreaComponent from './rich-text-area-component'
8
8
  import SlideoverComponent from './slideover-component'
9
+ import TabNavComponent from './tab-nav-component'
9
10
  import TooltipComponent from './tooltip-component'
10
11
 
12
+ import './tab-container-component'
11
13
  import './time-ago-component'
12
14
 
13
15
  const application = Application.start()
@@ -17,4 +19,5 @@ application.register('ariadne-form', AriadneForm)
17
19
  application.register('comment-component', CommentComponent)
18
20
  application.register('rich-text-area-component', RichTextAreaComponent)
19
21
  application.register('slideover-component', SlideoverComponent)
22
+ application.register('tab-nav-component', TabNavComponent)
20
23
  application.register('tooltip-component', TooltipComponent)
@@ -17,7 +17,7 @@ module Ariadne
17
17
  @tag = :body
18
18
  @classes = class_names(
19
19
  DEFAULT_CLASSES,
20
- classes
20
+ classes,
21
21
  )
22
22
 
23
23
  @attributes = attributes
@@ -82,7 +82,7 @@ module Ariadne
82
82
  #
83
83
  # @example With leading visual
84
84
  # <%= render(Ariadne::ButtonComponent.new) do |c| %>
85
- # <% c.icon(icon: :star, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE, classes: "text-yellow-600") %>
85
+ # <% c.icon(icon: :star, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE, classes: "ariadne-text-yellow-600") %>
86
86
  # Button
87
87
  # <% end %>
88
88
  #
@@ -135,7 +135,7 @@ module Ariadne
135
135
 
136
136
  @classes = class_names(
137
137
  SCHEME_CLASS_MAPPINGS[@scheme],
138
- classes
138
+ classes,
139
139
  )
140
140
  end
141
141
 
@@ -2,16 +2,12 @@
2
2
  <div class="ariadne-bg-white" data-comment-component-target="commentComponent">
3
3
  <div class="ariadne-hidden sm:ariadne-block">
4
4
  <div class="ariadne-border-b ariadne-border-gray-200">
5
- <%= render(Ariadne::TabBarComponent.new(sr_label: @sr_label, attributes: { :"data-comment-component-target" => "tabBarComponent" })) do |tab_bar| %>
6
- <%= public_tab %>
7
- <%= internal_tab %>
8
- <% end %>
9
5
  </div>
10
6
  </div>
11
7
  <%= ariadne_form_with(url: @url, method: @method, classes: @classes, attributes: @attributes) do |comment_box| %>
12
8
  <div class="ariadne-overflow-hidden ariadne-border-x ariadne-border-b ariadne-border-gray-300 ariadne-shadow-sm focus-within:ariadne-border-indigo-500 focus-within:ariadne-ring-1 focus-within:ariadne-ring-indigo-500">
13
- <%= hidden_field_tag 'message_public', true %>
14
- <%= render(Ariadne::RichTextAreaComponent.new(name: :bodytext, sr_label: "Select reply type", attributes: { required: true})) %>
9
+ <%= hidden_field_tag 'message_public', true %>
10
+ <%= render(Ariadne::RichTextAreaComponent.new(name: :bodytext, sr_label: "Select reply type", attributes: { required: true})) %>
15
11
  <% comment_box.submit { @submit } %>
16
12
  </div>
17
13
  <div class="ariadne-mt-2 ariadne-flex ariadne-justify-end">
@@ -42,7 +42,7 @@ module Ariadne
42
42
  @tag = DEFAULT_TAG
43
43
  @classes = class_names(
44
44
  DEFAULT_CLASSES,
45
- classes
45
+ classes,
46
46
  )
47
47
  @url = url
48
48
  @method = method
@@ -10,15 +10,13 @@ module Ariadne
10
10
  include ViewComponent::SlotableV2 unless ViewComponent::Base < ViewComponent::SlotableV2
11
11
  include ClassNameHelper
12
12
  include FetchOrFallbackHelper
13
- include TestSelectorHelper
14
- include JoinStyleArgumentsHelper
15
13
  include ViewHelper
16
14
  include Status::Dsl
17
15
  include Audited::Dsl
18
16
  include LoggerHelper
19
17
  include Ariadne::ActionViewExtensions::FormHelper
20
18
 
21
- BASE_HTML_CLASSES = "ariadne-h-full scroll-smooth ariadne-bg-white ariadne-antialiased"
19
+ BASE_HTML_CLASSES = "ariadne-h-full ariadne-scroll-smooth ariadne-bg-white ariadne-antialiased"
22
20
  BASE_BODY_CLASSES = "ariadne-flex ariadne-h-full ariadne-flex-col"
23
21
  BASE_WRAPPER_CLASSES = "ariadne-flex ariadne-flex-col ariadne-h-screen ariadne-justify-between"
24
22
  BASE_MAIN_CLASSES = "ariadne-flex-auto"
@@ -84,7 +82,7 @@ module Ariadne
84
82
  if (denylist = attributes[denylist_name])
85
83
  check_denylist(denylist, attributes)
86
84
 
87
- # Remove :attributes_denylist key and any denied keys from system arguments
85
+ # Remove :attributes_denylist key and any denied keys from attributes
88
86
  attributes.except!(denylist_name)
89
87
  attributes.except!(*denylist.keys.flatten)
90
88
  end
@@ -109,7 +107,7 @@ module Ariadne
109
107
  deny_aria_key(
110
108
  :label,
111
109
  "Don't use `aria-label` on `#{tag}` elements. See https://www.tpgi.com/short-note-on-aria-label-aria-labelledby-and-aria-describedby/",
112
- attributes
110
+ attributes,
113
111
  )
114
112
  end
115
113
 
@@ -16,7 +16,7 @@ module Ariadne
16
16
  @tag = :div
17
17
  @classes = class_names(
18
18
  DEFAULT_CLASSES,
19
- classes
19
+ classes,
20
20
  )
21
21
 
22
22
  @attributes = attributes
@@ -50,7 +50,7 @@ module Ariadne
50
50
 
51
51
  @classes = class_names(
52
52
  DEFAULT_CLASSES,
53
- classes
53
+ classes,
54
54
  )
55
55
  @attributes[:hidden] = true if count == 0 && hide_if_zero
56
56
  end
@@ -104,7 +104,7 @@ module Ariadne
104
104
 
105
105
  @classes = class_names(
106
106
  CONTENT_SCHEME_CLASS_MAPPINGS[@scheme],
107
- classes
107
+ classes,
108
108
  )
109
109
 
110
110
  @attributes = attributes
@@ -36,7 +36,7 @@ module Ariadne
36
36
  @classes = class_names(
37
37
  DEFAULT_CLASSES,
38
38
  classes,
39
- flex_class
39
+ flex_class,
40
40
  )
41
41
 
42
42
  @attributes = attributes
@@ -14,7 +14,7 @@ module Ariadne
14
14
  def initialize(classes: "", attributes: {})
15
15
  @classes = class_names(
16
16
  DEFAULT_CLASSES,
17
- classes
17
+ classes,
18
18
  )
19
19
 
20
20
  @attributes = attributes
@@ -1,8 +1,8 @@
1
1
  <%= render Ariadne::BaseComponent.new(tag: :ul, classes: @classes, attributes: @attributes) do |grid| %>
2
2
  <% items.each do |item| %>
3
- <li class="<%= item.classes %> <%= item.has_href? ? Ariadne::GridComponent::DEFAULT_LINK_COLOR_CLASSES : "ariadne-bg-white" %>">
3
+ <li class="<%= item.classes %>">
4
4
  <% if item.has_href? %>
5
- <%= render Ariadne::LinkComponent.new(href: item.href) do %>
5
+ <%= render Ariadne::LinkComponent.new(href: item.href, classes: Ariadne::GridComponent::GridItem::DEFAULT_ACTION_LINK_COLORS) do %>
6
6
  <%= item.entry %>
7
7
  <% end %>
8
8
  <% if item.actions.any? %>
@@ -5,7 +5,7 @@ module Ariadne
5
5
  class GridComponent < Ariadne::Component
6
6
  DEFAULT_CLASSES = "ariadne-grid ariadne-gap-6 sm:ariadne-grid-cols-2 lg:ariadne-grid-cols-3"
7
7
 
8
- DEFAULT_LINK_COLOR_CLASSES = "ariadne-text-button-text-color ariadne-bg-button-bg-color focus:ariadne-outline-none focus:ariadne-ring-2 focus:ariadne-ring-offset-2 focus:ariadne-ring-purple-500"
8
+ DEFAULT_LINK_COLOR_CLASSES = "focus:ariadne-outline-none focus:ariadne-ring-2 focus:ariadne-ring-offset-2 focus:ariadne-ring-purple-500"
9
9
 
10
10
  # The items to show in the ariadne-grid.
11
11
  renders_many :items, "GridItem"
@@ -19,7 +19,7 @@ module Ariadne
19
19
  def initialize(classes: "", attributes: {})
20
20
  @classes = class_names(
21
21
  DEFAULT_CLASSES,
22
- classes
22
+ classes,
23
23
  )
24
24
 
25
25
  default_attributes = { role: "list" }
@@ -29,18 +29,20 @@ module Ariadne
29
29
  # This component is part of `GridComponent` and should not be
30
30
  # used as a standalone component.
31
31
  class GridItem < Ariadne::Component
32
- DEFAULT_ITEM_CLASSES = "ariadne-flex ariadne-flex-col ariadne-text-center ariadne-rounded-lg ariadne-shadow ariadne-my-4 text-black-700 ariadne-border-black"
32
+ DEFAULT_ITEM_CLASSES = "ariadne-flex ariadne-flex-col ariadne-text-center ariadne-rounded-lg ariadne-shadow text-black-700 ariadne-border-black"
33
33
 
34
- DEFAULT_ENTRY_CLASSES = "group ariadne-flex-1 ariadne-flex ariadne-flex-col ariadne-p-8 hover:ariadne-bg-button-hover-color hover:ariadne-text-gray-500 ariadne-rounded-lg"
35
- renders_one :entry, lambda { |classes: "", &block|
34
+ DEFAULT_ENTRY_CLASSES = "ariadne-group ariadne-flex-1 ariadne-flex ariadne-flex-col ariadne-p-8 hover:ariadne-text-gray-500"
35
+ renders_one :entry, lambda { |classes: "", attributes: {}, &block|
36
36
  view_context.capture do
37
- render(Ariadne::BaseComponent.new(tag: :div, classes: classes)) { block&.call }
37
+ actual_classes = class_names(DEFAULT_ENTRY_CLASSES, classes)
38
+ render(Ariadne::BaseComponent.new(tag: :div, classes: actual_classes, attributes: attributes)) { block&.call }
38
39
  end
39
40
  }
40
41
 
41
- DEFAULT_ACTION_LINK_CLASSES = "text-button-text-color ariadne-relative ariadne--mr-px ariadne-w-0 ariadne-flex-1 ariadne-inline-flex ariadne-items-center ariadne-justify-center ariadne-py-4 ariadne-text-sm ariadne-font-medium ariadne-border ariadne-border-transparent ariadne-rounded-bl-lg ariadne-rounded-lg"
42
+ DEFAULT_ACTION_LINK_CLASSES = "ariadne-relative ariadne--mr-px ariadne-w-0 ariadne-flex-1 ariadne-inline-flex ariadne-items-center ariadne-justify-center ariadne-py-4 ariadne-text-sm ariadne-font-medium ariadne-border ariadne-border-transparent"
43
+ DEFAULT_ACTION_LINK_COLORS = "hover:ariadne-text-gray-500"
42
44
  renders_many :actions, lambda { |href:, icon:, label:, size: Ariadne::HeroiconComponent::SIZE_DEFAULT, variant: HeroiconsHelper::Icon::VARIANT_SOLID, classes: "", attributes: {}, text_classes: ""|
43
- actual_classes = class_names(DEFAULT_ACTION_LINK_CLASSES, classes)
45
+ actual_classes = class_names(DEFAULT_ACTION_LINK_CLASSES, DEFAULT_ACTION_LINK_COLORS, classes)
44
46
  render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) do
45
47
  render(Ariadne::HeroiconComponent.new(icon: icon, size: size, variant: variant, text_classes: text_classes)) { label }
46
48
  end
@@ -85,7 +85,7 @@ module Ariadne
85
85
  def initialize(classes: "", attributes: {})
86
86
  @classes = class_names(
87
87
  DEFAULT_CLASSES,
88
- classes
88
+ classes,
89
89
  )
90
90
 
91
91
  @attributes = attributes
@@ -1,4 +1,4 @@
1
1
  <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
2
- <%= @icon.path.html_safe %>
2
+ <%= @icon.path %>
3
3
  <% end %>
4
4
  <%= render(Ariadne::BaseComponent.new(tag: :span, classes: @text_classes, attributes: @text_attributes)) { content } if content.present? %>
@@ -107,10 +107,9 @@ module Ariadne
107
107
  }
108
108
 
109
109
  @icon = heroicon(icon, variant: variant, **attributes)
110
-
111
110
  @classes = class_names(
112
111
  @icon.attributes[:class],
113
- classes
112
+ classes,
114
113
  )
115
114
  @attributes.merge!(@icon.attributes.except(:class, :"aria-hidden"))
116
115
 
@@ -1,13 +1,11 @@
1
1
  <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do |list| %>
2
2
  <% items.each do |item| %>
3
- <% if item.linked? %>
4
- <%= render Ariadne::BaseComponent.new(tag: :li, classes: item.classes, attributes: item.attributes) do %>
3
+ <%= render Ariadne::BaseComponent.new(tag: :li, classes: item.classes, attributes: item.attributes) do %>
4
+ <% if item.linked? %>
5
5
  <%= render Ariadne::LinkComponent.new(href: item.link[:href], classes: item.link[:classes], attributes: item.link[:attributes]) do %>
6
6
  <%= item.entry %>
7
7
  <% end %>
8
- <% end %>
9
- <% else %>
10
- <%= render Ariadne::BaseComponent.new(tag: :li, classes: item.classes, attributes: item.attributes) do %>
8
+ <% else %>
11
9
  <%= item.entry %>
12
10
  <% end %>
13
11
  <% end %>
@@ -36,11 +36,11 @@ module Ariadne
36
36
  class ListItem < Ariadne::Component
37
37
  DEFAULT_ITEM_CLASSES = "ariadne-relative ariadne-p-1.5 focus:ariadne-ring-2 focus:ariadne-ring-offset-2 focus:ariadne-ring-purple-500 hover:ariadne-bg-button-hover-color"
38
38
 
39
- renders_one :entry, lambda { |static_content = nil, &block|
40
- next static_content if static_content.present?
41
-
42
- render(Ariadne::BaseComponent.new(tag: :div)) do
43
- view_context.capture { block&.call }
39
+ DEFAULT_ENTRY_CLASSES = "hover:ariadne-text-gray-500"
40
+ renders_one :entry, lambda { |classes: "", attributes: {}, &block|
41
+ view_context.capture do
42
+ actual_classes = class_names(DEFAULT_ENTRY_CLASSES, classes)
43
+ render(Ariadne::BaseComponent.new(tag: :div, classes: actual_classes, attributes: attributes)) { block&.call }
44
44
  end
45
45
  }
46
46
 
@@ -17,7 +17,7 @@ module Ariadne
17
17
  @tag = :main
18
18
  @classes = class_names(
19
19
  DEFAULT_CLASSES,
20
- classes
20
+ classes,
21
21
  )
22
22
 
23
23
  @attributes = attributes
@@ -21,7 +21,7 @@ module Ariadne
21
21
  @tag = :div
22
22
  @classes = class_names(
23
23
  DEFAULT_CLASSES,
24
- classes
24
+ classes,
25
25
  )
26
26
 
27
27
  @attributes = attributes
@@ -23,7 +23,7 @@ module Ariadne
23
23
  @tag = DEFAULT_TAG
24
24
  @classes = class_names(
25
25
  DEFAULT_CLASSES,
26
- classes
26
+ classes,
27
27
  )
28
28
 
29
29
  @attributes = attributes
@@ -72,7 +72,7 @@ module Ariadne
72
72
  end
73
73
 
74
74
  def call
75
- Ariadne::BaseComponent.new(tag: :div, classes: @classes, attributes: @attributes)
75
+ render(Ariadne::BaseComponent.new(tag: :div, classes: @classes, attributes: @attributes))
76
76
  end
77
77
  end
78
78
  end
@@ -20,7 +20,7 @@ module Ariadne
20
20
  @tag = check_incoming_tag(DEFAULT_TAG, tag)
21
21
  @classes = class_names(
22
22
  DEFAULT_CLASSES,
23
- classes
23
+ classes,
24
24
  )
25
25
 
26
26
  @attributes = attributes
@@ -1,6 +1,6 @@
1
1
  <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
2
2
  <% if @has_form %>
3
- <input type="hidden" data-tiptap-value-container=true name="<%= @name %>" id="<%= @name %>" />
3
+ <input type="hidden" data-tiptap-value-container=true name="<%= @name %>" id="<%= @name %>">
4
4
  <% end %>
5
5
  <textarea class="tiptap-editor"></textarea>
6
6
  <% end %>
@@ -25,7 +25,7 @@ module Ariadne
25
25
 
26
26
  @classes = class_names(
27
27
  DEFAULT_CLASSES,
28
- classes
28
+ classes,
29
29
  )
30
30
  @attributes = attributes
31
31
  @attributes[:"data-controller"] = "rich-text-area-component"
@@ -57,7 +57,7 @@ module Ariadne
57
57
  #
58
58
  # @param tag [Symbol, String] The rendered tag name.
59
59
  # @param direction [Symbol] <%= one_of(Ariadne::SlideoverComponent::VALID_DIRECTIONS) %>
60
- # @param form_id [String] The ID of any <form> tag to submit when the button is clicked.
60
+ # @param form_id [String] The ID of any `form` tag to submit when the button is clicked.
61
61
  # @param open_text [String] The text to use to open the slideover.
62
62
  # @param close_text [String] The text to use to close the slideover.
63
63
  # @param classes [String] <%= link_to_classes_docs %>
@@ -66,7 +66,7 @@ module Ariadne
66
66
  @tag = check_incoming_tag(DEFAULT_TAG, tag)
67
67
  @classes = class_names(
68
68
  DEFAULT_CLASSES,
69
- classes
69
+ classes,
70
70
  )
71
71
 
72
72
  @direction = fetch_or_raise(VALID_DIRECTIONS, direction)
@@ -0,0 +1,24 @@
1
+ import '@github/tab-container-element'
2
+
3
+ // // keep in sync with tab_container_component.rb
4
+ // const DEFAULT_SELECTED_CLASSES: string[] = ['ariadne-border-indigo-500', 'ariadne-text-indigo-600']
5
+ // const DEFAULT_UNSELECTED_CLASSES: string[] = [
6
+ // 'ariadne-text-gray-500',
7
+ // 'hover:ariadne-text-gray-700',
8
+ // 'hover:ariadne-border-gray-300'
9
+ // ]
10
+
11
+ // for (const tabContainer of document.getElementsByTagName('tab-container')) {
12
+ // tabContainer.addEventListener('tab-container-change', function (event: Event) {
13
+ // const newPanel = (event as CustomEvent).detail.relatedTarget as HTMLElement
14
+ // const tabContainer = newPanel.closest('tab-container') as HTMLElement
15
+ // const tabList = tabContainer.firstElementChild as HTMLElement
16
+ // const currentTab = tabList.querySelector('[aria-selected="true"]') as HTMLElement
17
+ // const tabId = newPanel.getAttribute('id')?.split('-').slice(1).join('-')
18
+ // const newTab = tabList.querySelector(`#${tabId}`) as HTMLElement
19
+ // currentTab.classList.remove(...DEFAULT_SELECTED_CLASSES)
20
+ // currentTab.classList.add(...DEFAULT_UNSELECTED_CLASSES)
21
+ // newTab.classList.add(...DEFAULT_SELECTED_CLASSES)
22
+ // newTab.classList.remove(...DEFAULT_UNSELECTED_CLASSES)
23
+ // })
24
+ // }
@@ -0,0 +1,34 @@
1
+ import {Controller} from '@hotwired/stimulus'
2
+
3
+ export default class TabNavComponent extends Controller {
4
+ static targets = ['tab']
5
+
6
+ declare readonly tabTargets: [HTMLAnchorElement]
7
+
8
+ // keep in synch with tab_nav_component
9
+ SELECTED_CLASSES = ['ariadne-border-indigo-500', 'ariadne-text-indigo-600']
10
+ UNSELECTED_CLASSES = ['ariadne-text-gray-500', 'hover:ariadne-text-gray-700', 'hover:ariadne-border-gray-300']
11
+
12
+ connect() {
13
+ for (const tab of this.tabTargets) {
14
+ if (tab.hasAttribute('aria-current')) {
15
+ tab.classList.add(...this.SELECTED_CLASSES)
16
+ tab.classList.remove(...this.UNSELECTED_CLASSES)
17
+ }
18
+ }
19
+ }
20
+
21
+ toggle(event: Event) {
22
+ for (const tab of this.tabTargets) {
23
+ if (tab === event.target) {
24
+ tab.setAttribute('aria-current', 'page')
25
+ tab.classList.add(...this.SELECTED_CLASSES)
26
+ tab.classList.remove(...this.UNSELECTED_CLASSES)
27
+ } else if (tab.hasAttribute('aria-current')) {
28
+ tab.removeAttribute('aria-current')
29
+ tab.classList.add(...this.UNSELECTED_CLASSES)
30
+ tab.classList.remove(...this.SELECTED_CLASSES)
31
+ }
32
+ }
33
+ }
34
+ }
@@ -1,7 +1,3 @@
1
- <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do |component| %>
2
- <% if text.present? %>
3
- <%= text %>
4
- <% else %>
5
- <%= content %>
6
- <% end %>
1
+ <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
2
+ <%= text || content %>
7
3
  <% end %>
@@ -1,43 +1,102 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ariadne
4
- # Represents a sequence of tabs which, when clicked, swap between panel contents.
4
+ # This component is part of navigation components such as `Ariadne::TabContainerComponent`.
5
+ # and `Ariadne::TabNavComponent` and should not be used by itself.
6
+ #
7
+ # @accessibility
8
+ # `TabComponent` renders the selected anchor tab with `aria-current="page"` by default.
9
+ # When the selected tab does not correspond to the current page, such as in a nested inner tab, make sure to use aria-current="true"
5
10
  class TabComponent < Ariadne::Component
6
- DEFAULT_TAG = :button
7
- TAG_OPTIONS = [DEFAULT_TAG].freeze
11
+ DEFAULT_ARIA_CURRENT_FOR_ANCHOR = :page
12
+ ARIA_CURRENT_OPTIONS_FOR_ANCHOR = [true, DEFAULT_ARIA_CURRENT_FOR_ANCHOR].freeze
8
13
 
9
- # The Tab's text.
14
+ # Panel controlled by the Tab. This will not render anything in the tab itself.
15
+ # It will provide a accessor for the Tab's parent to call and render the panel
16
+ # content in the appropriate place.
17
+ #
18
+ # @param classes [String] <%= link_to_classes_docs %>
19
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
20
+ renders_one :panel, lambda { |classes: "", attributes: {}|
21
+ attributes[:id] = @panel_id
22
+ tag = :div
23
+ attributes[:role] ||= :tabpanel
24
+ attributes[:tabindex] = 0
25
+ attributes[:hidden] = true unless @selected
26
+
27
+ label_present = aria("label", attributes) || aria("labelledby", attributes)
28
+ unless label_present
29
+ if @id.present?
30
+ attributes[:"aria-labelledby"] = @id
31
+ else
32
+ raise ArgumentError, "Panels must be labelled. Either set a unique `id` on the tab, or set an `aria-label` directly on the panel"
33
+ end
34
+ end
35
+
36
+ Ariadne::BaseComponent.new(tag: tag, classes: classes, attributes: attributes)
37
+ }
38
+
39
+ # The tab's text.
10
40
  #
11
41
  # @param kwargs [Hash] The same arguments as <%= link_to_component(Ariadne::Text) %>.
12
42
  renders_one :text, Ariadne::Text
43
+ # TODO: test what hapenns with really long inbox names
13
44
 
14
45
  attr_reader :selected
15
46
 
16
- DEFAULT_CLASSES = "ariadne-border-gray-300 ariadne-border ariadne-shadow ariadne-py-5 ariadne-px-5 ariadne-rounded-md"
17
-
18
- BASE_TAB_CLASSES = "ariadne-whitespace-nowrap ariadne-py-4 ariadne-px-1 ariadne-border-b-2 ariadne-font-medium ariadne-text-sm"
47
+ DEFAULT_CLASSES = "ariadne-border-transparent ariadne-text-gray-500 hover:ariadne-text-gray-700 hover:ariadne-border-gray-300 ariadne-whitespace-nowrap ariadne-py-4 ariadne-px-1 ariadne-border-b-2 ariadne-font-medium ariadne-text-sm"
19
48
 
20
49
  # @example Default
21
50
  #
22
- # <%= render(Ariadne::TabComponent.new) { "Example" } %>
23
- #
24
- # @param tag [Symbol, String] The rendered tag name.
51
+ # <%= render(Ariadne::TabComponent.new(id: "pub-comment")) do |tab| %>
52
+ # <% tab.text { "Public comment" } %>
53
+ # <% tab.panel { "Public comment panel" } %>
54
+ # <% end %>
55
+ # @param id [String] The unique ID of the tab.
25
56
  # @param selected [Boolean] Whether the tab is selected or not.
57
+ # @param with_panel [Boolean] Whether the Tab has an associated panel.
58
+ # @param href [String] The link to navigate to when the tab is clicked.
26
59
  # @param classes [String] <%= link_to_classes_docs %>
27
60
  # @param attributes [Hash] <%= link_to_attributes_docs %>
28
- def initialize(tag: DEFAULT_TAG, selected: false, classes: "", attributes: {})
29
- @tag = check_incoming_tag(DEFAULT_TAG, tag)
61
+ def initialize(id: nil, selected: false, with_panel: false, href: "", classes: "", attributes: {})
62
+ @id = id
63
+
64
+ @href = href
65
+ @selected = selected
66
+
30
67
  @classes = class_names(
31
68
  DEFAULT_CLASSES,
32
- classes
69
+ classes,
33
70
  )
34
71
 
35
72
  @attributes = attributes
36
- @attributes[:id] ||= "tabs-tab-#{@tab_idx}"
37
- @attributes[:"aria-controls"] = "tabs-panel-#{@tab_idx}"
38
- @attributes[:"aria-current"] = "page" if selected
39
- @attributes[:selected] = selected
40
- @classes = class_names(BASE_TAB_CLASSES, classes)
73
+ @attributes[:id] ||= @id
74
+
75
+ if with_panel # TODO: test
76
+ if @id.blank?
77
+ raise ArgumentError, "Tabs with panels must have a unique `id`"
78
+ end
79
+
80
+ @tag = :button
81
+ @system_arguments[:tag] = :button
82
+ @system_arguments[:type] = :button
83
+ @system_arguments[:role] = :tab
84
+ @panel_id = "panel-#{@id}"
85
+ @attributes[:"aria-controls"] = @panel_id
86
+ # https://www.w3.org/TR/wai-aria-practices/#presentation_role
87
+ @wrapper_arguments[:role] = :presentation
88
+ else
89
+ @tag = :a
90
+ end
91
+
92
+ return unless @selected
93
+
94
+ if @tag == :a
95
+ aria_current = aria("current", attributes) || DEFAULT_ARIA_CURRENT_FOR_ANCHOR
96
+ @attributes[:"aria-current"] = fetch_or_raise(ARIA_CURRENT_OPTIONS_FOR_ANCHOR, aria_current)
97
+ else
98
+ @attributes[:"aria-selected"] = true
99
+ end
41
100
  end
42
101
  end
43
102
  end
@@ -0,0 +1,12 @@
1
+ <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do |component| %>
2
+ <div role="tablist">
3
+ <% tabs.each do |tab| %>
4
+ <%= tab %>
5
+ <% end %>
6
+ </div>
7
+ <% tabs.each do |tab| %>
8
+ <div class="ariadne-pt-2">
9
+ <%= tab.panel %>
10
+ </div>
11
+ <% end %>
12
+ <% end %>