1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | @use 'sass:list';
|
24 | @use 'sass:map';
|
25 | @use 'sass:meta';
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | $all-features: (structure, color, typography, animation);
|
47 | $all-query-operators: (any, all, without);
|
48 |
|
49 |
|
50 | @function create-target($feature-query, $targeted-feature) {
|
51 | $feature-target: (
|
52 | query: $feature-query,
|
53 | target: $targeted-feature,
|
54 | );
|
55 | $valid: verify-target_($feature-target);
|
56 |
|
57 | @return $feature-target;
|
58 | }
|
59 |
|
60 |
|
61 |
|
62 | @function parse-targets($feature-targets) {
|
63 | $valid: verify-target_($feature-targets...);
|
64 | $available-features: ();
|
65 |
|
66 | @each $target in $feature-targets {
|
67 | $available-features: list.append(
|
68 | $available-features,
|
69 | map.get($target, target)
|
70 | );
|
71 | }
|
72 |
|
73 | @return (
|
74 | available: $available-features,
|
75 | query: map.get(list.nth($feature-targets, 1), query)
|
76 | );
|
77 | }
|
78 |
|
79 |
|
80 | @function all($feature-queries...) {
|
81 | $valid: verify-query_($feature-queries...);
|
82 |
|
83 | @return (op: all, queries: $feature-queries);
|
84 | }
|
85 |
|
86 |
|
87 | @function any($feature-queries...) {
|
88 | $valid: verify-query_($feature-queries...);
|
89 |
|
90 | @return (op: any, queries: $feature-queries);
|
91 | }
|
92 |
|
93 |
|
94 | @function without($feature-query) {
|
95 | $valid: verify-query_($feature-query);
|
96 |
|
97 |
|
98 | @return (op: without, queries: list.append((), $feature-query));
|
99 | }
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 | @function verify-target_($feature-targets...) {
|
107 | @each $target in $feature-targets {
|
108 | @if meta.type-of($target) != map {
|
109 | @error "Invalid feature target: '#{$target}'. Must be a map.";
|
110 | }
|
111 |
|
112 | $targeted-feature: map.get($target, target);
|
113 | $feature-query: map.get($target, query);
|
114 | $valid: verify-feature_($targeted-feature) and
|
115 | verify-query_($feature-query);
|
116 | }
|
117 |
|
118 | @return true;
|
119 | }
|
120 |
|
121 |
|
122 | @function is-query-satisfied_($feature-query, $available-features) {
|
123 | $valid: verify-query_($feature-query);
|
124 | $valid: verify-feature_($available-features...);
|
125 |
|
126 | @if meta.type-of($feature-query) == map {
|
127 | $op: map.get($feature-query, op);
|
128 | $sub-queries: map.get($feature-query, queries);
|
129 |
|
130 | @if $op == without {
|
131 | @return not
|
132 | is-query-satisfied_(list.nth($sub-queries, 1), $available-features);
|
133 | }
|
134 |
|
135 | @if $op == any {
|
136 | @each $sub-query in $sub-queries {
|
137 | @if is-query-satisfied_($sub-query, $available-features) {
|
138 | @return true;
|
139 | }
|
140 | }
|
141 |
|
142 | @return false;
|
143 | }
|
144 |
|
145 | @if $op == all {
|
146 | @each $sub-query in $sub-queries {
|
147 | @if not is-query-satisfied_($sub-query, $available-features) {
|
148 | @return false;
|
149 | }
|
150 | }
|
151 |
|
152 | @return true;
|
153 | }
|
154 | }
|
155 |
|
156 | @return list-contains_($available-features, $feature-query);
|
157 | }
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | @function verify-feature_($features...) {
|
165 | @each $feature in $features {
|
166 | @if not list-contains_($all-features, $feature) {
|
167 | @error "Invalid feature: '#{$feature}'. Valid features are: #{$all-features}.";
|
168 | }
|
169 | }
|
170 |
|
171 | @return true;
|
172 | }
|
173 |
|
174 |
|
175 | @function verify-query_($feature-queries...) {
|
176 | @each $query in $feature-queries {
|
177 | @if meta.type-of($query) == map {
|
178 | $op: map.get($query, op);
|
179 | $sub-queries: map.get($query, queries);
|
180 | $valid: verify-query_($sub-queries...);
|
181 |
|
182 | @if not list-contains_($all-query-operators, $op) {
|
183 | @error "Invalid feature query operator: '#{$op}'. " +
|
184 | "Valid operators are: #{$all-query-operators}";
|
185 | }
|
186 | } @else {
|
187 | $valid: verify-feature_($query);
|
188 | }
|
189 | }
|
190 |
|
191 | @return true;
|
192 | }
|
193 |
|
194 |
|
195 | @function list-contains_($list, $item) {
|
196 | @return list.index($list, $item) != null;
|
197 | }
|
198 |
|
199 |
|
200 | $targets-context_: false;
|
201 |
|
202 |
|
203 |
|
204 | @mixin targets($feature-targets...) {
|
205 |
|
206 | @if $targets-context_ {
|
207 | @error "mdc-feature-targets must not be used inside of another mdc-feature-targets block";
|
208 | }
|
209 |
|
210 | $targets-context_: true !global;
|
211 | $parsed-targets: parse-targets($feature-targets);
|
212 |
|
213 | @if is-query-satisfied_(
|
214 | map.get($parsed-targets, query),
|
215 | map.get($parsed-targets, available)
|
216 | )
|
217 | {
|
218 | @content;
|
219 | }
|
220 |
|
221 | $targets-context_: false !global;
|
222 | }
|