vident 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +80 -0
- data/LICENSE.txt +1 -1
- data/README.md +720 -164
- data/lib/vident/base.rb +44 -10
- data/lib/vident/component.rb +1 -1
- data/lib/vident/root_component.rb +25 -12
- data/lib/vident/version.rb +6 -1
- data/lib/vident.rb +10 -4
- metadata +12 -22
- data/.ruby-version +0 -1
- data/.standard.yml +0 -3
- data/CODE_OF_CONDUCT.md +0 -84
- data/Rakefile +0 -3
- data/lib/tasks/vident_tasks.rake +0 -4
- data/sig/vident.rbs +0 -4
- data/vident.gemspec +0 -30
data/README.md
CHANGED
@@ -1,76 +1,448 @@
|
|
1
1
|
# Vident
|
2
2
|
|
3
|
-
|
3
|
+
Vident is a collection of gems that provide a set of tools for building web applications with Ruby on Rails.
|
4
4
|
|
5
|
-
|
5
|
+
## Included Gems
|
6
6
|
|
7
|
-
|
7
|
+
- `vident`: The core Vident library
|
8
|
+
- `vident-better_html`: Better HTML integration for Vident
|
9
|
+
- `vident-phlex`: Phlex integration for Vident
|
10
|
+
- `vident-tailwind`: Tailwind CSS integration for Vident
|
11
|
+
- `vident-typed`: Type system for Vident components
|
12
|
+
- `vident-typed-minitest`: Minitest integration for typed Vident components
|
13
|
+
- `vident-typed-phlex`: Phlex integration for typed Vident components
|
14
|
+
- `vident-typed-view_component`: ViewComponent integration for typed Vident
|
15
|
+
- `vident-view_component`: ViewComponent integration for Vident
|
16
|
+
- `vident-view_component-caching`: Caching support for Vident ViewComponents
|
8
17
|
|
9
|
-
|
18
|
+
## Directory Structure
|
10
19
|
|
11
|
-
|
20
|
+
The repository is structured like this:
|
12
21
|
|
13
|
-
|
14
|
-
|
15
|
-
|
22
|
+
```
|
23
|
+
vident/
|
24
|
+
├── lib/ # All gem code
|
25
|
+
│ ├── vident.rb # Core entry point
|
26
|
+
│ ├── vident-phlex.rb # Gem entry points
|
27
|
+
│ ├── vident-better_html.rb
|
28
|
+
│ ├── vident/ # Shared code
|
29
|
+
│ ├── base.rb
|
30
|
+
│ ├── phlex/ # Phlex integration
|
31
|
+
│ ├── better_html/ # Better HTML integration
|
32
|
+
│ └── ...
|
33
|
+
├── test/ # All tests
|
34
|
+
│ ├── vident/ # Core tests
|
35
|
+
│ ├── vident-phlex/ # Tests for each gem
|
36
|
+
│ └── ...
|
37
|
+
├── docs/ # Documentation
|
38
|
+
├── examples/ # Examples
|
39
|
+
├── vident.gemspec # Gemspec for core gem
|
40
|
+
├── vident-phlex.gemspec # Gemspecs for each gem
|
41
|
+
└── ...
|
42
|
+
```
|
43
|
+
|
44
|
+
## Development
|
45
|
+
|
46
|
+
### Setting Up Development Environment
|
47
|
+
|
48
|
+
```bash
|
49
|
+
# Clone the repository
|
50
|
+
git clone https://github.com/stevegeek/vident.git
|
51
|
+
cd vident
|
52
|
+
|
53
|
+
# Install dependencies
|
54
|
+
bundle install
|
55
|
+
```
|
16
56
|
|
17
|
-
|
18
|
-
but is still evolving.
|
57
|
+
### Running Tests
|
19
58
|
|
20
|
-
|
59
|
+
To run tests for all gems:
|
21
60
|
|
22
|
-
|
61
|
+
```bash
|
62
|
+
rake test
|
63
|
+
```
|
64
|
+
|
65
|
+
To run tests for a specific gem:
|
66
|
+
|
67
|
+
```bash
|
68
|
+
rake test:vident-phlex
|
69
|
+
```
|
23
70
|
|
24
|
-
|
71
|
+
### Building and Installing Gems
|
25
72
|
|
26
|
-
|
73
|
+
To build all gems:
|
27
74
|
|
28
|
-
|
75
|
+
```bash
|
76
|
+
rake build
|
77
|
+
```
|
78
|
+
|
79
|
+
To install all gems locally:
|
80
|
+
|
81
|
+
```bash
|
82
|
+
rake install
|
83
|
+
```
|
84
|
+
|
85
|
+
## Contributing
|
86
|
+
|
87
|
+
1. Fork the repository
|
88
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
89
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
90
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
91
|
+
5. Create a new Pull Request
|
92
|
+
|
93
|
+
## License
|
94
|
+
|
95
|
+
The gems are available as open source under the terms of the [MIT License](LICENSE.txt).
|
96
|
+
|
97
|
+
---
|
98
|
+
|
99
|
+
# Component Documentation
|
100
|
+
|
101
|
+
## gem: vident-better_html
|
102
|
+
|
103
|
+
# Vident::BetterHtml
|
104
|
+
Short description and motivation.
|
105
|
+
|
106
|
+
### Usage
|
107
|
+
How to use my plugin.
|
29
108
|
|
30
109
|
```ruby
|
31
|
-
|
110
|
+
BetterHtml.config = BetterHtml::Config.new(YAML.load(File.read(".better-html.yml")))
|
32
111
|
|
33
|
-
|
34
|
-
|
112
|
+
BetterHtml.configure do |config|
|
113
|
+
config.template_exclusion_filter = proc { |filename| !filename.start_with?(Rails.root.to_s) }
|
114
|
+
end
|
115
|
+
# ViewComponent needs to do this hack to work in certain cases
|
116
|
+
# see https://github.com/Shopify/better-html/pull/98
|
117
|
+
class BetterHtml::HtmlAttributes
|
118
|
+
alias_method :to_s_without_html_safe, :to_s
|
119
|
+
|
120
|
+
def to_s
|
121
|
+
to_s_without_html_safe.html_safe
|
122
|
+
end
|
35
123
|
end
|
36
124
|
```
|
37
125
|
|
38
|
-
|
126
|
+
### Installation
|
127
|
+
Add this line to your application's Gemfile:
|
39
128
|
|
40
|
-
```
|
41
|
-
|
129
|
+
```ruby
|
130
|
+
gem "vident-better_html"
|
131
|
+
```
|
42
132
|
|
43
|
-
|
44
|
-
|
133
|
+
And then execute:
|
134
|
+
```bash
|
135
|
+
$ bundle
|
136
|
+
```
|
45
137
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
138
|
+
Or install it yourself as:
|
139
|
+
```bash
|
140
|
+
$ gem install vident-better_html
|
141
|
+
```
|
142
|
+
|
143
|
+
---
|
144
|
+
|
145
|
+
## gem: vident-phlex
|
146
|
+
|
147
|
+
# Vident::Phlex
|
148
|
+
|
149
|
+
[Phlex](https://phlex.fun/) powered [Vident](https://github.com/stevegeek/vident) components.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
class ApplicationComponent < ::Vident::Phlex::HTML
|
153
|
+
end
|
154
|
+
```
|
155
|
+
|
156
|
+
For more details see [vident](https://github.com/stevegeek/vident).
|
157
|
+
|
158
|
+
### Usage
|
159
|
+
How to use my plugin.
|
160
|
+
|
161
|
+
### Installation
|
162
|
+
Add this line to your application's Gemfile:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
gem "vident-phlex"
|
166
|
+
```
|
167
|
+
|
168
|
+
And then execute:
|
169
|
+
```bash
|
170
|
+
$ bundle
|
171
|
+
```
|
172
|
+
|
173
|
+
Or install it yourself as:
|
174
|
+
```bash
|
175
|
+
$ gem install vident-phlex
|
176
|
+
```
|
177
|
+
|
178
|
+
---
|
179
|
+
|
180
|
+
## gem: vident-tailwind
|
181
|
+
|
182
|
+
# Vident::Tailwind
|
183
|
+
Short description and motivation.
|
184
|
+
|
185
|
+
### Usage
|
186
|
+
How to use my plugin.
|
187
|
+
|
188
|
+
### Installation
|
189
|
+
Add this line to your application's Gemfile:
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
gem "vident-tailwind"
|
193
|
+
```
|
194
|
+
|
195
|
+
And then execute:
|
196
|
+
```bash
|
197
|
+
$ bundle
|
198
|
+
```
|
199
|
+
|
200
|
+
Or install it yourself as:
|
201
|
+
```bash
|
202
|
+
$ gem install vident-tailwind
|
203
|
+
```
|
204
|
+
|
205
|
+
---
|
206
|
+
|
207
|
+
## gem: vident-typed-minitest
|
208
|
+
|
209
|
+
# Vident::Typed::Minitest
|
210
|
+
Short description and motivation.
|
211
|
+
|
212
|
+
### Usage
|
213
|
+
How to use my plugin.
|
214
|
+
|
215
|
+
### Installation
|
216
|
+
Add this line to your application's Gemfile:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
gem "vident-typed-minitest"
|
220
|
+
```
|
221
|
+
|
222
|
+
And then execute:
|
223
|
+
```bash
|
224
|
+
$ bundle
|
225
|
+
```
|
226
|
+
|
227
|
+
Or install it yourself as:
|
228
|
+
```bash
|
229
|
+
$ gem install vident-typed-minitest
|
230
|
+
```
|
231
|
+
|
232
|
+
---
|
233
|
+
|
234
|
+
## gem: vident-typed-phlex
|
235
|
+
|
236
|
+
# Vident::Typed::Phlex
|
237
|
+
|
238
|
+
Adds typed attributes to Vident Phlex based components.
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
class ApplicationComponent < ::Vident::Typed::Phlex::HTML
|
242
|
+
end
|
243
|
+
```
|
244
|
+
|
245
|
+
For more details see [vident](https://github.com/stevegeek/vident).
|
246
|
+
|
247
|
+
### Usage
|
248
|
+
How to use my plugin.
|
249
|
+
|
250
|
+
### Installation
|
251
|
+
Add this line to your application's Gemfile:
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
gem "vident-typed-phlex"
|
255
|
+
```
|
256
|
+
|
257
|
+
And then execute:
|
258
|
+
```bash
|
259
|
+
$ bundle
|
260
|
+
```
|
261
|
+
|
262
|
+
Or install it yourself as:
|
263
|
+
```bash
|
264
|
+
$ gem install vident-typed-phlex
|
265
|
+
```
|
266
|
+
|
267
|
+
---
|
268
|
+
|
269
|
+
## gem: vident-typed-view_component
|
270
|
+
|
271
|
+
# Vident::Typed::ViewComponent
|
272
|
+
|
273
|
+
Adds typed attributes to Vident ViewComponent components.
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
class ApplicationComponent < ::Vident::Typed::ViewComponent::Base
|
277
|
+
end
|
278
|
+
```
|
279
|
+
|
280
|
+
For more details see [vident](https://github.com/stevegeek/vident).
|
281
|
+
|
282
|
+
### Examples
|
283
|
+
|
284
|
+
Before we dive into a specific example note that there are some components implemented in `test/dummy/app/components`.
|
285
|
+
|
286
|
+
Try them out by starting Rails:
|
287
|
+
|
288
|
+
```bash
|
289
|
+
cd test/dummy
|
290
|
+
bundle install
|
291
|
+
rails assets:precompile
|
292
|
+
rails s
|
293
|
+
```
|
294
|
+
|
295
|
+
and visiting http://localhost:3000
|
296
|
+
|
297
|
+
|
298
|
+
### A Vident component example (without Stimulus)
|
299
|
+
|
300
|
+
First is an example component that uses `Vident::Typed::ViewComponent::Base` but no Stimulus features.
|
301
|
+
|
302
|
+
It is an avatar component that can either be displayed as an image or as initials.
|
303
|
+
|
304
|
+
It supports numerous sizes and shapes and can optionally have a border. It also generates a cache key for use in fragment caching or etag generation.
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
class AvatarComponent < ::Vident::Typed::ViewComponent::Base
|
308
|
+
include ::Vident::Tailwind
|
309
|
+
include ::Vident::Caching
|
310
|
+
|
311
|
+
no_stimulus_controller
|
312
|
+
with_cache_key :attributes
|
313
|
+
|
314
|
+
attribute :url, String, allow_nil: true, allow_blank: false
|
315
|
+
attribute :initials, String, allow_blank: false
|
316
|
+
|
317
|
+
attribute :shape, Symbol, in: %i[circle square], default: :circle
|
318
|
+
|
319
|
+
attribute :border, :boolean, default: false
|
320
|
+
|
321
|
+
attribute :size, Symbol, in: %i[tiny small normal medium large x_large xx_large], default: :normal
|
322
|
+
|
323
|
+
private
|
324
|
+
|
325
|
+
def default_html_options
|
326
|
+
if image_avatar?
|
327
|
+
{ class: "inline-block object-contain", src: url, alt: t(".image") }
|
328
|
+
else
|
329
|
+
{ class: "inline-flex items-center justify-center bg-gray-500" }
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def element_classes
|
334
|
+
[size_classes, shape_class, border? ? "border" : ""]
|
335
|
+
end
|
336
|
+
|
337
|
+
alias_method :image_avatar?, :url?
|
338
|
+
|
339
|
+
def shape_class
|
340
|
+
(shape == :circle) ? "rounded-full" : "rounded-md"
|
341
|
+
end
|
342
|
+
|
343
|
+
def size_classes
|
344
|
+
case size
|
345
|
+
when :tiny
|
346
|
+
"w-6 h-6"
|
347
|
+
when :small
|
348
|
+
"w-8 h-8"
|
349
|
+
when :medium
|
350
|
+
"w-12 h-12"
|
351
|
+
when :large
|
352
|
+
"w-14 h-14"
|
353
|
+
when :x_large
|
354
|
+
"sm:w-24 sm:h-24 w-16 h-16"
|
355
|
+
when :xx_large
|
356
|
+
"sm:w-32 sm:h-32 w-24 h-24"
|
357
|
+
else
|
358
|
+
"w-10 h-10"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def text_size_class
|
363
|
+
case size
|
364
|
+
when :tiny
|
365
|
+
"text-xs"
|
366
|
+
when :small
|
367
|
+
"text-xs"
|
368
|
+
when :medium
|
369
|
+
"text-lg"
|
370
|
+
when :large
|
371
|
+
"sm:text-xl text-lg"
|
372
|
+
when :extra_large
|
373
|
+
"sm:text-2xl text-xl"
|
374
|
+
else
|
375
|
+
"text-medium"
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
```
|
380
|
+
|
381
|
+
```erb
|
382
|
+
<%= render root(
|
383
|
+
element_tag: image_avatar? ? :img : :div,
|
384
|
+
html_options: default_html_options
|
385
|
+
) do %>
|
386
|
+
<% unless image_avatar? %>
|
387
|
+
<span class="<%= text_size_class %> font-medium leading-none text-white"><%= initials %></span>
|
67
388
|
<% end %>
|
68
389
|
<% end %>
|
390
|
+
|
69
391
|
```
|
70
392
|
|
71
|
-
|
393
|
+
Example usages:
|
72
394
|
|
73
395
|
```erb
|
396
|
+
<!-- These will render -->
|
397
|
+
<%= render AvatarComponent.new(url: "https://someurl.com/avatar.jpg", initials: "AB" size: :large) %>
|
398
|
+
<%= render AvatarComponent.new(url: "https://someurl.com/avatar.jpg", html_options: {alt: "My alt text", class: "object-scale-down"}) %>
|
399
|
+
<%= render AvatarComponent.new(initials: "SG", size: :small) %>
|
400
|
+
<%= render AvatarComponent.new(initials: "SG", size: :large, html_options: {class: "border-2 border-red-600"}) %>
|
401
|
+
|
402
|
+
<!-- These will raise an error -->
|
403
|
+
<!-- missing initals -->
|
404
|
+
<%= render AvatarComponent.new(url: "https://someurl.com/avatar.jpg", size: :large) %>
|
405
|
+
<!-- initials blank -->
|
406
|
+
<%= render AvatarComponent.new(initials: "", size: :large) %>
|
407
|
+
<!-- invalid size -->
|
408
|
+
<%= render AvatarComponent.new(initials: "SG", size: :foo_bar) %>
|
409
|
+
```
|
410
|
+
|
411
|
+
|
412
|
+
The following is rendered when used `render AvatarComponent.new(initials: "SG", size: :small, border: true)`:
|
413
|
+
|
414
|
+
```html
|
415
|
+
<div class="avatar-component w-8 h-8 rounded-full border inline-flex items-center justify-center bg-gray-500" id="avatar-component-9790427-12">
|
416
|
+
<span class="text-xs font-medium leading-none text-white">SG</span>
|
417
|
+
</div>
|
418
|
+
```
|
419
|
+
|
420
|
+
The following is rendered when used `render AvatarComponent.new(url: "https://i.pravatar.cc/300", initials: "AB", html_options: {alt: "My alt text", class: "block"})`:
|
421
|
+
|
422
|
+
```html
|
423
|
+
<img src="https://i.pravatar.cc/300" alt="My alt text" class="avatar-component w-10 h-10 rounded-full object-contain block" id="avatar-component-7083941-11">
|
424
|
+
```
|
425
|
+
|
426
|
+
----
|
427
|
+
|
428
|
+

|
429
|
+
|
430
|
+
|
431
|
+
### Another ViewComponent + Vident example with Stimulus
|
432
|
+
|
433
|
+
Consider the following ERB that might be part of an application's views. The app uses `ViewComponent`, `Stimulus` and `Vident`.
|
434
|
+
|
435
|
+
The Greeter is a component that displays a text input and a button. When the button is clicked, the text input's value is
|
436
|
+
used to greet the user. At the same time the button changes to be a 'reset' button, which resets the greeting when clicked again.
|
437
|
+
|
438
|
+

|
439
|
+
|
440
|
+
```erb
|
441
|
+
<%# app/views/home/index.html.erb %>
|
442
|
+
|
443
|
+
<!-- ... -->
|
444
|
+
|
445
|
+
<!-- render the Greeter ViewComponent (that uses Vident) -->
|
74
446
|
<%= render ::GreeterComponent.new(cta: "Hey!", html_options: {class: "my-4"}) do |greeter| %>
|
75
447
|
<%# this component has a slot called `trigger` that renders a `ButtonComponent` (which also uses Vident) %>
|
76
448
|
<% greeter.with_trigger(
|
@@ -91,6 +463,8 @@ Now, imagine we render it in a view, and render a `ButtonComponent` in the `trig
|
|
91
463
|
}
|
92
464
|
) %>
|
93
465
|
<% end %>
|
466
|
+
|
467
|
+
<!-- ... -->
|
94
468
|
```
|
95
469
|
|
96
470
|
The output HTML of the above, using Vident, is:
|
@@ -118,193 +492,375 @@ The output HTML of the above, using Vident, is:
|
|
118
492
|
</div>
|
119
493
|
```
|
120
494
|
|
121
|
-
|
495
|
+
Let's look at the components in more detail.
|
122
496
|
|
123
|
-
|
497
|
+
The main component is the `GreeterComponent`:
|
124
498
|
|
125
|
-
|
126
|
-
|
127
|
-
- [`vident`](https://github.com/stevegeek/vident) to get the base functionality
|
128
|
-
- [`vident-typed`](https://github.com/stevegeek/vident-typed) to optionally define typed attributes for your view components
|
499
|
+
```ruby
|
500
|
+
# app/components/greeter_component.rb
|
129
501
|
|
130
|
-
|
502
|
+
class GreeterComponent < ::Vident::ViewComponent::Base
|
503
|
+
renders_one :trigger, ButtonComponent
|
504
|
+
end
|
505
|
+
```
|
131
506
|
|
132
|
-
|
133
|
-
|
134
|
-
- [`vident-phlex`](https://github.com/stevegeek/vident-phlex) for using with `Phlex` and untyped attributes
|
135
|
-
- [`vident-typed-phlex`](https://github.com/stevegeek/vident-typed-phlex) for using with `Phlex` and typed attributes
|
507
|
+
```erb
|
508
|
+
<%# app/components/greeter_component.html.erb %>
|
136
509
|
|
137
|
-
|
510
|
+
<%# Rendering the `root` element creates a tag which has stimulus `data-*`s, a unique id & other attributes set. %>
|
511
|
+
<%# The stimulus controller name (identifier) is derived from the component name, and then used to generate the relavent data attribute names. %>
|
138
512
|
|
139
|
-
|
140
|
-
-
|
141
|
-
-
|
513
|
+
<%= render root named_classes: {
|
514
|
+
pre_click: "text-md text-gray-500", # named classes are exposed to Stimulus as `data-<controller>-<n>-class` attributes
|
515
|
+
post_click: "text-xl text-blue-700",
|
516
|
+
html_options: {class: "py-2"}
|
517
|
+
} do |greeter| %>
|
142
518
|
|
519
|
+
<%# `greeter` is the root element and exposes methods to generate stimulus targets and actions %>
|
520
|
+
<input type="text"
|
521
|
+
<%= greeter.as_target(:name) %>
|
522
|
+
class="shadow appearance-none border rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
|
523
|
+
|
524
|
+
<%# Render the slot %>
|
525
|
+
<%= trigger %>
|
526
|
+
|
527
|
+
<%# you can also use the `target_tag` helper to render targets %>
|
528
|
+
<%= greeter.target_tag(
|
529
|
+
:span,
|
530
|
+
:output,
|
531
|
+
# Stimulus named classes can be referenced to set class attributes at render time
|
532
|
+
class: "ml-4 #{greeter.named_classes(:pre_click)}"
|
533
|
+
) do %>
|
534
|
+
...
|
535
|
+
<% end %>
|
536
|
+
<% end %>
|
143
537
|
|
144
|
-
|
538
|
+
```
|
145
539
|
|
146
|
-
|
540
|
+
```js
|
541
|
+
// app/components/greeter_component_controller.js
|
542
|
+
|
543
|
+
import { Controller } from "@hotwired/stimulus"
|
544
|
+
|
545
|
+
// This is a Stimulus controller that is automatically registered for the `GreeterComponent`
|
546
|
+
// and is 'sidecar' to the component. You can see that while in the ERB we use Ruby naming conventions
|
547
|
+
// with snake_case Symbols, here they are converted to camelCase names. We can also just use camelCase
|
548
|
+
// in the ERB if we want.
|
549
|
+
export default class extends Controller {
|
550
|
+
static targets = [ "name", "output" ]
|
551
|
+
static classes = [ "preClick", "postClick" ]
|
552
|
+
|
553
|
+
greet() {
|
554
|
+
this.clicked = !this.clicked;
|
555
|
+
this.outputTarget.classList.toggle(this.preClickClasses, !this.clicked);
|
556
|
+
this.outputTarget.classList.toggle(this.postClickClasses, this.clicked);
|
557
|
+
|
558
|
+
if (this.clicked)
|
559
|
+
this.outputTarget.textContent = `Hello, ${this.nameTarget.value}!`
|
560
|
+
else
|
561
|
+
this.clear();
|
562
|
+
}
|
563
|
+
|
564
|
+
clear() {
|
565
|
+
this.outputTarget.textContent = '...';
|
566
|
+
this.nameTarget.value = '';
|
567
|
+
}
|
568
|
+
}
|
569
|
+
```
|
147
570
|
|
148
|
-
|
149
|
-
- Add tests
|
150
|
-
- Make the gem more configurable to fit more use cases
|
151
|
-
- Create an example library of a few components for some design system
|
152
|
-
- Create a demo app with `lookbook` and those components
|
153
|
-
- Add more documentation
|
571
|
+
The slot renders a `ButtonComponent` component:
|
154
572
|
|
155
|
-
|
573
|
+
```ruby
|
574
|
+
# app/components/button_component.rb
|
575
|
+
|
576
|
+
class ButtonComponent < ::Vident::Typed::ViewComponent::Base
|
577
|
+
# The attributes can specify an expected type, a default value and if nil is allowed.
|
578
|
+
attribute :after_clicked, String, default: "Greeted!"
|
579
|
+
attribute :before_clicked, String, allow_nil: false
|
580
|
+
|
581
|
+
# This example is a templateless ViewComponent.
|
582
|
+
def call
|
583
|
+
# The button is rendered as a <button> tag with an click action on its own controller.
|
584
|
+
render root(
|
585
|
+
element_tag: :button,
|
586
|
+
|
587
|
+
# We can define actions as arrays of Symbols, or pass manually manually crafted strings.
|
588
|
+
# Here we specify the action name only, implying an action on the current components controller
|
589
|
+
# and the default event type of `click`.
|
590
|
+
actions: [:change_message],
|
591
|
+
# Alternatively: [:click, :change_message] or ["click", "changeMessage"] or even "click->button-component#changeMessage"
|
592
|
+
|
593
|
+
# A couple of data values are also set which will be available to the controller
|
594
|
+
data_maps: [{after_clicked_message: after_clicked, before_clicked_message: before_clicked}],
|
595
|
+
|
596
|
+
# The <button> tag has a default styling set directly on it. Note that
|
597
|
+
# if not using utility classes, you can style the component using its
|
598
|
+
# canonical class name (which is equal to the component's stimulus identifier),
|
599
|
+
# in this case `button-component`.
|
600
|
+
html_options: {class: "ml-4 whitespace-no-wrap bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"}
|
601
|
+
) do
|
602
|
+
@before_clicked
|
603
|
+
end
|
604
|
+
end
|
605
|
+
end
|
606
|
+
```
|
156
607
|
|
157
|
-
|
608
|
+
```js
|
609
|
+
// app/components/button_component_controller.js
|
158
610
|
|
159
|
-
|
160
|
-
all important 'root' element component (can be used with templated or template-less components).
|
611
|
+
import { Controller } from "@hotwired/stimulus"
|
161
612
|
|
162
|
-
|
163
|
-
|
164
|
-
|
613
|
+
export default class extends Controller {
|
614
|
+
// The action is in camelCase.
|
615
|
+
changeMessage() {
|
616
|
+
this.clicked = !this.clicked;
|
617
|
+
// The data attributes have their naming convention converted to camelCase.
|
618
|
+
this.element.textContent = this.clicked ? this.data.get("afterClickedMessage") : this.data.get("beforeClickedMessage");
|
619
|
+
}
|
620
|
+
}
|
165
621
|
|
166
|
-
|
622
|
+
```
|
167
623
|
|
168
|
-
###
|
624
|
+
### Usage
|
625
|
+
How to use my plugin.
|
169
626
|
|
170
|
-
|
627
|
+
### Installation
|
628
|
+
Add this line to your application's Gemfile:
|
171
629
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
fragment caching or etag generation.
|
176
|
-
- a test helper for your typed Vident ViewComponents which can be used to generate good and bad attribute/params/inputs
|
630
|
+
```ruby
|
631
|
+
gem "vident-typed-view_component"
|
632
|
+
```
|
177
633
|
|
178
|
-
|
634
|
+
And then execute:
|
635
|
+
```bash
|
636
|
+
$ bundle
|
637
|
+
```
|
179
638
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
- You can use the same component in multiple contexts and configure the root element differently in each context by passing
|
185
|
-
options to the component when instantiating it.
|
186
|
-
- Stimulus support is built in and sets a default controller name based on the component name.
|
187
|
-
- Stimulus actions, targets and classes can be setup using a simple DSL to avoid hand crafting the data attributes.
|
188
|
-
- Since data attribute names are generated from the component class name, you can rename easily refactor and move components without
|
189
|
-
having to update the data attributes in your views.
|
190
|
-
- Components are rendered with useful class names and IDs to make debugging easier (autogenerated IDs are 'random' but deterministic so they
|
191
|
-
are the same each time a given view is rendered to avoid content changing/Etag changing).
|
192
|
-
- (experimental) Support for fragment caching of components (`Vident::Caching` and `Vident::<ViewComponent | Phlex>::Caching`... implementation has caveats)
|
193
|
-
- (experimental) A test helper to make testing components easier by utilising type information from the component arguments to render
|
194
|
-
automatically configured good and bad examples of the component.
|
195
|
-
- (experimental) support for `better_html`
|
639
|
+
Or install it yourself as:
|
640
|
+
```bash
|
641
|
+
$ gem install vident-typed-view_component
|
642
|
+
```
|
196
643
|
|
644
|
+
---
|
197
645
|
|
198
|
-
##
|
646
|
+
## gem: vident-typed
|
199
647
|
|
200
|
-
|
201
|
-
|
648
|
+
# Vident::Typed
|
649
|
+
Short description and motivation.
|
202
650
|
|
203
|
-
|
204
|
-
|
651
|
+
### Usage
|
652
|
+
How to use my plugin.
|
205
653
|
|
206
|
-
|
654
|
+
### Installation
|
655
|
+
Add this line to your application's Gemfile:
|
207
656
|
|
208
|
-
```ruby
|
209
|
-
gem
|
657
|
+
```ruby
|
658
|
+
gem "vident-typed"
|
210
659
|
```
|
211
660
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
For ViewComponent use:
|
661
|
+
And then execute:
|
662
|
+
```bash
|
663
|
+
$ bundle
|
664
|
+
```
|
217
665
|
|
218
|
-
|
666
|
+
Or install it yourself as:
|
667
|
+
```bash
|
668
|
+
$ gem install vident-typed
|
669
|
+
```
|
219
670
|
|
220
|
-
|
671
|
+
---
|
221
672
|
|
222
|
-
|
673
|
+
## gem: vident-view_component-caching
|
223
674
|
|
675
|
+
# Vident::ViewComponent::Caching
|
676
|
+
Short description and motivation.
|
224
677
|
|
225
|
-
|
678
|
+
### Usage
|
679
|
+
How to use my plugin.
|
226
680
|
|
227
|
-
|
681
|
+
### Installation
|
682
|
+
Add this line to your application's Gemfile:
|
228
683
|
|
229
684
|
```ruby
|
230
|
-
gem
|
231
|
-
gem 'vident-view_component'
|
232
|
-
gem 'vident-phlex'
|
685
|
+
gem "vident-view_component-caching"
|
233
686
|
```
|
234
687
|
|
235
|
-
|
236
|
-
|
237
|
-
|
688
|
+
And then execute:
|
689
|
+
```bash
|
690
|
+
$ bundle
|
691
|
+
```
|
238
692
|
|
239
|
-
|
240
|
-
|
693
|
+
Or install it yourself as:
|
694
|
+
```bash
|
695
|
+
$ gem install vident-view_component-caching
|
241
696
|
```
|
242
697
|
|
243
|
-
|
698
|
+
---
|
244
699
|
|
245
|
-
|
246
|
-
- and/or [`vident-typed-phlex`](https://github.com/stevegeek/vident-typed-phlex)
|
700
|
+
## gem: vident-view_component
|
247
701
|
|
248
|
-
|
702
|
+
# Vident::ViewComponent
|
249
703
|
|
250
|
-
|
704
|
+
[ViewComponent](https://viewcomponent.org/) powered [Vident](https://github.com/stevegeek/vident) components.
|
251
705
|
|
252
706
|
```ruby
|
253
|
-
|
254
|
-
|
255
|
-
gem 'vident-typed'
|
256
|
-
gem 'vident-typed-view_component'
|
707
|
+
class ApplicationComponent < ::Vident::ViewComponent::Base
|
708
|
+
end
|
257
709
|
```
|
258
710
|
|
259
|
-
|
711
|
+
For more details see [vident](https://github.com/stevegeek/vident).
|
260
712
|
|
261
|
-
|
713
|
+
### Examples
|
262
714
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
715
|
+
Before we dive into a specific example note that there are some components implemented in the `test/dummy/app/components`.
|
716
|
+
|
717
|
+
Try them out by starting Rails:
|
718
|
+
|
719
|
+
```bash
|
720
|
+
cd test/dummy
|
721
|
+
bundle install
|
722
|
+
rails assets:precompile
|
723
|
+
rails s
|
267
724
|
```
|
268
725
|
|
269
|
-
|
726
|
+
and visiting http://localhost:3000
|
270
727
|
|
271
|
-
### Q4. Do you use or want to use [TailwindCSS](https://tailwindcss.com/)?
|
272
728
|
|
273
|
-
|
729
|
+
### A Vident component example (without Stimulus)
|
274
730
|
|
275
|
-
|
276
|
-
...
|
277
|
-
gem 'vident-tailwind'
|
278
|
-
```
|
731
|
+
First is an example component that uses `Vident::ViewComponent::Base` but no Stimulus features.
|
279
732
|
|
280
|
-
|
733
|
+
It is an avatar component that can either be displayed as an image or as initials. It supports numerous sizes and shapes and can optionally have a border. It also generates a cache key for use in fragment caching or etag generation.
|
281
734
|
|
282
|
-
|
735
|
+
```ruby
|
736
|
+
class AvatarComponent < ::Vident::ViewComponent::Base
|
737
|
+
include ::Vident::Tailwind
|
738
|
+
include ::Vident::Caching
|
739
|
+
|
740
|
+
no_stimulus_controller
|
741
|
+
with_cache_key :attributes
|
742
|
+
|
743
|
+
attribute :url, allow_nil: true
|
744
|
+
attribute :initials, allow_nil: false
|
745
|
+
|
746
|
+
attribute :shape, default: :circle
|
747
|
+
|
748
|
+
attribute :border, default: false
|
749
|
+
|
750
|
+
attribute :size, default: :normal
|
751
|
+
|
752
|
+
private
|
753
|
+
|
754
|
+
def default_html_options
|
755
|
+
if image_avatar?
|
756
|
+
{ class: "inline-block object-contain", src: url, alt: t(".image") }
|
757
|
+
else
|
758
|
+
{ class: "inline-flex items-center justify-center bg-gray-500" }
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
def element_classes
|
763
|
+
[size_classes, shape_class, border? ? "border" : ""]
|
764
|
+
end
|
765
|
+
|
766
|
+
alias_method :image_avatar?, :url?
|
767
|
+
|
768
|
+
def shape_class
|
769
|
+
(shape == :circle) ? "rounded-full" : "rounded-md"
|
770
|
+
end
|
771
|
+
|
772
|
+
def size_classes
|
773
|
+
case size
|
774
|
+
when :tiny
|
775
|
+
"w-6 h-6"
|
776
|
+
when :small
|
777
|
+
"w-8 h-8"
|
778
|
+
when :medium
|
779
|
+
"w-12 h-12"
|
780
|
+
when :large
|
781
|
+
"w-14 h-14"
|
782
|
+
when :x_large
|
783
|
+
"sm:w-24 sm:h-24 w-16 h-16"
|
784
|
+
when :xx_large
|
785
|
+
"sm:w-32 sm:h-32 w-24 h-24"
|
786
|
+
else
|
787
|
+
"w-10 h-10"
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
def text_size_class
|
792
|
+
case size
|
793
|
+
when :tiny
|
794
|
+
"text-xs"
|
795
|
+
when :small
|
796
|
+
"text-xs"
|
797
|
+
when :medium
|
798
|
+
"text-lg"
|
799
|
+
when :large
|
800
|
+
"sm:text-xl text-lg"
|
801
|
+
when :extra_large
|
802
|
+
"sm:text-2xl text-xl"
|
803
|
+
else
|
804
|
+
"text-medium"
|
805
|
+
end
|
806
|
+
end
|
807
|
+
end
|
808
|
+
```
|
283
809
|
|
284
|
-
|
810
|
+
```erb
|
811
|
+
<%= render root(
|
812
|
+
element_tag: image_avatar? ? :img : :div,
|
813
|
+
html_options: default_html_options
|
814
|
+
) do %>
|
815
|
+
<% unless image_avatar? %>
|
816
|
+
<span class="<%= text_size_class %> font-medium leading-none text-white"><%= initials %></span>
|
817
|
+
<% end %>
|
818
|
+
<% end %>
|
819
|
+
```
|
285
820
|
|
286
|
-
|
287
|
-
- [`vident-typed`](https://github.com/stevegeek/vident-typed) to define typed attributes for your own view component system
|
821
|
+
Example usages:
|
288
822
|
|
823
|
+
```erb
|
824
|
+
<%= render AvatarComponent.new(url: "https://someurl.com/avatar.jpg", initials: "AB" size: :large) %>
|
825
|
+
<%= render AvatarComponent.new(url: "https://someurl.com/avatar.jpg", html_options: {alt: "My alt text", class: "object-scale-down"}) %>
|
826
|
+
<%= render AvatarComponent.new(initials: "SG", size: :small) %>
|
827
|
+
<%= render AvatarComponent.new(initials: "SG", size: :large, html_options: {class: "border-2 border-red-600"}) %>
|
828
|
+
```
|
289
829
|
|
290
|
-
|
830
|
+
The following is rendered when used `render AvatarComponent.new(initials: "SG", size: :small, border: true)`:
|
291
831
|
|
292
|
-
|
832
|
+
```html
|
833
|
+
<div class="avatar-component w-8 h-8 rounded-full border inline-flex items-center justify-center bg-gray-500" id="avatar-component-9790427-12">
|
834
|
+
<span class="text-xs font-medium leading-none text-white">SG</span>
|
835
|
+
</div>
|
836
|
+
```
|
293
837
|
|
294
|
-
|
838
|
+
The following is rendered when used `render AvatarComponent.new(url: "https://i.pravatar.cc/300", initials: "AB", html_options: {alt: "My alt text", class: "block"})`:
|
295
839
|
|
296
|
-
|
840
|
+
```html
|
841
|
+
<img src="https://i.pravatar.cc/300" alt="My alt text" class="avatar-component w-10 h-10 rounded-full object-contain block" id="avatar-component-7083941-11">
|
842
|
+
```
|
297
843
|
|
298
|
-
|
844
|
+
----
|
299
845
|
|
300
|
-
|
846
|
+

|
301
847
|
|
302
|
-
|
848
|
+
### Usage
|
849
|
+
How to use my plugin.
|
303
850
|
|
304
|
-
|
851
|
+
### Installation
|
852
|
+
Add this line to your application's Gemfile:
|
305
853
|
|
306
|
-
|
854
|
+
```ruby
|
855
|
+
gem "vident-view_component"
|
856
|
+
```
|
307
857
|
|
308
|
-
|
858
|
+
And then execute:
|
859
|
+
```bash
|
860
|
+
$ bundle
|
861
|
+
```
|
309
862
|
|
310
|
-
|
863
|
+
Or install it yourself as:
|
864
|
+
```bash
|
865
|
+
$ gem install vident-view_component
|
866
|
+
```
|