ariadne_view_components 0.0.43 → 0.0.44

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/app/components/ariadne/ariadne-form.d.ts +22 -0
  4. data/app/components/ariadne/ariadne-form.js +85 -0
  5. data/app/components/ariadne/ariadne-form.ts +96 -0
  6. data/app/components/ariadne/ariadne.d.ts +2 -0
  7. data/app/components/ariadne/ariadne.js +16 -0
  8. data/app/components/ariadne/ariadne.ts +21 -0
  9. data/app/components/ariadne/avatar_component.rb +81 -0
  10. data/app/components/ariadne/avatar_stack_component/avatar_stack_component.html.erb +12 -0
  11. data/app/components/ariadne/avatar_stack_component.rb +75 -0
  12. data/app/components/ariadne/base_button.rb +70 -0
  13. data/app/components/ariadne/base_component.rb +37 -0
  14. data/app/components/ariadne/blankslate_component/blankslate_component.html.erb +26 -0
  15. data/app/components/ariadne/blankslate_component.rb +148 -0
  16. data/app/components/ariadne/body_component.rb +30 -0
  17. data/app/components/ariadne/button_component/button_component.html.erb +4 -0
  18. data/app/components/ariadne/button_component.rb +165 -0
  19. data/app/components/ariadne/clipboard_copy_component/clipboard-copy-component.d.ts +4 -0
  20. data/app/components/ariadne/clipboard_copy_component/clipboard-copy-component.js +18 -0
  21. data/app/components/ariadne/clipboard_copy_component/clipboard-copy-component.ts +19 -0
  22. data/app/components/ariadne/clipboard_copy_component/clipboard_copy_component.html.erb +9 -0
  23. data/app/components/ariadne/clipboard_copy_component.rb +90 -0
  24. data/app/components/ariadne/comment_component/comment_component.html.erb +37 -0
  25. data/app/components/ariadne/comment_component.rb +71 -0
  26. data/app/components/ariadne/component.rb +127 -0
  27. data/app/components/ariadne/container_component/container_component.html.erb +3 -0
  28. data/app/components/ariadne/container_component.rb +25 -0
  29. data/app/components/ariadne/content.rb +12 -0
  30. data/app/components/ariadne/counter_component.rb +100 -0
  31. data/app/components/ariadne/details_component/details_component.html.erb +4 -0
  32. data/app/components/ariadne/details_component.rb +81 -0
  33. data/app/components/ariadne/dropdown/menu_component.html.erb +20 -0
  34. data/app/components/ariadne/dropdown/menu_component.rb +101 -0
  35. data/app/components/ariadne/dropdown/menu_component.ts +1 -0
  36. data/app/components/ariadne/dropdown_component/dropdown_component.html.erb +8 -0
  37. data/app/components/ariadne/dropdown_component.rb +172 -0
  38. data/app/components/ariadne/flash_component/flash_component.html.erb +31 -0
  39. data/app/components/ariadne/flash_component.rb +128 -0
  40. data/app/components/ariadne/flex_component/flex_component.html.erb +5 -0
  41. data/app/components/ariadne/flex_component.rb +56 -0
  42. data/app/components/ariadne/footer_component/footer_component.html.erb +7 -0
  43. data/app/components/ariadne/footer_component.rb +23 -0
  44. data/app/components/ariadne/grid_component/grid_component.html.erb +26 -0
  45. data/app/components/ariadne/grid_component.rb +67 -0
  46. data/app/components/ariadne/header_component/header_component.html.erb +29 -0
  47. data/app/components/ariadne/header_component.rb +111 -0
  48. data/app/components/ariadne/heading_component.rb +49 -0
  49. data/app/components/ariadne/heroicon_component/heroicon_component.html.erb +4 -0
  50. data/app/components/ariadne/heroicon_component.rb +166 -0
  51. data/app/components/ariadne/image_component.rb +53 -0
  52. data/app/components/ariadne/inline_flex_component/inline_flex_component.html.erb +6 -0
  53. data/app/components/ariadne/inline_flex_component.rb +72 -0
  54. data/app/components/ariadne/link_component.rb +65 -0
  55. data/app/components/ariadne/list_component/list_component.html.erb +6 -0
  56. data/app/components/ariadne/list_component.rb +70 -0
  57. data/app/components/ariadne/narrow_container_component/narrow_container_component.html.erb +3 -0
  58. data/app/components/ariadne/narrow_container_component.rb +30 -0
  59. data/app/components/ariadne/panel_bar_component/panel_bar_component.html.erb +20 -0
  60. data/app/components/ariadne/panel_bar_component.rb +80 -0
  61. data/app/components/ariadne/pill_component/pill_component.html.erb +3 -0
  62. data/app/components/ariadne/pill_component.rb +44 -0
  63. data/app/components/ariadne/rich_text_area_component/rich-text-area-component.d.ts +6 -0
  64. data/app/components/ariadne/rich_text_area_component/rich-text-area-component.js +38 -0
  65. data/app/components/ariadne/rich_text_area_component/rich-text-area-component.ts +47 -0
  66. data/app/components/ariadne/rich_text_area_component/rich_text_area_component.html.erb +6 -0
  67. data/app/components/ariadne/rich_text_area_component.rb +35 -0
  68. data/app/components/ariadne/slideover_component/slideover-component.d.ts +9 -0
  69. data/app/components/ariadne/slideover_component/slideover-component.js +11 -0
  70. data/app/components/ariadne/slideover_component/slideover-component.ts +17 -0
  71. data/app/components/ariadne/slideover_component/slideover_component.html.erb +9 -0
  72. data/app/components/ariadne/slideover_component.rb +66 -0
  73. data/app/components/ariadne/tab_component/tab_component.html.erb +3 -0
  74. data/app/components/ariadne/tab_component.rb +98 -0
  75. data/app/components/ariadne/tab_container_component/tab-container-component.d.ts +1 -0
  76. data/app/components/ariadne/tab_container_component/tab-container-component.js +23 -0
  77. data/app/components/ariadne/tab_container_component/tab-container-component.ts +24 -0
  78. data/app/components/ariadne/tab_container_component.erb +10 -0
  79. data/app/components/ariadne/tab_container_component.rb +68 -0
  80. data/app/components/ariadne/tab_nav_component/tab-nav-component.d.ts +9 -0
  81. data/app/components/ariadne/tab_nav_component/tab-nav-component.js +33 -0
  82. data/app/components/ariadne/tab_nav_component/tab-nav-component.ts +34 -0
  83. data/app/components/ariadne/tab_nav_component/tab_nav_component.html.erb +7 -0
  84. data/app/components/ariadne/tab_nav_component.rb +72 -0
  85. data/app/components/ariadne/table_nav_component/table_nav_component.html.erb +52 -0
  86. data/app/components/ariadne/table_nav_component.rb +338 -0
  87. data/app/components/ariadne/text.rb +25 -0
  88. data/app/components/ariadne/time_ago_component/time-ago-component.d.ts +1 -0
  89. data/app/components/ariadne/time_ago_component/time-ago-component.js +1 -0
  90. data/app/components/ariadne/time_ago_component/time-ago-component.ts +1 -0
  91. data/app/components/ariadne/time_ago_component.rb +56 -0
  92. data/app/components/ariadne/timeline_component/timeline_component.html.erb +19 -0
  93. data/app/components/ariadne/timeline_component.rb +34 -0
  94. data/app/components/ariadne/tooltip_component/tooltip-component.d.ts +24 -0
  95. data/app/components/ariadne/tooltip_component/tooltip-component.js +43 -0
  96. data/app/components/ariadne/tooltip_component/tooltip-component.ts +57 -0
  97. data/app/components/ariadne/tooltip_component/tooltip_component.html.erb +4 -0
  98. data/app/components/ariadne/tooltip_component.rb +108 -0
  99. data/lib/ariadne/view_components/engine.rb +0 -22
  100. data/lib/ariadne/view_components/version.rb +1 -1
  101. data/tailwind.config.js +10 -15
  102. metadata +98 -2
@@ -0,0 +1,31 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do %>
2
+ <div class="ariadne-rounded-md ariadne-p-4 <%= BG_SCHEME_CLASS_MAPPINGS[@scheme] %>">
3
+ <div class="ariadne-flex">
4
+ <div class="ariadne-flex-shrink-0">
5
+ <%= icon %>
6
+ </div>
7
+ <div class="ariadne-ml-3">
8
+ <div class="ariadne-mt-2 ariadne-text-sm <%= CONTENT_SCHEME_CLASS_MAPPINGS[@scheme] %>">
9
+ <p><%= content %></p>
10
+ </div>
11
+ <% if has_action? %>
12
+ <div class="ariadne-mt-4 ariadne-pt-5">
13
+ <div class="ariadne--mx-2 ariadne--my-1.5 ariadne-flex">
14
+ <%= action %>
15
+ </div>
16
+ </div>
17
+ <% end %>
18
+ </div>
19
+ <% if dismissible? %>
20
+ <div class="ariadne-pl-3">
21
+ <div class="ariadne--mx-1.5 ariadne--my-1.5">
22
+ <button type="button" class="ariadne-inline-flex ariadne-rounded-md ariadne-p-1.5 focus:ariadne-outline-none focus:ariadne-ring-2 focus:ariadne-ring-offset-2 <%= dismissible_classes %>">
23
+ <span class="ariadne-sr-only">Dismiss</span>
24
+ <%= ariadne_heroicon icon: :"x-mark", variant: HeroiconsHelper::Icon::VARIANT_OUTLINE %>
25
+ </button>
26
+ </div>
27
+ </div>
28
+ <% end %>
29
+ </div>
30
+ </div>
31
+ <% end %>
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Use `FlashComponent` to inform users of successful messages, pending actions, or urgent notices
5
+ class FlashComponent < Ariadne::Component
6
+ include IconHelper
7
+
8
+ DEFAULT_SCHEME = :default
9
+
10
+ DEFAULT_CLASSES = "ariadne-mt-0"
11
+
12
+ DISMISSIBLE_SCHEME_CLASS_MAPPINGS = {
13
+ default: "ariadne-text-slate-500 ariadne-bg-blue-50 hover:ariadne-bg-blue-100 focus:ariadne-ring-offset-blue-50 focus:ariadne-ring-blue-600",
14
+ info: "ariadne-text-slate-500 ariadne-bg-blue-50 hover:ariadne-bg-blue-100 focus:ariadne-ring-offset-blue-50 focus:ariadne-ring-blue-600",
15
+ success: "ariadne-text-green-500 ariadne-bg-green-50 hover:ariadne-bg-green-100 focus:ariadne-ring-offset-green-50 focus:ariadne-ring-green-600",
16
+ warning: "ariadne-text-yellow-500 ariadne-bg-yellow-50 hover:ariadne-bg-yellow-100 focus:ariadne-ring-offset-yellow-50 focus:ariadne-ring-yellow-600",
17
+ danger: "ariadne-text-red-500 ariadne-bg-red-50 hover:ariadne-bg-red-100 focus:ariadne-ring-offset-red-50 focus:ariadne-ring-red-600",
18
+ }.freeze
19
+ VALID_DISMISSIBLE_SCHEMES = DISMISSIBLE_SCHEME_CLASS_MAPPINGS.keys.freeze
20
+
21
+ BG_SCHEME_CLASS_MAPPINGS = {
22
+ default: "ariadne-bg-blue-50",
23
+ info: "ariadne-bg-blue-50",
24
+ success: "ariadne-bg-green-50",
25
+ warning: "ariadne-bg-yellow-50",
26
+ danger: "ariadne-bg-red-50",
27
+ }.freeze
28
+ VALID_BG_SCHEMES = BG_SCHEME_CLASS_MAPPINGS.keys.freeze
29
+
30
+ CONTENT_SCHEME_CLASS_MAPPINGS = {
31
+ default: "ariadne-text-slate-700",
32
+ info: "ariadne-text-slate-700",
33
+ success: "ariadne-text-green-700",
34
+ warning: "ariadne-text-yellow-700",
35
+ danger: "ariadne-text-red-700",
36
+ }.freeze
37
+ VALID_CONTENT_SCHEMES = CONTENT_SCHEME_CLASS_MAPPINGS.keys.freeze
38
+
39
+ # Optional visuals appearing to the left of the flash banner.
40
+ #
41
+ # Use:
42
+ #
43
+ # - `icon` for a <%= link_to_component(Ariadne::HeroiconComponent) %>.
44
+ #
45
+ # @option params [Symbol] :icon The rendered tag name
46
+ # @option params [Symbol] :icon Name of <%= link_to_heroicons %> to use.
47
+ # @option params [Symbol] :variant <%= one_of(HeroiconsHelper::Icon::VALID_VARIANTS, sort: false) %>
48
+ # @option params [String] :classes <%= link_to_classes_docs %>
49
+ # @option params [Hash] :attributes Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
50
+ renders_one :icon, lambda { |tag: :svg, icon:, variant:, classes: "", attributes: {}|
51
+ @icon = icon
52
+ @variant = variant
53
+
54
+ tag = check_incoming_tag(:svg, tag)
55
+ Ariadne::HeroiconComponent.new(tag: tag, icon: icon, variant: variant, classes: classes, attributes: attributes)
56
+ }
57
+
58
+ # Optional action content showed at the bottom of the component.
59
+ #
60
+ # @param tag [Symbol, String] The rendered tag name
61
+ # @param classes [String] <%= link_to_classes_docs %>
62
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
63
+ renders_one :action, lambda { |tag: :div, classes: "", attributes: {}|
64
+ tag = check_incoming_tag(:div, tag)
65
+
66
+ actual_classes = merge_class_names(classes)
67
+
68
+ render(Ariadne::BaseComponent.new(tag: tag, classes: actual_classes, attributes: attributes))
69
+ }
70
+
71
+ # @example Schemes
72
+ # <%= render(Ariadne::FlashComponent.new) { "This is a flash message!" } %>
73
+ # <%= render(Ariadne::FlashComponent.new(scheme: :warning)) { "This is a warning flash message!" } %>
74
+ # <%= render(Ariadne::FlashComponent.new(scheme: :danger)) { "This is a danger flash message!" } %>
75
+ # <%= render(Ariadne::FlashComponent.new(scheme: :success)) { "This is a success flash message!" } %>
76
+ #
77
+ # @example Dismissible
78
+ # <%= render(Ariadne::FlashComponent.new(dismissible: true)) { "This is a dismissible flash message!" } %>
79
+ #
80
+ # @example Icon
81
+ # <%= render(Ariadne::FlashComponent.new) do |component| %>
82
+ # <% component.with_icon(icon: :"user-group", variant: HeroiconsHelper::Icon::VARIANT_OUTLINE) %>
83
+ # Look at this icon.
84
+ # <% end %>
85
+ #
86
+ # @example With actions
87
+ # <%= render(Ariadne::FlashComponent.new) do |component| %>
88
+ # <% component.with_action do %>
89
+ # <%= render(Ariadne::ButtonComponent.new(size: :sm)) { "Take action" } %>
90
+ # <% end %>
91
+ # This is a flash message with actions!
92
+ # <% end %>
93
+ #
94
+ # @param tag [Symbol, String] The rendered tag name.
95
+ # @param dismissible [Boolean] Whether the component can be dismissed with an X button.
96
+ # @param icon [Symbol, String] Name of <%= link_to_heroicons %> to use.
97
+ # @param scheme [Symbol] <%= one_of(Ariadne::FlashComponent::VALID_CONTENT_SCHEMES) %>
98
+ # @param classes [String] <%= link_to_classes_docs %>
99
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
100
+ def initialize(tag: :div, dismissible: false, scheme: DEFAULT_SCHEME, classes: "", attributes: {})
101
+ @dismissible = dismissible
102
+
103
+ @tag = check_incoming_tag(:div, tag)
104
+
105
+ @scheme = fetch_or_raise(VALID_CONTENT_SCHEMES, scheme)
106
+
107
+ @classes = merge_class_names(
108
+ DEFAULT_CLASSES,
109
+ CONTENT_SCHEME_CLASS_MAPPINGS[@scheme],
110
+ classes,
111
+ )
112
+
113
+ @attributes = attributes
114
+ end
115
+ # TODO: test this
116
+ private def has_action?
117
+ action.present?
118
+ end
119
+ # TODO: test this
120
+ private def dismissible?
121
+ @dismissible.present?
122
+ end
123
+
124
+ private def dismissible_classes
125
+ DISMISSIBLE_SCHEME_CLASS_MAPPINGS[@scheme]
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,5 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do |component| %>
2
+ <% items.each do |item| %>
3
+ <%= item %>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Adds a flex container to the page.
5
+ class FlexComponent < Ariadne::Component
6
+ DEFAULT_TAG = :div
7
+ TAG_OPTIONS = [DEFAULT_TAG].freeze
8
+
9
+ DEFAULT_CLASSES = "ariadne-flex"
10
+
11
+ VALID_TYPES = [:row, :column, :row_reverse, :column_reverse].freeze
12
+
13
+ # The sub-items(s) to render
14
+ renders_many :items, lambda { |static_content = nil, &block|
15
+ next static_content if static_content.present?
16
+
17
+ view_context.capture { block&.call }
18
+ }
19
+
20
+ # @example Default
21
+ #
22
+ # <%= render(Ariadne::FlexComponent.new(type: :column)) { "Example" } %>
23
+ #
24
+ # @param tag [Symbol, String] The rendered tag name.
25
+ # @param type [Symbol] <%= one_of(Ariadne::FlexComponent::VALID_TYPES) %>
26
+ # @param classes [String] <%= link_to_classes_docs %>
27
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
28
+ def initialize(tag: DEFAULT_TAG, type:, classes: "", attributes: {})
29
+ @tag = check_incoming_tag(DEFAULT_TAG, tag)
30
+ @type = fetch_or_raise(VALID_TYPES, type)
31
+
32
+ flex_class = case @type
33
+ when :row
34
+ "ariadne-flex-row"
35
+ when :column
36
+ "ariadne-flex-col"
37
+ when :row_reverse
38
+ "ariadne-flex-row-reverse"
39
+ when :column_reverse
40
+ "ariadne-flex-col-reverse"
41
+ end
42
+
43
+ @classes = merge_class_names(
44
+ DEFAULT_CLASSES,
45
+ classes,
46
+ flex_class,
47
+ )
48
+
49
+ @attributes = attributes
50
+ end
51
+
52
+ def render?
53
+ items?
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,7 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: :footer, classes: @classes, attributes: @attributes) do |footer| %>
2
+ <%= render Ariadne::ContainerComponent.new do |container| %>
3
+ <p class="ariadne-mt-6 ariadne-text-sm ariadne-text-slate-500 sm:ariadne-mt-0">
4
+ <%= content %>
5
+ </p>
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Adds a footer to the bottom of every page.
5
+ class FooterComponent < Ariadne::Component
6
+ DEFAULT_CLASSES = "ariadne-py-5"
7
+
8
+ # @example Default
9
+ #
10
+ # <%= render(Ariadne::FooterComponent.new) { "Footer." } %>
11
+ #
12
+ # @param classes [String] <%= link_to_classes_docs %>
13
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
14
+ def initialize(classes: "", attributes: {})
15
+ @classes = merge_class_names(
16
+ DEFAULT_CLASSES,
17
+ classes,
18
+ )
19
+
20
+ @attributes = attributes
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: :ul, classes: @classes, attributes: @attributes) do |grid| %>
2
+ <% items.each do |item| %>
3
+ <li class="<%= item.classes %>">
4
+ <% if item.link? %>
5
+ <%= render(item.link) do %>
6
+ <%= item.entry %>
7
+ <% end %>
8
+ <% if item.actions.any? %>
9
+ <div>
10
+ <div class="ariadne--mt-px ariadne-flex ariadne-divide-x">
11
+ <% item.actions.each_with_index do |action, idx| %>
12
+ <div class="<%= idx.zero? ? '' : 'ariadne--ml-px ' %>w-0 ariadne-flex-1 ariadne-flex">
13
+ <%= action %>
14
+ </div>
15
+ <% end %>
16
+ </div>
17
+ </div>
18
+ <% end %>
19
+ <% else %>
20
+ <div class="ariadne-flex-1 ariadne-flex ariadne-flex-col ariadne-p-8">
21
+ <%= item.entry %>
22
+ </div>
23
+ <% end %>
24
+ </li>
25
+ <% end %>
26
+ <% end %>
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Arranges items as a ariadne-grid.
5
+ class GridComponent < Ariadne::Component
6
+ DEFAULT_CLASSES = "ariadne-grid ariadne-gap-6 sm:ariadne-grid-cols-2 lg:ariadne-grid-cols-3"
7
+
8
+ DEFAULT_LINK_COLOR_CLASSES = "focus:ariadne-outline-none focus:ariadne-ring-2 focus:ariadne-ring-offset-2 focus:ariadne-ring-purple-500"
9
+
10
+ # The items to show in the ariadne-grid.
11
+ renders_many :items, "GridItem"
12
+
13
+ # @example Default
14
+ #
15
+ # <%= render(Ariadne::GridComponent.new) { "Example" } %>
16
+ #
17
+ # @param classes [String] <%= link_to_classes_docs %>
18
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
19
+ def initialize(classes: "", attributes: {})
20
+ @classes = merge_class_names(
21
+ DEFAULT_CLASSES,
22
+ classes,
23
+ )
24
+
25
+ default_attributes = { role: "list" }
26
+ @attributes = default_attributes.merge(attributes)
27
+ end
28
+
29
+ # This component is part of `GridComponent` and should not be
30
+ # used as a standalone component.
31
+ class GridItem < Ariadne::Component
32
+ DEFAULT_ITEM_CLASSES = "ariadne-flex ariadne-flex-col ariadne-text-center ariadne-rounded-lg ariadne-shadow text-black-700 ariadne-border-black"
33
+
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
+ view_context.capture do
37
+ actual_classes = merge_class_names(DEFAULT_ENTRY_CLASSES, classes)
38
+ render(Ariadne::BaseComponent.new(tag: :div, classes: actual_classes, attributes: attributes)) { block&.call }
39
+ end
40
+ }
41
+
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 = "ariadne-h-full hover:ariadne-text-gray-500"
44
+ renders_many :actions, lambda { |href:, icon:, label:, size: Ariadne::HeroiconComponent::SIZE_DEFAULT, variant: HeroiconsHelper::Icon::VARIANT_SOLID, classes: "", attributes: {}, text_classes: ""|
45
+ actual_classes = merge_class_names(DEFAULT_ACTION_LINK_CLASSES, DEFAULT_ACTION_LINK_COLORS, classes)
46
+ render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) do
47
+ render(Ariadne::HeroiconComponent.new(icon: icon, size: size, variant: variant, text_classes: text_classes)) { label }
48
+ end
49
+ }
50
+
51
+ renders_one :link, lambda { |href:, classes: "", attributes: {}|
52
+ Ariadne::LinkComponent.new(href: href, classes: classes, attributes: attributes)
53
+ }
54
+
55
+ attr_reader :classes, :attributes
56
+
57
+ def initialize(classes: "", attributes: {})
58
+ @classes = merge_class_names(DEFAULT_ITEM_CLASSES, classes)
59
+ @attributes = attributes
60
+ end
61
+
62
+ def call
63
+ render(Ariadne::BaseComponent.new(tag: :div, classes: @classes, attributes: @attributes))
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,29 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: :header, classes: @classes, attributes: @attributes) do |header| %>
2
+ <%= render Ariadne::ContainerComponent.new do |container| %>
3
+ <nav class="ariadne-relative ariadne-z-50 ariadne-flex ariadne-justify-between">
4
+ <div class="ariadne-flex ariadne-items-center md:ariadne-gap-x-12">
5
+ <% if has_logo? %>
6
+ <%= render Ariadne::LinkComponent.new(href: @href, classes: "ariadne-text-billy-purple") do %>
7
+ <% if has_image_logo? %>
8
+ <span class="ariadne-sr-only"><%= @text %></span>
9
+ <% end %>
10
+ <%= logo %>
11
+ <% end %>
12
+ <% end %>
13
+ <div class="md:ariadne-flex md:ariadne-gap-x-6">
14
+ <% navigation_links.each do |link| %>
15
+ <%= link %>
16
+ <% end %>
17
+ </div>
18
+ </div>
19
+ <div class="ariadne-flex ariadne-items-center ariadne-gap-x-5 md:ariadne-gap-x-8">
20
+ <div class="md:ariadne-block">
21
+ <% action_links.each do |link| %>
22
+ <%= link %>
23
+ <% end %>
24
+ </div>
25
+ <%= signup_link if has_signup_link? %>
26
+ <%= profile_link if has_profile_link? %>
27
+ </nav>
28
+ <% end %>
29
+ <% end %>
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Represents the top navigation bar on every page.
5
+ class HeaderComponent < Ariadne::Component
6
+ DEFAULT_CLASSES = "ariadne-sticky ariadne-top-0 ariadne-z-50 ariadne-px-4 ariadne-pt-4 ariadne-h-16 ariadne-shadow-sm shadow-slate-900/5 ariadne-transition ariadne-duration-500"
7
+ LINK_CLASSES = "ariadne-rounded-lg ariadne-py-1 ariadne-px-2 text-slate-700 hover:bg-slate-100 hover:text-slate-900"
8
+
9
+ DEFAULT_TEXT_LOGO_CLASSES = "ariadne-flex ariadne-items-center ariadne-font-bold ariadne-text-xl"
10
+ DEFAULT_IMAGE_LOGO_CLASSES = "ariadne-h-10 ariadne-w-auto"
11
+
12
+ # Leading visuals at the far left of the header. You can pass either
13
+ # `src` or `as_text` but not both.
14
+ #
15
+ # @param href [String] Where the logo should link to.
16
+ # @param src [String] The URL of the image logo.
17
+ # @param alt [String] Alt text for accessibility.
18
+ # @param text [Boolean] The text to display
19
+ # @param classes [String] <%= link_to_classes_docs %>
20
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
21
+ renders_one :logo, lambda { |href: nil, src: nil, alt: nil, text: "", classes: "", attributes: {}|
22
+ @href = href
23
+ @text = text
24
+
25
+ if src.blank?
26
+ actual_classes = merge_class_names(DEFAULT_TEXT_LOGO_CLASSES, classes)
27
+ Ariadne::Text.new(tag: :span, classes: actual_classes, attributes: attributes)
28
+ else
29
+ actual_classes = merge_class_names(DEFAULT_IMAGE_LOGO_CLASSES, classes)
30
+ actual_attributes = { width: 40, height: 40 }.merge(attributes)
31
+ @image_logo = Ariadne::ImageComponent.new(src: src, alt: alt, classes: actual_classes, attributes: actual_attributes)
32
+ end
33
+ }
34
+
35
+ # Text within the header, representing navigation.
36
+ #
37
+ # @param tag [String, Symbol] The tag to use for the link.
38
+ # @param href [String] The link destination.
39
+ # @param classes [String] <%= link_to_classes_docs %>
40
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
41
+ DEFAULT_NAV_LINK_CLASSES = "ariadne-inline-block ariadne-rounded-lg ariadne-py-1 ariadne-px-2 ariadne-text-sm text-slate-700 hover:bg-slate-100 hover:text-slate-900"
42
+ renders_many :navigation_links, lambda { |tag: Ariadne::LinkComponent::DEFAULT_TAG, href:, classes: "", attributes: {}|
43
+ actual_classes = merge_class_names(DEFAULT_NAV_LINK_CLASSES, classes)
44
+ Ariadne::LinkComponent.new(tag: tag, href: href, classes: actual_classes, attributes: attributes)
45
+ }
46
+
47
+ # Text within the header, representing actions.
48
+ #
49
+ # @param tag [String, Symbol] The tag to use for the link.
50
+ # @param href [String] The link destination.
51
+ # @param classes [String] <%= link_to_classes_docs %>
52
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
53
+ renders_many :action_links, lambda { |tag: Ariadne::LinkComponent::DEFAULT_TAG, href:, classes: "", attributes: {}|
54
+ actual_classes = merge_class_names(DEFAULT_NAV_LINK_CLASSES, classes)
55
+ Ariadne::LinkComponent.new(tag: tag, href: href, classes: actual_classes, attributes: attributes)
56
+ }
57
+
58
+ # Text within the header, representing a signup.
59
+ #
60
+ # @param tag [String, Symbol] The tag to use for the link.
61
+ # @param href [String] The link destination.
62
+ # @param classes [String] <%= link_to_classes_docs %>
63
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
64
+ DEFAULT_SIGNUP_LINK_CLASSES = "group ariadne-inline-flex ariadne-items-center ariadne-justify-center ariadne-rounded-full ariadne-py-2 ariadne-px-4 ariadne-text-sm ariadne-font-semibold focus:ariadne-outline-none focus-visible:ariadne-outline-2 focus-visible:outline-offset-2"
65
+ renders_one :signup_link, lambda { |tag: Ariadne::LinkComponent::DEFAULT_TAG, href:, classes: "", attributes: {}|
66
+ actual_classes = merge_class_names(DEFAULT_SIGNUP_LINK_CLASSES, classes)
67
+ Ariadne::LinkComponent.new(tag: tag, href: href, classes: actual_classes, attributes: attributes)
68
+ }
69
+
70
+ DEFAULT_PROFILE_LINK_CLASSES = "group ariadne-inline-flex ariadne-items-center ariadne-justify-center ariadne-rounded-full ariadne-py-2 ariadne-px-4 ariadne-text-sm ariadne-font-semibold focus:ariadne-outline-none focus-visible:ariadne-outline-2 focus-visible:outline-offset-2"
71
+ renders_one :profile_link, lambda { |tag: Ariadne::LinkComponent::DEFAULT_TAG, href:, classes: "", attributes: {}|
72
+ actual_classes = merge_class_names(DEFAULT_PROFILE_LINK_CLASSES, classes)
73
+ Ariadne::LinkComponent.new(tag: tag, href: href, classes: actual_classes, attributes: attributes)
74
+ }
75
+
76
+ # @example Basic example
77
+ #
78
+ # <%= render(Ariadne::HeaderComponent.new) do |header| %>
79
+ # <%= header.logo(text: true) { "GitHub" } %>
80
+ # <%= header.navigation_link(href: "#features") { "Features" } %>
81
+ # <%= header.login_link(href: "/login") { "Login" } %>
82
+ # <% end %>
83
+ #
84
+ # @param classes [String] <%= link_to_classes_docs %>
85
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
86
+ def initialize(classes: "", attributes: {})
87
+ @classes = merge_class_names(
88
+ DEFAULT_CLASSES,
89
+ classes,
90
+ )
91
+
92
+ @attributes = attributes
93
+ end
94
+
95
+ private def has_logo?
96
+ logo.present?
97
+ end
98
+
99
+ private def has_image_logo?
100
+ has_logo? && @image_logo.present?
101
+ end
102
+
103
+ private def has_signup_link?
104
+ signup_link.present?
105
+ end
106
+
107
+ private def has_profile_link?
108
+ profile_link.present?
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # `Heading` should be used to communicate page organization and hierarchy.
5
+ #
6
+ # - Set tag to one of `:h1`, `:h2`, `:h3`, `:h4`, `:h5`, `:h6` based on what is appropriate for the page context.
7
+ # - Use `Heading` as the title of a section or sub section.
8
+ # - Do not use `Heading` for styling alone. For simply styling text, consider using <%= link_to_component(Ariadne::Text) %> with relevant <%= link_to_typography_docs %>
9
+ # such as `font_size` and `font_weight`.
10
+ # - Do not jump heading levels. For instance, do not follow a `<h1>` with an `<h3>`. Heading levels should increase by one in ascending order.
11
+ #
12
+ # @accessibility
13
+ # While sighted users rely on visual cues such as font size changes to determine what the heading is, assistive technology users rely on programatic cues that can be read out.
14
+ # When text on a page is visually implied to be a heading, ensure that it is coded as a heading. Additionally, visually implied heading level and coded heading level should be
15
+ # consistent. [See WCAG success criteria: 1.3.1: Info and Relationships](https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html)
16
+ #
17
+ # Headings allow assistive technology users to quickly navigate around a page. Navigation to text that is not meant to be a heading can be a confusing experience.
18
+ # <%= link_to_heading_practices %>
19
+ class HeadingComponent < Ariadne::Component
20
+ TAG_OPTIONS = [:h1, :h2, :h3, :h4, :h5, :h6].freeze
21
+
22
+ TAG_TO_CLASSES = {
23
+ h1: "ariadne-font-bold ariadne-leading-7 sm:ariadne-text-3xl",
24
+ h2: "ariadne-text-3xl ariadne-font-extrabold",
25
+ h3: "ariadne-text-2xl ariadne-font-extrabold",
26
+ }
27
+ # @example Default
28
+ # <%= render(Ariadne::HeadingComponent.new(tag: :h1)) { "H1 Text" } %>
29
+ # <%= render(Ariadne::HeadingComponent.new(tag: :h2)) { "H2 Text" } %>
30
+ # <%= render(Ariadne::HeadingComponent.new(tag: :h3)) { "H3 Text" } %>
31
+ # <%= render(Ariadne::HeadingComponent.new(tag: :h4)) { "H4 Text" } %>
32
+ # <%= render(Ariadne::HeadingComponent.new(tag: :h5)) { "H5 Text" } %>
33
+ # <%= render(Ariadne::HeadingComponent.new(tag: :h6)) { "H6 Text" } %>
34
+ #
35
+ # @param tag [String] <%= one_of(Ariadne::HeadingComponent::TAG_OPTIONS) %>
36
+ # @param classes [String] <%= link_to_classes_docs %>
37
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
38
+ def initialize(tag: nil, classes: "", attributes: {})
39
+ @tag = fetch_or_raise(TAG_OPTIONS, tag)
40
+ @attributes = attributes
41
+
42
+ @classes = merge_class_names(*TAG_TO_CLASSES[tag], classes)
43
+ end
44
+
45
+ def call
46
+ render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) { content }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
2
+ <%= @icon.path %>
3
+ <% end %>
4
+ <%= render(Ariadne::BaseComponent.new(tag: :span, classes: @text_classes, attributes: @text_attributes)) { content } if content.present? %>