ariadne_view_components 0.0.43 → 0.0.44

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/app/components/ariadne/ariadne-form.d.ts +22 -0
  4. data/app/components/ariadne/ariadne-form.js +85 -0
  5. data/app/components/ariadne/ariadne-form.ts +96 -0
  6. data/app/components/ariadne/ariadne.d.ts +2 -0
  7. data/app/components/ariadne/ariadne.js +16 -0
  8. data/app/components/ariadne/ariadne.ts +21 -0
  9. data/app/components/ariadne/avatar_component.rb +81 -0
  10. data/app/components/ariadne/avatar_stack_component/avatar_stack_component.html.erb +12 -0
  11. data/app/components/ariadne/avatar_stack_component.rb +75 -0
  12. data/app/components/ariadne/base_button.rb +70 -0
  13. data/app/components/ariadne/base_component.rb +37 -0
  14. data/app/components/ariadne/blankslate_component/blankslate_component.html.erb +26 -0
  15. data/app/components/ariadne/blankslate_component.rb +148 -0
  16. data/app/components/ariadne/body_component.rb +30 -0
  17. data/app/components/ariadne/button_component/button_component.html.erb +4 -0
  18. data/app/components/ariadne/button_component.rb +165 -0
  19. data/app/components/ariadne/clipboard_copy_component/clipboard-copy-component.d.ts +4 -0
  20. data/app/components/ariadne/clipboard_copy_component/clipboard-copy-component.js +18 -0
  21. data/app/components/ariadne/clipboard_copy_component/clipboard-copy-component.ts +19 -0
  22. data/app/components/ariadne/clipboard_copy_component/clipboard_copy_component.html.erb +9 -0
  23. data/app/components/ariadne/clipboard_copy_component.rb +90 -0
  24. data/app/components/ariadne/comment_component/comment_component.html.erb +37 -0
  25. data/app/components/ariadne/comment_component.rb +71 -0
  26. data/app/components/ariadne/component.rb +127 -0
  27. data/app/components/ariadne/container_component/container_component.html.erb +3 -0
  28. data/app/components/ariadne/container_component.rb +25 -0
  29. data/app/components/ariadne/content.rb +12 -0
  30. data/app/components/ariadne/counter_component.rb +100 -0
  31. data/app/components/ariadne/details_component/details_component.html.erb +4 -0
  32. data/app/components/ariadne/details_component.rb +81 -0
  33. data/app/components/ariadne/dropdown/menu_component.html.erb +20 -0
  34. data/app/components/ariadne/dropdown/menu_component.rb +101 -0
  35. data/app/components/ariadne/dropdown/menu_component.ts +1 -0
  36. data/app/components/ariadne/dropdown_component/dropdown_component.html.erb +8 -0
  37. data/app/components/ariadne/dropdown_component.rb +172 -0
  38. data/app/components/ariadne/flash_component/flash_component.html.erb +31 -0
  39. data/app/components/ariadne/flash_component.rb +128 -0
  40. data/app/components/ariadne/flex_component/flex_component.html.erb +5 -0
  41. data/app/components/ariadne/flex_component.rb +56 -0
  42. data/app/components/ariadne/footer_component/footer_component.html.erb +7 -0
  43. data/app/components/ariadne/footer_component.rb +23 -0
  44. data/app/components/ariadne/grid_component/grid_component.html.erb +26 -0
  45. data/app/components/ariadne/grid_component.rb +67 -0
  46. data/app/components/ariadne/header_component/header_component.html.erb +29 -0
  47. data/app/components/ariadne/header_component.rb +111 -0
  48. data/app/components/ariadne/heading_component.rb +49 -0
  49. data/app/components/ariadne/heroicon_component/heroicon_component.html.erb +4 -0
  50. data/app/components/ariadne/heroicon_component.rb +166 -0
  51. data/app/components/ariadne/image_component.rb +53 -0
  52. data/app/components/ariadne/inline_flex_component/inline_flex_component.html.erb +6 -0
  53. data/app/components/ariadne/inline_flex_component.rb +72 -0
  54. data/app/components/ariadne/link_component.rb +65 -0
  55. data/app/components/ariadne/list_component/list_component.html.erb +6 -0
  56. data/app/components/ariadne/list_component.rb +70 -0
  57. data/app/components/ariadne/narrow_container_component/narrow_container_component.html.erb +3 -0
  58. data/app/components/ariadne/narrow_container_component.rb +30 -0
  59. data/app/components/ariadne/panel_bar_component/panel_bar_component.html.erb +20 -0
  60. data/app/components/ariadne/panel_bar_component.rb +80 -0
  61. data/app/components/ariadne/pill_component/pill_component.html.erb +3 -0
  62. data/app/components/ariadne/pill_component.rb +44 -0
  63. data/app/components/ariadne/rich_text_area_component/rich-text-area-component.d.ts +6 -0
  64. data/app/components/ariadne/rich_text_area_component/rich-text-area-component.js +38 -0
  65. data/app/components/ariadne/rich_text_area_component/rich-text-area-component.ts +47 -0
  66. data/app/components/ariadne/rich_text_area_component/rich_text_area_component.html.erb +6 -0
  67. data/app/components/ariadne/rich_text_area_component.rb +35 -0
  68. data/app/components/ariadne/slideover_component/slideover-component.d.ts +9 -0
  69. data/app/components/ariadne/slideover_component/slideover-component.js +11 -0
  70. data/app/components/ariadne/slideover_component/slideover-component.ts +17 -0
  71. data/app/components/ariadne/slideover_component/slideover_component.html.erb +9 -0
  72. data/app/components/ariadne/slideover_component.rb +66 -0
  73. data/app/components/ariadne/tab_component/tab_component.html.erb +3 -0
  74. data/app/components/ariadne/tab_component.rb +98 -0
  75. data/app/components/ariadne/tab_container_component/tab-container-component.d.ts +1 -0
  76. data/app/components/ariadne/tab_container_component/tab-container-component.js +23 -0
  77. data/app/components/ariadne/tab_container_component/tab-container-component.ts +24 -0
  78. data/app/components/ariadne/tab_container_component.erb +10 -0
  79. data/app/components/ariadne/tab_container_component.rb +68 -0
  80. data/app/components/ariadne/tab_nav_component/tab-nav-component.d.ts +9 -0
  81. data/app/components/ariadne/tab_nav_component/tab-nav-component.js +33 -0
  82. data/app/components/ariadne/tab_nav_component/tab-nav-component.ts +34 -0
  83. data/app/components/ariadne/tab_nav_component/tab_nav_component.html.erb +7 -0
  84. data/app/components/ariadne/tab_nav_component.rb +72 -0
  85. data/app/components/ariadne/table_nav_component/table_nav_component.html.erb +52 -0
  86. data/app/components/ariadne/table_nav_component.rb +338 -0
  87. data/app/components/ariadne/text.rb +25 -0
  88. data/app/components/ariadne/time_ago_component/time-ago-component.d.ts +1 -0
  89. data/app/components/ariadne/time_ago_component/time-ago-component.js +1 -0
  90. data/app/components/ariadne/time_ago_component/time-ago-component.ts +1 -0
  91. data/app/components/ariadne/time_ago_component.rb +56 -0
  92. data/app/components/ariadne/timeline_component/timeline_component.html.erb +19 -0
  93. data/app/components/ariadne/timeline_component.rb +34 -0
  94. data/app/components/ariadne/tooltip_component/tooltip-component.d.ts +24 -0
  95. data/app/components/ariadne/tooltip_component/tooltip-component.js +43 -0
  96. data/app/components/ariadne/tooltip_component/tooltip-component.ts +57 -0
  97. data/app/components/ariadne/tooltip_component/tooltip_component.html.erb +4 -0
  98. data/app/components/ariadne/tooltip_component.rb +108 -0
  99. data/lib/ariadne/view_components/engine.rb +0 -22
  100. data/lib/ariadne/view_components/version.rb +1 -1
  101. data/tailwind.config.js +10 -15
  102. metadata +98 -2
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Use `Blankslate` when there is a lack of content within a page or section. Use as placeholder to tell users why something isn't there.
5
+ #
6
+ # @accessibility
7
+ # - The blankslate uses a semantic heading that must be set at the appropriate level based on the hierarchy of the page.
8
+ # - All blankslate visuals have been programmed as decorative images, using `aria-hidden=”true”` and `img alt=””`, which will hide the visual from screen reader users.
9
+ # - The blankslate supports a primary and secondary action. Both actions have been built as semantic links with primary and secondary styling.
10
+ # - `secondary_action` text should be meaningful out of context and clearly describe the destination. Avoid using vague text like, "Learn more" or "Click here".
11
+ class BlankslateComponent < Ariadne::Component
12
+ DEFAULT_TAG = :div
13
+ TAG_OPTIONS = [DEFAULT_TAG].freeze
14
+
15
+ DEFAULT_CLASSES = "ariadne-text-center"
16
+
17
+ # Optional visual that renders an <%= link_to_component(Ariadne::HeroiconComponent) %>.
18
+ #
19
+ # @param tag [Symbol, String] The rendered tag name
20
+ # @param icon [String] Name of <%= link_to_heroicons %> to use.
21
+ # @param size [Symbol] <%= one_of(Ariadne::HeroiconComponent::SIZE_MAPPINGS, sort: false) %>
22
+ # @param variant [String] <%= one_of(HeroiconsHelper::Icon::VALID_VARIANTS, sort: false) %>
23
+ # @param classes [String] <%= link_to_classes_docs %>
24
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
25
+ renders_one :icon, lambda { |tag: :svg, icon:, size: Ariadne::HeroiconComponent::SIZE_DEFAULT, variant: HeroiconsHelper::Icon::VARIANT_SOLID, classes: "", attributes: {}|
26
+ @icon = icon
27
+ @variant = variant
28
+ tag = check_incoming_tag(:svg, tag)
29
+ Ariadne::HeroiconComponent.new(tag: tag, icon: icon, size: size, variant: variant, classes: classes, attributes: attributes)
30
+ }
31
+
32
+ # Optional visual that renders an <%= link_to_component(Ariadne::ImageComponent) %>.
33
+ #
34
+ # @param tag [Symbol, String] The rendered tag name
35
+ # @param src [String] The source url of the image.
36
+ # @param alt [String] Specifies an alternate text for the image.
37
+ # @param lazy [Boolean] Whether or not to lazily load the image.
38
+ # @param classes [String] <%= link_to_classes_docs %>
39
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
40
+ DEFAULT_IMAGE_SIZE = 56
41
+ renders_one :image, lambda { |tag: Ariadne::ImageComponent::DEFAULT_TAG, src:, alt:, lazy: false, classes: "", attributes: {}|
42
+ attributes[:height] = DEFAULT_IMAGE_SIZE
43
+ attributes[:width] = DEFAULT_IMAGE_SIZE
44
+
45
+ Ariadne::ImageComponent.new(tag: tag, src: src, alt: alt, lazy: lazy, classes: classes, attributes: attributes)
46
+ }
47
+
48
+ # Required heading.
49
+ #
50
+ # @param tag [Symbol, String] <%= one_of(Ariadne::HeadingComponent::TAG_OPTIONS) %>
51
+ # @param classes [String] <%= link_to_classes_docs %>
52
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
53
+ DEFAULT_HEADING_CLASSES = "ariadne-text-3xl ariadne-font-extrabold ariadne-tracking-tight ariadne-text-gray-900 sm:ariadne-text-4xl"
54
+ renders_one :heading, lambda { |tag: :h1, classes: "", attributes: {}|
55
+ actual_classes = merge_class_names(DEFAULT_HEADING_CLASSES, classes)
56
+
57
+ Ariadne::HeadingComponent.new(tag: tag, classes: actual_classes, attributes: attributes)
58
+ }
59
+
60
+ # Optional description.
61
+ #
62
+ # - The description should always be informative and actionable.
63
+ # - Don't use phrases like "You can".
64
+ #
65
+ # @param tag [Symbol, String] The rendered tag name
66
+ # @param classes [String] <%= link_to_classes_docs %>
67
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
68
+ renders_one :description, lambda { |tag: :p, classes: "", attributes: {}|
69
+ actual_tag = check_incoming_tag(:p, tag)
70
+
71
+ Ariadne::BaseComponent.new(tag: actual_tag, classes: classes, attributes: attributes)
72
+ }
73
+
74
+ # Optional primary action
75
+ #
76
+ # The `primary_action` slot renders an anchor link which is visually styled as a button to provide more emphasis to the
77
+ # Blankslate's primary action.
78
+ #
79
+ # @param href [String] URL to be used for the primary action.
80
+ # @param classes [String] <%= link_to_classes_docs %>
81
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
82
+ renders_one :primary_action, lambda { |href: nil, tag: :a, method: nil, size: :md, scheme: :default, classes: "", attributes: {}|
83
+ attributes[:href] = href
84
+ attributes[:method] = method
85
+
86
+ Ariadne::ButtonComponent.new(
87
+ tag: tag,
88
+ type: Ariadne::BaseButton::DEFAULT_TYPE,
89
+ scheme: scheme,
90
+ size: size,
91
+ classes: classes,
92
+ attributes: attributes,
93
+ )
94
+ }
95
+
96
+ # Optional secondary action
97
+ #
98
+ # The `secondary_action` slot renders a normal anchor link, which can be used to redirect the user to additional information
99
+ # (e.g. Help documentation).
100
+ #
101
+ # @param href [String] URL to be used for the secondary action.
102
+ # @param classes [String] <%= link_to_classes_docs %>
103
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
104
+ renders_one :secondary_action, lambda { |href:, classes: "", attributes: {}|
105
+ attributes[:href] = href
106
+
107
+ Ariadne::LinkComponent.new(href: href, classes: classes, attributes: attributes)
108
+ }
109
+
110
+ # @example Basic
111
+ # <%= render Ariadne::BlankslateComponent.new do |c| %>
112
+ # <% c.with_heading(tag: :h2) { "heading" } %>
113
+ # <% c.with_description { "Description"} %>
114
+ # <% end %>
115
+ #
116
+ # @example Icon
117
+ # @description
118
+ # Add an `icon` to give additional context. Refer to the [Heroicons](https://heroicons.com) documentation to choose an icon.
119
+ # @code
120
+ # <%= render Ariadne::BlankslateComponent.new do |c| %>
121
+ # <% c.with_icon(icon: :"globe-europe-africa") %>
122
+ # <% c.with_heading(tag: :h2) { "heading" } %>
123
+ # <% c.with_description { "Description"} %>
124
+ # <% end %>
125
+ #
126
+ # @example Using an image
127
+ # @description
128
+ # Add an `image` to give context that an Octicon couldn't.
129
+ # @code
130
+ # <%= render Ariadne::BlankslateComponent.new do |c| %>
131
+ # <% c.with_image(src: "https://github.githubassets.com/images/modules/site/features/security-icon.svg", alt: "Security - secure vault") %>
132
+ # <% c.with_heading(tag: :h2){ "heading" } %>
133
+ # <% end %>
134
+ #
135
+ # @param tag [Symbol, String] The rendered tag name
136
+ # @param classes [String] <%= link_to_classes_docs %>
137
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
138
+ def initialize(tag: DEFAULT_TAG, classes: "", attributes: {})
139
+ @tag = check_incoming_tag(DEFAULT_TAG, tag)
140
+ @classes = merge_class_names(DEFAULT_CLASSES, classes)
141
+ @attributes = attributes
142
+ end
143
+
144
+ def render?
145
+ heading.present?
146
+ end
147
+ end
148
+ end
@@ -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 = "ariadne-scroll-smooth ariadne-antialiased"
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 = merge_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
@@ -0,0 +1,4 @@
1
+ <%= render Ariadne::BaseButton.new(tag: @tag, type: @type, classes: @classes, attributes: @attributes) do -%>
2
+ <%= icon %><%= trimmed_content %><%= counter %><%= ariadne_heroicon(icon: :"chevron-down", variant: HeroiconsHelper::Icon::VARIANT_MINI, classes: "ariadne-ml-2 ariadne--mr-1") if dropdown? %>
3
+ <%= tooltip %>
4
+ <% end -%>
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Use `Button` for actions (e.g. in forms). Use links for destinations, or moving from one page to another.
5
+ class ButtonComponent < Ariadne::Component
6
+ include IconHelper
7
+
8
+ DEFAULT_SCHEME = :default
9
+ LINK_SCHEME = :link
10
+
11
+ SCHEME_CLASS_MAPPINGS = {
12
+ link: Ariadne::LinkComponent::DEFAULT_ACTIONABLE_CLASSES,
13
+ none: "",
14
+ default: "ariadne-text-slate-800 ariadne-bg-slate-50 hover:ariadne-bg-slate-100 ariadne-border-slate-300 focus:ariadne-ring-offset-slate-50 focus:ariadne-ring-slate-600",
15
+ info: "ariadne-text-slate-800 ariadne-bg-slate-50 hover:ariadne-bg-slate-100 ariadne-border-slate-300 focus:ariadne-ring-offset-blue-50 focus:ariadne-ring-slate-600",
16
+ success: "ariadne-text-green-800 ariadne-bg-green-50 hover:ariadne-bg-green-100 ariadne-border-green-300 focus:ariadne-ring-offset-green-50 focus:ariadne-ring-green-600",
17
+ warning: "ariadne-text-yellow-800 ariadne-bg-yellow-50 hover:ariadne-bg-yellow-100 ariadne-border-yellow-300 focus:ariadne-ring-offset-yellow-50 focus:ariadne-ring-yellow-600",
18
+ danger: "ariadne-text-red-800 ariadne-bg-red-50 hover:ariadne-bg-red-100 ariadne-border-red-300 focus:ariadne-ring-offset-red-50 focus:ariadne-ring-red-600",
19
+ }.freeze
20
+ VALID_SCHEMES = SCHEME_CLASS_MAPPINGS.keys.freeze
21
+
22
+ # Leading visuals appear to the left of the button text.
23
+ #
24
+ # Use:
25
+ #
26
+ # - `icon` for a <%= link_to_component(Ariadne::HeroiconComponent) %>.
27
+ #
28
+ # @param tag [Symbol, String] The rendered tag name
29
+ # @param classes [String] <%= link_to_classes_docs %>
30
+ # @param icon [String] Name of <%= link_to_heroicons %> to use.
31
+ # @param variant [String] <%= one_of(HeroiconsHelper::Icon::VALID_VARIANTS, sort: false) %>
32
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>.
33
+ renders_one :icon, lambda { |tag: :svg, icon:, variant:, classes: "", attributes: {}|
34
+ @icon = icon
35
+ @variant = variant
36
+ tag = check_incoming_tag(:svg, tag)
37
+ Ariadne::HeroiconComponent.new(tag: tag, icon: icon, variant: variant, classes: classes, attributes: attributes)
38
+ }
39
+
40
+ # Trailing visuals appear to the right of the button text.
41
+ #
42
+ # Use:
43
+ #
44
+ # - `counter` for a <%= link_to_component(Ariadne::CounterComponent) %>.
45
+ #
46
+ # @param tag [Symbol, String] The rendered tag name
47
+ # @param classes [String] <%= link_to_classes_docs %>
48
+ # @param counter [Number] The starting counter value
49
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::CounterComponent) %>.
50
+ renders_one :counter, lambda { |tag: :span, count: 0, classes: "", attributes: {}|
51
+ tag = check_incoming_tag(:span, tag)
52
+ attributes[:ml] = check_incoming_value(2, ml: attributes[:ml])
53
+
54
+ Ariadne::CounterComponent.new(tag: tag, count: count, classes: classes, attributes: attributes)
55
+ }
56
+
57
+ # `Tooltip` that appears on mouse hover or keyboard focus over the button. Use tooltips sparingly and as a last resort.
58
+ # **Important:** This tooltip defaults to `type: :description`. In a few scenarios, `type: :label` may be more appropriate.
59
+ # Consult the <%= link_to_component(Ariadne::TooltipComponent) %> documentation for more information.
60
+ #
61
+ # @param tag [Symbol, String] The rendered tag name
62
+ # @param text [String] The text content of the tooltip. This should be brief and no longer than a sentence.
63
+ # @param direction [Symbol] <%= one_of(Ariadne::TooltipComponent::VALID_PLACEMENTS) %>
64
+ # @param classes [String] <%= link_to_classes_docs %>
65
+ # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::TooltipComponent) %>.
66
+ renders_one :tooltip, lambda { |tag: Ariadne::TooltipComponent::DEFAULT_TAG, text:, direction: Ariadne::TooltipComponent::DEFAULT_PLACEMENT, type: Ariadne::TooltipComponent::TYPE_DEFAULT, classes: "", attributes: {}|
67
+ raise ArgumentError, "Buttons with a tooltip must have a unique `id` set on the `Button`." if @id.blank?
68
+
69
+ Ariadne::TooltipComponent.new(for_id: @id, tag: tag, text: text, direction: direction, type: type, classes: classes, attributes: attributes)
70
+ }
71
+
72
+ # @example Schemes
73
+ # <%= render(Ariadne::ButtonComponent.new) { "Default" } %>
74
+ # <%= render(Ariadne::ButtonComponent.new(scheme: :default)) { "Default" } %>
75
+ # <%= render(Ariadne::ButtonComponent.new(scheme: :info)) { "Info" } %>
76
+ # <%= render(Ariadne::ButtonComponent.new(scheme: :success)) { "Success" } %>
77
+ # <%= render(Ariadne::ButtonComponent.new(scheme: :warning)) { "Warning" } %>
78
+ # <%= render(Ariadne::ButtonComponent.new(scheme: :danger)) { "Danger" } %>
79
+ #
80
+ # @example Sizes
81
+ # <%= render(Ariadne::ButtonComponent.new(size: :sm)) { "Small" } %>
82
+ # <%= render(Ariadne::ButtonComponent.new(size: :md)) { "Medium" } %>
83
+ #
84
+ # @example With leading visual
85
+ # <%= render(Ariadne::ButtonComponent.new) do |c| %>
86
+ # <% c.with_icon(icon: :star, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE, classes: "ariadne-text-yellow-600") %>
87
+ # Button
88
+ # <% end %>
89
+ #
90
+ # @example With trailing visual
91
+ # <%= render(Ariadne::ButtonComponent.new) do |c| %>
92
+ # <% c.with_counter(count: 15) %>
93
+ # Button
94
+ # <% end %>
95
+ #
96
+ # @example With leading and trailing visuals
97
+ # <%= render(Ariadne::ButtonComponent.new) do |c| %>
98
+ # <% c.with_icon(icon: :star, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE) %>
99
+ # <% c.with_counter(count: 15) %>
100
+ # Button
101
+ # <% end %>
102
+ #
103
+ # @example With tooltip
104
+ # @description
105
+ # Use tooltips sparingly and as a last resort. Consult the <%= link_to_component(Ariadne::TooltipComponent) %> documentation for more information.
106
+ # @code
107
+ # <%= render(Ariadne::ButtonComponent.new(attributes: { id: "button-with-tooltip" })) do |c| %>
108
+ # <% c.with_tooltip(text: "Tooltip text") %>
109
+ # Button
110
+ # <% end %>
111
+ #
112
+ # @param tag [Symbol] <%= one_of(Ariadne::BaseButton::TAG_OPTIONS) %>
113
+ # @param type [Symbol] <%= one_of(Ariadne::BaseButton::VALID_TYPES) %>
114
+ # @param scheme [Symbol] <%= one_of(Ariadne::ButtonComponent::VALID_SCHEMES) %>
115
+ # @param size [Symbol] <%= one_of(Ariadne::BaseButton::VALID_SIZES) %>
116
+ # @param dropdown [Boolean] Whether or not to render a dropdown caret.
117
+ # @param classes [String] <%= link_to_classes_docs %>
118
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
119
+ def initialize(
120
+ tag: Ariadne::BaseButton::DEFAULT_TAG,
121
+ type: Ariadne::BaseButton::DEFAULT_TYPE,
122
+ scheme: DEFAULT_SCHEME,
123
+ size: BaseButton::DEFAULT_SIZE,
124
+ dropdown: false,
125
+ classes: "",
126
+ attributes: {}
127
+ )
128
+ @tag = tag
129
+ @type = type
130
+
131
+ @attributes = attributes
132
+ @id = @attributes[:id]
133
+
134
+ @size = fetch_or_raise(Ariadne::BaseButton::VALID_SIZES, size)
135
+ @scheme = fetch_or_raise(VALID_SCHEMES, scheme)
136
+ @scheme = LINK_SCHEME if @tag == :a
137
+
138
+ @classes = merge_class_names(
139
+ SCHEME_CLASS_MAPPINGS[@scheme],
140
+ classes,
141
+ )
142
+
143
+ @dropdown = dropdown
144
+ end
145
+
146
+ private def dropdown?
147
+ @dropdown
148
+ end
149
+
150
+ private def trimmed_content
151
+ return if content.blank?
152
+
153
+ trimmed_content = content.strip
154
+
155
+ return trimmed_content unless content.html_safe?
156
+
157
+ # strip unsets `html_safe`, so we have to set it back again to guarantee that HTML blocks won't break
158
+ trimmed_content.html_safe # rubocop:disable Rails/OutputSafety
159
+ end
160
+
161
+ private def link?
162
+ @scheme == LINK_SCHEME
163
+ end
164
+ end
165
+ end
@@ -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,19 @@
1
+ import {Controller} from '@hotwired/stimulus'
2
+
3
+ export default class ClipboardCopyComponent extends Controller {
4
+ copy() {
5
+ const value = this.element.attributes.getNamedItem('value')
6
+
7
+ const forNode = this.element.attributes.getNamedItem('for')
8
+
9
+ if (value) {
10
+ navigator.clipboard.writeText(value.value)
11
+ } else if (forNode) {
12
+ const node = document.getElementById(forNode.value)
13
+ navigator.clipboard.writeText(node?.textContent || '')
14
+ } else {
15
+ // just copy inner text
16
+ navigator.clipboard.writeText(this.element.textContent || '')
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,9 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes.merge(controller_data)) do %>
2
+ <% if content.present? %>
3
+ <%= content.to_s + tooltip.to_s %>
4
+ <% else %>
5
+ <%= render Ariadne::HeroiconComponent.new(icon: :"document-duplicate", variant: HeroiconsHelper::Icon::VARIANT_OUTLINE) %>
6
+ <!-- TODO: fix, check should replace on click -->
7
+ <%= render Ariadne::HeroiconComponent.new(icon: :check, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE, classes: "ariadne-text-green-600", attributes: { style: "display: none;" }) %>
8
+ <% end %>
9
+ <% end %>
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Use `ClipboardCopyComponent` to copy element text content or input values to the clipboard.
5
+ #
6
+ # @accessibility
7
+ # Always set an accessible label to help the user interact with the component.
8
+ class ClipboardCopyComponent < Ariadne::Component
9
+ DEFAULT_TAG = :"clipboard-copy"
10
+
11
+ DEFAULT_CLASSES = LinkComponent::DEFAULT_ACTIONABLE_CLASSES
12
+
13
+ DATA_CONTROLLER = "clipboard-copy-component"
14
+ DATA_ACTION = "click->clipboard-copy-component#copy"
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
+
33
+ # @example Default
34
+ # <%= render(Ariadne::ClipboardCopyComponent.new(value: "Text to copy", aria_label: "Copy text to the system clipboard" )) %>
35
+ #
36
+ # @example With text instead of icons
37
+ # <%= render(Ariadne::ClipboardCopyComponent.new(value: "Text to copy", aria_label: "Copy text to the system clipboard" )) do %>
38
+ # Click to copy!
39
+ # <% end %>
40
+ #
41
+ # @example Copying from an element
42
+ # <%= render(Ariadne::ClipboardCopyComponent.new(for_id: "blob-path", aria_label: "Copy text to the system clipboard" )) %>
43
+ # <div id="blob-path">src/index.js</div>
44
+ #
45
+ # @param tag [Symbol, String] The rendered tag name
46
+ # @param classes [String] <%= link_to_classes_docs %>
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.
49
+ # @param aria_label [String] Text for accessibility. Can also be passed in as part of `attributes`, but it must be present.
50
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
51
+ def initialize(tag: DEFAULT_TAG, value: "", for_id: nil, aria_label: "", classes: "", attributes: {})
52
+ @attributes = attributes
53
+ @value = value
54
+
55
+ @attributes[:"aria-label"] = aria_label
56
+ @attributes[:for] ||= for_id
57
+ @id = @attributes[:id]
58
+
59
+ validate!
60
+
61
+ @classes = merge_class_names(DEFAULT_CLASSES, classes)
62
+ @tag = check_incoming_tag(DEFAULT_TAG, tag)
63
+ @attributes[:value] = value if value.present?
64
+ end
65
+
66
+ private def validate!
67
+ validate_aria_label!
68
+ raise ArgumentError, "Must provide either `value` or `for`" if @value.blank? && @attributes[:for].blank?
69
+ raise ArgumentError, "Must provide only `value` or `for`, not both" if @value.present? && @attributes[:for].present?
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
89
+ end
90
+ end
@@ -0,0 +1,37 @@
1
+ <%= render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do %>
2
+ <%= render(Ariadne::TabContainerComponent.new(sr_label: @sr_label)) do |tab_container| %>
3
+ <%= tab_container.with_tab(id: public_tab.id, selected: public_tab.selected, classes: public_tab.classes, attributes: public_tab.attributes) do |tab| %>
4
+ <% tab.with_text { @public_tab_text } %>
5
+ <% tab.with_panel(attributes: {:"data-public" => true}) do %>
6
+ <%= ariadne_form_with(url: @url, method: @method, classes: @classes, attributes: @attributes) do |comment_box| %>
7
+ <% @hidden_fields.each do |name, value| %>
8
+ <%= hidden_field_tag name, value %>
9
+ <% end %>
10
+ <div class="ariadne-overflow-hidden ariadne-border ariadne-border-slate-300 ariadne-shadow-sm">
11
+ <%= hidden_field_tag 'message_is_public', true %>
12
+ <%= render(Ariadne::RichTextAreaComponent.new(name: :message_public_bodytext, sr_label: "Select reply type", attributes: { required: true})) %>
13
+ <% comment_box.submit { @submit } %>
14
+ </div>
15
+ <div class="ariadne-py-2 ariadne-flex ariadne-justify-end">
16
+ <%= public_submit %>
17
+ </div>
18
+ <% end %>
19
+ <% end %>
20
+ <% end %>
21
+ <%= tab_container.with_tab(id: internal_tab.id, selected: internal_tab.selected, classes: internal_tab.classes, attributes: internal_tab.attributes) do |tab| %>
22
+ <% tab.with_text { @internal_tab_text } %>
23
+ <% tab.with_panel do %>
24
+ <%= ariadne_form_with(url: @url, method: @method, classes: @classes, attributes: @attributes) do |comment_box| %>
25
+ <div class="ariadne-overflow-hidden ariadne-border ariadne-border-gray-300 ariadne-shadow-sm">
26
+ <%= hidden_field_tag 'message_is_public', false %>
27
+ <%= render(Ariadne::RichTextAreaComponent.new(name: :message_internal_bodytext, sr_label: "Select reply type", attributes: { required: true})) %>
28
+ <% comment_box.submit { @submit } %>
29
+ </div>
30
+ <div class="ariadne-py-2 ariadne-flex ariadne-justify-end">
31
+ <%= internal_submit %>
32
+ </div>
33
+ <% end %>
34
+ <% end %>
35
+ <% end %>
36
+ <% end %>
37
+ <% end %>
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Defines a submittable form for adding comments to a conversation.
5
+ #
6
+ #
7
+ # @accessibility This component requires you to pass in a `sr_label`
8
+ # attribute, which will be used to label the tabs for screen readers.
9
+ class CommentComponent < Ariadne::Component
10
+ DEFAULT_TAG = :div
11
+ TAG_OPTIONS = [DEFAULT_TAG].freeze
12
+
13
+ DEFAULT_CLASSES = "ariadne-border-gray-300 ariadne-border ariadne-shadow ariadne-py-5 ariadne-px-5 ariadne-rounded-md "
14
+
15
+ renders_one :public_tab, lambda { |selected: false, text:, classes: "", attributes: {}|
16
+ attributes[:"data-public"] = true
17
+ @tab_idx += 1
18
+ id = attributes.fetch(:id, "public-tab-#{@tab_idx}")
19
+ @public_tab_text = text
20
+
21
+ Ariadne::TabComponent.new(selected: selected, classes: classes, id: id, with_panel: true, attributes: attributes)
22
+ }
23
+
24
+ renders_one :public_submit, lambda { |classes: "", attributes: {}|
25
+ Ariadne::ButtonComponent.new(type: Ariadne::BaseButton::TYPE_SUBMIT, scheme: Ariadne::ButtonComponent::DEFAULT_SCHEME, classes: classes, attributes: attributes)
26
+ }
27
+
28
+ renders_one :internal_tab, lambda { |selected: false, text:, classes: "", attributes: {}|
29
+ attributes[:"data-public"] = false
30
+ @tab_idx += 1
31
+ id = attributes.fetch(:id, "internal-tab-#{@tab_idx}")
32
+ @internal_tab_text = text
33
+
34
+ Ariadne::TabComponent.new(selected: selected, classes: classes, id: id, with_panel: true, attributes: attributes)
35
+ }
36
+
37
+ renders_one :internal_submit, lambda { |classes: "", attributes: {}|
38
+ Ariadne::ButtonComponent.new(type: Ariadne::BaseButton::TYPE_SUBMIT, scheme: Ariadne::ButtonComponent::DEFAULT_SCHEME, classes: classes, attributes: attributes)
39
+ }
40
+
41
+ # @example Default
42
+ #
43
+ # <%= render(Ariadne::CommentComponent.new(url: "/messages", method: :post, sr_label: "Select delivery time")) do |comment| %>
44
+ # <% comment.with_public_tab(selected: true, text: "Reply to sender") %>
45
+ # <% comment.with_public_submit { "Send" } %>
46
+ # <% comment.with_internal_tab(text: "Internal comment") %>
47
+ # <% comment.with_internal_submit { "Send" } %>
48
+ # <% end %>
49
+ #
50
+ # @param url [String] The URL to take action against.
51
+ # @param method [String] The method to use when submitting the form.
52
+ # @param sr_label [String] A label to introduce these tabs for screen readers.
53
+ # @param hidden_fields [[String]] An array of arrays of additional (hidden) fields to add to the form.
54
+ # @param classes [String] <%= link_to_classes_docs %>
55
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
56
+ def initialize(url:, method: :post, sr_label:, hidden_fields: [], classes: "", attributes: {})
57
+ @tag = DEFAULT_TAG
58
+ @classes = merge_class_names(
59
+ DEFAULT_CLASSES,
60
+ classes,
61
+ )
62
+ @url = url
63
+ @method = method
64
+ @sr_label = sr_label
65
+
66
+ @tab_idx = -1
67
+ @hidden_fields = hidden_fields
68
+ @attributes = attributes
69
+ end
70
+ end
71
+ end