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.
- checksums.yaml +4 -4
- data/README.md +7 -6
- data/app/assets/config/manifest.js +2 -0
- data/app/assets/javascripts/ariadne_view_components.js +1 -1
- data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
- data/app/assets/stylesheets/{application.tailwind.css → application.ariadne_view_components.css} +0 -0
- data/app/components/ariadne/ariadne.ts +5 -9
- data/app/components/ariadne/base_button.rb +1 -0
- data/app/components/ariadne/blankslate_component.html.erb +26 -0
- data/app/components/ariadne/blankslate_component.rb +151 -0
- data/app/components/ariadne/button_component.html.erb +1 -1
- data/app/components/ariadne/button_component.rb +4 -1
- data/app/components/ariadne/{clipboard_copy_component.ts → clipboard-copy-component.ts} +0 -0
- data/app/components/ariadne/clipboard_copy_component.rb +5 -3
- data/app/components/ariadne/component.rb +5 -1
- data/app/components/ariadne/container_component.html.erb +3 -0
- data/app/components/ariadne/container_component.rb +25 -0
- data/app/components/ariadne/flash_component.rb +8 -8
- data/app/components/ariadne/footer_component.html.erb +7 -0
- data/app/components/ariadne/footer_component.rb +23 -0
- data/app/components/ariadne/grid_component.html.erb +17 -0
- data/app/components/ariadne/grid_component.rb +55 -0
- data/app/components/ariadne/header_component.html.erb +29 -0
- data/app/components/ariadne/header_component.rb +114 -0
- data/app/components/ariadne/heading_component.rb +1 -1
- data/app/components/ariadne/heroicon_component.html.erb +4 -5
- data/app/components/ariadne/heroicon_component.rb +5 -3
- data/app/components/ariadne/image_component.rb +5 -3
- data/app/components/ariadne/inline_flex_component.html.erb +5 -0
- data/app/components/ariadne/inline_flex_component.rb +63 -0
- data/app/components/ariadne/link_component.rb +60 -0
- data/app/components/ariadne/list_component.html.erb +17 -0
- data/app/components/ariadne/list_component.rb +97 -0
- data/app/components/ariadne/pill_component.html.erb +3 -0
- data/app/components/ariadne/pill_component.rb +30 -0
- data/app/components/ariadne/slideover-component.ts +26 -0
- data/app/components/ariadne/slideover_component.html.erb +14 -0
- data/app/components/ariadne/slideover_component.rb +77 -0
- data/app/components/ariadne/time_ago_component.rb +56 -0
- data/app/components/ariadne/time_ago_component.ts +1 -0
- data/app/components/ariadne/timeline_component.html.erb +19 -0
- data/app/components/ariadne/timeline_component.rb +34 -0
- data/app/lib/ariadne/action_view_extensions/form_helper.rb +23 -0
- data/app/lib/ariadne/form_builder.rb +71 -0
- data/lib/ariadne/classify.rb +9 -1
- data/lib/ariadne/view_components/engine.rb +6 -0
- data/lib/ariadne/view_components/version.rb +1 -1
- data/lib/rubocop/cop/ariadne/ariadne_heroicon.rb +2 -2
- data/lib/tasks/docs.rake +48 -58
- data/static/arguments.yml +204 -7
- data/static/audited_at.json +14 -0
- data/static/classes.yml +157 -1
- data/static/constants.json +171 -10
- data/static/statuses.json +14 -0
- metadata +31 -5
- data/lib/tasks/tailwind.rake +0 -31
data/app/assets/stylesheets/{application.tailwind.css → application.ariadne_view_components.css}
RENAMED
File without changes
|
@@ -1,14 +1,10 @@
|
|
1
1
|
import {Application} from '@hotwired/stimulus'
|
2
2
|
|
3
|
-
import ClipboardCopyComponent from './
|
3
|
+
import ClipboardCopyComponent from './clipboard-copy-component'
|
4
|
+
import SlideoverComponent from './slideover-component'
|
4
5
|
|
5
|
-
|
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('
|
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:
|
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-
|
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
|
File without changes
|
@@ -8,8 +8,10 @@ module Ariadne
|
|
8
8
|
class ClipboardCopyComponent < Ariadne::Component
|
9
9
|
DEFAULT_TAG = :"clipboard-copy"
|
10
10
|
|
11
|
-
|
12
|
-
|
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,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
|
-
|
50
|
-
|
49
|
+
@icon = icon
|
50
|
+
@variant = variant
|
51
51
|
|
52
|
-
|
53
|
-
|
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
|
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
|
-
|
2
|
-
|
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
|
-
|
5
|
+
<%= render(Ariadne::BaseComponent.new(tag: :span, classes: @text_classes, attributes: @attributes)) { content } if content.present? %>
|
6
|
+
</span>
|