ariadne_view_components 0.0.1 → 0.0.4

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -6
  3. data/app/assets/config/manifest.js +2 -0
  4. data/app/assets/javascripts/ariadne_view_components.js +1 -1
  5. data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
  6. data/app/assets/stylesheets/{application.tailwind.css → application.ariadne_view_components.css} +0 -0
  7. data/app/components/ariadne/ariadne.ts +5 -9
  8. data/app/components/ariadne/base_button.rb +1 -0
  9. data/app/components/ariadne/blankslate_component.html.erb +26 -0
  10. data/app/components/ariadne/blankslate_component.rb +151 -0
  11. data/app/components/ariadne/button_component.html.erb +1 -1
  12. data/app/components/ariadne/button_component.rb +4 -1
  13. data/app/components/ariadne/{clipboard_copy_component.ts → clipboard-copy-component.ts} +0 -0
  14. data/app/components/ariadne/clipboard_copy_component.rb +5 -3
  15. data/app/components/ariadne/component.rb +5 -1
  16. data/app/components/ariadne/container_component.html.erb +3 -0
  17. data/app/components/ariadne/container_component.rb +25 -0
  18. data/app/components/ariadne/flash_component.rb +8 -8
  19. data/app/components/ariadne/footer_component.html.erb +7 -0
  20. data/app/components/ariadne/footer_component.rb +23 -0
  21. data/app/components/ariadne/grid_component.html.erb +17 -0
  22. data/app/components/ariadne/grid_component.rb +55 -0
  23. data/app/components/ariadne/header_component.html.erb +29 -0
  24. data/app/components/ariadne/header_component.rb +114 -0
  25. data/app/components/ariadne/heading_component.rb +1 -1
  26. data/app/components/ariadne/heroicon_component.html.erb +4 -5
  27. data/app/components/ariadne/heroicon_component.rb +5 -3
  28. data/app/components/ariadne/image_component.rb +5 -3
  29. data/app/components/ariadne/inline_flex_component.html.erb +5 -0
  30. data/app/components/ariadne/inline_flex_component.rb +63 -0
  31. data/app/components/ariadne/link_component.rb +60 -0
  32. data/app/components/ariadne/list_component.html.erb +17 -0
  33. data/app/components/ariadne/list_component.rb +97 -0
  34. data/app/components/ariadne/pill_component.html.erb +3 -0
  35. data/app/components/ariadne/pill_component.rb +30 -0
  36. data/app/components/ariadne/slideover-component.ts +26 -0
  37. data/app/components/ariadne/slideover_component.html.erb +14 -0
  38. data/app/components/ariadne/slideover_component.rb +77 -0
  39. data/app/components/ariadne/time_ago_component.rb +56 -0
  40. data/app/components/ariadne/time_ago_component.ts +1 -0
  41. data/app/components/ariadne/timeline_component.html.erb +19 -0
  42. data/app/components/ariadne/timeline_component.rb +34 -0
  43. data/app/lib/ariadne/action_view_extensions/form_helper.rb +23 -0
  44. data/app/lib/ariadne/form_builder.rb +71 -0
  45. data/lib/ariadne/classify.rb +9 -1
  46. data/lib/ariadne/view_components/engine.rb +6 -0
  47. data/lib/ariadne/view_components/version.rb +1 -1
  48. data/lib/rubocop/cop/ariadne/ariadne_heroicon.rb +2 -2
  49. data/lib/tasks/docs.rake +48 -58
  50. data/static/arguments.yml +204 -7
  51. data/static/audited_at.json +14 -0
  52. data/static/classes.yml +157 -1
  53. data/static/constants.json +171 -10
  54. data/static/statuses.json +14 -0
  55. metadata +31 -5
  56. data/lib/tasks/tailwind.rake +0 -31
@@ -1,14 +1,10 @@
1
1
  import {Application} from '@hotwired/stimulus'
2
2
 
3
- import ClipboardCopyComponent from './clipboard_copy_component'
3
+ import ClipboardCopyComponent from './clipboard-copy-component'
4
+ import SlideoverComponent from './slideover-component'
4
5
 
5
- // import './clipboard_copy_component'
6
- // import './tab_container_component'
7
- // import './time_ago_component'
8
- // import './local_time'
9
- // import './image_crop'
10
- // import './dropdown'
11
- // import './alpha/tool-tip-element'
6
+ import './time_ago_component'
12
7
 
13
8
  const application = Application.start()
14
- application.register('clipboard_copy_component', ClipboardCopyComponent)
9
+ application.register('clipboard-copy-component', ClipboardCopyComponent)
10
+ application.register('slideover-component', SlideoverComponent)
@@ -9,6 +9,7 @@ module Ariadne
9
9
  DEFAULT_TYPE = :button
10
10
  TYPE_OPTIONS = [DEFAULT_TYPE, :reset, :submit].freeze
11
11
 
12
+ # TODO: dedupe the classes
12
13
  SIZE_CLASS_MAPPINGS = {
13
14
  xs: "inline-flex items-center px-2.5 py-1.5 text-xs font-medium rounded",
14
15
  s: "inline-flex items-center px-3 py-2 text-sm leading-4 font-medium rounded-m",
@@ -0,0 +1,26 @@
1
+ <div class="bg-white">
2
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do %>
3
+ <% if icon.present? %>
4
+ <%= icon %>
5
+ <% elsif image.present? %>
6
+ <%= image %>
7
+ <% end %>
8
+ <%= heading %>
9
+ <% if description.present? %>
10
+ <%= description %>
11
+ <% end %>
12
+ <%= content %>
13
+ <div class="mt-8 flex justify-center">
14
+ <% if primary_action.present? %>
15
+ <div class="inline-flex rounded-md shadow">
16
+ <%= primary_action %>
17
+ </div>
18
+ <% end %>
19
+ <% if secondary_action.present? %>
20
+ <div class="ml-3 inline-flex">
21
+ <%= secondary_action %>
22
+ </div>
23
+ <% end %>
24
+ </div>
25
+ <% end %>
26
+ </div>
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Use `Blankslate` when there is a lack of content within a page or section. Use as placeholder to tell users why something isn't there.
5
+ #
6
+ # @accessibility
7
+ # - The blankslate uses a semantic heading that must be set at the appropriate level based on the hierarchy of the page.
8
+ # - All blankslate visuals have been programmed as decorative images, using `aria-hidden=”true”` and `img alt=””`, which will hide the visual from screen reader users.
9
+ # - The blankslate supports a primary and secondary action. Both actions have been built as semantic links with primary and secondary styling.
10
+ # - `secondary_action` text should be meaningful out of context and clearly describe the destination. Avoid using vague text like, "Learn more" or "Click here".
11
+ class BlankslateComponent < Ariadne::Component
12
+ DEFAULT_TAG = :div
13
+ TAG_OPTIONS = [DEFAULT_TAG].freeze
14
+
15
+ DEFAULT_CLASSES = "max-w-7xl mx-auto text-center py-12 px-4 sm:px-6 lg:py-16 lg:px-8"
16
+
17
+ # Optional visual that renders an <%= link_to_component(Ariadne::HeroiconComponent) %>.
18
+ #
19
+ # @param tag [Symbol, String] The rendered tag name
20
+ # @param icon [String] Name of <%= link_to_heroicons %> to use.
21
+ # @param size [Symbol] <%= one_of(Ariadne::HeroiconComponent::SIZE_MAPPINGS, sort: false) %>
22
+ # @param variant [String] <%= one_of(HeroiconsHelper::Icon::VARIANTS, sort: false) %>
23
+ # @param classes [String] <%= link_to_classes_docs %>
24
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
25
+ renders_one :icon, lambda { |tag: :svg, icon:, size: Ariadne::HeroiconComponent::SIZE_DEFAULT, variant: HeroiconsHelper::Icon::VARIANT_SOLID, classes: "", attributes: {}|
26
+ @icon = icon
27
+ @variant = variant
28
+ tag = check_incoming_tag(:svg, tag)
29
+ Ariadne::HeroiconComponent.new(tag: tag, icon: icon, size: size, variant: variant, classes: classes, attributes: attributes)
30
+ }
31
+
32
+ # Optional visual that renders an <%= link_to_component(Ariadne::ImageComponent) %>.
33
+ #
34
+ # @param tag [Symbol, String] The rendered tag name
35
+ # @param src [String] The source url of the image.
36
+ # @param alt [String] Specifies an alternate text for the image.
37
+ # @param lazy [Boolean] Whether or not to lazily load the image.
38
+ # @param classes [String] <%= link_to_classes_docs %>
39
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
40
+ DEFAULT_IMAGE_SIZE = 56
41
+ renders_one :image, lambda { |tag: Ariadne::ImageComponent::DEFAULT_TAG, src:, alt:, lazy: false, classes: "", attributes: {}|
42
+ attributes[:height] = DEFAULT_IMAGE_SIZE
43
+ attributes[:width] = DEFAULT_IMAGE_SIZE
44
+
45
+ Ariadne::ImageComponent.new(tag: tag, src: src, alt: alt, lazy: lazy, classes: classes, attributes: attributes)
46
+ }
47
+
48
+ # Required heading.
49
+ #
50
+ # @param tag [Symbol, String] <%= one_of(Ariadne::HeadingComponent::TAG_OPTIONS) %>
51
+ # @param classes [String] <%= link_to_classes_docs %>
52
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
53
+ DEFAULT_HEADING_CLASSES = "text-3xl font-extrabold tracking-tight text-gray-900 sm:text-4xl"
54
+ renders_one :heading, lambda { |tag: :h1, classes: "", attributes: {}|
55
+ actual_classes = class_names(DEFAULT_HEADING_CLASSES, classes)
56
+
57
+ Ariadne::HeadingComponent.new(tag: tag, classes: actual_classes, attributes: attributes)
58
+ }
59
+
60
+ # Optional description.
61
+ #
62
+ # - The description should always be informative and actionable.
63
+ # - Don't use phrases like "You can".
64
+ #
65
+ # @param tag [Symbol, String] The rendered tag name
66
+ # @param classes [String] <%= link_to_classes_docs %>
67
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
68
+ renders_one :description, lambda { |tag: :p, classes: "", attributes: {}|
69
+ actual_tag = check_incoming_tag(:p, tag)
70
+
71
+ Ariadne::BaseComponent.new(tag: actual_tag, classes: classes, attributes: attributes)
72
+ }
73
+
74
+ # Optional primary action
75
+ #
76
+ # The `primary_action` slot renders an anchor link which is visually styled as a button to provide more emphasis to the
77
+ # Blankslate's primary action.
78
+ #
79
+ # @param href [String] URL to be used for the primary action.
80
+ # @param classes [String] <%= link_to_classes_docs %>
81
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
82
+ renders_one :primary_action, lambda { |href: nil, tag: :a, method: nil, size: :m, scheme: :default, classes: "", attributes: {}|
83
+ attributes[:href] = href
84
+ attributes[:method] = method
85
+
86
+ Ariadne::ButtonComponent.new(tag: tag,
87
+ type: Ariadne::BaseButton::DEFAULT_TYPE,
88
+ scheme: scheme,
89
+ size: size,
90
+ classes: classes,
91
+ attributes: attributes)
92
+ }
93
+
94
+ # Optional secondary action
95
+ #
96
+ # The `secondary_action` slot renders a normal anchor link, which can be used to redirect the user to additional information
97
+ # (e.g. Help documentation).
98
+ #
99
+ # @param href [String] URL to be used for the secondary action.
100
+ # @param classes [String] <%= link_to_classes_docs %>
101
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
102
+ renders_one :secondary_action, lambda { |href:, classes: "", attributes: {}|
103
+ attributes[:href] = href
104
+
105
+ Ariadne::LinkComponent.new(href: href, classes: classes, attributes: attributes)
106
+ }
107
+
108
+ # @example Basic
109
+ # <%= render Ariadne::BlankslateComponent.new do |c| %>
110
+ # <% c.heading(tag: :h2).with_content("heading") %>
111
+ # <% c.description { "Description"} %>
112
+ # <% end %>
113
+ #
114
+ # @example Icon
115
+ # @description
116
+ # Add an `icon` to give additional context. Refer to the [Octicons](https://primer.style/octicons/) documentation to choose an icon.
117
+ # @code
118
+ # <%= render Ariadne::BlankslateComponent.new do |c| %>
119
+ # <% c.icon(icon: :globe) %>
120
+ # <% c.heading(tag: :h2).with_content("heading") %>
121
+ # <% c.description { "Description"} %>
122
+ # <% end %>
123
+ #
124
+ # @example Using an image
125
+ # @description
126
+ # Add an `image` to give context that an Octicon couldn't.
127
+ # @code
128
+ # <%= render Ariadne::BlankslateComponent.new do |c| %>
129
+ # <% c.image(src: "https://github.githubassets.com/images/modules/site/features/security-icon.svg", alt: "Security - secure vault") %>
130
+ # <% c.heading(tag: :h2).with_content("heading") %>
131
+ # <% end %>
132
+ #
133
+ # @param tag [Symbol, String] The rendered tag name
134
+ # @param classes [String] <%= link_to_classes_docs %>
135
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
136
+ def initialize(tag: DEFAULT_TAG, classes: "", attributes: {})
137
+ @tag = check_incoming_tag(DEFAULT_TAG, tag)
138
+ @classes = class_names(DEFAULT_CLASSES, classes)
139
+ @attributes = attributes
140
+ end
141
+
142
+ def render?
143
+ heading.present?
144
+ end
145
+
146
+ def wrapper
147
+ yield
148
+ nil # returning `yield` causes a double render
149
+ end
150
+ end
151
+ end
@@ -1,4 +1,4 @@
1
- <%= render Ariadne::BaseButton.new(tag: @tag, type: Ariadne::BaseButton::DEFAULT_TYPE, classes: @classes, attributes: @attributes) do -%>
1
+ <%= render Ariadne::BaseButton.new(tag: @tag, type: @type, classes: @classes, attributes: @attributes) do -%>
2
2
  <%= icon %><%= trimmed_content %><%= counter %>
3
3
  <%= tooltip %>
4
4
  <% end -%>
@@ -9,7 +9,7 @@ module Ariadne
9
9
  LINK_SCHEME = :link
10
10
 
11
11
  SCHEME_CLASS_MAPPINGS = {
12
- default: "text-blue-800 bg-blue-50 hover:bg-blue-100 border-blue-300 focus:ring-offset-blue-50 focus:ring-blue-600",
12
+ default: "text-purple-800 bg-purple-50 hover:bg-purple-100 border-purple-300 focus:ring-offset-purple-50 focus:ring-purple-600",
13
13
  info: "text-blue-800 bg-blue-50 hover:bg-blue-100 border-blue-300 focus:ring-offset-blue-50 focus:ring-blue-600",
14
14
  success: "text-green-800 bg-green-50 hover:bg-green-100 border-green-300 focus:ring-offset-green-50 focus:ring-green-600",
15
15
  warning: "text-yellow-800 bg-yellow-50 hover:bg-yellow-100 border-yellow-300 focus:ring-offset-yellow-50 focus:ring-yellow-600",
@@ -113,6 +113,7 @@ module Ariadne
113
113
  # <% end %>
114
114
  #
115
115
  # @param tag [Symbol] <%= one_of(Ariadne::BaseButton::TAG_OPTIONS) %>
116
+ # @param type [Symbol] <%= one_of(Ariadne::BaseButton::TYPE_OPTIONS) %>
116
117
  # @param scheme [Symbol] <%= one_of(Ariadne::ButtonComponent::VALID_SCHEMES) %>
117
118
  # @param size [Symbol] <%= one_of(Ariadne::BaseButton::VALID_SIZES) %>
118
119
  # @param type [Symbol] <%= one_of(Ariadne::BaseButton::TYPE_OPTIONS) %>
@@ -120,12 +121,14 @@ module Ariadne
120
121
  # @param attributes [Hash] <%= link_to_attributes_docs %>
121
122
  def initialize(
122
123
  tag: Ariadne::BaseButton::DEFAULT_TAG,
124
+ type: Ariadne::BaseButton::DEFAULT_TYPE,
123
125
  scheme: DEFAULT_SCHEME,
124
126
  size: BaseButton::DEFAULT_SIZE,
125
127
  classes: "",
126
128
  attributes: {}
127
129
  )
128
130
  @tag = tag
131
+ @type = type
129
132
  @scheme = scheme
130
133
 
131
134
  @attributes = attributes
@@ -8,8 +8,10 @@ module Ariadne
8
8
  class ClipboardCopyComponent < Ariadne::Component
9
9
  DEFAULT_TAG = :"clipboard-copy"
10
10
 
11
- DATA_CONTROLLER = "clipboard_copy_component"
12
- DATA_ACTION = "click->clipboard_copy_component#copy"
11
+ DEFAULT_CLASSES = "cursor-pointer"
12
+
13
+ DATA_CONTROLLER = "clipboard-copy-component"
14
+ DATA_ACTION = "click->clipboard-copy-component#copy"
13
15
 
14
16
  # @example Default
15
17
  # <%= render(Ariadne::ClipboardCopyComponent.new(value: "Text to copy", aria_label: "Copy text to the system clipboard" )) %>
@@ -36,7 +38,7 @@ module Ariadne
36
38
 
37
39
  validate!
38
40
 
39
- @classes = classes
41
+ @classes = class_names(DEFAULT_CLASSES, classes)
40
42
  @tag = check_incoming_tag(DEFAULT_TAG, tag)
41
43
  @attributes[:value] = value if value.present?
42
44
  end
@@ -8,7 +8,6 @@ module Ariadne
8
8
  # @private
9
9
  class Component < ViewComponent::Base
10
10
  include ViewComponent::SlotableV2 unless ViewComponent::Base < ViewComponent::SlotableV2
11
- include ViewComponent::PolymorphicSlots
12
11
  include ClassNameHelper
13
12
  include FetchOrFallbackHelper
14
13
  include TestSelectorHelper
@@ -17,6 +16,11 @@ module Ariadne
17
16
  include Status::Dsl
18
17
  include Audited::Dsl
19
18
  include LoggerHelper
19
+ include Ariadne::ActionViewExtensions::FormHelper
20
+
21
+ BASE_HTML_CLASSES = "h-full scroll-smooth bg-white antialiased"
22
+ BASE_BODY_CLASSES = "flex flex-col min-h-screen"
23
+ BASE_MAIN_CLASSES = "flex-auto"
20
24
 
21
25
  INVALID_ARIA_LABEL_TAGS = [:div, :span, :p].freeze
22
26
 
@@ -0,0 +1,3 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do |component| %>
2
+ <%= content %>
3
+ <% end %>
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # The container wraps the majority, if not all, of the content on a page.
5
+ class ContainerComponent < Ariadne::Component
6
+ DEFAULT_CLASSES = "px-4 py-5 sm:px-6 lg:px-8"
7
+
8
+ # @example Default
9
+ # <%= render(Ariadne::ContainerComponent.new) do |container| %>
10
+ # <%= render(Ariadne::ButtonComponent.new) { "Click me!" } %>
11
+ # <% end %>
12
+ #
13
+ # @param classes [String] <%= link_to_classes_docs %>
14
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
15
+ def initialize(classes: "", attributes: {})
16
+ @tag = :div
17
+ @classes = class_names(
18
+ DEFAULT_CLASSES,
19
+ classes
20
+ )
21
+
22
+ @attributes = attributes
23
+ end
24
+ end
25
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ariadne
4
- # Use `FlashComponent` to inform users of successful messages, pending actions, or urgent notices.
4
+ # Use `FlashComponent` to inform users of successful messages, pending actions, or urgent notices
5
5
  class FlashComponent < Ariadne::Component
6
6
  include IconHelper
7
7
 
@@ -46,12 +46,12 @@ module Ariadne
46
46
  # @option params [String] :classes <%= link_to_classes_docs %>
47
47
  # @option params [Hash] :attributes Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
48
48
  renders_one :icon, lambda { |tag: :svg, icon:, variant:, classes: "", attributes: {}|
49
- @icon = icon
50
- @variant = variant
49
+ @icon = icon
50
+ @variant = variant
51
51
 
52
- tag = check_incoming_tag(:svg, tag)
53
- Ariadne::HeroiconComponent.new(tag: tag, icon: icon, variant: variant, classes: classes, attributes: attributes)
54
- }
52
+ tag = check_incoming_tag(:svg, tag)
53
+ Ariadne::HeroiconComponent.new(tag: tag, icon: icon, variant: variant, classes: classes, attributes: attributes)
54
+ }
55
55
 
56
56
  # Optional action content showed at the bottom of the component.
57
57
  #
@@ -109,11 +109,11 @@ module Ariadne
109
109
 
110
110
  @attributes = attributes
111
111
  end
112
-
112
+ # TODO: test this
113
113
  private def has_action?
114
114
  action.present?
115
115
  end
116
-
116
+ # TODO: test this
117
117
  private def dismissible?
118
118
  @dismissible.present?
119
119
  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="mt-6 text-sm text-slate-500 sm: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 = "py-5"
7
+
8
+ # @example Default
9
+ #
10
+ # <%= render(Ariadne::FooterComponent.new) %>
11
+ #
12
+ # @param classes [String] <%= link_to_classes_docs %>
13
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
14
+ def initialize(classes: "", attributes: {})
15
+ @classes = class_names(
16
+ DEFAULT_CLASSES,
17
+ classes
18
+ )
19
+
20
+ @attributes = attributes
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: :ul, classes: @classes, attributes: @attributes) do |grid| %>
2
+ <% items.each do |item| %>
3
+ <li class="col-span-1 flex flex-col text-center rounded-lg shadow divide-y divide-gray-200 my-4 py-4 px-4 text-black-700 border-black <%= item.has_href? ? Ariadne::GridComponent::DEFAULT_LINK_COLOR_CLASSES : "bg-white" %>">
4
+ <% if item.has_href? %>
5
+ <%= render Ariadne::LinkComponent.new(href: item.href) do %>
6
+ <div class="flex-1 flex flex-col p-8">
7
+ <%= item.entry %>
8
+ </div>
9
+ <% end %>
10
+ <% else %>
11
+ <div class="flex-1 flex flex-col p-8">
12
+ <%= item.entry %>
13
+ </div>
14
+ <% end %>
15
+ </li>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Arranges items as a grid.
5
+ class GridComponent < Ariadne::Component
6
+ DEFAULT_CLASSES = "grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4"
7
+
8
+ DEFAULT_LINK_COLOR_CLASSES = "text-button-text-color bg-button-bg-color hover:bg-button-hover-color focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
9
+
10
+ # The items to show in the grid.
11
+ renders_many :items, "Item"
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 = 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 Item < Ariadne::Component
32
+ renders_one :entry, lambda { |static_content = nil, &block|
33
+ next static_content if static_content.present?
34
+
35
+ view_context.capture { block&.call }
36
+ }
37
+
38
+ attr_reader :href
39
+
40
+ def initialize(href: nil, classes: "", attributes: {})
41
+ @href = href
42
+ @classes = classes
43
+ @attributes = attributes
44
+ end
45
+
46
+ def has_href?
47
+ @href.present?
48
+ end
49
+
50
+ def call
51
+ render(Ariadne::BaseComponent.new(tag: :div, classes: @classes, attributes: @attributes))
52
+ end
53
+ end
54
+ end
55
+ 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="relative z-50 flex justify-between">
4
+ <div class="flex items-center md:gap-x-12">
5
+ <% if has_logo? %>
6
+ <%= render Ariadne::LinkComponent.new(href: @href) do %>
7
+ <% if has_image_logo? %>
8
+ <span class="sr-only"><%= content %></span>
9
+ <% end %>
10
+ <%= logo %>
11
+ <% end %>
12
+ <% end %>
13
+ <div class="md:flex md:gap-x-6">
14
+ <% navigation_links.each do |link| %>
15
+ <%= link %>
16
+ <% end %>
17
+ </div>
18
+ </div>
19
+ <div class="flex items-center gap-x-5 md:gap-x-8">
20
+ <div class="md: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,114 @@
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 = "sticky top-0 z-50 px-4 py-2 bg-white shadow-sm shadow-slate-900/5 transition duration-500"
7
+ # flex flex-wrap items-center justify-between bg-white dark:shadow-none dark:bg-transparent
8
+ LINK_CLASSES = "rounded-lg py-1 px-2 text-slate-700 hover:bg-slate-100 hover:text-slate-900"
9
+
10
+ DEFAULT_TEXT_LOGO_CLASSES = "flex items-center font-bold text-xl"
11
+ DEFAULT_IMAGE_LOGO_CLASSES = "h-10 w-auto"
12
+
13
+ # Leading visuals at the far left of the header. You can pass either
14
+ # `src` or `as_text` but not both.
15
+ #
16
+ # @param href [String] Where the logo should link to.
17
+ # @param src [String] The URL of the image logo.
18
+ # @param alt [String] Alt text for accessibility.
19
+ # @param as_text [Boolean] The text to display
20
+ # @param classes [String] <%= link_to_classes_docs %>
21
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
22
+ renders_one :logo, lambda { |href: nil, src: nil, alt: nil, as_text: false, classes: "", attributes: {}|
23
+ @href = href
24
+ if as_text.present?
25
+ actual_classes = class_names(DEFAULT_TEXT_LOGO_CLASSES, classes)
26
+ @text_logo = Ariadne::Text.new(tag: :span, classes: actual_classes, attributes: attributes)
27
+ else
28
+ actual_classes = class_names(DEFAULT_IMAGE_LOGO_CLASSES, classes)
29
+ actual_attributes = { width: 40, height: 40 }.merge(attributes)
30
+ @image_logo = Ariadne::ImageComponent.new(src: src, alt: alt, classes: actual_classes, attributes: actual_attributes)
31
+ end
32
+ }
33
+
34
+ # Text within the header, representing navigation.
35
+ #
36
+ # @param tag [String, Symbol] The tag to use for the link.
37
+ # @param href [String] The link destination.
38
+ # @param classes [String] <%= link_to_classes_docs %>
39
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
40
+ DEFAULT_NAV_LINK_CLASSES = "inline-block rounded-lg py-1 px-2 text-sm text-slate-700 hover:bg-slate-100 hover:text-slate-900"
41
+ renders_many :navigation_links, lambda { |tag: Ariadne::LinkComponent::DEFAULT_TAG, href:, classes: "", attributes: {}|
42
+ actual_classes = class_names(DEFAULT_NAV_LINK_CLASSES, classes)
43
+ Ariadne::LinkComponent.new(tag: tag, href: href, classes: actual_classes, attributes: attributes)
44
+ }
45
+
46
+ # Text within the header, representing actions.
47
+ #
48
+ # @param tag [String, Symbol] The tag to use for the link.
49
+ # @param href [String] The link destination.
50
+ # @param classes [String] <%= link_to_classes_docs %>
51
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
52
+ renders_many :action_links, lambda { |tag: Ariadne::LinkComponent::DEFAULT_TAG, href:, classes: "", attributes: {}|
53
+ actual_classes = class_names(DEFAULT_NAV_LINK_CLASSES, classes)
54
+ Ariadne::LinkComponent.new(tag: tag, href: href, classes: actual_classes, attributes: attributes)
55
+ }
56
+
57
+ # Text within the header, representing a signup.
58
+ #
59
+ # @param tag [String, Symbol] The tag to use for the link.
60
+ # @param href [String] The link destination.
61
+ # @param classes [String] <%= link_to_classes_docs %>
62
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
63
+ DEFAULT_SIGNUP_LINK_CLASSES = "group inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2"
64
+ renders_one :signup_link, lambda { |tag: Ariadne::LinkComponent::DEFAULT_TAG, href:, classes: "", attributes: {}|
65
+ actual_classes = class_names(DEFAULT_SIGNUP_LINK_CLASSES, classes)
66
+ Ariadne::LinkComponent.new(tag: tag, href: href, classes: actual_classes, attributes: attributes)
67
+ }
68
+
69
+ DEFAULT_PROFILE_LINK_CLASSES = "group inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2"
70
+ renders_one :profile_link, lambda { |tag: Ariadne::LinkComponent::DEFAULT_TAG, href:, classes: "", attributes: {}|
71
+ actual_classes = class_names(DEFAULT_PROFILE_LINK_CLASSES, classes)
72
+ Ariadne::LinkComponent.new(tag: tag, href: href, classes: actual_classes, attributes: attributes)
73
+ }
74
+
75
+ # @example Basic example
76
+ #
77
+ # <%= render(Ariadne::HeaderComponent.new) do |header| %>
78
+ # <%= header.logo(as_text: true) { "GitHub" } %>
79
+ # <%= header.navigation_link(href: "#features") { "Features" } %>
80
+ # <%= header.login_link(href: "/login") { "Login" } %>
81
+ # <% end %>
82
+ #
83
+ # @param classes [String] <%= link_to_classes_docs %>
84
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
85
+ def initialize(classes: "", attributes: {})
86
+ @classes = class_names(
87
+ DEFAULT_CLASSES,
88
+ classes
89
+ )
90
+
91
+ @attributes = attributes
92
+ end
93
+
94
+ private def has_logo?
95
+ logo.present?
96
+ end
97
+
98
+ private def has_text_logo?
99
+ logo.present? && @text_logo.present?
100
+ end
101
+
102
+ private def has_image_logo?
103
+ logo.present? && @image_logo.present?
104
+ end
105
+
106
+ private def has_signup_link?
107
+ signup_link.present?
108
+ end
109
+
110
+ private def has_profile_link?
111
+ profile_link.present?
112
+ end
113
+ end
114
+ end
@@ -20,7 +20,7 @@ module Ariadne
20
20
  TAG_OPTIONS = [:h1, :h2, :h3, :h4, :h5, :h6].freeze
21
21
 
22
22
  TAG_TO_CLASSES = {
23
- h1: "font-bold leading-7 sm:text-3xl sm:truncate",
23
+ h1: "font-bold leading-7 sm:text-3xl",
24
24
  h2: "text-3xl font-extrabold text-gray-900",
25
25
  h3: "text-2xl font-extrabold text-gray-900",
26
26
  }
@@ -1,7 +1,6 @@
1
- <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
2
- <% if @use_symbol %>
3
- <use href="#heroicon_<%= [@icon.symbol, @icon.height].join("_") %>"></use>
4
- <% else %>
1
+ <span class="inline-flex items-baseline">
2
+ <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
5
3
  <%= @icon.path.html_safe %>
6
4
  <% end %>
7
- <% end %>
5
+ <%= render(Ariadne::BaseComponent.new(tag: :span, classes: @text_classes, attributes: @attributes)) { content } if content.present? %>
6
+ </span>