ariadne_view_components 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/ariadne.d.ts +1 -0
- data/app/assets/javascripts/ariadne_view_components.js +2 -2
- data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
- data/app/assets/javascripts/tab-container-component.d.ts +1 -0
- data/app/assets/javascripts/tab-nav-component.d.ts +9 -0
- data/app/components/ariadne/ariadne.ts +3 -0
- data/app/components/ariadne/body_component.rb +1 -1
- data/app/components/ariadne/button_component.rb +2 -2
- data/app/components/ariadne/comment_component.html.erb +2 -6
- data/app/components/ariadne/comment_component.rb +1 -1
- data/app/components/ariadne/component.rb +3 -5
- data/app/components/ariadne/container_component.rb +1 -1
- data/app/components/ariadne/counter_component.rb +1 -1
- data/app/components/ariadne/flash_component.rb +1 -1
- data/app/components/ariadne/flex_component.rb +1 -1
- data/app/components/ariadne/footer_component.rb +1 -1
- data/app/components/ariadne/grid_component.html.erb +2 -2
- data/app/components/ariadne/grid_component.rb +10 -8
- data/app/components/ariadne/header_component.rb +1 -1
- data/app/components/ariadne/heroicon_component.html.erb +1 -1
- data/app/components/ariadne/heroicon_component.rb +1 -2
- data/app/components/ariadne/list_component.html.erb +3 -5
- data/app/components/ariadne/list_component.rb +5 -5
- data/app/components/ariadne/main_component.rb +1 -1
- data/app/components/ariadne/narrow_container_component.rb +1 -1
- data/app/components/ariadne/panel_bar_component.rb +2 -2
- data/app/components/ariadne/pill_component.rb +1 -1
- data/app/components/ariadne/rich_text_area_component.html.erb +1 -1
- data/app/components/ariadne/rich_text_area_component.rb +1 -1
- data/app/components/ariadne/slideover_component.rb +2 -2
- data/app/components/ariadne/tab-container-component.ts +24 -0
- data/app/components/ariadne/tab-nav-component.ts +34 -0
- data/app/components/ariadne/tab_component.html.erb +2 -6
- data/app/components/ariadne/tab_component.rb +77 -18
- data/app/components/ariadne/tab_container_component.erb +12 -0
- data/app/components/ariadne/tab_container_component.rb +61 -0
- data/app/components/ariadne/tab_nav_component.html.erb +7 -0
- data/app/components/ariadne/tab_nav_component.rb +72 -0
- data/app/components/ariadne/table_component.html.erb +17 -0
- data/app/components/ariadne/table_component.rb +281 -0
- data/app/components/ariadne/time_ago_component.rb +1 -1
- data/app/components/ariadne/timeline_component.rb +1 -1
- data/app/lib/ariadne/fetch_or_fallback_helper.rb +11 -3
- data/app/lib/ariadne/icon_helper.rb +17 -13
- data/lib/ariadne/view_components/constants.rb +2 -2
- data/lib/ariadne/view_components/statuses.rb +2 -2
- data/lib/ariadne/view_components/version.rb +1 -1
- data/lib/rubocop/config/default.yml +1 -1
- data/lib/tasks/docs.rake +5 -96
- data/static/arguments.yml +51 -15
- data/static/audited_at.json +9 -1
- data/static/classes.yml +157 -269
- data/static/constants.json +55 -15
- data/static/statuses.json +9 -1
- data/tailwind.config.js +11 -26
- metadata +13 -10
- data/app/components/ariadne/tab_bar_component.html.erb +0 -3
- data/app/components/ariadne/tab_bar_component.rb +0 -45
- data/app/lib/ariadne/join_style_arguments_helper.rb +0 -14
- data/app/lib/ariadne/tab_nav_helper.rb +0 -35
- data/app/lib/ariadne/tabbed_component_helper.rb +0 -39
- data/app/lib/ariadne/test_selector_helper.rb +0 -20
- 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)
|
@@ -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">
|
@@ -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
|
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
|
|
@@ -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 %>
|
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 = "
|
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
|
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-
|
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
|
-
|
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 = "
|
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
|
@@ -1,4 +1,4 @@
|
|
1
1
|
<%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
|
2
|
-
<%= @icon.path
|
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
|
-
|
4
|
-
|
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
|
-
<%
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
|
@@ -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
|
@@ -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 %>
|
@@ -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
|
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
|
2
|
-
|
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
|
-
#
|
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
|
-
|
7
|
-
|
11
|
+
DEFAULT_ARIA_CURRENT_FOR_ANCHOR = :page
|
12
|
+
ARIA_CURRENT_OPTIONS_FOR_ANCHOR = [true, DEFAULT_ARIA_CURRENT_FOR_ANCHOR].freeze
|
8
13
|
|
9
|
-
#
|
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-
|
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
|
-
#
|
23
|
-
#
|
24
|
-
#
|
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(
|
29
|
-
@
|
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] ||=
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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 %>
|