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,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Add a general description of component here
|
5
|
+
# Add additional usage considerations or best practices that may aid the user to use the component correctly.
|
6
|
+
# @accessibility Add any accessibility considerations
|
7
|
+
class PanelBarComponent < Ariadne::Component
|
8
|
+
DEFAULT_TAG = :ol
|
9
|
+
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
10
|
+
|
11
|
+
DEFAULT_CLASSES = "ariadne-border ariadne-border-gray-300 ariadne-rounded-md ariadne-divide-y ariadne-divide-gray-300 md:ariadne-flex md:ariadne-divide-y-0"
|
12
|
+
|
13
|
+
renders_many :panels, "PanelItem"
|
14
|
+
|
15
|
+
# @example Default
|
16
|
+
#
|
17
|
+
# <%= render(Ariadne::PanelBarComponent.new) { "Example" } %>
|
18
|
+
#
|
19
|
+
# @param tag [Symbol, String] The rendered tag name.
|
20
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
21
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
22
|
+
def initialize(classes: "", attributes: {})
|
23
|
+
@tag = DEFAULT_TAG
|
24
|
+
@classes = class_names(
|
25
|
+
DEFAULT_CLASSES,
|
26
|
+
classes
|
27
|
+
)
|
28
|
+
|
29
|
+
@attributes = attributes
|
30
|
+
@attributes[:role] ||= "list"
|
31
|
+
end
|
32
|
+
|
33
|
+
# def render?
|
34
|
+
# items.any?
|
35
|
+
# end
|
36
|
+
|
37
|
+
# This component is part of `PanelBarComponent` and should not be
|
38
|
+
# used as a standalone component.
|
39
|
+
class PanelItem < Ariadne::Component
|
40
|
+
DEFAULT_ITEM_CLASSES = "ariadne-relative md:ariadne-flex-1 md:ariadne-flex"
|
41
|
+
DEFAULT_WRAPPER_CLASSES = "group ariadne-flex ariadne-items-center ariadne-w-full"
|
42
|
+
|
43
|
+
renders_one :icon, lambda { |static_content = nil, &block|
|
44
|
+
next static_content if static_content.present?
|
45
|
+
|
46
|
+
view_context.capture { block&.call }
|
47
|
+
}
|
48
|
+
|
49
|
+
renders_one :label, lambda { |static_content = nil, &block|
|
50
|
+
next static_content if static_content.present?
|
51
|
+
|
52
|
+
view_context.capture { block&.call }
|
53
|
+
}
|
54
|
+
|
55
|
+
attr_reader :link, :classes, :attributes
|
56
|
+
|
57
|
+
def initialize(link: {}, classes: "", attributes: {})
|
58
|
+
@link = link
|
59
|
+
if @link.present?
|
60
|
+
@link["classes"] = class_names(DEFAULT_WRAPPER_CLASSES, @link["classes"])
|
61
|
+
end
|
62
|
+
@classes = class_names(DEFAULT_ITEM_CLASSES, classes)
|
63
|
+
@attributes = attributes
|
64
|
+
end
|
65
|
+
|
66
|
+
def selected?
|
67
|
+
@selected
|
68
|
+
end
|
69
|
+
|
70
|
+
def linked?
|
71
|
+
@link.present?
|
72
|
+
end
|
73
|
+
|
74
|
+
def call
|
75
|
+
Ariadne::BaseComponent.new(tag: :div, classes: @classes, attributes: @attributes)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Creates a ariadne-rounded label that resembles a medicine pill.
|
5
|
+
class PillComponent < Ariadne::Component
|
6
|
+
DEFAULT_TAG = :span
|
7
|
+
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
8
|
+
|
9
|
+
DEFAULT_CLASSES = "ariadne-flex-shrink-0 ariadne-inline-block ariadne-px-2 ariadne-py-0.5 ariadne-text-xs ariadne-font-medium ariadne-rounded-full"
|
10
|
+
|
11
|
+
# @example Default
|
12
|
+
#
|
13
|
+
# <%= render(Ariadne::PillComponent.new(color: "FF0000")) { "Admin" } %>
|
14
|
+
#
|
15
|
+
# @param tag [Symbol, String] The rendered tag name.
|
16
|
+
# @param color [String] The hex color of the pill.
|
17
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
18
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
19
|
+
def initialize(tag: DEFAULT_TAG, color:, classes: "", attributes: {})
|
20
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
21
|
+
@classes = class_names(
|
22
|
+
DEFAULT_CLASSES,
|
23
|
+
classes
|
24
|
+
)
|
25
|
+
|
26
|
+
@attributes = attributes
|
27
|
+
@attributes["style"] = "background-color: ##{color};"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import {Controller} from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
import {Editor} from '@tiptap/core'
|
4
|
+
import {StarterKit} from '@tiptap/starter-kit'
|
5
|
+
|
6
|
+
export default class RichTextArea extends Controller {
|
7
|
+
connect() {
|
8
|
+
const editorElement = document.querySelector('.tiptap-editor')
|
9
|
+
if (editorElement) {
|
10
|
+
new Editor({
|
11
|
+
element: editorElement,
|
12
|
+
extensions: [StarterKit],
|
13
|
+
content: 'Hello World!',
|
14
|
+
editorProps: {
|
15
|
+
attributes: {
|
16
|
+
class:
|
17
|
+
'ariadne-prose ariadne-prose-sm sm:ariadne-prose lg:ariadne-prose-lg xl:ariadne-prose-2xl ariadne-m-5 focus:ariadne-outline-none'
|
18
|
+
}
|
19
|
+
}
|
20
|
+
})
|
21
|
+
|
22
|
+
const tiptapValueContainer = document.querySelector('input[data-tiptap-value-container=true]')
|
23
|
+
if (tiptapValueContainer) {
|
24
|
+
const parentForm = editorElement.closest('form')
|
25
|
+
|
26
|
+
parentForm?.addEventListener('submit', () => {
|
27
|
+
tiptapValueContainer.setAttribute('value', editorElement.textContent || '')
|
28
|
+
})
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
|
2
|
+
<% if @has_form %>
|
3
|
+
<input type="hidden" data-tiptap-value-container=true name="<%= @name %>" id="<%= @name %>" />
|
4
|
+
<% end %>
|
5
|
+
<textarea class="tiptap-editor"></textarea>
|
6
|
+
<% end %>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Add a general description of component here
|
5
|
+
# Add additional usage considerations or best practices that may aid the user to use the component correctly.
|
6
|
+
# @accessibility Add any accessibility considerations
|
7
|
+
class RichTextAreaComponent < Ariadne::Component
|
8
|
+
DEFAULT_TAG = :div
|
9
|
+
DEFAULT_CLASSES = "ariadne-block ariadne-w-full ariadne-border-0 ariadne-py-3 focus:ariadne-ring-0 sm:ariadne-text-sm"
|
10
|
+
|
11
|
+
# @example Default
|
12
|
+
#
|
13
|
+
# <%= render(Ariadne::RichTextAreaComponent.new(name: "bodytext", sr_label: "Enter message contents")) { "Example" } %>
|
14
|
+
#
|
15
|
+
# @param name [Symbol] Identifies the form/param name for this rich text area.
|
16
|
+
# @param sr_label [String] A label to introduce these tabs for screen readers.
|
17
|
+
# @param has_form [Boolean] Indicates whether this component is wrapped in a form.
|
18
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
19
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
20
|
+
def initialize(name:, sr_label:, has_form: true, classes: "", attributes: {})
|
21
|
+
@tag = DEFAULT_TAG
|
22
|
+
@name = name
|
23
|
+
@sr_label = sr_label
|
24
|
+
@has_form = has_form
|
25
|
+
|
26
|
+
@classes = class_names(
|
27
|
+
DEFAULT_CLASSES,
|
28
|
+
classes
|
29
|
+
)
|
30
|
+
@attributes = attributes
|
31
|
+
@attributes[:"data-controller"] = "rich-text-area-component"
|
32
|
+
@attributes[:"data-has-form"] = true if @has_form
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import {Controller} from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
export default class SlideoverComponent extends Controller {
|
4
|
+
static targets = ['expandable', 'expandWrapper', 'slidePanel', 'buttonWrapper']
|
5
|
+
|
6
|
+
declare readonly expandableTarget: HTMLDivElement
|
7
|
+
declare readonly expandWrapperTarget: HTMLDivElement
|
8
|
+
declare readonly slidePanelTargets: [HTMLDivElement]
|
9
|
+
declare readonly buttonWrapperTarget: HTMLDivElement
|
10
|
+
|
11
|
+
toggle() {
|
12
|
+
this.expandableTarget.classList.toggle('ariadne-hidden')
|
13
|
+
this.expandWrapperTarget.classList.toggle('bg-filter-panel')
|
14
|
+
for (const slidePanel of this.slidePanelTargets) {
|
15
|
+
slidePanel.classList.toggle('ariadne-hidden')
|
16
|
+
}
|
17
|
+
this.buttonWrapperTarget.classList.toggle('bg-filter-panel')
|
18
|
+
if (document.getElementById('btnClose')?.classList.contains('ariadne-hidden')) {
|
19
|
+
const formID = this.buttonWrapperTarget.getAttribute('data-slideover-component-form-id')
|
20
|
+
if (formID) {
|
21
|
+
const form = <HTMLFormElement>document.getElementById(formID)
|
22
|
+
form?.submit()
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do |slideover| %>
|
2
|
+
<div data-slideover-component-target="expandWrapper" class="ariadne-flex ariadne-flex-col ariadne-z-10">
|
3
|
+
<div data-slideover-component-target="expandable" class="ariadne-hidden bg-filter-panel ariadne-px-3 ariadne-pb-4">
|
4
|
+
<%= content %>
|
5
|
+
</div>
|
6
|
+
<div data-slideover-component-target="buttonWrapper" data-slideover-component-form-id="<%= @form_id %>" class="ariadne-relative ariadne-flex ariadne-justify-center">
|
7
|
+
<%= open_button %>
|
8
|
+
<%= close_button %>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Add a general description of component here
|
5
|
+
# Add additional usage considerations or best practices that may aid the user to use the component correctly.
|
6
|
+
# @accessibility Add any accessibility considerations
|
7
|
+
class SlideoverComponent < Ariadne::Component
|
8
|
+
DEFAULT_TAG = :div
|
9
|
+
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
10
|
+
|
11
|
+
DIRECTION_Y_DOWN = :yd
|
12
|
+
DIRECTION_X_LEFT = :xl
|
13
|
+
VALID_DIRECTIONS = [DIRECTION_Y_DOWN, DIRECTION_X_LEFT].freeze
|
14
|
+
|
15
|
+
DEFAULT_CLASSES = ""
|
16
|
+
|
17
|
+
DEFAULT_BUTTON_CLASSES = "ariadne-inline-flex ariadne-items-center ariadne-shadow-sm ariadne-px-4 ariadne-py-1.5 ariadne-pb-2 ariadne-text-sm ariadne-leading-5 ariadne-font-medium ariadne-rounded-full " + Ariadne::ButtonComponent::SCHEME_CLASS_MAPPINGS[:default]
|
18
|
+
renders_one :open_button, lambda { |label:, classes: "", attributes: {}, text_classes: ""|
|
19
|
+
actual_classes = class_names(DEFAULT_BUTTON_CLASSES, classes)
|
20
|
+
|
21
|
+
attributes[:id] ||= "btnOpen"
|
22
|
+
attributes[:"data-slideover-component-target"] ||= "slidePanel"
|
23
|
+
attributes[:"data-action"] ||= "click->slideover-component#toggle"
|
24
|
+
attributes[:type] ||= "submit"
|
25
|
+
|
26
|
+
icon = case @direction
|
27
|
+
when DIRECTION_Y_DOWN
|
28
|
+
:"chevron-double-down"
|
29
|
+
end
|
30
|
+
|
31
|
+
render(Ariadne::BaseComponent.new(tag: :button, classes: actual_classes, attributes: attributes)) do
|
32
|
+
render(Ariadne::HeroiconComponent.new(icon: icon, size: :sm, variant: HeroiconsHelper::Icon::VARIANT_SOLID, attributes: { "data-slideover-component-target" => "slide-panel" }, text_classes: text_classes)) { label }
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
renders_one :close_button, lambda { |label:, classes: "", attributes: {}, text_classes: ""|
|
37
|
+
actual_classes = class_names(DEFAULT_BUTTON_CLASSES, classes)
|
38
|
+
|
39
|
+
attributes[:id] ||= "btnClose"
|
40
|
+
attributes[:"data-slideover-component-target"] ||= "slidePanel"
|
41
|
+
attributes[:"data-action"] ||= "click->slideover-component#toggle"
|
42
|
+
attributes[:type] ||= "submit"
|
43
|
+
|
44
|
+
icon = case @direction
|
45
|
+
when DIRECTION_Y_DOWN
|
46
|
+
:"chevron-double-up"
|
47
|
+
end
|
48
|
+
|
49
|
+
render(Ariadne::BaseComponent.new(tag: :button, classes: actual_classes, attributes: attributes)) do
|
50
|
+
render(Ariadne::HeroiconComponent.new(icon: icon, size: :sm, variant: HeroiconsHelper::Icon::VARIANT_SOLID, attributes: { "data-slideover-component-target" => "slide-panel" }, text_classes: text_classes)) { label }
|
51
|
+
end
|
52
|
+
}
|
53
|
+
|
54
|
+
# @example Default
|
55
|
+
#
|
56
|
+
# <%= render(Ariadne::SlideoverComponent.new(direction: Ariadne::SlideoverComponent::DIRECTION_Y_DOWN)) { "Example" } %>
|
57
|
+
#
|
58
|
+
# @param tag [Symbol, String] The rendered tag name.
|
59
|
+
# @param direction [Symbol] <%= one_of(Ariadne::SlideoverComponent::VALID_DIRECTIONS) %>
|
60
|
+
# @param form_id [String] The ID of any <form> tag to submit when the button is clicked.
|
61
|
+
# @param open_text [String] The text to use to open the slideover.
|
62
|
+
# @param close_text [String] The text to use to close the slideover.
|
63
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
64
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
65
|
+
def initialize(tag: DEFAULT_TAG, direction:, form_id: nil, open_text: "Open", close_text: "Close", classes: "", attributes: {})
|
66
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
67
|
+
@classes = class_names(
|
68
|
+
DEFAULT_CLASSES,
|
69
|
+
classes
|
70
|
+
)
|
71
|
+
|
72
|
+
@direction = fetch_or_raise(VALID_DIRECTIONS, direction)
|
73
|
+
@form_id = form_id
|
74
|
+
|
75
|
+
@open_text = open_text
|
76
|
+
@close_text = close_text
|
77
|
+
@attributes = attributes
|
78
|
+
@attributes[:"data-controller"] = "slideover-component"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# A container for a row of tags.
|
5
|
+
# @accessibility This component requires you to pass in a `sr_label`
|
6
|
+
# attribute, which will be used to label the tabs for screen readers.
|
7
|
+
class TabBarComponent < Ariadne::Component
|
8
|
+
DEFAULT_TAG = :nav
|
9
|
+
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
10
|
+
|
11
|
+
DEFAULT_CLASSES = "ariadne--mb-px ariadne-flex ariadne-space-x-8 ariadne-bg-transparent"
|
12
|
+
|
13
|
+
# Tabs to be rendered. For more information, refer to <%= link_to_component(Ariadne::TabComponent) %>.
|
14
|
+
#
|
15
|
+
# @param selected [Boolean] Whether the tab is selected.
|
16
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
17
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
18
|
+
renders_many :tabs, lambda { |selected: false, classes: "", attributes: {}|
|
19
|
+
Ariadne::TabComponent.new(
|
20
|
+
selected: selected,
|
21
|
+
classes: classes,
|
22
|
+
attributes: attributes
|
23
|
+
)
|
24
|
+
}
|
25
|
+
|
26
|
+
# @example Default
|
27
|
+
#
|
28
|
+
# <%= render(Ariadne::TabBarComponent.new(sr_label: "Navigation tabs")) { "Example" } %>
|
29
|
+
#
|
30
|
+
# @param tag [Symbol, String] The rendered tag name.
|
31
|
+
# @param sr_label [String] A label to introduce these tabs for screen readers.
|
32
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
33
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
34
|
+
def initialize(tag: DEFAULT_TAG, sr_label:, classes: "", attributes: {})
|
35
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
36
|
+
@classes = class_names(
|
37
|
+
DEFAULT_CLASSES,
|
38
|
+
classes
|
39
|
+
)
|
40
|
+
@sr_label = sr_label
|
41
|
+
@attributes = attributes
|
42
|
+
@attributes[:"aria-label"] ||= "Tabs"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Represents a sequence of tabs which, when clicked, swap between panel contents.
|
5
|
+
class TabComponent < Ariadne::Component
|
6
|
+
DEFAULT_TAG = :button
|
7
|
+
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
8
|
+
|
9
|
+
# The Tab's text.
|
10
|
+
#
|
11
|
+
# @param kwargs [Hash] The same arguments as <%= link_to_component(Ariadne::Text) %>.
|
12
|
+
renders_one :text, Ariadne::Text
|
13
|
+
|
14
|
+
attr_reader :selected
|
15
|
+
|
16
|
+
DEFAULT_CLASSES = "ariadne-border-gray-300 ariadne-border ariadne-shadow ariadne-py-5 ariadne-px-5 ariadne-rounded-md"
|
17
|
+
|
18
|
+
BASE_TAB_CLASSES = "ariadne-whitespace-nowrap ariadne-py-4 ariadne-px-1 ariadne-border-b-2 ariadne-font-medium ariadne-text-sm"
|
19
|
+
|
20
|
+
# @example Default
|
21
|
+
#
|
22
|
+
# <%= render(Ariadne::TabComponent.new) { "Example" } %>
|
23
|
+
#
|
24
|
+
# @param tag [Symbol, String] The rendered tag name.
|
25
|
+
# @param selected [Boolean] Whether the tab is selected or not.
|
26
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
27
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
28
|
+
def initialize(tag: DEFAULT_TAG, selected: false, classes: "", attributes: {})
|
29
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
30
|
+
@classes = class_names(
|
31
|
+
DEFAULT_CLASSES,
|
32
|
+
classes
|
33
|
+
)
|
34
|
+
|
35
|
+
@attributes = attributes
|
36
|
+
@attributes[:id] ||= "tabs-tab-#{@tab_idx}"
|
37
|
+
@attributes[:"aria-controls"] = "tabs-panel-#{@tab_idx}"
|
38
|
+
@attributes[:"aria-current"] = "page" if selected
|
39
|
+
@attributes[:selected] = selected
|
40
|
+
@classes = class_names(BASE_TAB_CLASSES, classes)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# `Text` is a wrapper component that will apply typography styles to the text inside.
|
5
|
+
class Text < Ariadne::Component
|
6
|
+
DEFAULT_TAG = :span
|
7
|
+
|
8
|
+
# @example Default
|
9
|
+
# <%= render(Ariadne::Text.new(tag: :p, attributes: { font_weight: :bold })) { "Bold Text" } %>
|
10
|
+
# <%= render(Ariadne::Text.new(tag: :p, attributes: { color: :danger })) { "Danger Text" } %>
|
11
|
+
#
|
12
|
+
# @param tag [Symbol, String] The rendered tag name
|
13
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
14
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
15
|
+
def initialize(tag: DEFAULT_TAG, classes: "", attributes: {})
|
16
|
+
@tag = tag
|
17
|
+
@classes = classes
|
18
|
+
@attributes = attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
def call
|
22
|
+
render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) { content }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
import '@github/time-elements'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Displays a time ariadne-relative to how long ago it was. This component requires JavaScript.
|
5
|
+
class TimeAgoComponent < Ariadne::Component
|
6
|
+
DEFAULT_TAG = :"time-ago"
|
7
|
+
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
8
|
+
|
9
|
+
DEFAULT_CLASSES = "ariadne-whitespace-nowrap"
|
10
|
+
|
11
|
+
# @example Default
|
12
|
+
#
|
13
|
+
# <%= render(Ariadne::TimeAgoComponent.new(time: Time.now)) %>
|
14
|
+
#
|
15
|
+
# @param tag [Symbol, String] The rendered tag name.
|
16
|
+
# @param time [Time] The time to be formatted
|
17
|
+
# @param micro [Boolean] If true then the text will be formatted in "micro" mode, using as few characters as possible
|
18
|
+
# @param classes [String] <%%= link_to_classes_docs %>
|
19
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
20
|
+
def initialize(tag: DEFAULT_TAG, time:, micro: false, classes: "", attributes: {})
|
21
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
22
|
+
@classes = class_names(
|
23
|
+
DEFAULT_CLASSES,
|
24
|
+
classes
|
25
|
+
)
|
26
|
+
|
27
|
+
@time = time
|
28
|
+
@micro = micro
|
29
|
+
@attributes = attributes
|
30
|
+
@attributes[:datetime] = @time.utc.iso8601
|
31
|
+
@attributes[:format] = "micro" if @micro
|
32
|
+
end
|
33
|
+
|
34
|
+
def call
|
35
|
+
render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) { time_in_words }
|
36
|
+
end
|
37
|
+
|
38
|
+
private def time_in_words
|
39
|
+
return @time.in_time_zone.strftime("%b %-d, %Y") unless @micro
|
40
|
+
|
41
|
+
seconds_ago = Time.current - @time
|
42
|
+
|
43
|
+
if seconds_ago < 1.minute
|
44
|
+
"1m"
|
45
|
+
elsif seconds_ago >= 1.minute && seconds_ago < 1.hour
|
46
|
+
"#{(seconds_ago / 60).floor}m"
|
47
|
+
elsif seconds_ago >= 1.hour && seconds_ago < 1.day
|
48
|
+
"#{(seconds_ago / 60 / 60).floor}h"
|
49
|
+
elsif seconds_ago >= 1.day && seconds_ago < 1.year
|
50
|
+
"#{(seconds_ago / 60 / 60 / 24).floor}d"
|
51
|
+
elsif seconds_ago >= 1.year
|
52
|
+
"#{(seconds_ago / 60 / 60 / 24 / 365).floor}y"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do %>
|
2
|
+
<div>
|
3
|
+
<div class="ariadne-divide-y ariadne-divide-gray-200">
|
4
|
+
<div class="ariadne-pb-4">
|
5
|
+
<h2 id="activity-title" class="ariadne-text-lg ariadne-font-medium ariadne-text-gray-900">Timeline</h2>
|
6
|
+
</div>
|
7
|
+
<div class="ariadne-pt-6">
|
8
|
+
<!-- Activity feed-->
|
9
|
+
<div class="ariadne-flow-root">
|
10
|
+
<ul role="list" class="ariadne--mb-8">
|
11
|
+
<% items.each do %>
|
12
|
+
<%= items %>
|
13
|
+
<% end %>
|
14
|
+
</ul>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
<% end %>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Represents a linear timeline of events. Typically, this is shown
|
5
|
+
# as part of the Conversation component.
|
6
|
+
class TimelineComponent < Ariadne::Component
|
7
|
+
DEFAULT_TAG = :div
|
8
|
+
DEFAULT_CLASSES = ""
|
9
|
+
|
10
|
+
# The sub-items(s) to render
|
11
|
+
renders_many :items, lambda { |static_content = nil, &block|
|
12
|
+
next static_content if static_content.present?
|
13
|
+
|
14
|
+
view_context.capture { block&.call }
|
15
|
+
}
|
16
|
+
|
17
|
+
# @example Default
|
18
|
+
#
|
19
|
+
# <%= render(Ariadne::TimelineComponent.new) { "Example" } %>
|
20
|
+
#
|
21
|
+
# @param tag [Symbol, String] The rendered tag name
|
22
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
23
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
24
|
+
def initialize(tag: DEFAULT_TAG, classes: "", attributes: {})
|
25
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
26
|
+
@classes = class_names(
|
27
|
+
DEFAULT_CLASSES,
|
28
|
+
classes
|
29
|
+
)
|
30
|
+
|
31
|
+
@attributes = attributes
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import {Controller} from '@hotwired/stimulus'
|
2
|
+
import {createPopper} from '@popperjs/core'
|
3
|
+
import type {Instance, Placement} from '@popperjs/core'
|
4
|
+
|
5
|
+
export default class TooltipComponent extends Controller {
|
6
|
+
static targets = ['trigger', 'tooltip']
|
7
|
+
declare readonly triggerTarget: HTMLElement
|
8
|
+
declare readonly tooltipTarget: HTMLElement
|
9
|
+
|
10
|
+
static values = {
|
11
|
+
placement: {type: String, default: 'top'},
|
12
|
+
offset: {type: Array, default: [0, 8]}
|
13
|
+
}
|
14
|
+
declare readonly placementValue: Placement
|
15
|
+
declare readonly offsetValue: Array<number>
|
16
|
+
|
17
|
+
popperInstance: Instance
|
18
|
+
|
19
|
+
// Create a new Popper instance
|
20
|
+
connect() {
|
21
|
+
this.popperInstance = createPopper(this.triggerTarget, this.tooltipTarget, {
|
22
|
+
placement: this.placementValue,
|
23
|
+
modifiers: [
|
24
|
+
{
|
25
|
+
name: 'offset',
|
26
|
+
options: {
|
27
|
+
offset: this.offsetValue
|
28
|
+
}
|
29
|
+
}
|
30
|
+
]
|
31
|
+
})
|
32
|
+
}
|
33
|
+
|
34
|
+
// Destroy the Popper instance
|
35
|
+
disconnect() {
|
36
|
+
if (this.popperInstance) {
|
37
|
+
this.popperInstance.destroy()
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
show() {
|
42
|
+
this.tooltipTarget.setAttribute('data-tooltip-show', '')
|
43
|
+
this.tooltipTarget.classList.remove('ariadne-invisible')
|
44
|
+
|
45
|
+
// We need to tell Popper to update the tooltip position
|
46
|
+
// after we show the tooltip, otherwise it will be incorrect
|
47
|
+
this.popperInstance.update()
|
48
|
+
this.dispatch('shown', {detail: {trigger: this.triggerTarget, tooltip: this.tooltipTarget}})
|
49
|
+
}
|
50
|
+
|
51
|
+
hide() {
|
52
|
+
this.tooltipTarget.removeAttribute('data-tooltip-show')
|
53
|
+
this.tooltipTarget.classList.add('ariadne-invisible')
|
54
|
+
|
55
|
+
this.dispatch('ariadne-hidden', {detail: {trigger: this.triggerTarget, tooltip: this.tooltipTarget}})
|
56
|
+
}
|
57
|
+
}
|