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 +7 -0
- data/lib/css-native.rb +86 -0
- data/lib/css-native/errors.rb +40 -0
- data/lib/css-native/rule.rb +284 -0
- data/lib/css-native/rule/stylesheet.rb +37 -0
- metadata +47 -0
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: []
|