vident 0.6.3 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34159659a1af3114685b2170e3c2f9667f1b87cc1a03082f99abb45d2b86bec4
4
- data.tar.gz: 4bf98392df9f0f4c531930bcd26245c91287d48065941374eb4715ccfa15d439
3
+ metadata.gz: fec9413d88c42202dce9386932c39b6caee8add4a12bc3983447226785da0e3c
4
+ data.tar.gz: aaf6f4626cd32a21ed12b372a1d340e5a8056c37e20e059052053a0ce56a59cb
5
5
  SHA512:
6
- metadata.gz: d5826f1ccef2cbfec3fe8fbf717e81441c438c5091d0ff60ff48a84272700bef7c4fdc33b9f51e4a6293cecae8d0d59802b03667d11eb8fd3c69ff25ca88d552
7
- data.tar.gz: bd33d01fef040b09f5ca97a259d4e2f035d4fb4aead3c7a4115f758423ec247b676b8f37eee5b1a96fe8ba2dbb20dd0498c9e68fb2de972f3dd93dc9eca7885a
6
+ metadata.gz: 26fc4e800be45c42a08ea263d7d26bac7f47d987044525d2da3dee52ceed5e799cb1a1372ca94a0d039961702b252d7ddd42c5f387477888fb8e09e12ff06fcd
7
+ data.tar.gz: 3cf469fcdc2851ea4da8dd683b47642aacd7dadce53b77a0a450d882a7b61a1897054386ac213f756ea6be2784afd5d4afeed348a7b58fc818daaadf418b933a
data/CHANGELOG.md CHANGED
@@ -13,6 +13,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
13
13
 
14
14
  ### Fixed
15
15
 
16
+ ## [0.7.0] - 2023-03-08
17
+
18
+ ### Added
19
+
20
+ - new `Vident::Tailwind` module which uses [tailwind_merge](https://github.com/gjtorikian/tailwind_merge) to merge TailwindCSS classes
21
+
22
+ ### Changed
23
+
24
+ - Removed a dependency on intenal constants from `phlex`
25
+
16
26
  ## [0.6.3] - 2023-03-03
17
27
 
18
28
  ### Fixed
data/Gemfile CHANGED
@@ -19,6 +19,7 @@ gem "dry-struct"
19
19
 
20
20
  gem "phlex-rails"
21
21
  gem "better_html"
22
+ gem "tailwind_merge"
22
23
 
23
24
  # FIXME: versions greater than 2.74 cause issues: https://github.com/ViewComponent/view_component/pull/1571
24
25
  gem "view_component", "2.74.1"
data/README.md CHANGED
@@ -4,19 +4,6 @@ Vident helps you create flexible & maintainable component libraries for your app
4
4
 
5
5
  Vident makes using Stimulus with your [`ViewComponent`](https://viewcomponent.org/) or [`Phlex`](https://phlex.fun) components easier.
6
6
 
7
- ## Things still to do...
8
-
9
- This is a work in progress. Here's what's left to do for first release:
10
-
11
- - Iterate on the interfaces and functionality
12
- - Add tests
13
- - Make the gem more configurable to fit more use cases
14
- - Create an example library of a few components for some design system
15
- - Create a demo app with `lookbook` and those components
16
- - Add more documentation
17
- - split `vident` into `vident` + `vident-rails` gems (and maybe `vident-rspec`) (Phlex can be used outside of Rails)
18
- - possibly also split into `vident-phlex` and `vident-view_component` gems ?
19
-
20
7
  # Motivation
21
8
 
22
9
  I love working with Stimulus, but I find manually crafting the data attributes for
@@ -35,10 +22,51 @@ This gem is a work in progress and I would love to get your feedback and contrib
35
22
 
36
23
  - `Vident::TypedComponent`: like `Vident::Component` but uses `dry-types` to define typed attributes for your components.
37
24
 
25
+ ### Various utilities
26
+
27
+ - `Vident::Tailwind`: a mixin for your vident component which uses [tailwind_merge](https://github.com/gjtorikian/tailwind_merge) to merge TailwindCSS classes
28
+ so you can easily override classes when rendering a component.
29
+
30
+ - `Vident::Caching::CacheKey`: a mixin for your vident component which provides a `cache_key` method that can be used to generate a cache key for
31
+ fragment caching or etag generation.
32
+
38
33
  - `Vident::RootComponent::*` which are components for creating the 'root' element in your view components. Similar to `Primer::BaseComponent` but
39
34
  exposes a simple API for configuring and adding Stimulus controllers, targets and actions. Normally you create these
40
35
  using the `root` helper method on `Vident::Component`/`Vident::TypedComponent`.
41
36
 
37
+ # Features
38
+
39
+ - A helper to create the root HTML element for your component, which then handles creation of attributes.
40
+ - Component arguments are defined using the `attribute` method which allows you to define default values, (optionally) types and
41
+ if blank or nil values should be allowed.
42
+ - You can use the same component in multiple contexts and configure the root element differently in each context by passing
43
+ options to the component when instantiating it.
44
+ - Stimulus support is built in and sets a default controller name based on the component name.
45
+ - Stimulus actions, targets and classes can be setup using a simple DSL to avoid hand crafting the data attributes.
46
+ - Since data attribute names are generated from the component class name, you can rename easily refactor and move components without
47
+ having to update the data attributes in your views.
48
+ - Components are rendered with useful class names and IDs to make debugging easier (autogenerated IDs are 'random' but deterministic so they
49
+ are the same each time a given view is rendered to avoid content changing/Etag changing).
50
+ - (experimental) Support for fragment caching of components (only with ViewComponent and with caveats)
51
+ - (experimental) A test helper to make testing components easier by utilising type information from the component arguments to render
52
+ automatically configured good and bad examples of the component.
53
+ - (experimental) support for `better_html`
54
+
55
+
56
+ ## Things still to do...
57
+
58
+ This is a work in progress. Here's what's left to do for first release:
59
+
60
+ - Iterate on the interfaces and functionality
61
+ - Add tests
62
+ - Make the gem more configurable to fit more use cases
63
+ - Create an example library of a few components for some design system
64
+ - Create a demo app with `lookbook` and those components
65
+ - Add more documentation
66
+ - split `vident` into `vident` + `vident-rails` gems (and maybe `vident-rspec`) (Phlex can be used outside of Rails)
67
+ - possibly also split into `vident-phlex` and `vident-view_component` gems ?
68
+
69
+
42
70
 
43
71
  # Examples
44
72
 
@@ -58,7 +86,141 @@ rails s
58
86
 
59
87
  and visit http://localhost:3000
60
88
 
61
- ## ViewComponent + Vident example
89
+
90
+ ## A Vident component example (without Stimulus)
91
+
92
+ First is an example component that uses `Vident::TypedComponent` but no Stimulus features.
93
+
94
+ It is an avatar component that can either be displayed as an image or as initials.
95
+
96
+ 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.
97
+
98
+ ```ruby
99
+ class AvatarComponent < ViewComponent::Base
100
+ include ::Vident::TypedComponent
101
+ include ::Vident::Tailwind
102
+ include ::Vident::Caching::CacheKey
103
+
104
+ no_stimulus_controller
105
+ with_cache_key :attributes
106
+
107
+ attribute :url, String, allow_nil: true, allow_blank: false
108
+ attribute :initials, String, allow_blank: false
109
+
110
+ attribute :shape, Symbol, in: %i[circle square], default: :circle
111
+
112
+ attribute :border, :boolean, default: false
113
+
114
+ attribute :size, Symbol, in: %i[tiny small normal medium large x_large xx_large], default: :normal
115
+
116
+ private
117
+
118
+ def html_options
119
+ if image_avatar?
120
+ { class: "inline-block object-contain", src: url, alt: t(".image") }
121
+ else
122
+ { class: "inline-flex items-center justify-center bg-gray-500" }
123
+ end
124
+ end
125
+
126
+ def element_classes
127
+ [size_classes, shape_class, border? ? "border" : ""]
128
+ end
129
+
130
+ alias_method :image_avatar?, :url?
131
+
132
+ def shape_class
133
+ (shape == :circle) ? "rounded-full" : "rounded-md"
134
+ end
135
+
136
+ def size_classes
137
+ case size
138
+ when :tiny
139
+ "w-6 h-6"
140
+ when :small
141
+ "w-8 h-8"
142
+ when :medium
143
+ "w-12 h-12"
144
+ when :large
145
+ "w-14 h-14"
146
+ when :x_large
147
+ "sm:w-24 sm:h-24 w-16 h-16"
148
+ when :xx_large
149
+ "sm:w-32 sm:h-32 w-24 h-24"
150
+ else
151
+ "w-10 h-10"
152
+ end
153
+ end
154
+
155
+ def text_size_class
156
+ case size
157
+ when :tiny
158
+ "text-xs"
159
+ when :small
160
+ "text-xs"
161
+ when :medium
162
+ "text-lg"
163
+ when :large
164
+ "sm:text-xl text-lg"
165
+ when :extra_large
166
+ "sm:text-2xl text-xl"
167
+ else
168
+ "text-medium"
169
+ end
170
+ end
171
+ end
172
+ ```
173
+
174
+ ```erb
175
+ <%= render root(
176
+ element_tag: image_avatar? ? :img : :div,
177
+ html_options: html_options
178
+ ) do %>
179
+ <% unless image_avatar? %>
180
+ <span class="<%= text_size_class %> font-medium leading-none text-white"><%= initials %></span>
181
+ <% end %>
182
+ <% end %>
183
+
184
+ ```
185
+
186
+ Example usages:
187
+
188
+ ```erb
189
+ <!-- These will render -->
190
+ <%= render AvatarComponent.new(url: "https://someurl.com/avatar.jpg", initials: "AB" size: :large) %>
191
+ <%= render AvatarComponent.new(url: "https://someurl.com/avatar.jpg", html_options: {alt: "My alt text", class: "object-scale-down"}) %>
192
+ <%= render AvatarComponent.new(initials: "SG", size: :small) %>
193
+ <%= render AvatarComponent.new(initials: "SG", size: :large, html_options: {class: "border-2 border-red-600"}) %>
194
+
195
+ <!-- These will raise an error -->
196
+ <!-- missing initals -->
197
+ <%= render AvatarComponent.new(url: "https://someurl.com/avatar.jpg", size: :large) %>
198
+ <!-- initials blank -->
199
+ <%= render AvatarComponent.new(initials: "", size: :large) %>
200
+ <!-- invalid size -->
201
+ <%= render AvatarComponent.new(initials: "SG", size: :foo_bar) %>
202
+ ```
203
+
204
+
205
+ The following is rendered when used `render AvatarComponent.new(initials: "SG", size: :small, border: true)`:
206
+
207
+ ```html
208
+ <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">
209
+ <span class="text-xs font-medium leading-none text-white">SG</span>
210
+ </div>
211
+ ```
212
+
213
+ 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"})`:
214
+
215
+ ```html
216
+ <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">
217
+ ```
218
+
219
+ ----
220
+
221
+ ![Example](examples/avatar.png)
222
+
223
+ ## Another ViewComponent + Vident example with Stimulus
62
224
 
63
225
  Consider the following ERB that might be part of an application's views. The app uses `ViewComponent`, `Stimulus` and `Vident`.
64
226
 
@@ -73,7 +235,7 @@ used to greet the user. At the same time the button changes to be a 'reset' butt
73
235
  <!-- ... -->
74
236
 
75
237
  <!-- render the Greeter ViewComponent (that uses Vident) -->
76
- <%= render ::GreeterComponent.new do |greeter| %>
238
+ <%= render ::GreeterComponent.new(cta: "Hey!", html_options: {class: "my-4"}) do |greeter| %>
77
239
  <%# this component has a slot called `trigger` that renders a `ButtonComponent` (which also uses Vident) %>
78
240
  <% greeter.trigger(
79
241
 
@@ -100,7 +262,7 @@ used to greet the user. At the same time the button changes to be a 'reset' butt
100
262
  The output HTML of the above, using Vident, is:
101
263
 
102
264
  ```html
103
- <div class="greeter-component"
265
+ <div class="greeter-component py-2 my-4"
104
266
  data-controller="greeter-component"
105
267
  data-greeter-component-pre-click-class="text-md text-gray-500"
106
268
  data-greeter-component-post-click-class="text-xl text-blue-700"
@@ -113,7 +275,7 @@ The output HTML of the above, using Vident, is:
113
275
  data-action="click->greeter-component#greet button-component#changeMessage"
114
276
  data-button-component-after-clicked-message="Greeted! Reset?"
115
277
  data-button-component-before-clicked-message="Greet"
116
- id="button-component-7799479-7">Greet</button>
278
+ id="button-component-7799479-7">Hey!</button>
117
279
  <!-- you can also use the `target_tag` helper to render targets -->
118
280
  <span class="ml-4 text-md text-gray-500"
119
281
  data-greeter-component-target="output">
@@ -144,7 +306,8 @@ end
144
306
 
145
307
  <%= render root named_classes: {
146
308
  pre_click: "text-md text-gray-500", # named classes are exposed to Stimulus as `data-<controller>-<name>-class` attributes
147
- post_click: "text-xl text-blue-700"
309
+ post_click: "text-xl text-blue-700",
310
+ html_options: {class: "py-2"}
148
311
  } do |greeter| %>
149
312
 
150
313
  <%# `greeter` is the root element and exposes methods to generate stimulus targets and actions %>
Binary file
@@ -7,7 +7,7 @@ if Gem.loaded_specs.has_key? "dry-struct"
7
7
  require_relative "./typed_niling_struct"
8
8
 
9
9
  module Vident
10
- # Adapts Dry Types to confinus Typed Attributes. We use dry-struct (see ::Core::NilingStruct) but
10
+ # Adapts Dry Types to confinus Typed Attributes. We use dry-struct (see TypedNilingStruct) but
11
11
  # we could probably also use dry-initializer directly, saving us from maintaining the schema.
12
12
  module Attributes
13
13
  module Typed
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Rails fragment caching works by either expecting the cached key object to respond to `cache_key` or for that object
4
- # to be an array or hash. In our case the object maybe an instance of Core::Presenter so here we add a default
5
- # `cache_key` implementation.
4
+ # to be an array or hash.
6
5
  module Vident
7
6
  module Caching
8
7
  module CacheKey
@@ -11,9 +11,10 @@ if Gem.loaded_specs.has_key? "phlex"
11
11
  include UsingBetterHTML
12
12
  end
13
13
 
14
- VALID_TAGS = Set[
15
- *(Phlex::HTML::VoidElements::REGISTERED_ELEMENTS.keys + Phlex::HTML::StandardElements::REGISTERED_ELEMENTS.keys)
16
- ].freeze
14
+ STANDARD_ELEMENTS = [:a, :abbr, :address, :article, :aside, :b, :bdi, :bdo, :blockquote, :body, :button, :caption, :cite, :code, :colgroup, :data, :datalist, :dd, :del, :details, :dfn, :dialog, :div, :dl, :dt, :em, :fieldset, :figcaption, :figure, :footer, :form, :g, :h1, :h2, :h3, :h4, :h5, :h6, :head, :header, :hgroup, :html, :i, :iframe, :ins, :kbd, :label, :legend, :li, :main, :map, :mark, :menuitem, :meter, :nav, :noscript, :object, :ol, :optgroup, :option, :output, :p, :path, :picture, :pre, :progress, :q, :rp, :rt, :ruby, :s, :samp, :script, :section, :select, :slot, :small, :span, :strong, :style, :sub, :summary, :sup, :svg, :table, :tbody, :td, :template_tag, :textarea, :tfoot, :th, :thead, :time, :title, :tr, :u, :ul, :video, :wbr].freeze
15
+ VOID_ELEMENTS = [:area, :br, :embed, :hr, :img, :input, :link, :meta, :param, :source, :track, :col].freeze
16
+
17
+ VALID_TAGS = Set[*(STANDARD_ELEMENTS + VOID_ELEMENTS)].freeze
17
18
 
18
19
  # Create a tag for a target with a block containing content
19
20
  def target_tag(tag_name, targets, **options, &block)
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ module Vident
5
+ module Tailwind
6
+ # Adds a utility class merge specifically for Tailwind, allowing you to more easily specify class overrides
7
+ # without having to worry about the specificity of the classes.
8
+ def produce_style_classes(class_names)
9
+ ::TailwindMerge::Merger.new.merge(dedupe_view_component_classes(class_names))
10
+ end
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vident
4
- VERSION = "0.6.3"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/vident.rb CHANGED
@@ -33,7 +33,7 @@ require_relative "vident/base"
33
33
  require_relative "vident/component"
34
34
  require_relative "vident/typed_component"
35
35
  require_relative "vident/caching/cache_key"
36
-
36
+ require_relative "vident/tailwind" if Gem.loaded_specs.has_key? "tailwind_merge"
37
37
  require_relative "vident/testing/attributes_tester"
38
38
  require_relative "vident/testing/auto_test"
39
39
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vident
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Ierodiaconou
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-03 00:00:00.000000000 Z
11
+ date: 2023-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -47,6 +47,7 @@ files:
47
47
  - LICENSE.txt
48
48
  - README.md
49
49
  - Rakefile
50
+ - examples/avatar.png
50
51
  - examples/ex1.gif
51
52
  - lib/tasks/vident.rake
52
53
  - lib/vident.rb
@@ -63,6 +64,7 @@ files:
63
64
  - lib/vident/root_component/using_phlex_html.rb
64
65
  - lib/vident/root_component/using_view_component.rb
65
66
  - lib/vident/stable_id.rb
67
+ - lib/vident/tailwind.rb
66
68
  - lib/vident/test_case.rb
67
69
  - lib/vident/testing/attributes_tester.rb
68
70
  - lib/vident/testing/auto_test.rb