ariadne_view_components 0.0.12-x64-mingw32 → 0.0.14-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.
- 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/comment-component.d.ts +1 -2
- data/app/assets/javascripts/tab-container-component.d.ts +1 -0
- data/app/assets/javascripts/tab-nav-component.d.ts +9 -0
- data/app/assets/stylesheets/ariadne_view_components.css +1 -0
- data/app/assets/stylesheets/dropdown.css +46 -0
- data/app/assets/stylesheets/tooltip-component.css +8 -8
- data/app/components/ariadne/ariadne.ts +3 -2
- data/app/components/ariadne/avatar_component.rb +81 -0
- data/app/components/ariadne/avatar_stack_component.html.erb +12 -0
- data/app/components/ariadne/avatar_stack_component.rb +75 -0
- data/app/components/ariadne/base_button.rb +13 -4
- data/app/components/ariadne/blankslate_component.rb +4 -4
- data/app/components/ariadne/body_component.rb +1 -1
- data/app/components/ariadne/button_component.html.erb +1 -1
- data/app/components/ariadne/button_component.rb +11 -5
- data/app/components/ariadne/comment-component.ts +32 -50
- data/app/components/ariadne/comment_component.html.erb +31 -13
- data/app/components/ariadne/comment_component.rb +18 -6
- 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/details_component.html.erb +4 -0
- data/app/components/ariadne/details_component.rb +80 -0
- data/app/components/ariadne/dropdown/menu_component.html.erb +20 -0
- data/app/components/ariadne/dropdown/menu_component.rb +101 -0
- data/app/components/ariadne/dropdown/menu_component.ts +1 -0
- data/app/components/ariadne/dropdown_component.html.erb +8 -0
- data/app/components/ariadne/dropdown_component.rb +172 -0
- data/app/components/ariadne/flash_component.rb +1 -1
- data/app/components/ariadne/flex_component.rb +2 -2
- data/app/components/ariadne/footer_component.html.erb +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 +3 -3
- data/app/components/ariadne/heroicon_component.html.erb +1 -1
- data/app/components/ariadne/heroicon_component.rb +7 -6
- data/app/components/ariadne/inline_flex_component.html.erb +1 -0
- data/app/components/ariadne/inline_flex_component.rb +8 -1
- data/app/components/ariadne/link_component.rb +2 -2
- data/app/components/ariadne/list_component.html.erb +2 -11
- data/app/components/ariadne/list_component.rb +11 -15
- data/app/components/ariadne/main_component.rb +2 -2
- 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 +20 -6
- data/app/components/ariadne/rich-text-area-component.ts +4 -4
- data/app/components/ariadne/rich_text_area_component.html.erb +2 -2
- data/app/components/ariadne/rich_text_area_component.rb +2 -2
- data/app/components/ariadne/slideover_component.html.erb +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 +75 -19
- data/app/components/ariadne/tab_container_component.erb +12 -0
- data/app/components/ariadne/tab_container_component.rb +67 -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_nav_component.html.erb +52 -0
- data/app/components/ariadne/table_nav_component.rb +338 -0
- data/app/components/ariadne/time_ago_component.rb +1 -1
- data/app/components/ariadne/timeline_component.rb +1 -1
- data/app/components/ariadne/tooltip_component.html.erb +1 -1
- data/app/components/ariadne/tooltip_component.rb +4 -4
- data/app/lib/ariadne/action_view_extensions/form_helper.rb +21 -7
- data/app/lib/ariadne/fetch_or_fallback_helper.rb +11 -3
- data/app/lib/ariadne/form_builder.rb +2 -2
- 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 +22 -97
- data/static/arguments.yml +151 -23
- data/static/audited_at.json +17 -1
- data/static/classes.yml +160 -268
- data/static/constants.json +208 -40
- data/static/statuses.json +17 -1
- data/tailwind.config.js +28 -19
- metadata +24 -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
@@ -11,13 +11,15 @@ module Ariadne
|
|
11
11
|
include IconHelper
|
12
12
|
include HeroiconsHelper
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
SIZE_MICRO = :mu
|
15
|
+
SIZE_SMALL = :sm
|
16
|
+
SIZE_DEFAULT = :md
|
16
17
|
SIZE_LARGE = :lg
|
17
18
|
|
18
19
|
SIZE_MAPPINGS = {
|
19
|
-
|
20
|
-
|
20
|
+
SIZE_MICRO => 16,
|
21
|
+
SIZE_SMALL => 20,
|
22
|
+
SIZE_DEFAULT => 24,
|
21
23
|
SIZE_LARGE => 128,
|
22
24
|
}.freeze
|
23
25
|
SIZE_OPTIONS = SIZE_MAPPINGS.keys
|
@@ -107,10 +109,9 @@ module Ariadne
|
|
107
109
|
}
|
108
110
|
|
109
111
|
@icon = heroicon(icon, variant: variant, **attributes)
|
110
|
-
|
111
112
|
@classes = class_names(
|
112
113
|
@icon.attributes[:class],
|
113
|
-
classes
|
114
|
+
classes,
|
114
115
|
)
|
115
116
|
@attributes.merge!(@icon.attributes.except(:class, :"aria-hidden"))
|
116
117
|
|
@@ -19,7 +19,7 @@ module Ariadne
|
|
19
19
|
</svg>
|
20
20
|
MSG
|
21
21
|
STATE_CLOSED_SVG = <<~MSG
|
22
|
-
<svg viewBox="0 0 24 24" width="12" height="12" class="ariadne-stroke-state-closed fill-state-closed " stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
22
|
+
<svg viewBox="0 0 24 24" width="12" height="12" class="ariadne-stroke-state-closed ariadne-fill-state-closed " stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
23
23
|
<circle cx="12" cy="12" r="10"></circle>
|
24
24
|
</svg>
|
25
25
|
MSG
|
@@ -42,12 +42,19 @@ module Ariadne
|
|
42
42
|
Ariadne::BaseComponent.new(tag: :span, classes: actual_classes, attributes: attributes) { content }
|
43
43
|
}
|
44
44
|
|
45
|
+
renders_one :dropdown, "Ariadne::DropdownComponent"
|
46
|
+
|
45
47
|
# @example Default
|
46
48
|
#
|
47
49
|
# <%= render(Ariadne::InlineFlexComponent.new) do |c| %>
|
48
50
|
# <% c.item { Ariadne::InlineFlexComponent::STATE_OPEN_SVG.html_safe } %>
|
49
51
|
# <% end %>
|
50
52
|
#
|
53
|
+
# # TODO: STATE_CLOSED_SVG colors didn't show until it was listed in an example
|
54
|
+
# <%= render(Ariadne::InlineFlexComponent.new) do |c| %>
|
55
|
+
# <% c.item { Ariadne::InlineFlexComponent::STATE_CLOSED_SVG.html_safe } %>
|
56
|
+
# <% end %>
|
57
|
+
#
|
51
58
|
# <%= render(Ariadne::InlineFlexComponent.new) do |c| %>
|
52
59
|
# <% c.icon(icon: :check, size: :sm, variant: HeroiconsHelper::Icon::VARIANT_SOLID) %>
|
53
60
|
# <% c.text { "Closed" } %>
|
@@ -6,8 +6,8 @@ module Ariadne
|
|
6
6
|
DEFAULT_TAG = :a
|
7
7
|
TAG_OPTIONS = [DEFAULT_TAG, :span].freeze
|
8
8
|
|
9
|
-
DEFAULT_CLASSES = "ariadne-cursor-pointer"
|
10
|
-
DEFAULT_ACTIONABLE_CLASSES = "
|
9
|
+
DEFAULT_CLASSES = "ariadne-cursor-pointer ariadne-font-semibold hover:ariadne-text-button-text-color focus:ariadne-outline-none focus:ariadne-ring-2 focus:ariadne-ring-offset-2 focus:ariadne-ring-purple-500"
|
10
|
+
DEFAULT_ACTIONABLE_CLASSES = "ariadne-underline ariadne-decoration-double"
|
11
11
|
|
12
12
|
# `Tooltip` that appears on mouse hover or keyboard focus over the button. Use tooltips sparingly and as a last resort.
|
13
13
|
# **Important:** This tooltip defaults to `type: :description`. In a few scenarios, `type: :label` may be more appropriate.
|
@@ -1,15 +1,6 @@
|
|
1
1
|
<%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do |list| %>
|
2
|
+
<%= content %>
|
2
3
|
<% items.each do |item| %>
|
3
|
-
|
4
|
-
<%= render Ariadne::BaseComponent.new(tag: :li, classes: item.classes, attributes: item.attributes) do %>
|
5
|
-
<%= render Ariadne::LinkComponent.new(href: item.link[:href], classes: item.link[:classes], attributes: item.link[:attributes]) do %>
|
6
|
-
<%= item.entry %>
|
7
|
-
<% end %>
|
8
|
-
<% end %>
|
9
|
-
<% else %>
|
10
|
-
<%= render Ariadne::BaseComponent.new(tag: :li, classes: item.classes, attributes: item.attributes) do %>
|
11
|
-
<%= item.entry %>
|
12
|
-
<% end %>
|
13
|
-
<% end %>
|
4
|
+
<%= item %>
|
14
5
|
<% end %>
|
15
6
|
<% end %>
|
@@ -13,7 +13,7 @@ module Ariadne
|
|
13
13
|
# <%= render(Ariadne::ListComponent.new) do |list| %>
|
14
14
|
# <% numbers.each do |number| %>
|
15
15
|
# <%= list.item do |item| %>
|
16
|
-
# <%=
|
16
|
+
# <%= number %>
|
17
17
|
# <% end %>
|
18
18
|
# <% end %>
|
19
19
|
# <% end %>
|
@@ -27,23 +27,11 @@ module Ariadne
|
|
27
27
|
@attributes = attributes
|
28
28
|
end
|
29
29
|
|
30
|
-
def render?
|
31
|
-
items.any?
|
32
|
-
end
|
33
|
-
|
34
30
|
# This component is part of `ListComponent` and should not be
|
35
31
|
# used as a standalone component.
|
36
32
|
class ListItem < Ariadne::Component
|
37
33
|
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
34
|
|
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 }
|
44
|
-
end
|
45
|
-
}
|
46
|
-
|
47
35
|
attr_reader :link, :classes, :attributes
|
48
36
|
|
49
37
|
def initialize(link: {}, classes: "", attributes: {})
|
@@ -56,12 +44,20 @@ module Ariadne
|
|
56
44
|
@selected
|
57
45
|
end
|
58
46
|
|
59
|
-
def linked?
|
47
|
+
private def linked?
|
60
48
|
@link.present?
|
61
49
|
end
|
62
50
|
|
63
51
|
def call
|
64
|
-
Ariadne::BaseComponent.new(tag: :
|
52
|
+
render(Ariadne::BaseComponent.new(tag: :li, classes: @classes, attributes: @attributes)) do
|
53
|
+
if linked?
|
54
|
+
render(Ariadne::LinkComponent.new(href: @link[:href], classes: @link[:classes], attributes: @link[:attributes])) do
|
55
|
+
content
|
56
|
+
end
|
57
|
+
else
|
58
|
+
content
|
59
|
+
end
|
60
|
+
end
|
65
61
|
end
|
66
62
|
end
|
67
63
|
end
|
@@ -5,7 +5,7 @@ module Ariadne
|
|
5
5
|
# Add additional usage considerations or best practices that may aid the user to use the component correctly.
|
6
6
|
# @accessibility Add any accessibility considerations
|
7
7
|
class MainComponent < Ariadne::Component
|
8
|
-
DEFAULT_CLASSES = "flex-auto"
|
8
|
+
DEFAULT_CLASSES = "ariadne-flex-auto"
|
9
9
|
|
10
10
|
# @example Default
|
11
11
|
#
|
@@ -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
|
@@ -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
|
@@ -6,25 +6,39 @@ module Ariadne
|
|
6
6
|
DEFAULT_TAG = :span
|
7
7
|
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
8
8
|
|
9
|
-
DEFAULT_CLASSES = "ariadne-flex-shrink-0 ariadne-inline-block ariadne-px-2 ariadne-py-0.5 ariadne-text-xs ariadne-font-medium ariadne-rounded-full"
|
9
|
+
DEFAULT_CLASSES = "ariadne-flex-shrink-0 ariadne-inline-block ariadne-px-2 ariadne-py-0.5 ariadne-text-xs ariadne-font-medium ariadne-rounded-full ariadne-whitespace-nowrap"
|
10
10
|
|
11
11
|
# @example Default
|
12
12
|
#
|
13
|
-
# <%= render(Ariadne::PillComponent.new(color:
|
13
|
+
# <%= render(Ariadne::PillComponent.new(color: [49, 186, 115, 1.0])) { "Admin" } %>
|
14
14
|
#
|
15
15
|
# @param tag [Symbol, String] The rendered tag name.
|
16
|
-
# @param color [String] The
|
16
|
+
# @param color [String] The rgba color of the pill.
|
17
17
|
# @param classes [String] <%= link_to_classes_docs %>
|
18
18
|
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
19
19
|
def initialize(tag: DEFAULT_TAG, color:, classes: "", attributes: {})
|
20
20
|
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
21
|
+
|
22
|
+
@red = color[0]
|
23
|
+
@green = color[1]
|
24
|
+
@blue = color[2]
|
25
|
+
@alpha = color[3]
|
26
|
+
|
27
|
+
@attributes = attributes
|
28
|
+
@attributes["style"] = "background-color: rgba(#{@red}, #{@green}, #{@blue}, #{@alpha});"
|
29
|
+
@text_color = contrast_of(@red, @green, @blue)
|
30
|
+
|
21
31
|
@classes = class_names(
|
22
32
|
DEFAULT_CLASSES,
|
23
|
-
classes
|
33
|
+
classes,
|
34
|
+
@text_color,
|
24
35
|
)
|
36
|
+
end
|
25
37
|
|
26
|
-
|
27
|
-
|
38
|
+
private def contrast_of(red, green, blue)
|
39
|
+
luminance = (0.299 * red + 0.587 * green + 0.114 * blue) / 255
|
40
|
+
|
41
|
+
luminance > 0.5 ? "ariadne-text-black" : "ariadne-text-white"
|
28
42
|
end
|
29
43
|
end
|
30
44
|
end
|
@@ -5,16 +5,16 @@ import {StarterKit} from '@tiptap/starter-kit'
|
|
5
5
|
|
6
6
|
export default class RichTextArea extends Controller {
|
7
7
|
connect() {
|
8
|
-
const
|
9
|
-
|
8
|
+
const editorElements = document.getElementsByClassName('tiptap-editor')
|
9
|
+
for (const editorElement of editorElements) {
|
10
10
|
new Editor({
|
11
11
|
element: editorElement,
|
12
12
|
extensions: [StarterKit],
|
13
|
-
content: '
|
13
|
+
content: '',
|
14
14
|
editorProps: {
|
15
15
|
attributes: {
|
16
16
|
class:
|
17
|
-
'ariadne-
|
17
|
+
'ariadne-m-5 focus:ariadne-outline-none ariadne-block ariadne-w-full ariadne-resize-none ariadne-p-0 ariadne-pb-2 ariadne-border-none focus:ariadne-ring-0 sm:ariadne-text-sm'
|
18
18
|
}
|
19
19
|
}
|
20
20
|
})
|
@@ -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
|
+
<div class="tiptap-editor"></div>
|
6
6
|
<% end %>
|
@@ -6,7 +6,7 @@ module Ariadne
|
|
6
6
|
# @accessibility Add any accessibility considerations
|
7
7
|
class RichTextAreaComponent < Ariadne::Component
|
8
8
|
DEFAULT_TAG = :div
|
9
|
-
DEFAULT_CLASSES = "ariadne-block
|
9
|
+
DEFAULT_CLASSES = "ariadne-block"
|
10
10
|
|
11
11
|
# @example Default
|
12
12
|
#
|
@@ -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"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do |slideover| %>
|
2
2
|
<div data-slideover-component-target="expandWrapper" class="ariadne-flex ariadne-flex-col ariadne-z-10">
|
3
|
-
<div data-slideover-component-target="expandable" class="ariadne-hidden
|
3
|
+
<div data-slideover-component-target="expandable" class="ariadne-hidden ariadne-px-3 ariadne-pb-4">
|
4
4
|
<%= content %>
|
5
5
|
</div>
|
6
6
|
<div data-slideover-component-target="buttonWrapper" data-slideover-component-form-id="<%= @form_id %>" class="ariadne-relative ariadne-flex ariadne-justify-center">
|
@@ -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,99 @@
|
|
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
|
-
attr_reader :selected
|
15
|
-
|
16
|
-
DEFAULT_CLASSES = "ariadne-border-gray-300 ariadne-border ariadne-shadow ariadne-py-5 ariadne-px-5 ariadne-rounded-md"
|
45
|
+
attr_reader :selected, :id, :classes, :attributes
|
17
46
|
|
18
|
-
|
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: nil, 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
|
+
@attributes[:type] = :button
|
82
|
+
@attributes[:role] = :tab
|
83
|
+
@panel_id = "panel-#{@id}"
|
84
|
+
@attributes[:"aria-controls"] = @panel_id
|
85
|
+
else
|
86
|
+
@tag = :a
|
87
|
+
end
|
88
|
+
|
89
|
+
return unless @selected
|
90
|
+
|
91
|
+
if @tag == :a
|
92
|
+
aria_current = aria("current", attributes) || DEFAULT_ARIA_CURRENT_FOR_ANCHOR
|
93
|
+
@attributes[:"aria-current"] = fetch_or_raise(ARIA_CURRENT_OPTIONS_FOR_ANCHOR, aria_current)
|
94
|
+
else
|
95
|
+
@attributes[:"aria-selected"] = true
|
96
|
+
end
|
41
97
|
end
|
42
98
|
end
|
43
99
|
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 %>
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# A container for a row of tags. Keyboard support is provided.
|
5
|
+
# @accessibility This component requires you to pass in a `sr_label`
|
6
|
+
# attribute, which will be used to label the tabs for screen readers.
|
7
|
+
class TabContainerComponent < Ariadne::Component
|
8
|
+
DEFAULT_TAG = :"tab-container"
|
9
|
+
|
10
|
+
DEFAULT_CLASSES = ""
|
11
|
+
|
12
|
+
DEFAULT_SELECTED_CLASSES = "ariadne-border-indigo-500 ariadne-text-indigo-600"
|
13
|
+
DEFAULT_UNSELECTED_CLASSES = "ariadne-text-gray-500 hover:ariadne-text-gray-700 hover:ariadne-border-gray-300"
|
14
|
+
|
15
|
+
# Tabs and panels to be rendered.
|
16
|
+
#
|
17
|
+
# @param id [String] The unique ID of the tab.
|
18
|
+
# @param selected [Boolean] Whether the tab is selected.
|
19
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
20
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
21
|
+
renders_many :tabs, lambda { |id: "", selected: false, classes: "", attributes: {}|
|
22
|
+
actual_classes = class_names(selected ? DEFAULT_SELECTED_CLASSES : DEFAULT_UNSELECTED_CLASSES, classes)
|
23
|
+
Ariadne::TabComponent.new(
|
24
|
+
id: id,
|
25
|
+
selected: selected,
|
26
|
+
with_panel: true,
|
27
|
+
|
28
|
+
classes: actual_classes,
|
29
|
+
attributes: attributes,
|
30
|
+
)
|
31
|
+
}
|
32
|
+
|
33
|
+
# @example Default
|
34
|
+
#
|
35
|
+
# <%= render(Ariadne::TabContainerComponent.new(sr_label: "Comments")) do |tab_container| %>
|
36
|
+
# <%= render(Ariadne::TabComponent.new(id: "pub-comment")) do |tab| %>
|
37
|
+
# <% tab.text { "Tab 1" } %>
|
38
|
+
# <% tab.panel { "Panel 1" } %>
|
39
|
+
# <% end %>
|
40
|
+
# <%= render(Ariadne::TabComponent.new(id: "pub-comment")) do |tab| %>
|
41
|
+
# <% tab.text { "Tab 2" } %>
|
42
|
+
# <% tab.panel { "Panel 2" } %>
|
43
|
+
# <% end %>
|
44
|
+
# <% end %>
|
45
|
+
#
|
46
|
+
# %>
|
47
|
+
#
|
48
|
+
# @param sr_label [String] Sets an `aria-label` that helps assistive technology users understand the purpose of the tabs.
|
49
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
50
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
51
|
+
def initialize(sr_label:, classes: "", attributes: {})
|
52
|
+
@tag = DEFAULT_TAG
|
53
|
+
@classes = class_names(
|
54
|
+
DEFAULT_CLASSES,
|
55
|
+
classes,
|
56
|
+
)
|
57
|
+
|
58
|
+
@attributes = attributes
|
59
|
+
@attributes[:"aria-label"] = sr_label
|
60
|
+
@attributes[:role] = :presentation
|
61
|
+
end
|
62
|
+
|
63
|
+
def render?
|
64
|
+
tabs.present?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|