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.
data/README.md CHANGED
@@ -1,76 +1,448 @@
1
1
  # Vident
2
2
 
3
- **Vident** is a collection of gems that help you create **flexible** & **maintainable** component libraries for your Rails application.
3
+ Vident is a collection of gems that provide a set of tools for building web applications with Ruby on Rails.
4
4
 
5
- <a href="https://github.com/stevegeek/vident"><img alt="Vident logo" src="https://raw.githubusercontent.com/stevegeek/vident/main/docs/images/logo-by-sd-256-colors.png" width="180" /></a>
5
+ ## Included Gems
6
6
 
7
- Vident also provides a neat Ruby DSL to make wiring up **Stimulus easier & less error prone** in your view components.
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
- [`ViewComponent`](https://viewcomponent.org/) and [`Phlex`](https://phlex.fun) supported.
18
+ ## Directory Structure
10
19
 
11
- # Motivation
20
+ The repository is structured like this:
12
21
 
13
- I love working with Stimulus, but I find manually crafting the data attributes for
14
- targets and actions error-prone and tedious. Vident aims to make this process easier
15
- and keep me thinking in Ruby.
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
- Vident has been used with `ViewComponent` and `Phlex` in production apps for a while now
18
- but is still evolving.
57
+ ### Running Tests
19
58
 
20
- I would love to get your feedback and contributions!
59
+ To run tests for all gems:
21
60
 
22
- ## Example
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
- The Greeter ViewComponent (that uses Vident):
71
+ ### Building and Installing Gems
25
72
 
26
- ![docs/images/ex1.gif](docs/images/ex1.gif)
73
+ To build all gems:
27
74
 
28
- Consider a component, the `GreeterComponent`:
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
- # app/components/greeter_component.rb
110
+ BetterHtml.config = BetterHtml::Config.new(YAML.load(File.read(".better-html.yml")))
32
111
 
33
- class GreeterComponent < ::Vident::ViewComponent::Base
34
- renders_one :trigger, ButtonComponent
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
- with ERB as follows:
126
+ ### Installation
127
+ Add this line to your application's Gemfile:
39
128
 
40
- ```erb
41
- <%# app/components/greeter_component.html.erb %>
129
+ ```ruby
130
+ gem "vident-better_html"
131
+ ```
42
132
 
43
- <%# Rendering the `root` element creates a tag which has stimulus `data-*`s, a unique id & other attributes set. %>
44
- <%# The stimulus controller name (identifier) is derived from the component name, and then used to generate the relavent data attribute names. %>
133
+ And then execute:
134
+ ```bash
135
+ $ bundle
136
+ ```
45
137
 
46
- <%= render root named_classes: {
47
- pre_click: "text-md text-gray-500", # named classes are exposed to Stimulus as `data-<controller>-<name>-class` attributes
48
- post_click: "text-xl text-blue-700",
49
- html_options: {class: "py-2"}
50
- } do |greeter| %>
51
- <%# `greeter` is the root element and exposes methods to generate stimulus targets and actions %>
52
- <input type="text"
53
- <%= greeter.as_target(:name) %>
54
- class="shadow appearance-none border rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
55
-
56
- <%# Render the slot %>
57
- <%= trigger %>
58
-
59
- <%# you can also use the `target_tag` helper to render targets %>
60
- <%= greeter.target_tag(
61
- :span,
62
- :output,
63
- # Stimulus named classes can be referenced to set class attributes at render time
64
- class: "ml-4 #{greeter.named_classes(:pre_click)}"
65
- ) do %>
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
- Now, imagine we render it in a view, and render a `ButtonComponent` in the `trigger` slot:
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
+ ![Example](examples/avatar.png)
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
+ ![ex1.gif](examples/ex1.gif)
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
- To see this example in more detail, see the [vident-typed-view_component](https://github.com/stevegeek/vident-typed-view_component/tree/main/test/dummy/app/components) test dummy app.
495
+ Let's look at the components in more detail.
122
496
 
123
- # Vident is a collection of gems
497
+ The main component is the `GreeterComponent`:
124
498
 
125
- The core gems are:
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
- Gems that provide support for `ViewComponent` and `Phlex`:
502
+ class GreeterComponent < ::Vident::ViewComponent::Base
503
+ renders_one :trigger, ButtonComponent
504
+ end
505
+ ```
131
506
 
132
- - [`vident-view_component`](https://github.com/stevegeek/vident-view_component) for using with `ViewComponent` and untyped attributes
133
- - [`vident-typed-view_component`](https://github.com/stevegeek/vident-typed-view_component) for using with `ViewComponent` and typed attributes
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
- There is also:
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
- - [`vident-typed-minitest`](https://github.com/stevegeek/vident-typed-minitest) to get some test helpers for typed attributes (auto generates inputs to test attributes)
140
- - [`vident-better_html`](https://github.com/stevegeek/vident-better_html) to support `better_html` if you use it in your Rails app
141
- - [`vident-tailwind`](https://github.com/stevegeek/vident-tailwind) to get all the benefits of the amazing [`tailwind_merge`](https://github.com/gjtorikian/tailwind_merge/).
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
- # Things still to do...
538
+ ```
145
539
 
146
- This is a work in progress. Here's what's left to do for first release:
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
- - Iterate on the interfaces and functionality
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
- # About Vident
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
- ## What does Vident provide?
608
+ ```js
609
+ // app/components/button_component_controller.js
158
610
 
159
- - Base classes for your `ViewComponent` components or `Phlex` components that provides a helper to create the
160
- all important 'root' element component (can be used with templated or template-less components).
611
+ import { Controller } from "@hotwired/stimulus"
161
612
 
162
- - implementations of these root components for creating the 'root' element in your view components. Similar to `Primer::BaseComponent` but
163
- exposes a simple API for configuring and adding Stimulus controllers, targets and actions. The root component also handles deduplication
164
- of classes, creating a unique ID, setting the element tag type, handling possible overrides set at the render site, and determining stimulus controller identifiers etc
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
- - a way to define attributes for your components, either typed or untyped, with default values and optional validation.
622
+ ```
167
623
 
168
- ### Various utilities
624
+ ### Usage
625
+ How to use my plugin.
169
626
 
170
- Such as...
627
+ ### Installation
628
+ Add this line to your application's Gemfile:
171
629
 
172
- - for Taiwind users, a mixin for your vident component which uses [tailwind_merge](https://github.com/gjtorikian/tailwind_merge) to merge TailwindCSS classes
173
- so you can easily override classes when rendering a component.
174
- - a mixin for your Vident Components which provides a `#cache_key` method that can be used to generate a cache key for
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
- ## All the Features...
634
+ And then execute:
635
+ ```bash
636
+ $ bundle
637
+ ```
179
638
 
180
- - use Vident with `ViewComponent` or `Phlex` or your own view component system
181
- - A helper to create the root HTML element for your component, which then handles creation of attributes.
182
- - Component arguments are defined using the `attribute` method which allows you to define default values, (optionally) types and
183
- if blank or nil values should be allowed.
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
- ## Installation
646
+ ## gem: vident-typed
199
647
 
200
- This gem (`vident`) provides only base functionality but there are a number of gems that provide additional functionality
201
- or an "out of the box" experience.
648
+ # Vident::Typed
649
+ Short description and motivation.
202
650
 
203
- It's a "pick your own adventure" approach. You decide what frameworks and features you want to use
204
- and add the gems as needed.
651
+ ### Usage
652
+ How to use my plugin.
205
653
 
206
- First, add this line to your application's Gemfile:
654
+ ### Installation
655
+ Add this line to your application's Gemfile:
207
656
 
208
- ```ruby
209
- gem 'vident'
657
+ ```ruby
658
+ gem "vident-typed"
210
659
  ```
211
660
 
212
- Then go on to choose the gems you want to use:
213
-
214
- #### Q1. Do you want to use [`ViewComponent`](https://viewcomponent.org/) or [`Phlex`](https://www.phlex.fun/) for your view components?
215
-
216
- For ViewComponent use:
661
+ And then execute:
662
+ ```bash
663
+ $ bundle
664
+ ```
217
665
 
218
- - [`vident-view_component`](https://github.com/stevegeek/vident-view_component)
666
+ Or install it yourself as:
667
+ ```bash
668
+ $ gem install vident-typed
669
+ ```
219
670
 
220
- For Phlex use:
671
+ ---
221
672
 
222
- - [`vident-phlex`](https://github.com/stevegeek/vident-phlex)
673
+ ## gem: vident-view_component-caching
223
674
 
675
+ # Vident::ViewComponent::Caching
676
+ Short description and motivation.
224
677
 
225
- Note: you can also use both in the same app.
678
+ ### Usage
679
+ How to use my plugin.
226
680
 
227
- For example, if you want to use ViewComponent and Phlex in the same app, you might end up with:
681
+ ### Installation
682
+ Add this line to your application's Gemfile:
228
683
 
229
684
  ```ruby
230
- gem 'vident'
231
- gem 'vident-view_component'
232
- gem 'vident-phlex'
685
+ gem "vident-view_component-caching"
233
686
  ```
234
687
 
235
- #### Q2. Do you want to build components where the attributes have runtime type checking (powered by [`dry-types`](https://github.com/dry-rb/dry-types))?
236
-
237
- If yes, then add `vident-typed` to your Gemfile:
688
+ And then execute:
689
+ ```bash
690
+ $ bundle
691
+ ```
238
692
 
239
- ```ruby
240
- gem 'vident-typed'
693
+ Or install it yourself as:
694
+ ```bash
695
+ $ gem install vident-view_component-caching
241
696
  ```
242
697
 
243
- and then use the relavent `*-typed-*` gems for your chosen view component system:
698
+ ---
244
699
 
245
- - use [`vident-typed-view_component`](https://github.com/stevegeek/vident-typed-view_component)
246
- - and/or [`vident-typed-phlex`](https://github.com/stevegeek/vident-typed-phlex)
700
+ ## gem: vident-view_component
247
701
 
248
- Note you must also include the gem for the view component system you are using.
702
+ # Vident::ViewComponent
249
703
 
250
- For example, for ViewComponent, you might end up with:
704
+ [ViewComponent](https://viewcomponent.org/) powered [Vident](https://github.com/stevegeek/vident) components.
251
705
 
252
706
  ```ruby
253
- gem 'vident'
254
- gem 'vident-view_component'
255
- gem 'vident-typed'
256
- gem 'vident-typed-view_component'
707
+ class ApplicationComponent < ::Vident::ViewComponent::Base
708
+ end
257
709
  ```
258
710
 
259
- #### Q3. Do you use or want to use [BetterHTML](https://github.com/Shopify/better-html) in your Rails project?
711
+ For more details see [vident](https://github.com/stevegeek/vident).
260
712
 
261
- If yes, then include [`vident-better_html`](https://github.com/stevegeek/vident-better_html) in your Gemfile alongside `better_html` and your vident gems of choice.
713
+ ### Examples
262
714
 
263
- ```ruby
264
- ...
265
- gem 'better_html'
266
- gem 'vident-better_html'
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
- Note that `vident-better_html` automatically enables `better_html` support in Vident root components.
726
+ and visiting http://localhost:3000
270
727
 
271
- ### Q4. Do you use or want to use [TailwindCSS](https://tailwindcss.com/)?
272
728
 
273
- If yes, then consider adding [`vident-tailwind`](https://github.com/stevegeek/vident-tailwind) to your Gemfile alongside your vident gems of choice.
729
+ ### A Vident component example (without Stimulus)
274
730
 
275
- ```ruby
276
- ...
277
- gem 'vident-tailwind'
278
- ```
731
+ First is an example component that uses `Vident::ViewComponent::Base` but no Stimulus features.
279
732
 
280
- When creating your components you can then include `Vident::Tailwind` to get all the benefits of the amazing [`tailwind_merge`](https://github.com/gjtorikian/tailwind_merge/).
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
- ### Q5. Did none of the above gems suit your needs?
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
- You can always just use base `vident` gems and then roll your own solutions:
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
- - [`vident`](https://github.com/stevegeek/vident) to get the base functionality to mix with your own view component system
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
- ## Documentation
830
+ The following is rendered when used `render AvatarComponent.new(initials: "SG", size: :small, border: true)`:
291
831
 
292
- See the [docs](docs/) directory and visit the individual gem pages for more information.
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
- ## Development
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
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
844
+ ----
299
845
 
300
- ## Contributing
846
+ ![Example](examples/avatar.png)
301
847
 
302
- Bug reports and pull requests are welcome on GitHub at https://github.com/stevegeek/vident. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/vident/blob/master/CODE_OF_CONDUCT.md).
848
+ ### Usage
849
+ How to use my plugin.
303
850
 
304
- ## License
851
+ ### Installation
852
+ Add this line to your application's Gemfile:
305
853
 
306
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
854
+ ```ruby
855
+ gem "vident-view_component"
856
+ ```
307
857
 
308
- ## Code of Conduct
858
+ And then execute:
859
+ ```bash
860
+ $ bundle
861
+ ```
309
862
 
310
- Everyone interacting in the Vident project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/vident/blob/master/CODE_OF_CONDUCT.md).
863
+ Or install it yourself as:
864
+ ```bash
865
+ $ gem install vident-view_component
866
+ ```