ariadne_view_components 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/assets/builds/ariadne_view_components.css +1874 -0
  4. data/app/assets/javascripts/ariadne.d.ts +1 -0
  5. data/app/assets/javascripts/ariadne_view_components.js +1 -1
  6. data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
  7. data/app/assets/javascripts/clipboard-copy-component.d.ts +4 -0
  8. data/app/assets/javascripts/slideover-component.d.ts +9 -0
  9. data/app/assets/javascripts/time_ago_component.d.ts +1 -0
  10. data/app/assets/javascripts/tooltip-component.d.ts +24 -0
  11. data/app/assets/stylesheets/application.ariadne_view_components.css +5 -3
  12. data/app/assets/stylesheets/tooltip-component.css +37 -0
  13. data/app/components/ariadne/ariadne.d.ts +1 -0
  14. data/app/components/ariadne/ariadne.js +9 -0
  15. data/app/components/ariadne/ariadne.ts +3 -0
  16. data/app/components/ariadne/base_button.rb +9 -8
  17. data/app/components/ariadne/blankslate_component.rb +1 -1
  18. data/app/components/ariadne/body_component.rb +30 -0
  19. data/app/components/ariadne/button_component.rb +5 -10
  20. data/app/components/ariadne/clipboard-copy-component.d.ts +4 -0
  21. data/app/components/ariadne/clipboard-copy-component.js +18 -0
  22. data/app/components/ariadne/clipboard_copy_component.d.ts +4 -0
  23. data/app/components/ariadne/clipboard_copy_component.html.erb +2 -2
  24. data/app/components/ariadne/clipboard_copy_component.js +18 -0
  25. data/app/components/ariadne/clipboard_copy_component.rb +41 -3
  26. data/app/components/ariadne/comment_component.html.erb +25 -0
  27. data/app/components/ariadne/comment_component.rb +45 -0
  28. data/app/components/ariadne/component.rb +2 -1
  29. data/app/components/ariadne/container_component.rb +1 -1
  30. data/app/components/ariadne/flash_component.rb +1 -1
  31. data/app/components/ariadne/flex_component.rb +51 -0
  32. data/app/components/ariadne/grid_component.html.erb +12 -3
  33. data/app/components/ariadne/grid_component.rb +18 -7
  34. data/app/components/ariadne/header_component.rb +1 -1
  35. data/app/components/ariadne/heading_component.rb +2 -2
  36. data/app/components/ariadne/heroicon_component.html.erb +4 -6
  37. data/app/components/ariadne/heroicon_component.rb +18 -7
  38. data/app/components/ariadne/inline_flex_component.rb +11 -9
  39. data/app/components/ariadne/link_component.rb +13 -8
  40. data/app/components/ariadne/list_component.html.erb +5 -7
  41. data/app/components/ariadne/list_component.rb +4 -34
  42. data/app/components/ariadne/main_component.rb +32 -0
  43. data/app/components/ariadne/slideover-component.d.ts +9 -0
  44. data/app/components/ariadne/slideover-component.js +20 -0
  45. data/app/components/ariadne/slideover_component.d.ts +9 -0
  46. data/app/components/ariadne/slideover_component.html.erb +1 -4
  47. data/app/components/ariadne/slideover_component.js +19 -0
  48. data/app/components/ariadne/slideover_component.rb +19 -15
  49. data/app/components/ariadne/time_ago_component.d.ts +1 -0
  50. data/app/components/ariadne/time_ago_component.js +1 -0
  51. data/app/components/ariadne/tooltip-component.d.ts +24 -0
  52. data/app/components/ariadne/tooltip-component.js +42 -0
  53. data/app/components/ariadne/tooltip-component.ts +57 -0
  54. data/app/components/ariadne/tooltip_component.html.erb +4 -0
  55. data/app/components/ariadne/tooltip_component.rb +34 -31
  56. data/app/lib/ariadne/form_builder.rb +14 -14
  57. data/lib/ariadne/classify.rb +4 -98
  58. data/lib/ariadne/view_components/version.rb +1 -1
  59. data/lib/ariadne/view_components.rb +31 -29
  60. data/lib/rubocop/cop/ariadne/ariadne_heroicon.rb +2 -2
  61. data/lib/tasks/docs.rake +4 -0
  62. data/static/arguments.yml +89 -13
  63. data/static/audited_at.json +4 -0
  64. data/static/classes.yml +40 -8
  65. data/static/constants.json +83 -101
  66. data/static/statuses.json +4 -0
  67. metadata +48 -6
@@ -0,0 +1,4 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class ClipboardCopyComponent extends Controller {
3
+ copy(): void;
4
+ }
@@ -0,0 +1,9 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class SlideoverComponent extends Controller {
3
+ static targets: string[];
4
+ readonly expandableTarget: HTMLDivElement;
5
+ readonly expandWrapperTarget: HTMLDivElement;
6
+ readonly slidePanelTargets: [HTMLDivElement];
7
+ readonly buttonWrapperTarget: HTMLDivElement;
8
+ toggle(): void;
9
+ }
@@ -0,0 +1 @@
1
+ import '@github/time-elements';
@@ -0,0 +1,24 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ import type { Instance, Placement } from '@popperjs/core';
3
+ export default class TooltipComponent extends Controller {
4
+ static targets: string[];
5
+ readonly triggerTarget: HTMLElement;
6
+ readonly tooltipTarget: HTMLElement;
7
+ static values: {
8
+ placement: {
9
+ type: StringConstructor;
10
+ default: string;
11
+ };
12
+ offset: {
13
+ type: ArrayConstructor;
14
+ default: number[];
15
+ };
16
+ };
17
+ readonly placementValue: Placement;
18
+ readonly offsetValue: Array<number>;
19
+ popperInstance: Instance;
20
+ connect(): void;
21
+ disconnect(): void;
22
+ show(): void;
23
+ hide(): void;
24
+ }
@@ -1,3 +1,5 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
1
+ @import 'tailwindcss/base';
2
+ @import 'tailwindcss/components';
3
+ @import 'tailwindcss/utilities';
4
+
5
+ @import 'tooltip-component.css';
@@ -0,0 +1,37 @@
1
+ .tooltip-arrow,
2
+ .tooltip-arrow::before {
3
+ position: absolute;
4
+ width: 8px;
5
+ height: 8px;
6
+ background: inherit;
7
+ }
8
+
9
+ .tooltip-arrow {
10
+ visibility: hidden;
11
+ }
12
+
13
+ tooltip[data-tooltip-show] .tooltip-arrow::before {
14
+ visibility: visible;
15
+ content: '';
16
+ transform: rotate(45deg);
17
+ }
18
+
19
+ tooltip[data-popper-placement^='top'][data-tooltip-show] > .tooltip-arrow {
20
+ bottom: -4px;
21
+ }
22
+
23
+ tooltip[data-popper-placement^='bottom'] > .tooltip-arrow {
24
+ top: -4px;
25
+ }
26
+
27
+ tooltip[data-popper-placement^='left'] > .tooltip-arrow {
28
+ right: -4px;
29
+ }
30
+
31
+ tooltip[data-popper-placement^='right'] > .tooltip-arrow {
32
+ left: -4px;
33
+ }
34
+
35
+ tooltip[data-tooltip-show] {
36
+ display: block;
37
+ }
@@ -0,0 +1 @@
1
+ import './time_ago_component';
@@ -0,0 +1,9 @@
1
+ import { Application } from '@hotwired/stimulus';
2
+ import ClipboardCopyComponent from './clipboard-copy-component';
3
+ import SlideoverComponent from './slideover-component';
4
+ import TooltipComponent from './tooltip-component';
5
+ import './time_ago_component';
6
+ const application = Application.start();
7
+ application.register('clipboard-copy-component', ClipboardCopyComponent);
8
+ application.register('slideover-component', SlideoverComponent);
9
+ application.register('tooltip-component', TooltipComponent);
@@ -2,9 +2,12 @@ import {Application} from '@hotwired/stimulus'
2
2
 
3
3
  import ClipboardCopyComponent from './clipboard-copy-component'
4
4
  import SlideoverComponent from './slideover-component'
5
+ import TooltipComponent from './tooltip-component'
5
6
 
6
7
  import './time_ago_component'
7
8
 
8
9
  const application = Application.start()
10
+
9
11
  application.register('clipboard-copy-component', ClipboardCopyComponent)
10
12
  application.register('slideover-component', SlideoverComponent)
13
+ application.register('tooltip-component', TooltipComponent)
@@ -7,27 +7,28 @@ module Ariadne
7
7
  TAG_OPTIONS = [DEFAULT_TAG, :a, :summary].freeze
8
8
 
9
9
  DEFAULT_TYPE = :button
10
- TYPE_OPTIONS = [DEFAULT_TYPE, :reset, :submit].freeze
10
+ TYPE_SUBMIT = :submit
11
+ TYPE_OPTIONS = [DEFAULT_TYPE, :reset, TYPE_SUBMIT].freeze
11
12
 
12
13
  # TODO: dedupe the classes
13
14
  SIZE_CLASS_MAPPINGS = {
14
15
  xs: "inline-flex items-center px-2.5 py-1.5 text-xs font-medium rounded",
15
- s: "inline-flex items-center px-3 py-2 text-sm leading-4 font-medium rounded-m",
16
- m: "inline-flex items-center px-4 py-2 text-sm font-medium rounded-md",
17
- l: "inline-flex items-center px-4 py-2 text-base font-medium rounded-md",
16
+ sm: "inline-flex items-center px-3 py-2 text-sm leading-4 font-medium rounded-m",
17
+ md: "inline-flex items-center px-4 py-2 text-sm font-medium rounded-md",
18
+ lg: "inline-flex items-center px-4 py-2 text-base font-medium rounded-md",
18
19
  xl: "inline-flex items-center px-6 py-3 text-base font-medium rounded-md",
19
20
  }.freeze
20
21
  VALID_SIZES = SIZE_CLASS_MAPPINGS.keys.freeze
21
22
 
22
23
  DEFAULT_CLASSES = "inline-flex items-center px-4 py-2 text-sm border border-gray-300 shadow-sm text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
23
24
 
24
- DEFAULT_SIZE = :m
25
+ DEFAULT_SIZE = :md
25
26
 
26
27
  # @example Setting the size
27
28
  # <%= render(Ariadne::BaseButton.new(size: :xs)) { "I am an extra small button!" } %>
28
- # <%= render(Ariadne::BaseButton.new(size: :s)) { "I am a small button!" } %>
29
- # <%= render(Ariadne::BaseButton.new(size: :m)) { "I am a medium button!" } %>
30
- # <%= render(Ariadne::BaseButton.new(size: :l)) { "I am a large button!" } %>
29
+ # <%= render(Ariadne::BaseButton.new(size: :sm)) { "I am a small button!" } %>
30
+ # <%= render(Ariadne::BaseButton.new(size: :md)) { "I am a medium button!" } %>
31
+ # <%= render(Ariadne::BaseButton.new(size: :lg)) { "I am a large button!" } %>
31
32
  # <%= render(Ariadne::BaseButton.new(size: :xl)) { "I am an extra large button!" } %>
32
33
  #
33
34
  # @param tag [Symbol] <%= one_of(Ariadne::BaseButton::TAG_OPTIONS) %>
@@ -79,7 +79,7 @@ module Ariadne
79
79
  # @param href [String] URL to be used for the primary action.
80
80
  # @param classes [String] <%= link_to_classes_docs %>
81
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: {}|
82
+ renders_one :primary_action, lambda { |href: nil, tag: :a, method: nil, size: :md, scheme: :default, classes: "", attributes: {}|
83
83
  attributes[:href] = href
84
84
  attributes[:method] = method
85
85
 
@@ -0,0 +1,30 @@
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 BodyComponent < Ariadne::Component
8
+ DEFAULT_CLASSES = "flex flex-col min-h-screen"
9
+
10
+ # @example Default
11
+ #
12
+ # <%= render(Ariadne::BodyComponent.new) { "Example" } %>
13
+ #
14
+ # @param classes [String] <%= link_to_classes_docs %>
15
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
16
+ def initialize(classes: "", attributes: {})
17
+ @tag = :body
18
+ @classes = class_names(
19
+ DEFAULT_CLASSES,
20
+ classes
21
+ )
22
+
23
+ @attributes = attributes
24
+ end
25
+
26
+ def call
27
+ render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) { content }
28
+ end
29
+ end
30
+ end
@@ -58,18 +58,13 @@ module Ariadne
58
58
  #
59
59
  # @param tag [Symbol, String] The rendered tag name
60
60
  # @param text [String] The text content of the tooltip. This should be brief and no longer than a sentence.
61
- # @param direction [Symbol] <%= one_of(Ariadne::TooltipComponent::DIRECTION_OPTIONS) %>
61
+ # @param direction [Symbol] <%= one_of(Ariadne::TooltipComponent::VALID_PLACEMENTS) %>
62
62
  # @param classes [String] <%= link_to_classes_docs %>
63
63
  # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::TooltipComponent) %>.
64
- renders_one :tooltip, lambda { |tag: :"tool-tip", text:, direction: Ariadne::TooltipComponent::DIRECTION_DEFAULT, type: Ariadne::TooltipComponent::TYPE_DEFAULT, classes: "", attributes: {}|
64
+ renders_one :tooltip, lambda { |tag: Ariadne::TooltipComponent::DEFAULT_TAG, text:, direction: Ariadne::TooltipComponent::DEFAULT_PLACEMENT, type: Ariadne::TooltipComponent::TYPE_DEFAULT, classes: "", attributes: {}|
65
65
  raise ArgumentError, "Buttons with a tooltip must have a unique `id` set on the `Button`." if @id.blank?
66
66
 
67
- # TODO: test this
68
- tag = check_incoming_tag(:"tool-tip", tag)
69
- attributes[:for] = @id
70
- attributes[:type] = check_incoming_attribute(:description, attributes[:type])
71
-
72
- Ariadne::TooltipComponent.new(tag: tag, text: text, direction: direction, type: type, classes: classes, attributes: attributes)
67
+ Ariadne::TooltipComponent.new(for_id: @id, tag: tag, text: text, direction: direction, type: type, classes: classes, attributes: attributes)
73
68
  }
74
69
 
75
70
  # @example Schemes
@@ -81,8 +76,8 @@ module Ariadne
81
76
  # <%= render(Ariadne::ButtonComponent.new(scheme: :danger)) { "Danger" } %>
82
77
  #
83
78
  # @example Sizes
84
- # <%= render(Ariadne::ButtonComponent.new(size: :s)) { "Small" } %>
85
- # <%= render(Ariadne::ButtonComponent.new(size: :m)) { "Medium" } %>
79
+ # <%= render(Ariadne::ButtonComponent.new(size: :sm)) { "Small" } %>
80
+ # <%= render(Ariadne::ButtonComponent.new(size: :md)) { "Medium" } %>
86
81
  #
87
82
  # @example With leading visual
88
83
  # <%= render(Ariadne::ButtonComponent.new) do |c| %>
@@ -0,0 +1,4 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class ClipboardCopyComponent extends Controller {
3
+ copy(): void;
4
+ }
@@ -0,0 +1,18 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class ClipboardCopyComponent extends Controller {
3
+ copy() {
4
+ const value = this.element.attributes.getNamedItem('value');
5
+ const forNode = this.element.attributes.getNamedItem('for');
6
+ if (value) {
7
+ navigator.clipboard.writeText(value.value);
8
+ }
9
+ else if (forNode) {
10
+ const node = document.getElementById(forNode.value);
11
+ navigator.clipboard.writeText((node === null || node === void 0 ? void 0 : node.textContent) || '');
12
+ }
13
+ else {
14
+ // just copy inner text
15
+ navigator.clipboard.writeText(this.element.textContent || '');
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,4 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class ClipboardCopyComponent extends Controller {
3
+ copy(): void;
4
+ }
@@ -1,6 +1,6 @@
1
- <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes.merge(:"data-controller" => DATA_CONTROLLER, :"data-action" => DATA_ACTION)) do %>
1
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes.merge(controller_data)) do %>
2
2
  <% if content.present? %>
3
- <%= content %>
3
+ <%= content.to_s + tooltip.to_s %>
4
4
  <% else %>
5
5
  <%= render Ariadne::HeroiconComponent.new(icon: :duplicate, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE) %>
6
6
  <%= render Ariadne::HeroiconComponent.new(icon: :check, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE, attributes: { color: :success, style: "display: none;" }) %>
@@ -0,0 +1,18 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class ClipboardCopyComponent extends Controller {
3
+ copy() {
4
+ const value = this.element.attributes.getNamedItem('value');
5
+ const forNode = this.element.attributes.getNamedItem('for');
6
+ if (value) {
7
+ navigator.clipboard.writeText(value.value);
8
+ }
9
+ else if (forNode) {
10
+ const node = document.getElementById(forNode.value);
11
+ navigator.clipboard.writeText((node === null || node === void 0 ? void 0 : node.textContent) || '');
12
+ }
13
+ else {
14
+ // just copy inner text
15
+ navigator.clipboard.writeText(this.element.textContent || '');
16
+ }
17
+ }
18
+ }
@@ -8,11 +8,28 @@ module Ariadne
8
8
  class ClipboardCopyComponent < Ariadne::Component
9
9
  DEFAULT_TAG = :"clipboard-copy"
10
10
 
11
- DEFAULT_CLASSES = "cursor-pointer"
11
+ DEFAULT_CLASSES = LinkComponent::DEFAULT_ACTIONABLE_CLASSES
12
12
 
13
13
  DATA_CONTROLLER = "clipboard-copy-component"
14
14
  DATA_ACTION = "click->clipboard-copy-component#copy"
15
15
 
16
+ # `Tooltip` that appears on mouse hover or keyboard focus over the button. Use tooltips sparingly and as a last resort.
17
+ # **Important:** This tooltip defaults to `type: :description`. In a few scenarios, `type: :label` may be more appropriate.
18
+ # Consult the <%= link_to_component(Ariadne::TooltipComponent) %> documentation for more information.
19
+ #
20
+ # @param tag [Symbol, String] The rendered tag name
21
+ # @param text [String] The text content of the tooltip. This should be brief and no longer than a sentence.
22
+ # @param direction [Symbol] <%= one_of(Ariadne::TooltipComponent::VALID_PLACEMENTS) %>
23
+ # @param classes [String] <%= link_to_classes_docs %>
24
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::TooltipComponent) %>.
25
+ renders_one :tooltip, lambda { |tag: Ariadne::TooltipComponent::DEFAULT_TAG, text:, direction: Ariadne::TooltipComponent::DEFAULT_PLACEMENT, type: Ariadne::TooltipComponent::TYPE_DEFAULT, classes: "", attributes: {}|
26
+ raise ArgumentError, "CopyClipboardComponents with a tooltip must have a unique `id` set on the `CopyClipboardComponent`." if @id.blank?
27
+
28
+ @data_tooltip_direction = { "data-tooltip-component-direction": direction }
29
+
30
+ Ariadne::TooltipComponent.new(tag: tag, for_id: @id, text: text, direction: direction, type: type, classes: classes, attributes: attributes)
31
+ }
32
+
16
33
  # @example Default
17
34
  # <%= render(Ariadne::ClipboardCopyComponent.new(value: "Text to copy", aria_label: "Copy text to the system clipboard" )) %>
18
35
  #
@@ -22,19 +39,22 @@ module Ariadne
22
39
  # <% end %>
23
40
  #
24
41
  # @example Copying from an element
25
- # <%= render(Ariadne::ClipboardCopyComponent.new(attributes: { for: "blob-path"}, aria_label: "Copy text to the system clipboard" )) %>
42
+ # <%= render(Ariadne::ClipboardCopyComponent.new(for_id: "blob-path", aria_label: "Copy text to the system clipboard" )) %>
26
43
  # <div id="blob-path">src/index.js</div>
27
44
  #
28
45
  # @param tag [Symbol, String] The rendered tag name
29
46
  # @param classes [String] <%= link_to_classes_docs %>
30
47
  # @param value [String] Text to copy into the users clipboard when they click the component.
48
+ # @param for_id [String] If `value` is not provided, the element with this id will be copied.
31
49
  # @param aria_label [String] Text for accessibility. Can also be passed in as part of `attributes`, but it must be present.
32
50
  # @param attributes [Hash] <%= link_to_attributes_docs %>
33
- def initialize(tag: DEFAULT_TAG, value: "", aria_label: "", classes: "", attributes: {})
51
+ def initialize(tag: DEFAULT_TAG, value: "", for_id: nil, aria_label: "", classes: "", attributes: {})
34
52
  @attributes = attributes
35
53
  @value = value
36
54
 
37
55
  @attributes[:"aria-label"] = aria_label
56
+ @attributes[:for] ||= for_id
57
+ @id = @attributes[:id]
38
58
 
39
59
  validate!
40
60
 
@@ -48,5 +68,23 @@ module Ariadne
48
68
  raise ArgumentError, "Must provide either `value` or `for`" if @value.blank? && @attributes[:for].blank?
49
69
  raise ArgumentError, "Must provide only `value` or `for`, not both" if @value.present? && @attributes[:for].present?
50
70
  end
71
+
72
+ DATA_CONTROLLERS_WITH_TOOLTIPS = {
73
+ "data-controller": "#{DATA_CONTROLLER} #{Ariadne::TooltipComponent::DATA_CONTROLLER}",
74
+ "data-action": "#{DATA_ACTION} #{Ariadne::TooltipComponent::DATA_ACTION}",
75
+ "data-tooltip-component-target": "trigger",
76
+ }
77
+
78
+ DATA_CONTROLLERS =
79
+ {
80
+ "data-controller": DATA_CONTROLLER.to_s,
81
+ "data-action": DATA_ACTION,
82
+ }
83
+
84
+ def controller_data
85
+ return DATA_CONTROLLERS_WITH_TOOLTIPS if tooltip.present?
86
+
87
+ DATA_CONTROLLERS
88
+ end
51
89
  end
52
90
  end
@@ -0,0 +1,25 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do |comment| %>
2
+ <div>
3
+ <div class="flex items-center" aria-orientation="horizontal" role="tablist">
4
+ <% tabs.each do |tab| %>
5
+ <%= tab %>
6
+ <% end %>
7
+ </div>
8
+ <div class="mt-2">
9
+ <div id="tabs-1-panel-1" class="p-0.5 -m-0.5 rounded-lg" aria-labelledby="tabs-1-tab-1" role="tabpanel" tabindex="0">
10
+ <label for="comment" class="sr-only">Comment</label>
11
+ <div>
12
+ <textarea rows="5" name="comment" id="comment" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" placeholder="Add your comment..."></textarea>
13
+ </div>
14
+ </div>
15
+ <div id="tabs-1-panel-2" class="p-0.5 -m-0.5 rounded-lg" aria-labelledby="tabs-1-tab-2" role="tabpanel" tabindex="0">
16
+ <div class="border-b">
17
+ <div class="mx-px mt-px px-3 pt-2 pb-12 text-sm leading-5 text-gray-800">Preview content will render here.</div>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ <div class="mt-2 flex justify-end">
23
+ <%= submit %>
24
+ </div>
25
+ <% end %>
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Defines a submittble form for adding comments to a conversation.
5
+ class CommentComponent < Ariadne::Component
6
+ DEFAULT_TAG = :form
7
+ TAG_OPTIONS = [DEFAULT_TAG].freeze
8
+
9
+ DEFAULT_CLASSES = ""
10
+
11
+ DEFAULT_TAB_CLASSES = "text-gray-500 hover:text-gray-900 bg-white hover:bg-gray-100 px-3 py-1.5 border border-transparent text-sm font-medium rounded-md"
12
+ renders_many :tabs, lambda { |classes: "", attributes: {}|
13
+ actual_classes = class_names(DEFAULT_TAB_CLASSES, classes)
14
+ @tab_idx += 1
15
+ attributes[:id] = "comment-tabs-tab-#{@tab_idx}"
16
+ attributes[:"aria-controls"] = "comment-tabs-panel-#{@tab_idx}"
17
+ attributes[:role] = "tab"
18
+ attributes[:type] = "button"
19
+
20
+ Ariadne::BaseComponent.new(tag: :button, classes: actual_classes, attributes: attributes)
21
+ }
22
+
23
+ renders_one :submit, lambda { |classes: "", attributes: {}|
24
+ Ariadne::ButtonComponent.new(type: Ariadne::BaseButton::TYPE_SUBMIT, classes: classes, attributes: attributes)
25
+ }
26
+
27
+ # @example Default
28
+ #
29
+ # <%= render(Ariadne::CommentComponent.new(action: "#")) { "Example" } %>
30
+ #
31
+ # @param action [String] The action to take when the form is submitted.
32
+ # @param classes [String] <%= link_to_classes_docs %>
33
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
34
+ def initialize(action:, classes: "", attributes: {})
35
+ @tag = DEFAULT_TAG
36
+ @classes = class_names(
37
+ DEFAULT_CLASSES,
38
+ classes
39
+ )
40
+
41
+ @tab_idx = 0
42
+ @attributes = attributes.merge(action: action)
43
+ end
44
+ end
45
+ end
@@ -19,7 +19,8 @@ module Ariadne
19
19
  include Ariadne::ActionViewExtensions::FormHelper
20
20
 
21
21
  BASE_HTML_CLASSES = "h-full scroll-smooth bg-white antialiased"
22
- BASE_BODY_CLASSES = "flex flex-col min-h-screen"
22
+ BASE_BODY_CLASSES = "flex h-full flex-col"
23
+ BASE_WRAPPER_CLASSES = "flex flex-col h-screen justify-between"
23
24
  BASE_MAIN_CLASSES = "flex-auto"
24
25
 
25
26
  INVALID_ARIA_LABEL_TAGS = [:div, :span, :p].freeze
@@ -3,7 +3,7 @@
3
3
  module Ariadne
4
4
  # The container wraps the majority, if not all, of the content on a page.
5
5
  class ContainerComponent < Ariadne::Component
6
- DEFAULT_CLASSES = "px-4 py-5 sm:px-6 lg:px-8"
6
+ DEFAULT_CLASSES = "px-4 sm:px-6 lg:px-8"
7
7
 
8
8
  # @example Default
9
9
  # <%= render(Ariadne::ContainerComponent.new) do |container| %>
@@ -84,7 +84,7 @@ module Ariadne
84
84
  # @example With actions
85
85
  # <%= render(Ariadne::FlashComponent.new) do |component| %>
86
86
  # <% component.action do %>
87
- # <%= render(Ariadne::ButtonComponent.new(size: :s)) { "Take action" } %>
87
+ # <%= render(Ariadne::ButtonComponent.new(size: :sm)) { "Take action" } %>
88
88
  # <% end %>
89
89
  # This is a flash message with actions!
90
90
  # <% end %>
@@ -0,0 +1,51 @@
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 FlexComponent < Ariadne::Component
8
+ DEFAULT_TAG = :div
9
+ TAG_OPTIONS = [DEFAULT_TAG].freeze
10
+
11
+ DEFAULT_CLASSES = "flex"
12
+
13
+ VALID_TYPES = [:row, :column, :row_reverse, :column_reverse].freeze
14
+
15
+ # @example Default
16
+ #
17
+ # <%= render(Ariadne::FlexComponent.new(type: :column)) { "Example" } %>
18
+ #
19
+ # @param tag [Symbol, String] The rendered tag name.
20
+ # @param type [Symbol] <%= one_of(Ariadne::FlexComponent::VALID_TYPES) %>
21
+ # @param classes [String] <%= link_to_classes_docs %>
22
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
23
+ def initialize(tag: DEFAULT_TAG, type:, classes: "", attributes: {})
24
+ @tag = check_incoming_tag(DEFAULT_TAG, tag)
25
+ @type = fetch_or_raise(VALID_TYPES, type)
26
+
27
+ flex_class = case @type
28
+ when :row
29
+ "flex-row"
30
+ when :column
31
+ "flex-col"
32
+ when :row_reverse
33
+ "flex-row-reverse"
34
+ when :column_reverse
35
+ "flex-col-reverse"
36
+ end
37
+
38
+ @classes = class_names(
39
+ DEFAULT_CLASSES,
40
+ classes,
41
+ flex_class
42
+ )
43
+
44
+ @attributes = attributes
45
+ end
46
+
47
+ def call
48
+ render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) { content }
49
+ end
50
+ end
51
+ end
@@ -1,10 +1,19 @@
1
1
  <%= render Ariadne::BaseComponent.new(tag: :ul, classes: @classes, attributes: @attributes) do |grid| %>
2
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" %>">
3
+ <li class="<%= item.classes %> <%= item.has_href? ? Ariadne::GridComponent::DEFAULT_LINK_COLOR_CLASSES : "bg-white" %>">
4
4
  <% if item.has_href? %>
5
5
  <%= render Ariadne::LinkComponent.new(href: item.href) do %>
6
- <div class="flex-1 flex flex-col p-8">
7
- <%= item.entry %>
6
+ <%= item.entry %>
7
+ <% end %>
8
+ <% if item.actions.any? %>
9
+ <div>
10
+ <div class="-mt-px flex divide-x">
11
+ <% item.actions.each_with_index do |action, idx| %>
12
+ <div class="<%= idx.zero? ? '' : '-ml-px ' %>w-0 flex-1 flex">
13
+ <%= action %>
14
+ </div>
15
+ <% end %>
16
+ </div>
8
17
  </div>
9
18
  <% end %>
10
19
  <% else %>
@@ -3,9 +3,9 @@
3
3
  module Ariadne
4
4
  # Arranges items as a grid.
5
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"
6
+ DEFAULT_CLASSES = "grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3"
7
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"
8
+ DEFAULT_LINK_COLOR_CLASSES = "text-button-text-color bg-button-bg-color focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
9
9
 
10
10
  # The items to show in the grid.
11
11
  renders_many :items, "Item"
@@ -29,17 +29,28 @@ module Ariadne
29
29
  # This component is part of `GridComponent` and should not be
30
30
  # used as a standalone component.
31
31
  class Item < Ariadne::Component
32
- renders_one :entry, lambda { |static_content = nil, &block|
33
- next static_content if static_content.present?
32
+ DEFAULT_ITEM_CLASSES = "flex flex-col text-center rounded-lg shadow my-4 text-black-700 border-black"
34
33
 
35
- view_context.capture { block&.call }
34
+ DEFAULT_ENTRY_CLASSES = "group flex-1 flex flex-col p-8 hover:bg-button-hover-color hover:text-gray-500 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
36
39
  }
37
40
 
38
- attr_reader :href
41
+ DEFAULT_ACTION_LINK_CLASSES = "text-button-text-color relative -mr-px w-0 flex-1 inline-flex items-center justify-center py-4 text-sm font-medium border border-transparent rounded-bl-lg 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
39
50
 
40
51
  def initialize(href: nil, classes: "", attributes: {})
41
52
  @href = href
42
- @classes = classes
53
+ @classes = class_names(DEFAULT_ITEM_CLASSES, classes)
43
54
  @attributes = attributes
44
55
  end
45
56
 
@@ -3,7 +3,7 @@
3
3
  module Ariadne
4
4
  # Represents the top navigation bar on every page.
5
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"
6
+ DEFAULT_CLASSES = "sticky top-0 z-50 px-4 py-5 bg-white shadow-sm shadow-slate-900/5 transition duration-500"
7
7
  # flex flex-wrap items-center justify-between bg-white dark:shadow-none dark:bg-transparent
8
8
  LINK_CLASSES = "rounded-lg py-1 px-2 text-slate-700 hover:bg-slate-100 hover:text-slate-900"
9
9
 
@@ -21,8 +21,8 @@ module Ariadne
21
21
 
22
22
  TAG_TO_CLASSES = {
23
23
  h1: "font-bold leading-7 sm:text-3xl",
24
- h2: "text-3xl font-extrabold text-gray-900",
25
- h3: "text-2xl font-extrabold text-gray-900",
24
+ h2: "text-3xl font-extrabold",
25
+ h3: "text-2xl font-extrabold",
26
26
  }
27
27
  # @example Default
28
28
  # <%= render(Ariadne::HeadingComponent.new(tag: :h1)) { "H1 Text" } %>
@@ -1,6 +1,4 @@
1
- <span class="inline-flex items-baseline">
2
- <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
3
- <%= @icon.path.html_safe %>
4
- <% end %>
5
- <%= render(Ariadne::BaseComponent.new(tag: :span, classes: @text_classes, attributes: @attributes)) { content } if content.present? %>
6
- </span>
1
+ <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
2
+ <%= @icon.path.html_safe %>
3
+ <% end %>
4
+ <%= render(Ariadne::BaseComponent.new(tag: :span, classes: @text_classes, attributes: @text_attributes)) { content } if content.present? %>