css-native 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: eb41e593fc373779c07707c9877b3c8edcfef509c3e5ba993e89efe90ed5ce6b
4
+ data.tar.gz: 2fffc3afef1ec477c2495ae7a72f5dbcc5988502d89fb2d15416ef63318e9c17
5
+ SHA512:
6
+ metadata.gz: 9d76de8661a17d3b350e7360828d2ad734c046c1e85d16d2fefd0ac294e6c11b2acfcd2486cdfa431f5fbb7d9b80b843dee38779eb395f3db04dad6421f656a7
7
+ data.tar.gz: 7142da4d6b9d7c146fc9bb8239df2c1d6c76b77d240b49d8888f9791f0d7de9a8cb3ca537f7525224920e215b16976e916e0fc7ba049830e10a98e61bfefb97b
data/lib/css-native.rb ADDED
@@ -0,0 +1,86 @@
1
+ require "css-native/errors"
2
+ require "css-native/rule"
3
+
4
+ class CSSNative
5
+ private_class_method :new
6
+ def self.stylesheet(&block)
7
+ sheet = new
8
+ sheet.instance_eval(&block)
9
+ sheet
10
+ end
11
+
12
+ def self.format_class(name)
13
+ ".#{name}"
14
+ end
15
+
16
+ def self.format_id(name)
17
+ "##{name}"
18
+ end
19
+
20
+ def self.format_attribute(name, operation = :none, value = nil, case_sensitive: true)
21
+ op = case operation.to_sym
22
+ when :none then ""
23
+ when :equals then "="
24
+ when :include then "~="
25
+ when :matches then "|="
26
+ when :starts_with then "^="
27
+ when :ends_with then "$="
28
+ when :contains then "*="
29
+ else
30
+ raise AttributeError.new("undefined comparison '#{operation}' for css attribute selector")
31
+ end
32
+ "[#{name}#{op}#{value.nil? ? "" : "\"#{value}\""}#{case_sensitive ? "" : " i"}]"
33
+ end
34
+
35
+ attr_reader :rules
36
+ def initialize
37
+ @rules = []
38
+ end
39
+
40
+ alias_method :klass, :class
41
+ def class(name = nil)
42
+ if name.nil?
43
+ klass
44
+ else
45
+ Rule.new(self).with_class(name)
46
+ end
47
+ end
48
+
49
+ def id(name)
50
+ Rule.new(self).with_id(name)
51
+ end
52
+
53
+ def attribute(name, operation = :none, value = nil, case_sensitive: true)
54
+ Rule.new(self).with_attribute(name, *args)
55
+ end
56
+
57
+ def select(name, *args, type: :element)
58
+ case type
59
+ when :element
60
+ Rule.new(self).with(name)
61
+ when :class
62
+ self.class(name)
63
+ when :id
64
+ id(name)
65
+ when :attribute
66
+ attribute(name, *args)
67
+ else
68
+ raise RuleError.new("undefined rule type '#{type}' for css selector")
69
+ end
70
+ end
71
+
72
+ def to_s
73
+ @rules.join("\n")
74
+ end
75
+ end
76
+
77
+ class Numeric
78
+ [:em, :ex, :ch, :rem, :lh, :vw, :vh, :vmin,
79
+ :vmax, :px, :pt, :pc, :in, :Q, :mm, :cm].each do |m|
80
+ define_method(m) {"#{self}#{m}"}
81
+ end
82
+ def pct
83
+ "#{self}%"
84
+ end
85
+ alias_method :percent, :pct
86
+ end
@@ -0,0 +1,40 @@
1
+ class CSSNative
2
+ class CSSError < StandardError
3
+ end
4
+
5
+ class RuleError < CSSError
6
+ end
7
+
8
+ class GrammarError < RuleError
9
+ def initialize(element = nil)
10
+ super("Rule selector#{element.nil? ? "" : " #{element}"} not valid in current position")
11
+ end
12
+ end
13
+
14
+ class PseudoClassError < RuleError
15
+ def initialize(msg = "invalid pseudo-class", argument: nil, method: nil)
16
+ if argument.nil? && method.nil?
17
+ super(msg)
18
+ elsif argument.nil?
19
+ super("invalid pseudo-class '#{method}'")
20
+ else
21
+ super("argument '#{argument}' invalid for pseudo-class '#{method}'")
22
+ end
23
+ end
24
+ end
25
+
26
+ class PseudoElementError < RuleError
27
+ def initialize(msg = "invalid pseudo-element", argument: nil, method: nil)
28
+ if argument.nil? && method.nil?
29
+ super(msg)
30
+ elsif argument.nil?
31
+ super("invalid pseudo-element '#{method}'")
32
+ else
33
+ super("argument '#{argument}' invalid for pseudo-element '#{method}'")
34
+ end
35
+ end
36
+ end
37
+
38
+ class AttributeError < CSSError
39
+ end
40
+ end
@@ -0,0 +1,284 @@
1
+ require "css-native/rule/stylesheet"
2
+ class CSSNative
3
+ class Rule
4
+ def initialize(parent, name = "", previous: nil)
5
+ @previous = previous
6
+ @parent = parent
7
+ @selector = name.to_s
8
+ @body = {}
9
+ @stylesheet = Stylesheet.new(self)
10
+ end
11
+
12
+ # basic selectors
13
+ def with_class(name, &block)
14
+ s = CSSNative::format_class(name)
15
+ @previous = :class
16
+ @selector += s
17
+ if block_given?
18
+ @stylesheet.instance_exec(@stylesheet, &block)
19
+ @parent.rules << to_s
20
+ else
21
+ self
22
+ end
23
+ end
24
+
25
+ def with_id(name, &block)
26
+ s = CSSNative::format_id(name)
27
+ @previous = :id
28
+ @selector += s
29
+ if block_given?
30
+ @stylesheet.instance_exec(@stylesheet, &block)
31
+ @parent.rules << to_s
32
+ else
33
+ self
34
+ end
35
+ end
36
+
37
+ def with_attribute(name, operation = :none, value = nil, case_sensitive: true, &block)
38
+ s = CSSNative::format_attribute(name, operation, value, case_sensitive: case_sensitive)
39
+ @previous = :attr
40
+ @selector += s
41
+ if block_given?
42
+ @stylesheet.instance_exec(@stylesheet, &block)
43
+ @parent.rules << to_s
44
+ else
45
+ self
46
+ end
47
+ end
48
+
49
+ def with(name, *args, type: :element, &block)
50
+ case type
51
+ when :element
52
+ name = (name == :all ? "*" : name.to_s)
53
+ raise GrammarError.new(name) if previous_selector?
54
+ @previous = (name == :all ? :all : :element)
55
+ @selector += name
56
+ if block_given?
57
+ @stylesheet.instance_exec(@stylesheet, &block)
58
+ @parent.rules << to_s
59
+ else
60
+ self
61
+ end
62
+ when :class
63
+ with_class(name, &block)
64
+ when :id
65
+ with_id(name, &block)
66
+ when :attribute
67
+ with_attribute(name, *args, &block)
68
+ else
69
+ raise CSSNative::RuleError.new("undefined rule type '#{type}' for css selector")
70
+ end
71
+ end
72
+ alias_method :select, :with
73
+
74
+ def all(&block)
75
+ raise GrammarError.new("*") if previous_selector?
76
+ @previous = :all
77
+ @selector += "*"
78
+ if block_given?
79
+ @stylesheet.instance_exec(@stylesheet, &block)
80
+ @parent.rules << to_s
81
+ else
82
+ self
83
+ end
84
+ end
85
+
86
+ # Grouping selectors
87
+ def join(rule = nil)
88
+ raise GrammarError.new(",") if previous_combinator?
89
+ @previous = :join
90
+ @selector += ","
91
+ if rule.kind_of? Rule
92
+ @selector += rule.instance_variable_get(:@selector)
93
+ else
94
+ @selector += rule.to_s
95
+ end
96
+ self
97
+ end
98
+
99
+ # Combinators
100
+ COMBINATORS = {
101
+ descendant: " ",
102
+ child: " > ",
103
+ sibling: " + ",
104
+ adjacent: " + ",
105
+ column: " || "
106
+ }
107
+
108
+ def combinator(c)
109
+ m = c.to_sym
110
+ raise GrammarError.new(COMBINATORS[]) if previous_combinator?
111
+ @previous = :combinator
112
+ @selector += COMBINATORS[m]
113
+ self
114
+ end
115
+
116
+ # pseudo-classes
117
+ # name of value is pseudo-class, array values are acceptad options
118
+ # Equality is compared by <option> === <value>, allowing
119
+ # classes to test for instances or regex matching
120
+ # If array is empty, there's no limitations (in the code, foraml CSS may differ)
121
+ # If array is nil, it takes no options
122
+ PSEUDO_CLASSES = {
123
+ # Linguistic
124
+ dir: ["ltr", "rtl"],
125
+ lang: [],
126
+ # Location
127
+ "any_link": nil,
128
+ link: nil,
129
+ visited: nil,
130
+ "local_link": nil,
131
+ target: nil,
132
+ "target_within": nil,
133
+ scope: nil,
134
+ # User action
135
+ hover: nil,
136
+ active: nil,
137
+ focus: nil,
138
+ "focus_visible": nil,
139
+ "focus_within": nil,
140
+ # Time_dimensional _ ill_defined
141
+ current: nil,
142
+ past: nil,
143
+ future: nil,
144
+ # Resource state
145
+ playing: nil,
146
+ paused: nil,
147
+ # Input
148
+ enabled: nil,
149
+ disabled: nil,
150
+ "read_only": nil,
151
+ "read_write": nil,
152
+ "placeholder_shown": nil,
153
+ default: nil,
154
+ checked: nil,
155
+ indeterminate: nil,
156
+ blank: nil,
157
+ valid: nil,
158
+ invalid: nil,
159
+ "in_range": nil,
160
+ "out_of_range": nil,
161
+ required: nil,
162
+ optional: nil,
163
+ "user_invalid": nil,
164
+ root: nil,
165
+ empty: nil,
166
+ "nth_child": ["odd", "even", /^\d+(n(\s*\+\s*\d+)?)?$/],
167
+ "nth_last_child": ["odd", "even", /^\d+(n(\s*\+\s*\d+)?)?$/],
168
+ "first_child": nil,
169
+ "last_child": nil,
170
+ "only_child": nil,
171
+ "nth_of_type": ["odd", "even", /^\d+(n(\s*\+\s*\d+)?)?$/],
172
+ "nth_last_of_type": ["odd", "even", /^\d+(n(\s*\+\s*\d+)?)?$/],
173
+ "first_of_type": nil,
174
+ "last_of_type": nil,
175
+ "only_of_type": nil,
176
+ }
177
+
178
+ def pseudo_class(name, *args, &block)
179
+ pc = name.to_s.gsub("_", "-")
180
+ m = name.to_s.gsub("-", "_").to_sym
181
+ raise PseudoClassError.new(method: pc) unless PSEUDO_CLASSES.key?(m)
182
+ arg_defs = PSEUDO_CLASSES[m]
183
+ @previous = :pseudo_class
184
+ args.all? do |arg|
185
+ raise PseudoClassError.new(argument: arg, method: pc) unless matches_arg_defs?(arg_defs, arg.to_s)
186
+ end
187
+ @selector += ":" + pc
188
+ @selector += "(#{args.join(" ")})" unless args.empty?
189
+ if block_given?
190
+ @stylesheet.instance_exec(@stylesheet, &block)
191
+ @parent.rules << to_s
192
+ else
193
+ self
194
+ end
195
+ end
196
+
197
+ # pseudo-elements
198
+ # definition semantics same as pseudo-classes
199
+ PSEUDO_ELEMENTS = {
200
+ after: nil,
201
+ before: nil,
202
+ backdrop: nil,
203
+ cue: nil,
204
+ "cue_region": nil,
205
+ "first_letter": nil,
206
+ "first_line": nil,
207
+ "file_selector_button": nil,
208
+ "grammar_error": nil,
209
+ marker: nil,
210
+ part: [/[-a-zA-Z]+( [-a-zA-Z]+)?/],
211
+ placeholder: nil,
212
+ selection: nil,
213
+ slotted: [],
214
+ "spelling_error": nil,
215
+ "target_text": nil,
216
+ }
217
+
218
+ def pseudo_element(name, *args, &block)
219
+ pe = name.to_s.gsub("_", "-")
220
+ m = name.to_s.gsub("-", "_").to_sym
221
+ raise PseudoElementError.new(method: pe) unless PSEUDO_ELEMENTS.key?(m)
222
+ arg_defs = PSEUDO_ELEMENTS[m]
223
+ @previous = :pseudo_element
224
+ args.all? do |arg|
225
+ raise PseudoElementError.new(argument: arg, method: ps) unless matches_arg_defs?(arg_defs, arg.to_s)
226
+ end
227
+ @selector += "::" + pe
228
+ @selector += "(#{args.join(" ")})" unless args.empty?
229
+ if block_given?
230
+ @stylesheet.instance_exec(@stylesheet, &block)
231
+ @parent.rules << to_s
232
+ else
233
+ self
234
+ end
235
+ end
236
+
237
+ def method_missing(m, *args, &block)
238
+ if COMBINATORS.key?(m)
239
+ combinator(m)
240
+ elsif PSEUDO_CLASSES.key?(m)
241
+ pseudo_class(m, *args, &block)
242
+ elsif PSEUDO_ELEMENTS.key?(m)
243
+ pseudo_element(m, *args, &block)
244
+ else
245
+ super(m, *args, &block)
246
+ end
247
+ end
248
+
249
+ def respond_to_missing?(m, include_all = false)
250
+ COMBINATORS.key?(m) ||
251
+ PSEUDO_CLASSES.key?(m) ||
252
+ PSEUDO_ELEMENTS.key?(m) ||
253
+ super(m, include_all)
254
+ end
255
+
256
+ def to_s
257
+ "#{@selector} #{@stylesheet}"
258
+ end
259
+
260
+ private
261
+
262
+ def previous?(*args)
263
+ args.include?(@previous)
264
+ end
265
+
266
+ def previous_combinator?
267
+ previous?(:join, :combinator)
268
+ end
269
+
270
+ def previous_selector?
271
+ previous?(:element, :class, :id, :attribute, :all, :pseudo_class, :pseudo_element)
272
+ end
273
+
274
+ def matches_arg_defs?(defs, arg)
275
+ if defs.nil?
276
+ arg.nil?
277
+ elsif defs.empty?
278
+ true
279
+ else
280
+ defs.any? {|d| d === arg}
281
+ end
282
+ end
283
+ end
284
+ end
@@ -0,0 +1,37 @@
1
+ class CSSNative
2
+ class Rule
3
+ private
4
+ class Stylesheet
5
+ def initialize(controller)
6
+ @properties = {}
7
+ @controller = controller
8
+ end
9
+
10
+ def subrule
11
+ parent = @controller.instance_variable_get(:@parent)
12
+ selector = @controller.instance_variable_get(:@selector)
13
+ previous = @controller.instance_variable_get(:@previous)
14
+
15
+ Rule.new(parent, selector, previous: previous)
16
+ end
17
+
18
+ def to_s
19
+ "{\n" +
20
+ @properties.map do |k, v|
21
+ " #{k}: #{v};"
22
+ end.join("\n") +
23
+ "\n}"
24
+ end
25
+
26
+ def method_missing(m, *args, &block)
27
+ if m.to_s.end_with? "="
28
+ props = args[0]
29
+ props = props.join(" ") if props.kind_of?(Array)
30
+ @properties[m.to_s.gsub("_", "-")[...-1]] = props.to_s
31
+ else
32
+ super(m, *args, &block)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: css-native
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kellen Watt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-02-10 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A CSS generator designed to make writing CSS-compatible code cleaner
14
+ and easier to undestand
15
+ email:
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/css-native.rb
21
+ - lib/css-native/errors.rb
22
+ - lib/css-native/rule.rb
23
+ - lib/css-native/rule/stylesheet.rb
24
+ homepage: https://github.com/KellenWatt/css-native
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubygems_version: 3.1.4
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: CSS generation made cleaner
47
+ test_files: []