ariadne_view_components 0.0.10-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +49 -0
  3. data/README.md +73 -0
  4. data/app/assets/config/manifest.js +2 -0
  5. data/app/assets/javascripts/ariadne-form-with.d.ts +20 -0
  6. data/app/assets/javascripts/ariadne-form.d.ts +22 -0
  7. data/app/assets/javascripts/ariadne.d.ts +1 -0
  8. data/app/assets/javascripts/ariadne_view_components.js +8 -0
  9. data/app/assets/javascripts/ariadne_view_components.js.map +1 -0
  10. data/app/assets/javascripts/clipboard-copy-component.d.ts +4 -0
  11. data/app/assets/javascripts/comment-component.d.ts +13 -0
  12. data/app/assets/javascripts/rich-text-area-component.d.ts +4 -0
  13. data/app/assets/javascripts/slideover-component.d.ts +9 -0
  14. data/app/assets/javascripts/time-ago-component.d.ts +1 -0
  15. data/app/assets/javascripts/time_ago_component.d.ts +1 -0
  16. data/app/assets/javascripts/tooltip-component.d.ts +24 -0
  17. data/app/assets/stylesheets/ariadne_view_components.css +6 -0
  18. data/app/assets/stylesheets/prosemirror.css +323 -0
  19. data/app/assets/stylesheets/tooltip-component.css +37 -0
  20. data/app/components/ariadne/ariadne-form.ts +96 -0
  21. data/app/components/ariadne/ariadne.ts +20 -0
  22. data/app/components/ariadne/base_button.rb +61 -0
  23. data/app/components/ariadne/base_component.rb +37 -0
  24. data/app/components/ariadne/blankslate_component.html.erb +26 -0
  25. data/app/components/ariadne/blankslate_component.rb +146 -0
  26. data/app/components/ariadne/body_component.rb +30 -0
  27. data/app/components/ariadne/button_component.html.erb +4 -0
  28. data/app/components/ariadne/button_component.rb +157 -0
  29. data/app/components/ariadne/clipboard-copy-component.ts +19 -0
  30. data/app/components/ariadne/clipboard_copy_component.html.erb +9 -0
  31. data/app/components/ariadne/clipboard_copy_component.rb +90 -0
  32. data/app/components/ariadne/comment-component.ts +55 -0
  33. data/app/components/ariadne/comment_component.html.erb +22 -0
  34. data/app/components/ariadne/comment_component.rb +57 -0
  35. data/app/components/ariadne/component.rb +128 -0
  36. data/app/components/ariadne/container_component.html.erb +3 -0
  37. data/app/components/ariadne/container_component.rb +25 -0
  38. data/app/components/ariadne/content.rb +12 -0
  39. data/app/components/ariadne/counter_component.rb +100 -0
  40. data/app/components/ariadne/flash_component.html.erb +31 -0
  41. data/app/components/ariadne/flash_component.rb +125 -0
  42. data/app/components/ariadne/flex_component.rb +49 -0
  43. data/app/components/ariadne/footer_component.html.erb +7 -0
  44. data/app/components/ariadne/footer_component.rb +23 -0
  45. data/app/components/ariadne/grid_component.html.erb +26 -0
  46. data/app/components/ariadne/grid_component.rb +66 -0
  47. data/app/components/ariadne/header_component.html.erb +29 -0
  48. data/app/components/ariadne/header_component.rb +114 -0
  49. data/app/components/ariadne/heading_component.rb +49 -0
  50. data/app/components/ariadne/heroicon_component.html.erb +4 -0
  51. data/app/components/ariadne/heroicon_component.rb +129 -0
  52. data/app/components/ariadne/image_component.rb +53 -0
  53. data/app/components/ariadne/inline_flex_component.html.erb +5 -0
  54. data/app/components/ariadne/inline_flex_component.rb +65 -0
  55. data/app/components/ariadne/link_component.rb +65 -0
  56. data/app/components/ariadne/list_component.html.erb +15 -0
  57. data/app/components/ariadne/list_component.rb +68 -0
  58. data/app/components/ariadne/main_component.rb +32 -0
  59. data/app/components/ariadne/narrow_container_component.html.erb +3 -0
  60. data/app/components/ariadne/narrow_container_component.rb +30 -0
  61. data/app/components/ariadne/panel_bar_component.html.erb +20 -0
  62. data/app/components/ariadne/panel_bar_component.rb +79 -0
  63. data/app/components/ariadne/pill_component.html.erb +3 -0
  64. data/app/components/ariadne/pill_component.rb +30 -0
  65. data/app/components/ariadne/rich-text-area-component.ts +32 -0
  66. data/app/components/ariadne/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.ts +26 -0
  69. data/app/components/ariadne/slideover_component.html.erb +11 -0
  70. data/app/components/ariadne/slideover_component.rb +81 -0
  71. data/app/components/ariadne/tab_bar_component.html.erb +3 -0
  72. data/app/components/ariadne/tab_bar_component.rb +45 -0
  73. data/app/components/ariadne/tab_component.html.erb +7 -0
  74. data/app/components/ariadne/tab_component.rb +43 -0
  75. data/app/components/ariadne/text.rb +25 -0
  76. data/app/components/ariadne/time-ago-component.ts +1 -0
  77. data/app/components/ariadne/time_ago_component.rb +56 -0
  78. data/app/components/ariadne/timeline_component.html.erb +19 -0
  79. data/app/components/ariadne/timeline_component.rb +34 -0
  80. data/app/components/ariadne/tooltip-component.ts +57 -0
  81. data/app/components/ariadne/tooltip_component.html.erb +4 -0
  82. data/app/components/ariadne/tooltip_component.rb +108 -0
  83. data/app/lib/ariadne/action_view_extensions/form_helper.rb +26 -0
  84. data/app/lib/ariadne/audited/dsl.rb +32 -0
  85. data/app/lib/ariadne/class_name_helper.rb +22 -0
  86. data/app/lib/ariadne/fetch_or_fallback_helper.rb +102 -0
  87. data/app/lib/ariadne/form_builder.rb +71 -0
  88. data/app/lib/ariadne/icon_helper.rb +47 -0
  89. data/app/lib/ariadne/join_style_arguments_helper.rb +14 -0
  90. data/app/lib/ariadne/logger_helper.rb +23 -0
  91. data/app/lib/ariadne/status/dsl.rb +41 -0
  92. data/app/lib/ariadne/tab_nav_helper.rb +35 -0
  93. data/app/lib/ariadne/tabbed_component_helper.rb +39 -0
  94. data/app/lib/ariadne/test_selector_helper.rb +20 -0
  95. data/app/lib/ariadne/underline_nav_helper.rb +44 -0
  96. data/app/lib/ariadne/view_helper.rb +22 -0
  97. data/exe/tailwindcss +21 -0
  98. data/exe/x86_64-linux/tailwindcss +0 -0
  99. data/lib/ariadne/view_components/commands.rb +90 -0
  100. data/lib/ariadne/view_components/constants.rb +53 -0
  101. data/lib/ariadne/view_components/engine.rb +75 -0
  102. data/lib/ariadne/view_components/linters.rb +3 -0
  103. data/lib/ariadne/view_components/statuses.rb +14 -0
  104. data/lib/ariadne/view_components/upstream.rb +20 -0
  105. data/lib/ariadne/view_components/version.rb +7 -0
  106. data/lib/ariadne/view_components.rb +61 -0
  107. data/lib/rubocop/config/default.yml +8 -0
  108. data/lib/rubocop/cop/ariadne/base_cop.rb +26 -0
  109. data/lib/rubocop/cop/ariadne/no_tag_memoize.rb +44 -0
  110. data/lib/rubocop/cop/ariadne.rb +3 -0
  111. data/lib/tasks/ariadne_view_components.rake +48 -0
  112. data/lib/tasks/build.rake +30 -0
  113. data/lib/tasks/coverage.rake +19 -0
  114. data/lib/tasks/custom_utilities.yml +310 -0
  115. data/lib/tasks/docs.rake +524 -0
  116. data/lib/tasks/helpers/ast_processor.rb +44 -0
  117. data/lib/tasks/helpers/ast_traverser.rb +77 -0
  118. data/lib/tasks/static.rake +15 -0
  119. data/lib/yard/docs_helper.rb +83 -0
  120. data/lib/yard/renders_many_handler.rb +19 -0
  121. data/lib/yard/renders_one_handler.rb +19 -0
  122. data/static/arguments.yml +619 -0
  123. data/static/assets/view-components.svg +18 -0
  124. data/static/audited_at.json +38 -0
  125. data/static/classes.yml +291 -0
  126. data/static/constants.json +426 -0
  127. data/static/statuses.json +38 -0
  128. data/static/tailwindcss.yml +727 -0
  129. data/tailwind.config.js +65 -0
  130. metadata +264 -0
@@ -0,0 +1,4 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class ClipboardCopyComponent extends Controller {
3
+ copy(): void;
4
+ }
@@ -0,0 +1,13 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class CommentComponent extends Controller {
3
+ static targets: string[];
4
+ readonly commentComponentTarget: HTMLDivElement;
5
+ readonly tabBarComponentTarget: HTMLElement;
6
+ readonly tabTargets: [HTMLButtonElement];
7
+ SELECTED_TAB_CLASSES: string[];
8
+ PUBLIC_BACKGROUND_COLOR: string;
9
+ INTERNAL_BACKGROUND_COLOR: string;
10
+ connect(): void;
11
+ toggleTab(): void;
12
+ toggleBackgrounds(tab: HTMLButtonElement): void;
13
+ }
@@ -0,0 +1,4 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class RichTextArea extends Controller {
3
+ connect(): 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 @@
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
+ }
@@ -0,0 +1,6 @@
1
+ @import 'tailwindcss/base';
2
+ @import 'tailwindcss/components';
3
+ @import 'tailwindcss/utilities';
4
+
5
+ @import 'tooltip-component.css';
6
+ @import 'prosemirror.css';
@@ -0,0 +1,323 @@
1
+ .ProseMirror {
2
+ position: relative;
3
+ }
4
+
5
+ .ProseMirror {
6
+ word-wrap: break-word;
7
+ white-space: pre-wrap;
8
+ -webkit-font-variant-ligatures: none;
9
+ font-variant-ligatures: none;
10
+ }
11
+
12
+ .ProseMirror pre {
13
+ white-space: pre-wrap;
14
+ }
15
+
16
+ .ProseMirror li {
17
+ position: relative;
18
+ }
19
+
20
+ .ProseMirror-hideselection *::selection {
21
+ background: transparent;
22
+ }
23
+ .ProseMirror-hideselection *::-moz-selection {
24
+ background: transparent;
25
+ }
26
+ .ProseMirror-hideselection {
27
+ caret-color: transparent;
28
+ }
29
+
30
+ .ProseMirror-selectednode {
31
+ outline: 2px solid #8cf;
32
+ }
33
+
34
+ /* Make sure li selections wrap around markers */
35
+
36
+ li.ProseMirror-selectednode {
37
+ outline: none;
38
+ }
39
+
40
+ li.ProseMirror-selectednode:after {
41
+ content: '';
42
+ position: absolute;
43
+ left: -32px;
44
+ right: -2px;
45
+ top: -2px;
46
+ bottom: -2px;
47
+ border: 2px solid #8cf;
48
+ pointer-events: none;
49
+ }
50
+ .ProseMirror-textblock-dropdown {
51
+ min-width: 3em;
52
+ }
53
+
54
+ .ProseMirror-menu {
55
+ margin: 0 -4px;
56
+ line-height: 1;
57
+ }
58
+
59
+ .ProseMirror-tooltip .ProseMirror-menu {
60
+ width: -webkit-fit-content;
61
+ width: fit-content;
62
+ white-space: pre;
63
+ }
64
+
65
+ .ProseMirror-menuitem {
66
+ margin-right: 3px;
67
+ display: inline-block;
68
+ }
69
+
70
+ .ProseMirror-menuseparator {
71
+ border-right: 1px solid #ddd;
72
+ margin-right: 3px;
73
+ }
74
+
75
+ .ProseMirror-menu-dropdown,
76
+ .ProseMirror-menu-dropdown-menu {
77
+ font-size: 90%;
78
+ white-space: nowrap;
79
+ }
80
+
81
+ .ProseMirror-menu-dropdown {
82
+ vertical-align: 1px;
83
+ cursor: pointer;
84
+ position: relative;
85
+ padding-right: 15px;
86
+ }
87
+
88
+ .ProseMirror-menu-dropdown-wrap {
89
+ padding: 1px 0 1px 4px;
90
+ display: inline-block;
91
+ position: relative;
92
+ }
93
+
94
+ .ProseMirror-menu-dropdown:after {
95
+ content: '';
96
+ border-left: 4px solid transparent;
97
+ border-right: 4px solid transparent;
98
+ border-top: 4px solid currentColor;
99
+ opacity: 0.6;
100
+ position: absolute;
101
+ right: 4px;
102
+ top: calc(50% - 2px);
103
+ }
104
+
105
+ .ProseMirror-menu-dropdown-menu,
106
+ .ProseMirror-menu-submenu {
107
+ position: absolute;
108
+ background: white;
109
+ color: #666;
110
+ border: 1px solid #aaa;
111
+ padding: 2px;
112
+ }
113
+
114
+ .ProseMirror-menu-dropdown-menu {
115
+ z-index: 15;
116
+ min-width: 6em;
117
+ }
118
+
119
+ .ProseMirror-menu-dropdown-item {
120
+ cursor: pointer;
121
+ padding: 2px 8px 2px 4px;
122
+ }
123
+
124
+ .ProseMirror-menu-dropdown-item:hover {
125
+ background: #f2f2f2;
126
+ }
127
+
128
+ .ProseMirror-menu-submenu-wrap {
129
+ position: relative;
130
+ margin-right: -4px;
131
+ }
132
+
133
+ .ProseMirror-menu-submenu-label:after {
134
+ content: '';
135
+ border-top: 4px solid transparent;
136
+ border-bottom: 4px solid transparent;
137
+ border-left: 4px solid currentColor;
138
+ opacity: 0.6;
139
+ position: absolute;
140
+ right: 4px;
141
+ top: calc(50% - 4px);
142
+ }
143
+
144
+ .ProseMirror-menu-submenu {
145
+ display: none;
146
+ min-width: 4em;
147
+ left: 100%;
148
+ top: -3px;
149
+ }
150
+
151
+ .ProseMirror-menu-active {
152
+ background: #eee;
153
+ border-radius: 4px;
154
+ }
155
+
156
+ .ProseMirror-menu-active {
157
+ background: #eee;
158
+ border-radius: 4px;
159
+ }
160
+
161
+ .ProseMirror-menu-disabled {
162
+ opacity: 0.3;
163
+ }
164
+
165
+ .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu,
166
+ .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu {
167
+ display: block;
168
+ }
169
+
170
+ .ProseMirror-menubar {
171
+ border-top-left-radius: inherit;
172
+ border-top-right-radius: inherit;
173
+ position: relative;
174
+ min-height: 1em;
175
+ color: #666;
176
+ padding: 1px 6px;
177
+ top: 0;
178
+ left: 0;
179
+ right: 0;
180
+ border-bottom: 1px solid silver;
181
+ background: white;
182
+ z-index: 10;
183
+ -moz-box-sizing: border-box;
184
+ box-sizing: border-box;
185
+ overflow: visible;
186
+ }
187
+
188
+ .ProseMirror-icon {
189
+ display: inline-block;
190
+ line-height: 0.8;
191
+ vertical-align: -2px; /* Compensate for padding */
192
+ padding: 2px 8px;
193
+ cursor: pointer;
194
+ }
195
+
196
+ .ProseMirror-menu-disabled.ProseMirror-icon {
197
+ cursor: default;
198
+ }
199
+
200
+ .ProseMirror-icon svg {
201
+ fill: currentColor;
202
+ height: 1em;
203
+ }
204
+
205
+ .ProseMirror-icon span {
206
+ vertical-align: text-top;
207
+ }
208
+ /* Add space around the hr to make clicking it easier */
209
+
210
+ .ProseMirror-example-setup-style hr {
211
+ padding: 2px 10px;
212
+ border: none;
213
+ margin: 1em 0;
214
+ }
215
+
216
+ .ProseMirror-example-setup-style hr:after {
217
+ content: '';
218
+ display: block;
219
+ height: 1px;
220
+ background-color: silver;
221
+ line-height: 2px;
222
+ }
223
+
224
+ .ProseMirror ul,
225
+ .ProseMirror ol {
226
+ padding-left: 30px;
227
+ }
228
+
229
+ .ProseMirror blockquote {
230
+ padding-left: 1em;
231
+ border-left: 3px solid #eee;
232
+ margin-left: 0;
233
+ margin-right: 0;
234
+ }
235
+
236
+ .ProseMirror-example-setup-style img {
237
+ cursor: default;
238
+ }
239
+
240
+ .ProseMirror-prompt {
241
+ background: white;
242
+ padding: 5px 10px 5px 15px;
243
+ border: 1px solid silver;
244
+ position: fixed;
245
+ border-radius: 3px;
246
+ z-index: 11;
247
+ box-shadow: -0.5px 2px 5px rgba(0, 0, 0, 0.2);
248
+ }
249
+
250
+ .ProseMirror-prompt h5 {
251
+ margin: 0;
252
+ font-weight: normal;
253
+ font-size: 100%;
254
+ color: #444;
255
+ }
256
+
257
+ .ProseMirror-prompt input[type='text'],
258
+ .ProseMirror-prompt textarea {
259
+ background: #eee;
260
+ border: none;
261
+ outline: none;
262
+ }
263
+
264
+ .ProseMirror-prompt input[type='text'] {
265
+ padding: 0 4px;
266
+ }
267
+
268
+ .ProseMirror-prompt-close {
269
+ position: absolute;
270
+ left: 2px;
271
+ top: 1px;
272
+ color: #666;
273
+ border: none;
274
+ background: transparent;
275
+ padding: 0;
276
+ }
277
+
278
+ .ProseMirror-prompt-close:after {
279
+ content: '✕';
280
+ font-size: 12px;
281
+ }
282
+
283
+ .ProseMirror-invalid {
284
+ background: #ffc;
285
+ border: 1px solid #cc7;
286
+ border-radius: 4px;
287
+ padding: 5px 10px;
288
+ position: absolute;
289
+ min-width: 10em;
290
+ }
291
+
292
+ .ProseMirror-prompt-buttons {
293
+ margin-top: 5px;
294
+ display: none;
295
+ }
296
+ :root {
297
+ --validation-color: #3dcde6;
298
+ --validation-color-hover: #3dcde624;
299
+ --validation-gray: #a0a0a0;
300
+ --validation-debug-color-dirty: #f7a85e66;
301
+ --validation-debug-color-inflight: #f7a85ea1;
302
+ }
303
+
304
+ .Button {
305
+ padding: 1px 4px;
306
+ border: 1px solid lightgrey;
307
+ border-radius: 3px;
308
+ font-size: 16px;
309
+ cursor: pointer;
310
+ }
311
+
312
+ .Button:hover {
313
+ background-color: #eee;
314
+ }
315
+
316
+ .Input[type='checkbox'] {
317
+ margin-left: calc(var(--gutter-width));
318
+ }
319
+
320
+ .Button:active,
321
+ .Button:focus {
322
+ outline: none;
323
+ }
@@ -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,96 @@
1
+ import {Controller} from '@hotwired/stimulus'
2
+ import {html} from 'lit-html'
3
+ import type {TemplateResult} from 'lit-html'
4
+
5
+ type HTMLFormField = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
6
+
7
+ export default class AriadneFormWith extends Controller {
8
+ static targets = ['formField']
9
+ declare readonly formFieldTargets: [HTMLFormField]
10
+
11
+ connect() {
12
+ this.element.setAttribute('novalidate', 'true')
13
+ this.element.addEventListener('blur', this.onBlur, true)
14
+ this.element.addEventListener('submit', this.onSubmit)
15
+ this.element.addEventListener('ajax:beforeSend', this.onSubmit)
16
+ }
17
+
18
+ disconnect() {
19
+ this.element.removeEventListener('blur', this.onBlur)
20
+ this.element.removeEventListener('submit', this.onSubmit)
21
+ this.element.removeEventListener('ajax:beforeSend', this.onSubmit)
22
+ }
23
+
24
+ onBlur = (event: Event) => {
25
+ this.validateField(event.target as HTMLFormField)
26
+ }
27
+
28
+ onSubmit = (event: Event) => {
29
+ if (!this.validateForm()) {
30
+ event.preventDefault()
31
+ this.firstInvalidField?.focus()
32
+ }
33
+ }
34
+
35
+ validateForm() {
36
+ let isValid = true
37
+ // Not using `find` because we want to validate all the fields
38
+ for (const field of this.formFields) {
39
+ if (this.shouldValidateField(field) && !this.validateField(field)) isValid = false
40
+ }
41
+ return isValid
42
+ }
43
+
44
+ validateField(field: HTMLFormField) {
45
+ if (!this.shouldValidateField(field)) return true
46
+ const isValid = field.checkValidity()
47
+ field.classList.toggle('invalid', !isValid)
48
+ this.refreshErrorForInvalidField(field, isValid)
49
+ return isValid
50
+ }
51
+
52
+ shouldValidateField(field: HTMLFormField) {
53
+ return (
54
+ !field.disabled &&
55
+ !field.classList.contains('ProseMirror') &&
56
+ !['file', 'reset', 'submit', 'button'].includes(field.type)
57
+ )
58
+ }
59
+
60
+ refreshErrorForInvalidField(field: HTMLFormField, isValid: boolean) {
61
+ this.removeExistingErrorMessage(field)
62
+ if (!isValid) this.showErrorForInvalidField(field)
63
+ }
64
+
65
+ removeExistingErrorMessage(field: HTMLFormField) {
66
+ const fieldContainer = field.closest('.field')
67
+ if (!fieldContainer) return
68
+ const existingErrorMessageElement = fieldContainer.querySelector('.error')
69
+ if (existingErrorMessageElement) {
70
+ existingErrorMessageElement?.parentNode?.removeChild(existingErrorMessageElement)
71
+ }
72
+ }
73
+
74
+ showErrorForInvalidField(field: HTMLFormField) {
75
+ field.insertAdjacentHTML('afterend', this.buildFieldErrorHtml(field))
76
+ }
77
+
78
+ buildFieldErrorHtml(field: HTMLFormField) {
79
+ const data = html`<p class="error">${field.validationMessage}</p>`
80
+ return this.getRenderString(data)
81
+ }
82
+
83
+ get formFields(): HTMLFormField[] {
84
+ return Array.from(this.formFieldTargets)
85
+ }
86
+
87
+ get firstInvalidField() {
88
+ return this.formFields.find(field => !field.checkValidity())
89
+ }
90
+
91
+ getRenderString = (data: TemplateResult): string => {
92
+ const {strings, values} = data
93
+ const v = [...values, ''].map(e => (typeof e === 'object' ? this.getRenderString(e as TemplateResult) : e))
94
+ return strings.reduce((acc, s, i) => acc + s + v[i], '')
95
+ }
96
+ }
@@ -0,0 +1,20 @@
1
+ import {Application} from '@hotwired/stimulus'
2
+
3
+ import AriadneForm from './ariadne-form'
4
+
5
+ import ClipboardCopyComponent from './clipboard-copy-component'
6
+ import CommentComponent from './comment-component'
7
+ import RichTextAreaComponent from './rich-text-area-component'
8
+ import SlideoverComponent from './slideover-component'
9
+ import TooltipComponent from './tooltip-component'
10
+
11
+ import './time-ago-component'
12
+
13
+ const application = Application.start()
14
+
15
+ application.register('clipboard-copy-component', ClipboardCopyComponent)
16
+ application.register('ariadne-form', AriadneForm)
17
+ application.register('comment-component', CommentComponent)
18
+ application.register('rich-text-area-component', RichTextAreaComponent)
19
+ application.register('slideover-component', SlideoverComponent)
20
+ application.register('tooltip-component', TooltipComponent)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # Use `BaseButton` to render an unstyled `<button>` tag that can be customized.
5
+ class BaseButton < Ariadne::Component
6
+ DEFAULT_TAG = :button
7
+ TAG_OPTIONS = [DEFAULT_TAG, :a, :summary].freeze
8
+
9
+ DEFAULT_TYPE = :button
10
+ TYPE_SUBMIT = :submit
11
+ TYPE_OPTIONS = [DEFAULT_TYPE, :reset, TYPE_SUBMIT].freeze
12
+
13
+ SIZE_CLASS_MAPPINGS = {
14
+ xs: "ariadne-px-2.5 ariadne-py-1.5 ariadne-text-xs ariadne-font-medium ariadne-rounded",
15
+ sm: "ariadne-px-3 ariadne-py-2 ariadne-text-sm ariadne-leading-4 ariadne-font-medium ariadne-rounded-m",
16
+ md: "ariadne-px-4 ariadne-py-2 ariadne-text-sm ariadne-font-medium ariadne-rounded-md",
17
+ lg: "ariadne-px-4 ariadne-py-2 ariadne-text-base ariadne-font-medium ariadne-rounded-md",
18
+ xl: "ariadne-px-6 ariadne-py-3 ariadne-text-base ariadne-font-medium ariadne-rounded-md",
19
+ }.freeze
20
+ VALID_SIZES = SIZE_CLASS_MAPPINGS.keys.freeze
21
+
22
+ DEFAULT_CLASSES = "ariadne-inline-flex ariadne-items-center ariadne-border ariadne-shadow-sm focus:ariadne-outline-none focus:ariadne-ring-2 focus:ariadne-ring-offset-2"
23
+
24
+ DEFAULT_SIZE = :md
25
+
26
+ # @example Setting the size
27
+ # <%= render(Ariadne::BaseButton.new(size: :xs)) { "I am an extra small button!" } %>
28
+ # <%= render(Ariadne::BaseButton.new(size: :sm)) { "I am a small button!" } %>
29
+ # <%= render(Ariadne::BaseButton.new(size: :md)) { "I am a medium button!" } %>
30
+ # <%= render(Ariadne::BaseButton.new(size: :lg)) { "I am a large button!" } %>
31
+ # <%= render(Ariadne::BaseButton.new(size: :xl)) { "I am an extra large button!" } %>
32
+ #
33
+ # @param tag [Symbol] <%= one_of(Ariadne::BaseButton::TAG_OPTIONS) %>
34
+ # @param type [Symbol] <%= one_of(Ariadne::BaseButton::TYPE_OPTIONS) %>
35
+ # @param size [Symbol] <%= one_of(Ariadne::BaseButton::VALID_SIZES) %>
36
+ # @param classes [String] <%= link_to_classes_docs %>
37
+ # @param attributes [Hash] <%= link_to_attributes_docs %>
38
+ def initialize(
39
+ tag: DEFAULT_TAG,
40
+ type: DEFAULT_TYPE,
41
+ size: DEFAULT_SIZE,
42
+ classes: "",
43
+ attributes: {}
44
+ )
45
+ @attributes = attributes
46
+ @tag = fetch_or_raise(TAG_OPTIONS, tag)
47
+ @size = fetch_or_raise(VALID_SIZES, size)
48
+
49
+ @attributes[:type] = fetch_or_raise(TYPE_OPTIONS, type) if button?
50
+ @classes = class_names(DEFAULT_CLASSES, SIZE_CLASS_MAPPINGS[@size], classes)
51
+ end
52
+
53
+ def call
54
+ render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) { content }
55
+ end
56
+
57
+ private def button?
58
+ @tag == :button
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # All Ariadne ViewComponents accept a standard set of options: classes, which match Tailwind CSS classes, and attributes, which match HTML attributes..
5
+ #
6
+ # Under the hood, any-non class attributes is passed to Rails' [`tag`](https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-tag)
7
+ # (for self-closing tags) or [`content_tag`](https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag).
8
+ class BaseComponent < Ariadne::Component
9
+ SELF_CLOSING_TAGS = [:area, :base, :br, :col, :embed, :hr, :img, :input, :link, :meta, :param, :source, :track, :wbr].freeze
10
+
11
+ def initialize(tag:, classes: "", attributes: {})
12
+ @tag = tag
13
+
14
+ @attributes = validate_attributes(tag: tag, attributes: attributes) || {}
15
+ @attributes[:"data-ariadne-view-component"] = true
16
+
17
+ classes = @attributes.fetch(:classes, "") if classes.blank?
18
+ @classes = validate_class_names(classes) || {}
19
+ @content_tag_args = {}
20
+ end
21
+
22
+ def call
23
+ options = @attributes.merge(@classes)
24
+ if SELF_CLOSING_TAGS.include?(@tag)
25
+ tag(@tag, options)
26
+ else
27
+ content_tag(@tag, content, options)
28
+ end
29
+ end
30
+
31
+ private def validate_class_names(classes)
32
+ return if classes.blank?
33
+
34
+ { class: Ariadne::ViewComponents.tailwind_merger.merge(classes) }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ <%= render Ariadne::NarrowContainerComponent.new do %>
2
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do %>
3
+ <% if icon.present? %>
4
+ <%= icon %>
5
+ <% elsif image.present? %>
6
+ <%= image %>
7
+ <% end %>
8
+ <%= heading %>
9
+ <% if description.present? %>
10
+ <%= description %>
11
+ <% end %>
12
+ <%= content %>
13
+ <div class="ariadne-mt-8 ariadne-flex ariadne-justify-center">
14
+ <% if primary_action.present? %>
15
+ <div class="ariadne-inline-flex ariadne-rounded-md shadow">
16
+ <%= primary_action %>
17
+ </div>
18
+ <% end %>
19
+ <% if secondary_action.present? %>
20
+ <div class="ariadne-ml-3 ariadne-inline-flex">
21
+ <%= secondary_action %>
22
+ </div>
23
+ <% end %>
24
+ </div>
25
+ <% end %>
26
+ <% end %>