ariadne_view_components 0.0.43-x86_64-linux → 0.0.44-x86_64-linux

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,338 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # The `TableNavComponent` is used to render a table navigation.
5
+ class TableNavComponent < Ariadne::Component
6
+ DEFAULT_CLASSES = "ariadne-min-w-full ariadne-divide-y ariadne-divide-gray-300"
7
+
8
+ renders_one :header_row, "BaseRowItem::HeaderRowItem"
9
+
10
+ renders_many :rows, "BaseRowItem::RowItem"
11
+
12
+ renders_one :footer, "FooterItem"
13
+
14
+ DEFAULT_TAG = :table
15
+
16
+ # @example Default
17
+ #
18
+ # <%= render(Ariadne::TableNavComponent.new) do |table| %>
19
+ # <%= table.with_header_row do |header_row| %>
20
+ # <% header_row.with_selection_cell do %>
21
+ # Status
22
+ # <% end %>
23
+ # <% header_row.with_main_cell do %>
24
+ # State
25
+ # <% end %>
26
+ # <% header_row.with_action_cell do %>
27
+ # Labels
28
+ # <% end %>
29
+ # <% end %>
30
+ # <%= table.with_row do |row| %>
31
+ # <% row.with_selection_cell do %>
32
+ # "G"
33
+ # <% end %>
34
+ # <% row.with_main_cell do %>
35
+ # "California"
36
+ # <% end %>
37
+ # <% row.with_metadata_cell do %>
38
+ # "Labels"
39
+ # <% end %>
40
+ # <% end %>
41
+ # <%= table.with_row do |row| %>
42
+ # <% row.with_selection_cell do %>
43
+ # "V"
44
+ # <% end %>
45
+ # <% row.with_main_cell do %>
46
+ # "New York"
47
+ # <% end %>
48
+ # <% row.with_metadata_cell do %>
49
+ # "Labels"
50
+ # <% end %>
51
+ # <% end %>
52
+ # <%= table.with_row do |row| %>
53
+ # <% row.with_cell do %>
54
+ # "D"
55
+ # <% end %>
56
+ # <% row.with_selection_cell do %>
57
+ # "Texas"
58
+ # <% end %>
59
+ # <% row.with_metadata_cell do %>
60
+ # "Labels"
61
+ # <% end %>
62
+ # <% end %>
63
+ # <% end %>
64
+ #
65
+ # @param classes [String] <%= link_to_classes_docs %>
66
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
67
+ def initialize(classes: "", attributes: {})
68
+ @tag = DEFAULT_TAG
69
+ @classes = merge_class_names(
70
+ DEFAULT_CLASSES,
71
+ classes,
72
+ )
73
+
74
+ @attributes = attributes
75
+ end
76
+
77
+ def has_header_row?
78
+ header_row.present?
79
+ end
80
+
81
+ def has_footer?
82
+ footer.present?
83
+ end
84
+
85
+ # This component is part of `TableNavComponent` and should not be
86
+ # used as a standalone component.
87
+ class BaseRowItem < Ariadne::TableNavComponent
88
+ BASE_ROW_CLASSES = ""
89
+
90
+ DEFAULT_TAG = :tr
91
+
92
+ BASE_SELECTION_CLASSES = ""
93
+ renders_one :selection_cell, lambda { |classes: "", attributes: {}|
94
+ actual_classes = merge_class_names(BASE_SELECTION_CLASSES, classes)
95
+ if header?
96
+ Ariadne::TableNavComponent::BaseCellItem::HeaderCellItem.new(classes: actual_classes, attributes: attributes)
97
+ else
98
+ Ariadne::TableNavComponent::BaseCellItem::CellItem.new(classes: actual_classes, attributes: attributes)
99
+ end
100
+ }
101
+
102
+ BASE_MAIN_CLASSES = "ariadne-pr-5"
103
+ renders_one :main_cell, lambda { |classes: "", attributes: {}|
104
+ actual_classes = merge_class_names(BASE_MAIN_CLASSES, classes)
105
+ if header?
106
+ Ariadne::TableNavComponent::BaseCellItem::HeaderCellItem.new(classes: actual_classes, attributes: attributes)
107
+ else
108
+ Ariadne::TableNavComponent::BaseCellItem::CellItem.new(classes: actual_classes, attributes: attributes)
109
+ end
110
+ }
111
+
112
+ attr_reader :classes, :attributes
113
+
114
+ def initialize(classes: "", attributes: {})
115
+ @tag = DEFAULT_TAG
116
+ @classes = merge_class_names(BASE_ROW_CLASSES, classes)
117
+
118
+ @attributes = attributes
119
+ end
120
+
121
+ private def linked?
122
+ @href.present?
123
+ end
124
+
125
+ private def header?
126
+ @header.present?
127
+ end
128
+
129
+ def call
130
+ Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)
131
+ end
132
+
133
+ # This component is part of `TableNavComponent` and should not be
134
+ # used as a standalone component.
135
+ class RowItem < Ariadne::TableNavComponent::BaseRowItem
136
+ DEFAULT_ROW_CLASSES = "ariadne-bg-white"
137
+ DEFAULT_METADATA_CLASSES = ""
138
+ renders_many :metadata_cells, lambda { |classes: "", attributes: {}|
139
+ actual_classes = merge_class_names(DEFAULT_METADATA_CLASSES, classes)
140
+ Ariadne::TableNavComponent::BaseCellItem::CellItem.new(classes: actual_classes, attributes: attributes)
141
+ }
142
+
143
+ attr_reader :href
144
+
145
+ def initialize(classes: "", attributes: {})
146
+ @header = false
147
+
148
+ actual_classes = merge_class_names(DEFAULT_ROW_CLASSES, classes)
149
+
150
+ super(classes: actual_classes, attributes: attributes)
151
+ end
152
+ end
153
+
154
+ # This component is part of `TableNavComponent` and should not be
155
+ # used as a standalone component.
156
+ class HeaderRowItem < Ariadne::TableNavComponent::BaseRowItem
157
+ DEFAULT_HEADER_ROW_CLASSES = "ariadne-bg-gray-50 ariadne-text-left"
158
+
159
+ renders_many :action_cells, lambda { |classes: "", attributes: {}|
160
+ Ariadne::TableNavComponent::BaseCellItem::HeaderCellItem.new(classes: classes, attributes: attributes)
161
+ }
162
+
163
+ def initialize(classes: "", attributes: {})
164
+ @header = true
165
+
166
+ actual_classes = merge_class_names(
167
+ DEFAULT_HEADER_ROW_CLASSES,
168
+ classes,
169
+ )
170
+
171
+ super(classes: actual_classes, attributes: attributes)
172
+ end
173
+ end
174
+ end
175
+
176
+ # This component is part of `TableNavComponent` and should not be
177
+ # used as a standalone component.
178
+ class BaseCellItem < Ariadne::TableNavComponent
179
+ DEFAULT_CELL_CLASSES = "ariadne-py-3.5 ariadne-pl-4 ariadne-pr-3 ariadne-text-left ariadne-text-sm ariadne-text-gray-900"
180
+
181
+ attr_writer :first, :last
182
+
183
+ def initialize(classes: "", attributes: {})
184
+ @classes = merge_class_names(
185
+ DEFAULT_CELL_CLASSES,
186
+ classes,
187
+ )
188
+
189
+ @attributes = attributes
190
+ end
191
+
192
+ def call
193
+ render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) do
194
+ content
195
+ end
196
+ end
197
+
198
+ # This component is part of `TableNavComponent` and should not be
199
+ # used as a standalone component.
200
+ class HeaderCellItem < Ariadne::TableNavComponent::BaseCellItem
201
+ DEFAULT_HEADER_CELL_CLASSES = "ariadne-py-3.5 ariadne-pl-4 ariadne-pr-3 ariadne-text-left ariadne-text-sm ariadne-font-semibold ariadne-text-gray-900 sm:ariadne-pl-6 md:ariadne-pl-0"
202
+
203
+ DEFAULT_TAG = :th
204
+
205
+ WIDTH_CLASSES = {
206
+ none: "ariadne-flex-none ariadne-w-14 ariadne-px-4",
207
+ default: "ariadne-flex-initial",
208
+ narrow: "ariadne-flex-initial ariadne-w-1/5 ariadne-text-right",
209
+ wide: "ariadne-flex-grow ariadne-w-3/5 sm:ariadne-pl-6",
210
+ }
211
+
212
+ # TODO: add one_of check for width
213
+ def initialize(classes: "", attributes: {})
214
+ @tag = DEFAULT_TAG
215
+ actual_classes = merge_class_names(DEFAULT_HEADER_CELL_CLASSES, classes)
216
+ attributes["scope"] = "col"
217
+ super(classes: actual_classes, attributes: attributes)
218
+ end
219
+ end
220
+
221
+ # This component is part of `TableNavComponent` and should not be
222
+ # used as a standalone component.
223
+ class CellItem < Ariadne::TableNavComponent::BaseCellItem
224
+ DEFAULT_CELL_CLASSES = ""
225
+
226
+ DEFAULT_TAG = :td
227
+
228
+ # TODO: add one_of check for width
229
+ def initialize(classes: "", attributes: {})
230
+ @tag = DEFAULT_TAG
231
+ actual_classes = merge_class_names(DEFAULT_CELL_CLASSES, classes)
232
+ super(classes: actual_classes, attributes: attributes)
233
+ end
234
+ end
235
+ end
236
+
237
+ # This component is part of `TableNavComponent` and should not be
238
+ # used as a standalone component.
239
+ class FooterItem < Ariadne::TableNavComponent
240
+ DEFAULT_FOOTER_CLASSES = "ariadne-border-none ariadne-flex ariadne-items-center ariadne-justify-between ariadne-px-4 ariadne-py-3 sm:ariadne-px-6"
241
+
242
+ DEFAULT_RESULT_CLASSES = "ariadne-text-sm ariadne-text-gray-700"
243
+ renders_one :records_info, lambda { |classes: "", attributes: {}|
244
+ actual_classes = merge_class_names(DEFAULT_RESULT_CLASSES, classes)
245
+ Ariadne::BaseComponent.new(tag: :p, classes: actual_classes, attributes: attributes)
246
+ }
247
+
248
+ renders_one :pagination_bar, "Ariadne::TableNavComponent::PaginationBarItem"
249
+
250
+ attr_reader :classes, :attributes
251
+
252
+ def initialize(classes: "", attributes: {})
253
+ @classes = merge_class_names(
254
+ DEFAULT_FOOTER_CLASSES,
255
+ classes,
256
+ )
257
+
258
+ @attributes = attributes
259
+ end
260
+
261
+ def call
262
+ render(Ariadne::BaseComponent.new(tag: :div, classes: @classes, attributes: @attributes)) do
263
+ records_info.to_s + pagination_bar.to_s
264
+ end
265
+ end
266
+ end
267
+
268
+ # This component is part of `TableNavComponent` and should not be
269
+ # used as a standalone component.
270
+ class PaginationBarItem < Ariadne::FooterComponent
271
+ DEFAULT_PREV_PAGE_CLASSES = "ariadne-relative ariadne-inline-flex ariadne-items-center ariadne-rounded-l-md ariadne-border ariadne-border-gray-300 ariadne-bg-white ariadne-px-2 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-gray-500 hover:ariadne-bg-gray-50 focus:ariadne-z-20"
272
+ renders_one :prev_page, lambda { |disabled: false, href:, classes: "", attributes: {}|
273
+ if disabled
274
+ actual_classes = merge_class_names(DEFAULT_PREV_PAGE_CLASSES, "ariadne-bg-gray-50", classes)
275
+
276
+ render(Ariadne::BaseComponent.new(tag: :span, classes: actual_classes, attributes: attributes)) do
277
+ render(Ariadne::HeroiconComponent.new(icon: "chevron-left", size: :sm, variant: :mini, text_attributes: { "aria-hidden": true }, text_classes: "ariadne-sr-only"))
278
+ end
279
+ else
280
+ actual_classes = merge_class_names(DEFAULT_PREV_PAGE_CLASSES, "hover:ariadne-bg-gray-50", classes)
281
+ attributes[:"aria-label"] = "previous"
282
+
283
+ render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) do
284
+ render(Ariadne::HeroiconComponent.new(icon: "chevron-left", size: :sm, variant: :mini, text_attributes: { "aria-hidden": true }, text_classes: "ariadne-sr-only"))
285
+ end
286
+ end
287
+ }
288
+
289
+ DEFAULT_PAGE_CLASSES = "ariadne-relative ariadne-inline-flex ariadne-items-center ariadne-border ariadne-border-gray-300 ariadne-bg-white ariadne-px-4 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-gray-500 hover:ariadne-bg-gray-50 focus:ariadne-z-20"
290
+ DEFAULT_CURRENT_PAGE_CLASSES = "ariadne-relative ariadne-z-10 ariadne-inline-flex ariadne-items-center ariadne-border ariadne-border-slate-500 ariadne-bg-slate-50 ariadne-px-4 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-slate-600 focus:ariadne-z-20"
291
+ DEFAULT_GAP_CLASSES = " ariadne-relative ariadne-inline-flex ariadne-items-center ariadne-border ariadne-border-gray-300 ariadne-bg-white ariadne-px-4 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-gray-700"
292
+ renders_many :items, lambda { |link:, classes: "", attributes: {}|
293
+ page, href = link
294
+ if page.is_a?(Integer)
295
+ actual_classes = merge_class_names(DEFAULT_PAGE_CLASSES, classes)
296
+ render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) { page.to_s }
297
+ elsif page.is_a?(String)
298
+ actual_classes = merge_class_names(DEFAULT_CURRENT_PAGE_CLASSES, classes)
299
+ render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) { page.to_s }
300
+ elsif page == :gap
301
+ actual_classes = merge_class_names(DEFAULT_GAP_CLASSES, classes)
302
+ render(Ariadne::BaseComponent.new(tag: :span, classes: actual_classes, attributes: attributes)) { h(href.to_s) }
303
+ end
304
+ }
305
+
306
+ DEFAULT_NEXT_PAGE_CLASSES = "ariadne-relative ariadne-inline-flex ariadne-items-center ariadne-rounded-r-md ariadne-border ariadne-border-gray-300 ariadne-bg-white ariadne-px-2 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-text-gray-500 hover:ariadne-bg-gray-50 focus:ariadne-z-20"
307
+ renders_one :next_page, lambda { |disabled: false, href:, classes: "", attributes: {}|
308
+ if disabled
309
+ actual_classes = merge_class_names(DEFAULT_NEXT_PAGE_CLASSES, "ariadne-bg-gray-50", classes)
310
+
311
+ render(Ariadne::BaseComponent.new(tag: :span, classes: actual_classes, attributes: attributes)) do
312
+ render(Ariadne::HeroiconComponent.new(icon: "chevron-right", size: :sm, variant: :mini, text_attributes: { "aria-hidden": true }, text_classes: "ariadne-sr-only"))
313
+ end
314
+ else
315
+ actual_classes = merge_class_names(DEFAULT_NEXT_PAGE_CLASSES, "hover:ariadne-bg-gray-50", classes)
316
+ attributes[:"aria-label"] = "next"
317
+
318
+ render(Ariadne::LinkComponent.new(href: href, classes: actual_classes, attributes: attributes)) do
319
+ render(Ariadne::HeroiconComponent.new(icon: "chevron-right", size: :sm, variant: :mini, text_attributes: { "aria-hidden": true }, text_classes: "ariadne-sr-only"))
320
+ end
321
+ end
322
+ }
323
+
324
+ attr_reader :classes, :attributes
325
+
326
+ DEFAULT_PAGINATOR_CLASSES = "ariadne-flex ariadne-items-center ariadne-justify-between ariadne-m-10"
327
+ def initialize(classes: "", attributes: {})
328
+ @classes = merge_class_names(
329
+ DEFAULT_PAGINATOR_CLASSES,
330
+ classes,
331
+ )
332
+
333
+ @attributes = attributes
334
+ @attributes[:"aria-label"] ||= "paginator"
335
+ end
336
+ end
337
+ end
338
+ 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 @@
1
+ import '@github/time-elements';
@@ -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 = merge_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 = merge_class_names(
27
+ DEFAULT_CLASSES,
28
+ classes,
29
+ )
30
+
31
+ @attributes = attributes
32
+ end
33
+ end
34
+ end
@@ -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
+ }
@@ -0,0 +1,43 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ import { createPopper } from '@popperjs/core';
3
+ class TooltipComponent extends Controller {
4
+ // Create a new Popper instance
5
+ connect() {
6
+ this.popperInstance = createPopper(this.triggerTarget, this.tooltipTarget, {
7
+ placement: this.placementValue,
8
+ modifiers: [
9
+ {
10
+ name: 'offset',
11
+ options: {
12
+ offset: this.offsetValue,
13
+ },
14
+ },
15
+ ],
16
+ });
17
+ }
18
+ // Destroy the Popper instance
19
+ disconnect() {
20
+ if (this.popperInstance) {
21
+ this.popperInstance.destroy();
22
+ }
23
+ }
24
+ show() {
25
+ this.tooltipTarget.setAttribute('data-tooltip-show', '');
26
+ this.tooltipTarget.classList.remove('ariadne-invisible');
27
+ // We need to tell Popper to update the tooltip position
28
+ // after we show the tooltip, otherwise it will be incorrect
29
+ this.popperInstance.update();
30
+ this.dispatch('shown', { detail: { trigger: this.triggerTarget, tooltip: this.tooltipTarget } });
31
+ }
32
+ hide() {
33
+ this.tooltipTarget.removeAttribute('data-tooltip-show');
34
+ this.tooltipTarget.classList.add('ariadne-invisible');
35
+ this.dispatch('ariadne-hidden', { detail: { trigger: this.triggerTarget, tooltip: this.tooltipTarget } });
36
+ }
37
+ }
38
+ TooltipComponent.targets = ['trigger', 'tooltip'];
39
+ TooltipComponent.values = {
40
+ placement: { type: String, default: 'top' },
41
+ offset: { type: Array, default: [0, 8] },
42
+ };
43
+ export default TooltipComponent;
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do %>
2
+ <span><%= @text %></span>
3
+ <span class="ariadne-tooltip-arrow ariadne-absolute ariadne-block" data-popper-arrow></span>
4
+ <% end %>