vident 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 617bb447fa9a255a73fa4f70de7da9df8a0bf8b3f8a0528a32d01dc39780d30b
4
- data.tar.gz: d8898e7b5fcf959bc5fcacf46177d9ae550fbddd01e44368f26f1faa5bab916f
3
+ metadata.gz: fec9413d88c42202dce9386932c39b6caee8add4a12bc3983447226785da0e3c
4
+ data.tar.gz: aaf6f4626cd32a21ed12b372a1d340e5a8056c37e20e059052053a0ce56a59cb
5
5
  SHA512:
6
- metadata.gz: 7fedc5b84b8c99f5deb998378e2818519448375345e25857389866387ae2547d8ed5e8f9a1c99d33073d75d9ef9f5e89087b4b3aaefd01ba0df855e750da71be
7
- data.tar.gz: d14be93998560999bc3d84e0e59dbdb28f1a1827abde538363f03423121e4113abf40fcbaf598a8d872344b5531568e58e23a216c089e3c403a60b02625d3107
6
+ metadata.gz: 26fc4e800be45c42a08ea263d7d26bac7f47d987044525d2da3dee52ceed5e799cb1a1372ca94a0d039961702b252d7ddd42c5f387477888fb8e09e12ff06fcd
7
+ data.tar.gz: 3cf469fcdc2851ea4da8dd683b47642aacd7dadce53b77a0a450d882a7b61a1897054386ac213f756ea6be2784afd5d4afeed348a7b58fc818daaadf418b933a
data/CHANGELOG.md CHANGED
@@ -13,6 +13,23 @@ 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
+
26
+ ## [0.6.3] - 2023-03-03
27
+
28
+ ### Fixed
29
+
30
+ - Fix for changes to HTML tag collection in Phlex
31
+
32
+
16
33
  ## [0.6.2] - 2023-02-23
17
34
 
18
35
  ### 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,7 +11,10 @@ if Gem.loaded_specs.has_key? "phlex"
11
11
  include UsingBetterHTML
12
12
  end
13
13
 
14
- VALID_TAGS = Set[*(Phlex::HTML::VOID_ELEMENTS.keys + Phlex::HTML::STANDARD_ELEMENTS.keys)].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
15
18
 
16
19
  # Create a tag for a target with a block containing content
17
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.2"
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.2
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-02-23 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