ariadne_view_components 0.0.10-x86_64-linux
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 +7 -0
- data/LICENSE.txt +49 -0
- data/README.md +73 -0
- data/app/assets/config/manifest.js +2 -0
- data/app/assets/javascripts/ariadne-form-with.d.ts +20 -0
- data/app/assets/javascripts/ariadne-form.d.ts +22 -0
- data/app/assets/javascripts/ariadne.d.ts +1 -0
- data/app/assets/javascripts/ariadne_view_components.js +8 -0
- data/app/assets/javascripts/ariadne_view_components.js.map +1 -0
- data/app/assets/javascripts/clipboard-copy-component.d.ts +4 -0
- data/app/assets/javascripts/comment-component.d.ts +13 -0
- data/app/assets/javascripts/rich-text-area-component.d.ts +4 -0
- data/app/assets/javascripts/slideover-component.d.ts +9 -0
- data/app/assets/javascripts/time-ago-component.d.ts +1 -0
- data/app/assets/javascripts/time_ago_component.d.ts +1 -0
- data/app/assets/javascripts/tooltip-component.d.ts +24 -0
- data/app/assets/stylesheets/ariadne_view_components.css +6 -0
- data/app/assets/stylesheets/prosemirror.css +323 -0
- data/app/assets/stylesheets/tooltip-component.css +37 -0
- data/app/components/ariadne/ariadne-form.ts +96 -0
- data/app/components/ariadne/ariadne.ts +20 -0
- data/app/components/ariadne/base_button.rb +61 -0
- data/app/components/ariadne/base_component.rb +37 -0
- data/app/components/ariadne/blankslate_component.html.erb +26 -0
- data/app/components/ariadne/blankslate_component.rb +146 -0
- data/app/components/ariadne/body_component.rb +30 -0
- data/app/components/ariadne/button_component.html.erb +4 -0
- data/app/components/ariadne/button_component.rb +157 -0
- data/app/components/ariadne/clipboard-copy-component.ts +19 -0
- data/app/components/ariadne/clipboard_copy_component.html.erb +9 -0
- data/app/components/ariadne/clipboard_copy_component.rb +90 -0
- data/app/components/ariadne/comment-component.ts +55 -0
- data/app/components/ariadne/comment_component.html.erb +22 -0
- data/app/components/ariadne/comment_component.rb +57 -0
- data/app/components/ariadne/component.rb +128 -0
- data/app/components/ariadne/container_component.html.erb +3 -0
- data/app/components/ariadne/container_component.rb +25 -0
- data/app/components/ariadne/content.rb +12 -0
- data/app/components/ariadne/counter_component.rb +100 -0
- data/app/components/ariadne/flash_component.html.erb +31 -0
- data/app/components/ariadne/flash_component.rb +125 -0
- data/app/components/ariadne/flex_component.rb +49 -0
- 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 +26 -0
- data/app/components/ariadne/grid_component.rb +66 -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 +49 -0
- data/app/components/ariadne/heroicon_component.html.erb +4 -0
- data/app/components/ariadne/heroicon_component.rb +129 -0
- data/app/components/ariadne/image_component.rb +53 -0
- data/app/components/ariadne/inline_flex_component.html.erb +5 -0
- data/app/components/ariadne/inline_flex_component.rb +65 -0
- data/app/components/ariadne/link_component.rb +65 -0
- data/app/components/ariadne/list_component.html.erb +15 -0
- data/app/components/ariadne/list_component.rb +68 -0
- data/app/components/ariadne/main_component.rb +32 -0
- data/app/components/ariadne/narrow_container_component.html.erb +3 -0
- data/app/components/ariadne/narrow_container_component.rb +30 -0
- data/app/components/ariadne/panel_bar_component.html.erb +20 -0
- data/app/components/ariadne/panel_bar_component.rb +79 -0
- data/app/components/ariadne/pill_component.html.erb +3 -0
- data/app/components/ariadne/pill_component.rb +30 -0
- data/app/components/ariadne/rich-text-area-component.ts +32 -0
- data/app/components/ariadne/rich_text_area_component.html.erb +6 -0
- data/app/components/ariadne/rich_text_area_component.rb +35 -0
- data/app/components/ariadne/slideover-component.ts +26 -0
- data/app/components/ariadne/slideover_component.html.erb +11 -0
- data/app/components/ariadne/slideover_component.rb +81 -0
- data/app/components/ariadne/tab_bar_component.html.erb +3 -0
- data/app/components/ariadne/tab_bar_component.rb +45 -0
- data/app/components/ariadne/tab_component.html.erb +7 -0
- data/app/components/ariadne/tab_component.rb +43 -0
- data/app/components/ariadne/text.rb +25 -0
- data/app/components/ariadne/time-ago-component.ts +1 -0
- data/app/components/ariadne/time_ago_component.rb +56 -0
- data/app/components/ariadne/timeline_component.html.erb +19 -0
- data/app/components/ariadne/timeline_component.rb +34 -0
- data/app/components/ariadne/tooltip-component.ts +57 -0
- data/app/components/ariadne/tooltip_component.html.erb +4 -0
- data/app/components/ariadne/tooltip_component.rb +108 -0
- data/app/lib/ariadne/action_view_extensions/form_helper.rb +26 -0
- data/app/lib/ariadne/audited/dsl.rb +32 -0
- data/app/lib/ariadne/class_name_helper.rb +22 -0
- data/app/lib/ariadne/fetch_or_fallback_helper.rb +102 -0
- data/app/lib/ariadne/form_builder.rb +71 -0
- data/app/lib/ariadne/icon_helper.rb +47 -0
- data/app/lib/ariadne/join_style_arguments_helper.rb +14 -0
- data/app/lib/ariadne/logger_helper.rb +23 -0
- data/app/lib/ariadne/status/dsl.rb +41 -0
- data/app/lib/ariadne/tab_nav_helper.rb +35 -0
- data/app/lib/ariadne/tabbed_component_helper.rb +39 -0
- data/app/lib/ariadne/test_selector_helper.rb +20 -0
- data/app/lib/ariadne/underline_nav_helper.rb +44 -0
- data/app/lib/ariadne/view_helper.rb +22 -0
- data/exe/tailwindcss +21 -0
- data/exe/x86_64-linux/tailwindcss +0 -0
- data/lib/ariadne/view_components/commands.rb +90 -0
- data/lib/ariadne/view_components/constants.rb +53 -0
- data/lib/ariadne/view_components/engine.rb +75 -0
- data/lib/ariadne/view_components/linters.rb +3 -0
- data/lib/ariadne/view_components/statuses.rb +14 -0
- data/lib/ariadne/view_components/upstream.rb +20 -0
- data/lib/ariadne/view_components/version.rb +7 -0
- data/lib/ariadne/view_components.rb +61 -0
- data/lib/rubocop/config/default.yml +8 -0
- data/lib/rubocop/cop/ariadne/base_cop.rb +26 -0
- data/lib/rubocop/cop/ariadne/no_tag_memoize.rb +44 -0
- data/lib/rubocop/cop/ariadne.rb +3 -0
- data/lib/tasks/ariadne_view_components.rake +48 -0
- data/lib/tasks/build.rake +30 -0
- data/lib/tasks/coverage.rake +19 -0
- data/lib/tasks/custom_utilities.yml +310 -0
- data/lib/tasks/docs.rake +524 -0
- data/lib/tasks/helpers/ast_processor.rb +44 -0
- data/lib/tasks/helpers/ast_traverser.rb +77 -0
- data/lib/tasks/static.rake +15 -0
- data/lib/yard/docs_helper.rb +83 -0
- data/lib/yard/renders_many_handler.rb +19 -0
- data/lib/yard/renders_one_handler.rb +19 -0
- data/static/arguments.yml +619 -0
- data/static/assets/view-components.svg +18 -0
- data/static/audited_at.json +38 -0
- data/static/classes.yml +291 -0
- data/static/constants.json +426 -0
- data/static/statuses.json +38 -0
- data/static/tailwindcss.yml +727 -0
- data/tailwind.config.js +65 -0
- metadata +264 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "view_component/version"
|
4
|
+
|
5
|
+
require "heroicons_helper"
|
6
|
+
|
7
|
+
module Ariadne
|
8
|
+
# @private
|
9
|
+
class Component < ViewComponent::Base
|
10
|
+
include ViewComponent::SlotableV2 unless ViewComponent::Base < ViewComponent::SlotableV2
|
11
|
+
include ClassNameHelper
|
12
|
+
include FetchOrFallbackHelper
|
13
|
+
include TestSelectorHelper
|
14
|
+
include JoinStyleArgumentsHelper
|
15
|
+
include ViewHelper
|
16
|
+
include Status::Dsl
|
17
|
+
include Audited::Dsl
|
18
|
+
include LoggerHelper
|
19
|
+
include Ariadne::ActionViewExtensions::FormHelper
|
20
|
+
|
21
|
+
BASE_HTML_CLASSES = "ariadne-h-full scroll-smooth ariadne-bg-white ariadne-antialiased"
|
22
|
+
BASE_BODY_CLASSES = "ariadne-flex ariadne-h-full ariadne-flex-col"
|
23
|
+
BASE_WRAPPER_CLASSES = "ariadne-flex ariadne-flex-col ariadne-h-screen ariadne-justify-between"
|
24
|
+
BASE_MAIN_CLASSES = "ariadne-flex-auto"
|
25
|
+
|
26
|
+
INVALID_ARIA_LABEL_TAGS = [:div, :span, :p].freeze
|
27
|
+
|
28
|
+
private def raise_on_invalid_options?
|
29
|
+
Rails.application.config.ariadne_view_components.raise_on_invalid_options
|
30
|
+
end
|
31
|
+
|
32
|
+
private def raise_on_invalid_aria?
|
33
|
+
Rails.application.config.ariadne_view_components.raise_on_invalid_aria
|
34
|
+
end
|
35
|
+
|
36
|
+
private def deprecated_component_warning(new_class: nil, version: nil)
|
37
|
+
return if silence_deprecations?
|
38
|
+
|
39
|
+
message = "#{self.class.name} is deprecated"
|
40
|
+
message += " and will be removed in v#{version}." if version
|
41
|
+
message += " Use #{new_class.name} instead." if new_class
|
42
|
+
|
43
|
+
ActiveSupport::Deprecation.warn(message)
|
44
|
+
end
|
45
|
+
|
46
|
+
private def aria(val, attributes)
|
47
|
+
attributes[:"aria-#{val}"] || attributes.dig(:aria, val.to_sym)
|
48
|
+
end
|
49
|
+
|
50
|
+
private def validate_aria_label!
|
51
|
+
aria_label = aria("label", @attributes)
|
52
|
+
raise ArgumentError, "`aria-label` is required." if aria_label.blank?
|
53
|
+
end
|
54
|
+
|
55
|
+
private def check_denylist(denylist = [], attributes = {})
|
56
|
+
if should_raise_error?
|
57
|
+
|
58
|
+
# Convert denylist from:
|
59
|
+
# { [:p, :pt] => "message" } to:
|
60
|
+
# { p: "message", pt: "message" }
|
61
|
+
unpacked_denylist =
|
62
|
+
denylist.each_with_object({}) do |(keys, value), memo|
|
63
|
+
keys.each { |key| memo[key] = value }
|
64
|
+
end
|
65
|
+
|
66
|
+
violations = unpacked_denylist.keys & attributes.keys
|
67
|
+
|
68
|
+
if violations.any?
|
69
|
+
message = "Found #{violations.count} #{"violation".pluralize(violations)}:"
|
70
|
+
violations.each do |violation|
|
71
|
+
message += "\n The #{violation} argument is not allowed here. #{unpacked_denylist[violation]}"
|
72
|
+
end
|
73
|
+
|
74
|
+
raise(ArgumentError, message)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
attributes
|
79
|
+
end
|
80
|
+
|
81
|
+
private def validate_attributes(tag:, denylist_name: :attributes_denylist, attributes: {})
|
82
|
+
deny_single_argument(:class, "Use `classes` instead.", attributes)
|
83
|
+
|
84
|
+
if (denylist = attributes[denylist_name])
|
85
|
+
check_denylist(denylist, attributes)
|
86
|
+
|
87
|
+
# Remove :attributes_denylist key and any denied keys from system arguments
|
88
|
+
attributes.except!(denylist_name)
|
89
|
+
attributes.except!(*denylist.keys.flatten)
|
90
|
+
end
|
91
|
+
|
92
|
+
deny_aria_label(tag: tag, attributes: attributes)
|
93
|
+
|
94
|
+
attributes
|
95
|
+
end
|
96
|
+
|
97
|
+
private def deny_single_argument(key, help_text, attributes)
|
98
|
+
raise ArgumentError, "`#{key}` is an invalid argument. #{help_text}" \
|
99
|
+
if should_raise_error? && attributes.key?(key)
|
100
|
+
|
101
|
+
attributes.except!(key)
|
102
|
+
end
|
103
|
+
|
104
|
+
private def deny_aria_label(tag:, attributes:)
|
105
|
+
return attributes.except!(:skip_aria_label_check) if attributes[:skip_aria_label_check]
|
106
|
+
return if attributes[:role]
|
107
|
+
return unless INVALID_ARIA_LABEL_TAGS.include?(tag)
|
108
|
+
|
109
|
+
deny_aria_key(
|
110
|
+
:label,
|
111
|
+
"Don't use `aria-label` on `#{tag}` elements. See https://www.tpgi.com/short-note-on-aria-label-aria-labelledby-and-aria-describedby/",
|
112
|
+
attributes
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
private def deny_aria_key(key, help_text, attributes)
|
117
|
+
raise ArgumentError, help_text if should_raise_aria_error? && aria(key, attributes)
|
118
|
+
end
|
119
|
+
|
120
|
+
private def should_raise_error?
|
121
|
+
raise_on_invalid_options? && !ENV["ARIADNE_WARNINGS_DISABLED"]
|
122
|
+
end
|
123
|
+
|
124
|
+
private def should_raise_aria_error?
|
125
|
+
raise_on_invalid_aria? && !ENV["ARIADNE_WARNINGS_DISABLED"]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
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 = "ariadne-px-4 sm:ariadne-px-6 lg:ariadne-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
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Use `CounterComponent` to add a count to navigational elements and buttons.
|
5
|
+
#
|
6
|
+
# @accessibility
|
7
|
+
# Always use `CounterComponent` with adjacent text that provides supplementary information regarding what the count is for. For instance, `Counter`
|
8
|
+
# should be accompanied with text such as `issues` or `pull requests`.
|
9
|
+
#
|
10
|
+
class CounterComponent < Ariadne::Component
|
11
|
+
DEFAULT_CLASSES = "ariadne-inline-flex ariadne-items-center ariadne-p-1 ariadne-border ariadne-border-transparent ariadne-rounded-full ariadne-shadow-sm focus:ariadne-outline-none focus:ariadne-ring-2 focus:ariadne-ring-offset-2"
|
12
|
+
|
13
|
+
# @example Default
|
14
|
+
# <%= render(Ariadne::CounterComponent.new(count: 25)) %>
|
15
|
+
#
|
16
|
+
# @example Schemes
|
17
|
+
# <%= render(Ariadne::CounterComponent.new(count: 25)) %>
|
18
|
+
# <%= render(Ariadne::CounterComponent.new(count: 25)) %>
|
19
|
+
#
|
20
|
+
# @param tag [Symbol, String] The rendered tag name
|
21
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
22
|
+
# @param count [Integer, Float::INFINITY, nil] The number to be displayed (e.x. # of issues, pull requests)
|
23
|
+
# @param limit [Integer, nil] Maximum value to display. Pass `nil` for no limit. (e.x. if `count` == 6,000 and `limit` == 5000, counter will display "5,000+")
|
24
|
+
# @param hide_if_zero [Boolean] If true, a `hidden` attribute is added to the counter if `count` is zero.
|
25
|
+
# @param text [String] Text to display instead of count.
|
26
|
+
# @param round [Boolean] Whether to apply rounding logic to value.
|
27
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
28
|
+
def initialize(
|
29
|
+
tag: :span,
|
30
|
+
count: 0,
|
31
|
+
limit: 9_000,
|
32
|
+
hide_if_zero: false,
|
33
|
+
text: "",
|
34
|
+
round: false,
|
35
|
+
classes: "",
|
36
|
+
attributes: {}
|
37
|
+
)
|
38
|
+
@count = count
|
39
|
+
@limit = limit
|
40
|
+
@hide_if_zero = hide_if_zero
|
41
|
+
@text = text
|
42
|
+
@round = round
|
43
|
+
@attributes = attributes
|
44
|
+
|
45
|
+
@has_limit = !@limit.nil?
|
46
|
+
|
47
|
+
@tag = check_incoming_tag(:span, tag)
|
48
|
+
|
49
|
+
@attributes[:title] = title
|
50
|
+
|
51
|
+
@classes = class_names(
|
52
|
+
DEFAULT_CLASSES,
|
53
|
+
classes
|
54
|
+
)
|
55
|
+
@attributes[:hidden] = true if count == 0 && hide_if_zero
|
56
|
+
end
|
57
|
+
|
58
|
+
def call
|
59
|
+
render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) { value }
|
60
|
+
end
|
61
|
+
|
62
|
+
private def title
|
63
|
+
if @text.present?
|
64
|
+
@text
|
65
|
+
elsif @count.nil?
|
66
|
+
"Not available"
|
67
|
+
elsif @count == Float::INFINITY
|
68
|
+
"Infinity"
|
69
|
+
else
|
70
|
+
count = @count.to_i
|
71
|
+
str = number_with_delimiter(@has_limit ? [count, @limit].min : count)
|
72
|
+
str += "+" if @has_limit && count > @limit
|
73
|
+
str
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private def value
|
78
|
+
if @text.present?
|
79
|
+
@text
|
80
|
+
elsif @count.nil?
|
81
|
+
"" # CSS will hide it
|
82
|
+
elsif @count == Float::INFINITY
|
83
|
+
"∞"
|
84
|
+
else
|
85
|
+
if @round
|
86
|
+
count = @has_limit ? [@count.to_i, @limit].min : @count.to_i
|
87
|
+
precision = count.between?(100_000, 999_999) ? 0 : 1
|
88
|
+
units = { thousand: "k", million: "m", billion: "b" }
|
89
|
+
str = number_to_human(count, precision: precision, significant: false, units: units, format: "%n%u")
|
90
|
+
else
|
91
|
+
@count = @count.to_i
|
92
|
+
str = number_with_delimiter(@has_limit ? [@count, @limit].min : @count)
|
93
|
+
end
|
94
|
+
|
95
|
+
str += "+" if @has_limit && @count.to_i > @limit
|
96
|
+
str
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -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,125 @@
|
|
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
|
+
DISMISSIBLE_SCHEME_CLASS_MAPPINGS = {
|
11
|
+
default: "ariadne-text-blue-500 ariadne-bg-blue-50 hover:ariadne-bg-blue-100 focus:ariadne-ring-offset-blue-50 focus:ariadne-ring-blue-600",
|
12
|
+
info: "ariadne-text-blue-500 ariadne-bg-blue-50 hover:ariadne-bg-blue-100 focus:ariadne-ring-offset-blue-50 focus:ariadne-ring-blue-600",
|
13
|
+
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",
|
14
|
+
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",
|
15
|
+
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",
|
16
|
+
}.freeze
|
17
|
+
VALID_DISMISSIBLE_SCHEMES = DISMISSIBLE_SCHEME_CLASS_MAPPINGS.keys.freeze
|
18
|
+
|
19
|
+
BG_SCHEME_CLASS_MAPPINGS = {
|
20
|
+
default: "ariadne-bg-blue-50",
|
21
|
+
info: "ariadne-bg-blue-50",
|
22
|
+
success: "ariadne-bg-green-50",
|
23
|
+
warning: "ariadne-bg-yellow-50",
|
24
|
+
danger: "ariadne-bg-red-50",
|
25
|
+
}.freeze
|
26
|
+
VALID_BG_SCHEMES = BG_SCHEME_CLASS_MAPPINGS.keys.freeze
|
27
|
+
|
28
|
+
CONTENT_SCHEME_CLASS_MAPPINGS = {
|
29
|
+
default: "ariadne-text-blue-700",
|
30
|
+
info: "ariadne-text-blue-700",
|
31
|
+
success: "ariadne-text-green-700",
|
32
|
+
warning: "ariadne-text-yellow-700",
|
33
|
+
danger: "ariadne-text-red-700",
|
34
|
+
}.freeze
|
35
|
+
VALID_CONTENT_SCHEMES = CONTENT_SCHEME_CLASS_MAPPINGS.keys.freeze
|
36
|
+
|
37
|
+
# Optional visuals appearing to the left of the flash banner.
|
38
|
+
#
|
39
|
+
# Use:
|
40
|
+
#
|
41
|
+
# - `icon` for a <%= link_to_component(Ariadne::HeroiconComponent) %>.
|
42
|
+
#
|
43
|
+
# @option params [Symbol] :icon The rendered tag name
|
44
|
+
# @option params [Symbol] :icon Name of <%= link_to_heroicons %> to use.
|
45
|
+
# @option params [Symbol] :variant <%= one_of(HeroiconsHelper::Icon::VALID_VARIANTS, sort: false) %>
|
46
|
+
# @option params [String] :classes <%= link_to_classes_docs %>
|
47
|
+
# @option params [Hash] :attributes Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
|
48
|
+
renders_one :icon, lambda { |tag: :svg, icon:, variant:, classes: "", attributes: {}|
|
49
|
+
@icon = icon
|
50
|
+
@variant = variant
|
51
|
+
|
52
|
+
tag = check_incoming_tag(:svg, tag)
|
53
|
+
Ariadne::HeroiconComponent.new(tag: tag, icon: icon, variant: variant, classes: classes, attributes: attributes)
|
54
|
+
}
|
55
|
+
|
56
|
+
# Optional action content showed at the bottom of the component.
|
57
|
+
#
|
58
|
+
# @param tag [Symbol, String] The rendered tag name
|
59
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
60
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
61
|
+
renders_one :action, lambda { |tag: :div, classes: "", attributes: {}|
|
62
|
+
tag = check_incoming_tag(:div, tag)
|
63
|
+
|
64
|
+
actual_classes = class_names(classes)
|
65
|
+
|
66
|
+
Ariadne::BaseComponent.new(tag: tag, classes: actual_classes, attributes: attributes)
|
67
|
+
}
|
68
|
+
|
69
|
+
# @example Schemes
|
70
|
+
# <%= render(Ariadne::FlashComponent.new) { "This is a flash message!" } %>
|
71
|
+
# <%= render(Ariadne::FlashComponent.new(scheme: :warning)) { "This is a warning flash message!" } %>
|
72
|
+
# <%= render(Ariadne::FlashComponent.new(scheme: :danger)) { "This is a danger flash message!" } %>
|
73
|
+
# <%= render(Ariadne::FlashComponent.new(scheme: :success)) { "This is a success flash message!" } %>
|
74
|
+
#
|
75
|
+
# @example Dismissible
|
76
|
+
# <%= render(Ariadne::FlashComponent.new(dismissible: true)) { "This is a dismissible flash message!" } %>
|
77
|
+
#
|
78
|
+
# @example Icon
|
79
|
+
# <%= render(Ariadne::FlashComponent.new) do |component| %>
|
80
|
+
# <% component.icon(icon: :"user-group", variant: HeroiconsHelper::Icon::VARIANT_OUTLINE) %>
|
81
|
+
# Look at this icon.
|
82
|
+
# <% end %>
|
83
|
+
#
|
84
|
+
# @example With actions
|
85
|
+
# <%= render(Ariadne::FlashComponent.new) do |component| %>
|
86
|
+
# <% component.action do %>
|
87
|
+
# <%= render(Ariadne::ButtonComponent.new(size: :sm)) { "Take action" } %>
|
88
|
+
# <% end %>
|
89
|
+
# This is a flash message with actions!
|
90
|
+
# <% end %>
|
91
|
+
#
|
92
|
+
# @param tag [Symbol, String] The rendered tag name.
|
93
|
+
# @param dismissible [Boolean] Whether the component can be dismissed with an X button.
|
94
|
+
# @param icon [Symbol, String] Name of <%= link_to_heroicons %> to use.
|
95
|
+
# @param scheme [Symbol] <%= one_of(Ariadne::FlashComponent::VALID_CONTENT_SCHEMES) %>
|
96
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
97
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
98
|
+
def initialize(tag: :div, dismissible: false, scheme: DEFAULT_SCHEME, classes: "", attributes: {})
|
99
|
+
@dismissible = dismissible
|
100
|
+
|
101
|
+
@tag = check_incoming_tag(:div, tag)
|
102
|
+
|
103
|
+
@scheme = fetch_or_raise(VALID_CONTENT_SCHEMES, scheme)
|
104
|
+
|
105
|
+
@classes = class_names(
|
106
|
+
CONTENT_SCHEME_CLASS_MAPPINGS[@scheme],
|
107
|
+
classes
|
108
|
+
)
|
109
|
+
|
110
|
+
@attributes = attributes
|
111
|
+
end
|
112
|
+
# TODO: test this
|
113
|
+
private def has_action?
|
114
|
+
action.present?
|
115
|
+
end
|
116
|
+
# TODO: test this
|
117
|
+
private def dismissible?
|
118
|
+
@dismissible.present?
|
119
|
+
end
|
120
|
+
|
121
|
+
private def dismissible_classes
|
122
|
+
DISMISSIBLE_SCHEME_CLASS_MAPPINGS[@scheme]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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 = "flex"
|
10
|
+
|
11
|
+
VALID_TYPES = [:row, :column, :row_reverse, :column_reverse].freeze
|
12
|
+
|
13
|
+
# @example Default
|
14
|
+
#
|
15
|
+
# <%= render(Ariadne::FlexComponent.new(type: :column)) { "Example" } %>
|
16
|
+
#
|
17
|
+
# @param tag [Symbol, String] The rendered tag name.
|
18
|
+
# @param type [Symbol] <%= one_of(Ariadne::FlexComponent::VALID_TYPES) %>
|
19
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
20
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
21
|
+
def initialize(tag: DEFAULT_TAG, type:, classes: "", attributes: {})
|
22
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
23
|
+
@type = fetch_or_raise(VALID_TYPES, type)
|
24
|
+
|
25
|
+
flex_class = case @type
|
26
|
+
when :row
|
27
|
+
"ariadne-flex-row"
|
28
|
+
when :column
|
29
|
+
"ariadne-flex-col"
|
30
|
+
when :row_reverse
|
31
|
+
"ariadne-flex-row-reverse"
|
32
|
+
when :column_reverse
|
33
|
+
"ariadne-flex-col-reverse"
|
34
|
+
end
|
35
|
+
|
36
|
+
@classes = class_names(
|
37
|
+
DEFAULT_CLASSES,
|
38
|
+
classes,
|
39
|
+
flex_class
|
40
|
+
)
|
41
|
+
|
42
|
+
@attributes = attributes
|
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,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 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) %>
|
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,26 @@
|
|
1
|
+
<%= render Ariadne::BaseComponent.new(tag: :ul, classes: @classes, attributes: @attributes) do |grid| %>
|
2
|
+
<% items.each do |item| %>
|
3
|
+
<li class="<%= item.classes %> <%= item.has_href? ? Ariadne::GridComponent::DEFAULT_LINK_COLOR_CLASSES : "ariadne-bg-white" %>">
|
4
|
+
<% if item.has_href? %>
|
5
|
+
<%= render Ariadne::LinkComponent.new(href: item.href) 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,66 @@
|
|
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 = "ariadne-text-button-text-color ariadne-bg-button-bg-color 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 = 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 ariadne-my-4 text-black-700 ariadne-border-black"
|
33
|
+
|
34
|
+
DEFAULT_ENTRY_CLASSES = "group ariadne-flex-1 ariadne-flex ariadne-flex-col ariadne-p-8 hover:ariadne-bg-button-hover-color hover:ariadne-text-gray-500 ariadne-rounded-lg"
|
35
|
+
renders_one :entry, lambda { |classes: "", &block|
|
36
|
+
view_context.capture do
|
37
|
+
render(Ariadne::BaseComponent.new(tag: :div, classes: classes)) { block&.call }
|
38
|
+
end
|
39
|
+
}
|
40
|
+
|
41
|
+
DEFAULT_ACTION_LINK_CLASSES = "text-button-text-color 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 ariadne-rounded-bl-lg ariadne-rounded-lg"
|
42
|
+
renders_many :actions, lambda { |href:, icon:, label:, size: Ariadne::HeroiconComponent::SIZE_DEFAULT, variant: HeroiconsHelper::Icon::VARIANT_SOLID, classes: "", attributes: {}, text_classes: ""|
|
43
|
+
actual_classes = class_names(DEFAULT_ACTION_LINK_CLASSES, classes)
|
44
|
+
render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) do
|
45
|
+
render(Ariadne::HeroiconComponent.new(icon: icon, size: size, variant: variant, text_classes: text_classes)) { label }
|
46
|
+
end
|
47
|
+
}
|
48
|
+
|
49
|
+
attr_reader :href, :classes, :attributes
|
50
|
+
|
51
|
+
def initialize(href: nil, classes: "", attributes: {})
|
52
|
+
@href = href
|
53
|
+
@classes = class_names(DEFAULT_ITEM_CLASSES, classes)
|
54
|
+
@attributes = attributes
|
55
|
+
end
|
56
|
+
|
57
|
+
def has_href?
|
58
|
+
@href.present?
|
59
|
+
end
|
60
|
+
|
61
|
+
def call
|
62
|
+
render(Ariadne::BaseComponent.new(tag: :div, classes: @classes, attributes: @attributes))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
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"><%= content %></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 %>
|