vident 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.standard.yml +3 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +29 -0
- data/LICENSE.txt +21 -0
- data/README.md +349 -0
- data/Rakefile +14 -0
- data/examples/ex1.gif +0 -0
- data/lib/tasks/vident.rake +37 -0
- data/lib/vident/attributes/not_typed.rb +78 -0
- data/lib/vident/attributes/typed.rb +180 -0
- data/lib/vident/attributes/typed_niling_struct.rb +27 -0
- data/lib/vident/attributes/types.rb +16 -0
- data/lib/vident/base.rb +286 -0
- data/lib/vident/component.rb +32 -0
- data/lib/vident/railtie.rb +10 -0
- data/lib/vident/root_component/base.rb +239 -0
- data/lib/vident/root_component/using_phlex_html.rb +44 -0
- data/lib/vident/root_component/using_view_component.rb +44 -0
- data/lib/vident/stable_id.rb +24 -0
- data/lib/vident/typed_component.rb +48 -0
- data/lib/vident/version.rb +5 -0
- data/lib/vident.rb +33 -0
- data/sig/vident.rbs +4 -0
- metadata +85 -0
@@ -0,0 +1,239 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vident
|
4
|
+
module RootComponent
|
5
|
+
module Base
|
6
|
+
def initialize(
|
7
|
+
controllers: nil,
|
8
|
+
actions: nil,
|
9
|
+
targets: nil,
|
10
|
+
named_classes: nil, # https://stimulus.hotwired.dev/reference/css-classes
|
11
|
+
data_maps: nil,
|
12
|
+
element_tag: nil,
|
13
|
+
id: nil,
|
14
|
+
html_options: nil
|
15
|
+
)
|
16
|
+
@element_tag = element_tag
|
17
|
+
@html_options = html_options
|
18
|
+
@id = id
|
19
|
+
@controllers = Array.wrap(controllers)
|
20
|
+
@actions = actions
|
21
|
+
@targets = targets
|
22
|
+
@named_classes = named_classes
|
23
|
+
@data_map_kvs = {}
|
24
|
+
@data_maps = data_maps
|
25
|
+
end
|
26
|
+
|
27
|
+
# The view component's helpers for setting stimulus data-* attributes on this component.
|
28
|
+
|
29
|
+
# TODO: rename
|
30
|
+
# Create a Stimulus action string, and returns it
|
31
|
+
# examples:
|
32
|
+
# action(:my_thing) => "current_controller#myThing"
|
33
|
+
# action(:click, :my_thing) => "click->current_controller#myThing"
|
34
|
+
# action("click->current_controller#myThing") => "click->current_controller#myThing"
|
35
|
+
# action("path/to/current", :my_thing) => "path--to--current_controller#myThing"
|
36
|
+
# action(:click, "path/to/current", :my_thing) => "click->path--to--current_controller#myThing"
|
37
|
+
def action(*args)
|
38
|
+
part1, part2, part3 = args
|
39
|
+
(args.size == 1) ? parse_action_arg(part1) : parse_multiple_action_args(part1, part2, part3)
|
40
|
+
end
|
41
|
+
|
42
|
+
def action_data_attribute(*actions)
|
43
|
+
{action: parse_actions(actions).join(" ")}
|
44
|
+
end
|
45
|
+
|
46
|
+
# TODO: rename & make stimulus Target class instance and returns it, which can convert to String
|
47
|
+
# Create a Stimulus Target and returns it
|
48
|
+
# examples:
|
49
|
+
# target(:my_target) => {controller: 'current_controller' name: 'myTarget'}
|
50
|
+
# target("path/to/current", :my_target) => {controller: 'path--to--current_controller', name: 'myTarget'}
|
51
|
+
def target(name, part2 = nil)
|
52
|
+
if part2.nil?
|
53
|
+
{controller: implied_controller_name, name: js_name(name)}
|
54
|
+
else
|
55
|
+
{controller: stimulize_path(name), name: js_name(part2)}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def target_data_attribute(name)
|
60
|
+
build_target_data_attributes([target(name)])
|
61
|
+
end
|
62
|
+
|
63
|
+
# Getter for a named classes list so can be used in view to set initial state on SSR
|
64
|
+
# Returns a String of classes that can be used in a `class` attribute.
|
65
|
+
def named_classes(*names)
|
66
|
+
names.map { |name| convert_classes_list_to_string(@named_classes[name]) }.join(" ")
|
67
|
+
end
|
68
|
+
|
69
|
+
# Helpers for generating the Stimulus data-* attributes directly
|
70
|
+
|
71
|
+
# Return the HTML `data-controller` attribute
|
72
|
+
def with_controllers
|
73
|
+
"data-controller='#{controller_list}'".html_safe
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return the HTML `data-target` attribute
|
77
|
+
def as_targets(*targets)
|
78
|
+
build_target_data_attributes(parse_targets(targets))
|
79
|
+
.map { |dt, n| "data-#{dt}=\"#{n}\"" }
|
80
|
+
.join(" ")
|
81
|
+
.html_safe
|
82
|
+
end
|
83
|
+
alias_method :as_target, :as_targets
|
84
|
+
|
85
|
+
# Return the HTML `data-action` attribute to add these actions
|
86
|
+
def with_actions(*actions)
|
87
|
+
"data-action='#{parse_actions(actions).join(" ")}'".html_safe
|
88
|
+
end
|
89
|
+
alias_method :with_action, :with_actions
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# An implicit Stimulus controller name is built from the implicit controller path
|
94
|
+
def implied_controller_name
|
95
|
+
stimulize_path(implied_controller_path)
|
96
|
+
end
|
97
|
+
|
98
|
+
# When using the DSL if you dont specify, the first controller is implied
|
99
|
+
def implied_controller_path
|
100
|
+
@controllers&.first || raise(StandardError, "No controllers have been specified")
|
101
|
+
end
|
102
|
+
|
103
|
+
# A complete list of Stimulus controllers for this component
|
104
|
+
def controller_list
|
105
|
+
@controllers&.map { |c| stimulize_path(c) }&.join(" ")
|
106
|
+
end
|
107
|
+
|
108
|
+
# Complete list of actions ready to be use in the data-action attribute
|
109
|
+
def action_list
|
110
|
+
return nil unless @actions&.size&.positive?
|
111
|
+
parse_actions(@actions).join(" ")
|
112
|
+
end
|
113
|
+
|
114
|
+
# Complete list of targets ready to be use in the data attributes
|
115
|
+
def target_list
|
116
|
+
return {} unless @targets&.size&.positive?
|
117
|
+
build_target_data_attributes(parse_targets(@targets))
|
118
|
+
end
|
119
|
+
|
120
|
+
def named_classes_list
|
121
|
+
return {} unless @named_classes&.size&.positive?
|
122
|
+
build_named_classes_data_attributes(@named_classes)
|
123
|
+
end
|
124
|
+
|
125
|
+
# stimulus "data-*" attributes map for this component
|
126
|
+
def tag_data_attributes
|
127
|
+
{controller: controller_list, action: action_list}
|
128
|
+
.merge!(target_list)
|
129
|
+
.merge!(named_classes_list)
|
130
|
+
.merge!(data_map_attributes)
|
131
|
+
.compact_blank!
|
132
|
+
end
|
133
|
+
|
134
|
+
# Actions can be specified as a symbol, in which case they imply an action on the primary
|
135
|
+
# controller, or as a string in which case it implies an action that is already fully qualified
|
136
|
+
# stimulus action.
|
137
|
+
# 1 Symbol: :my_action => "my_controller#myAction"
|
138
|
+
# 1 String: "my_controller#myAction"
|
139
|
+
# 2 Symbols: [:click, :my_action] => "click->my_controller#myAction"
|
140
|
+
# 1 String, 1 Symbol: ["path/to/controller", :my_action] => "path--to--controller#myAction"
|
141
|
+
# 1 Symbol, 1 String, 1 Symbol: [:hover, "path/to/controller", :my_action] => "hover->path--to--controller#myAction"
|
142
|
+
|
143
|
+
def parse_action_arg(part1)
|
144
|
+
if part1.is_a?(Symbol)
|
145
|
+
# 1 symbol arg, name of method on this controller
|
146
|
+
"#{implied_controller_name}##{js_name(part1)}"
|
147
|
+
elsif part1.is_a?(String)
|
148
|
+
# 1 string arg, fully qualified action
|
149
|
+
part1
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def parse_multiple_action_args(part1, part2, part3)
|
154
|
+
if part3.nil? && part1.is_a?(Symbol)
|
155
|
+
# 2 symbol args = event + action
|
156
|
+
"#{part1}->#{implied_controller_name}##{js_name(part2)}"
|
157
|
+
elsif part3.nil?
|
158
|
+
# 1 string arg, 1 symbol = controller + action
|
159
|
+
"#{stimulize_path(part1)}##{js_name(part2)}"
|
160
|
+
else
|
161
|
+
# 1 symbol, 1 string, 1 symbol = as above but with event
|
162
|
+
"#{part1}->#{stimulize_path(part2)}##{js_name(part3)}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Parse actions, targets and attributes that are passed in as symbols or strings
|
167
|
+
|
168
|
+
def parse_targets(targets)
|
169
|
+
targets.map { |n| parse_target(n) }
|
170
|
+
end
|
171
|
+
|
172
|
+
def parse_target(raw_target)
|
173
|
+
return raw_target if raw_target.is_a?(String)
|
174
|
+
return raw_target if raw_target.is_a?(Hash)
|
175
|
+
target(raw_target)
|
176
|
+
end
|
177
|
+
|
178
|
+
def build_target_data_attributes(targets)
|
179
|
+
targets.map { |t| ["#{t[:controller]}-target".to_sym, t[:name]] }.to_h
|
180
|
+
end
|
181
|
+
|
182
|
+
def parse_actions(actions)
|
183
|
+
actions.map! { |a| a.is_a?(String) ? a : action(*a) }
|
184
|
+
end
|
185
|
+
|
186
|
+
def parse_attributes(attrs, controller = nil)
|
187
|
+
attrs.transform_keys { |k| "#{controller || implied_controller_name}-#{k}" }
|
188
|
+
end
|
189
|
+
|
190
|
+
def data_map_attributes
|
191
|
+
return {} unless @data_maps
|
192
|
+
@data_maps.each_with_object({}) do |m, obj|
|
193
|
+
if m.is_a?(Hash)
|
194
|
+
obj.merge!(parse_attributes(m))
|
195
|
+
elsif m.is_a?(Array)
|
196
|
+
controller_path = m.first
|
197
|
+
data = m.last
|
198
|
+
obj.merge!(parse_attributes(data, stimulize_path(controller_path)))
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def parse_named_classes_hash(named_classes)
|
204
|
+
named_classes.map do |name, classes|
|
205
|
+
logical_name = name.to_s.dasherize
|
206
|
+
classes_str = convert_classes_list_to_string(classes)
|
207
|
+
if classes.is_a?(Hash)
|
208
|
+
{controller: stimulize_path(classes[:controller_path]), name: logical_name, classes: classes_str}
|
209
|
+
else
|
210
|
+
{controller: implied_controller_name, name: logical_name, classes: classes_str}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def build_named_classes_data_attributes(named_classes)
|
216
|
+
parse_named_classes_hash(named_classes)
|
217
|
+
.map { |c| ["#{c[:controller]}-#{c[:name]}-class", c[:classes]] }
|
218
|
+
.to_h
|
219
|
+
end
|
220
|
+
|
221
|
+
def convert_classes_list_to_string(classes)
|
222
|
+
return "" if classes.nil?
|
223
|
+
return classes if classes.is_a?(String)
|
224
|
+
return classes.join(" ") if classes.is_a?(Array)
|
225
|
+
classes[:classes].is_a?(Array) ? classes[:classes].join(" ") : classes[:classes]
|
226
|
+
end
|
227
|
+
|
228
|
+
# Convert a file path to a stimulus controller name
|
229
|
+
def stimulize_path(path)
|
230
|
+
path.split("/").map { |p| p.to_s.dasherize }.join("--")
|
231
|
+
end
|
232
|
+
|
233
|
+
# Convert a Ruby 'snake case' string to a JavaScript camel case strings
|
234
|
+
def js_name(name)
|
235
|
+
name.to_s.camelize(:lower)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if Gem.loaded_specs.has_key? "phlex"
|
4
|
+
require "phlex"
|
5
|
+
|
6
|
+
module Vident
|
7
|
+
module RootComponent
|
8
|
+
class UsingPhlexHTML < Phlex::HTML
|
9
|
+
include Base
|
10
|
+
|
11
|
+
VALID_TAGS = Set[*(Phlex::HTML::VOID_ELEMENTS.keys + Phlex::HTML::STANDARD_ELEMENTS.keys)].freeze
|
12
|
+
|
13
|
+
# Create a tag for a target with a block containing content
|
14
|
+
def target_tag(tag_name, targets, **options, &block)
|
15
|
+
parsed = parse_targets(Array.wrap(targets))
|
16
|
+
options[:data] ||= {}
|
17
|
+
options[:data].merge!(build_target_data_attributes(parsed))
|
18
|
+
generate_tag(tag_name, **options, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Build a tag with the attributes determined by this components properties and stimulus
|
22
|
+
# data attributes.
|
23
|
+
def template(&block)
|
24
|
+
# Generate tag options and render
|
25
|
+
tag_type = @element_tag.presence&.to_sym || :div
|
26
|
+
raise ArgumentError, "Unsupported HTML tag name #{tag_type}" unless VALID_TAGS.include?(tag_type)
|
27
|
+
options = @html_options&.dup || {}
|
28
|
+
data_attrs = tag_data_attributes
|
29
|
+
data_attrs = options[:data].present? ? data_attrs.merge(options[:data]) : data_attrs
|
30
|
+
options = options.merge(id: @id) if @id
|
31
|
+
options.except!(:data)
|
32
|
+
options.merge!(data_attrs.transform_keys { |k| "data-#{k}" })
|
33
|
+
generate_tag(tag_type, **options, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def generate_tag(tag_type, **options, &block)
|
39
|
+
send((tag_type == :template) ? :template_tag : tag_type, **options, &block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if Gem.loaded_specs.has_key? "view_component"
|
4
|
+
require "view_component"
|
5
|
+
|
6
|
+
module Vident
|
7
|
+
module RootComponent
|
8
|
+
class UsingViewComponent < ::ViewComponent::Base
|
9
|
+
include Base
|
10
|
+
|
11
|
+
SELF_CLOSING_TAGS = Set[:area, :base, :br, :col, :embed, :hr, :img, :input, :link, :meta, :param, :source, :track, :wbr].freeze
|
12
|
+
|
13
|
+
def target_tag(tag_name, targets, **options, &block)
|
14
|
+
parsed = parse_targets(Array.wrap(targets))
|
15
|
+
options[:data] ||= {}
|
16
|
+
options[:data].merge!(build_target_data_attributes(parsed))
|
17
|
+
content = view_context.capture(&block) if block
|
18
|
+
view_context.content_tag(tag_name, content, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def call
|
22
|
+
# Capture inner block content
|
23
|
+
# It's important that we capture the block content before generating the tag options.
|
24
|
+
# This is because the content could contain calls to `s.data_map`.
|
25
|
+
# These calls add key-value pairs to the internal data_map, which can then be translated into
|
26
|
+
# the correct `data-*` attrs by the tag options.
|
27
|
+
generated = content
|
28
|
+
|
29
|
+
# Generate outer tag options and render
|
30
|
+
tag_type = @element_tag.presence || :div
|
31
|
+
options = @html_options&.dup || {}
|
32
|
+
data_attrs = tag_data_attributes
|
33
|
+
options[:data] = options[:data].present? ? data_attrs.merge(options[:data]) : data_attrs
|
34
|
+
options = options.merge(id: @id) if @id
|
35
|
+
if SELF_CLOSING_TAGS.include?(tag_type)
|
36
|
+
view_context.tag(tag_type, options)
|
37
|
+
else
|
38
|
+
view_context.content_tag(tag_type, generated, options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vident
|
4
|
+
class StableId
|
5
|
+
class << self
|
6
|
+
def set_current_sequence_generator
|
7
|
+
::Thread.current[:vident_number_sequence_generator] = id_sequence_generator
|
8
|
+
end
|
9
|
+
|
10
|
+
def next_id_in_sequence
|
11
|
+
generator = ::Thread.current[:vident_number_sequence_generator]
|
12
|
+
return "?" unless generator
|
13
|
+
generator.next.join("-")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def id_sequence_generator
|
19
|
+
number_generator = Random.new(296_865_628_524)
|
20
|
+
Enumerator.produce { number_generator.rand(10_000_000) }.with_index
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if Gem.loaded_specs.has_key? "dry-struct"
|
4
|
+
require_relative "./attributes/typed"
|
5
|
+
|
6
|
+
module Vident
|
7
|
+
module TypedComponent
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
include Vident::Base
|
12
|
+
include Vident::Attributes::Typed
|
13
|
+
|
14
|
+
attribute :id, String, delegates: false
|
15
|
+
attribute :html_options, Hash, delegates: false
|
16
|
+
attribute :element_tag, Symbol, delegates: false
|
17
|
+
|
18
|
+
# StimulusJS support
|
19
|
+
attribute :controllers, Array, default: [], delegates: false
|
20
|
+
attribute :actions, Array, default: [], delegates: false
|
21
|
+
attribute :targets, Array, default: [], delegates: false
|
22
|
+
attribute :data_maps, Array, default: [], delegates: false
|
23
|
+
|
24
|
+
# TODO normalise the syntax of defining actions, controllers, etc
|
25
|
+
attribute :named_classes, Hash, delegates: false
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(attrs = {})
|
29
|
+
before_initialise(attrs)
|
30
|
+
prepare_attributes(attrs)
|
31
|
+
# The attributes need to also be set as ivars
|
32
|
+
attributes.each do |attr_name, attr_value|
|
33
|
+
instance_variable_set(self.class.attribute_ivar_names[attr_name], attr_value)
|
34
|
+
end
|
35
|
+
after_initialise
|
36
|
+
super()
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else
|
41
|
+
module Vident
|
42
|
+
module TypedComponent
|
43
|
+
def self.included(base)
|
44
|
+
raise "Vident::TypedComponent requires dry-struct to be installed"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/vident.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "vident/version"
|
4
|
+
require_relative "vident/railtie"
|
5
|
+
|
6
|
+
module Vident
|
7
|
+
class << self
|
8
|
+
def configuration
|
9
|
+
@configuration ||= Configuration.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def configure
|
13
|
+
yield(configuration) if block_given?
|
14
|
+
configuration
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Configuration
|
19
|
+
attr_accessor :include_i18n_helpers
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@include_i18n_helpers = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require_relative "vident/stable_id"
|
28
|
+
require_relative "vident/root_component/base"
|
29
|
+
require_relative "vident/root_component/using_phlex_html"
|
30
|
+
require_relative "vident/root_component/using_view_component"
|
31
|
+
require_relative "vident/base"
|
32
|
+
require_relative "vident/component"
|
33
|
+
require_relative "vident/typed_component"
|
data/sig/vident.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vident
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephen Ierodiaconou
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-12-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
description: Vident makes using Stimulus with your `ViewComponent` or `Phlex` view
|
28
|
+
components as easy as writing Ruby. It also provides a base class for your components
|
29
|
+
to inherit from.
|
30
|
+
email:
|
31
|
+
- stevegeek@gmail.com
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- ".standard.yml"
|
37
|
+
- CODE_OF_CONDUCT.md
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE.txt
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- examples/ex1.gif
|
43
|
+
- lib/tasks/vident.rake
|
44
|
+
- lib/vident.rb
|
45
|
+
- lib/vident/attributes/not_typed.rb
|
46
|
+
- lib/vident/attributes/typed.rb
|
47
|
+
- lib/vident/attributes/typed_niling_struct.rb
|
48
|
+
- lib/vident/attributes/types.rb
|
49
|
+
- lib/vident/base.rb
|
50
|
+
- lib/vident/component.rb
|
51
|
+
- lib/vident/railtie.rb
|
52
|
+
- lib/vident/root_component/base.rb
|
53
|
+
- lib/vident/root_component/using_phlex_html.rb
|
54
|
+
- lib/vident/root_component/using_view_component.rb
|
55
|
+
- lib/vident/stable_id.rb
|
56
|
+
- lib/vident/typed_component.rb
|
57
|
+
- lib/vident/version.rb
|
58
|
+
- sig/vident.rbs
|
59
|
+
homepage: https://github.com/stevegeek/vident
|
60
|
+
licenses:
|
61
|
+
- MIT
|
62
|
+
metadata:
|
63
|
+
homepage_uri: https://github.com/stevegeek/vident
|
64
|
+
source_code_uri: https://github.com/stevegeek/vident
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 3.0.0
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubygems_version: 3.3.7
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Vident is a view component base class for your design system implementation,
|
84
|
+
which provides helpers for working with Stimulus. For ViewComponent and Phlex.
|
85
|
+
test_files: []
|