-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvariants.rb
152 lines (115 loc) · 3.83 KB
/
variants.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# frozen_string_literal: true
require "phlex"
require_relative "variants/version"
module Phlex
module Variants
VariantNotFoundError = Class.new(StandardError)
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def style(&)
StyleBuilder.build(self, &)
end
def build_style(**variants)
extra_classes = variants.delete(:extra_classes)
[self::STYLE_BASE, build_variants_style(variants), extra_classes].flatten.compact.join(" ")
end
private
# @api private
def build_variants_style(variants)
variants = variants.compact
variants = self::STYLE_DEFAULTS.merge(variants) unless self::STYLE_DEFAULTS.empty?
variants.map do |variant, option|
options = self::STYLE_VARIANTS[variant]
if options
value = options[option]
next value if value
# doesn't raise error when passing false for variant with only true option
next if option == false && options.has_key?(true)
end
raise_variant_not_found_error(options, variant, option)
end
end
def raise_variant_not_found_error(options, variant, option)
message = if options
"Option #{option.inspect} for #{variant.inspect} variant doesn't exist. Valid options are: #{options.keys}"
else
"Variant #{variant.inspect} doesn't exist. Available variants are: #{self::STYLE_VARIANTS.keys}"
end
raise VariantNotFoundError, message
end
end
private
def build_style(...)
self.class.build_style(...)
end
# @api private
class StyleBuilder
attr_reader :view_class
def self.build(view_class, &)
new(view_class).build(&)
end
def build(&)
view_class.const_set(:STYLE_BASE, [])
view_class.const_set(:STYLE_VARIANTS, {})
view_class.const_set(:STYLE_DEFAULTS, {})
instance_exec(&)
end
private
def initialize(view_class)
@view_class = view_class
end
def base(*values)
view_class::STYLE_BASE.concat values
end
def variants(&)
VariantBuilder.build(view_class, &)
end
def defaults(**variants)
view_class::STYLE_DEFAULTS.merge!(variants)
end
def method_missing(method, *args, &) # standard:disable Style/MissingRespondToMissing
message = "undefined method '#{method}' for an instance of Phlex::Variants::StyleBuilder. The available methods are: 'base', 'variants' and 'defaults'"
raise NoMethodError, message
end
end
# @api private
class VariantBuilder
attr_reader :view_class
def self.build(view_class, &)
new(view_class).instance_exec(&)
end
def initialize(view_class)
@view_class = view_class
end
def method_missing(name, &) # standard:disable Style/MissingRespondToMissing
variant_name = name.to_sym
view_class::STYLE_VARIANTS[variant_name] = {}
OptionsBuilder.build(view_class, variant_name, &)
end
end
# @api private
class OptionsBuilder
attr_reader :view_class, :variant_name
def self.build(view_class, variant_name, &)
new(view_class, variant_name).instance_exec(&)
end
private
def initialize(view_class, variant_name)
@view_class = view_class
@variant_name = variant_name
end
def method_missing(name, *args) # standard:disable Style/MissingRespondToMissing
option = name.to_sym
if option == :yes
view_class::STYLE_VARIANTS[variant_name][true] = args
elsif option == :no
view_class::STYLE_VARIANTS[variant_name][false] = args
else
view_class::STYLE_VARIANTS[variant_name][option] = args
end
end
end
end
end