aboutsummaryrefslogtreecommitdiffstats
path: root/src/_contexts.scss
diff options
context:
space:
mode:
Diffstat (limited to 'src/_contexts.scss')
-rw-r--r--src/_contexts.scss315
1 files changed, 315 insertions, 0 deletions
diff --git a/src/_contexts.scss b/src/_contexts.scss
new file mode 100644
index 0000000..556fde3
--- /dev/null
+++ b/src/_contexts.scss
@@ -0,0 +1,315 @@
1////
2/// Context handling.
3///
4/// Contexts allow you to pass data between mixins and let you enforce a certain nesting order.
5/// It's an essential part for the BEM-related mixins.
6///
7/// If you want to create a new context, the easiest pattern is to create a new mixin and wrap
8/// the @content between a pair of iro-context-push and iro-context-pop.
9/// From within the @content, you can access the context's data with iro-context-get.
10/// To make the compilation fail if a certain nesting order is violated, use
11/// iro-context-assert-stack-must-contain and iro-context-assert-stack-must-not-contain.
12///
13/// @group Contexts
14///
15/// @access public
16////
17
18///
19/// Map of all context stacks.
20///
21/// @type map
22///
23/// @access private
24///
25$iro-context-stacks: ();
26
27///
28/// Create a new context stack.
29///
30/// @param {string} $stack-id - ID of context stack
31///
32/// @throw If context stack already exists
33///
34@mixin iro-context-stack-create($stack-id) {
35 $noop: iro-context-stack-create($stack-id);
36}
37
38///
39/// Create a new context stack.
40///
41/// @param {string} $stack-id - ID of context stack
42///
43@function iro-context-stack-create($stack-id) {
44 @if map-has-key($iro-context-stacks, $stack-id) {
45 @error 'Context stack "#{inspect($stack-id)}" does not exist.';
46 }
47
48 $iro-context-stacks: map-merge($iro-context-stacks, ($stack-id: ())) !global;
49
50 @return null;
51}
52
53///
54/// Clear a context stack.
55///
56/// @param {string} $stack-id - ID of context stack
57///
58@mixin iro-context-stack-clear($stack-id) {
59 $noop: iro-context-stack-clear($stack-id);
60}
61
62///
63/// Clear a context stack.
64///
65/// @param {string} $stack-id - ID of context stack
66///
67@function iro-context-stack-clear($stack-id) {
68 @if not map-has-key($iro-context-stacks, $stack-id) {
69 @error 'Context stack "#{inspect($stack-id)}" does not exist.';
70 }
71
72 $context-stack: ();
73 $iro-context-stacks: map-merge($iro-context-stacks, ($stack-id: $context-stack)) !global;
74
75 @return null;
76}
77
78///
79/// Delete a context stack.
80///
81/// @param {string} $stack-id - ID of context stack
82///
83@mixin iro-context-stack-delete($stack-id) {
84 $noop: iro-context-stack-delete($stack-id);
85}
86
87///
88/// Delete a context stack.
89///
90/// @param {string} $stack-id - ID of context stack
91///
92@function iro-context-stack-delete($stack-id) {
93 @if not map-has-key($iro-context-stacks, $stack-id) {
94 @error 'Context stack "#{inspect($stack-id)}" does not exist.';
95 }
96
97 $iro-context-stacks: map-remove($iro-context-stacks, $stack-id) !global;
98
99 @return null;
100}
101
102///
103/// Push a new context to a context stack.
104///
105/// @param {string} $stack-id - ID of context stack to use
106/// @param {string} $id - ID of new context
107/// @param {any} $data [()] - Data that belongs to the context
108///
109@mixin iro-context-push($stack-id, $id, $data: ()) {
110 $noop: iro-context-push($stack-id, $id, $data);
111}
112
113///
114/// Push a new context to a context stack.
115///
116/// @param {string} $stack-id - ID of context stack to use
117/// @param {string} $id - ID of new context
118/// @param {any} $data [()] - Data that belongs to the context
119///
120/// @return {list} A list with two items: 1 = context id, 2 = context data
121///
122@function iro-context-push($stack-id, $id, $data: ()) {
123 @if not map-has-key($iro-context-stacks, $stack-id) {
124 @error 'Context stack "#{inspect($stack-id)}" does not exist.';
125 }
126
127 $context: $id $data;
128 $context-stack: map-get($iro-context-stacks, $stack-id);
129 $context-stack: append($context-stack, $context);
130 $iro-context-stacks: map-merge($iro-context-stacks, ($stack-id: $context-stack)) !global;
131
132 @return $context;
133}
134
135///
136/// Pop the latest context from a context stack.
137///
138/// @param {string} $stack-id - ID of context stack to use
139///
140/// @throw If context stack doesn't exist
141///
142@mixin iro-context-pop($stack-id) {
143 $noop: iro-context-pop($stack-id);
144}
145
146///
147/// Pop the latest context from a context stack.
148///
149/// @param {string} $stack-id - ID of context stack to use
150///
151/// @return {list} A list with two items: 1 = context id, 2 = context data
152///
153@function iro-context-pop($stack-id) {
154 @if not map-has-key($iro-context-stacks, $stack-id) {
155 @error 'Context stack "#{inspect($stack-id)}" does not exist.';
156 }
157
158 $context-stack: map-get($iro-context-stacks, $stack-id);
159
160 @if length($context-stack) == 0 {
161 @error 'Context stack "#{inspect($stack-id)}" is already empty.';
162 }
163
164 $popped-context: nth($context-stack, -1);
165
166 @if length($context-stack) == 1 {
167 $context-stack: ();
168 } @else {
169 $context-stack: iro-list-slice($context-stack, 1, length($context-stack) - 1);
170 }
171
172 $iro-context-stacks: map-merge($iro-context-stacks, ($stack-id: $context-stack)) !global;
173
174 @return $popped-context;
175}
176
177///
178/// Assert that a context stack must contain one of the given context IDs.
179///
180/// @param {string} $stack-id - ID of context stack to use
181/// @param {list} $context-ids - Context IDs
182/// @param {bool} $check-head-only [false] - If false, all items will be checked. If true, only the head will be checked.
183///
184/// @throw If assertion fails
185///
186@mixin iro-context-assert-stack-must-contain($stack-id, $context-ids, $check-head-only: false) {
187 @if not iro-context-stack-contains($stack-id, $context-ids, $check-head-only) {
188 @error 'Must be called inside of contexts "#{inspect($context-ids)}".';
189 }
190}
191
192///
193/// Assert that a context stack must not contain any of the given context IDs.
194///
195/// @param {string} $stack-id - ID of context stack to use
196/// @param {list} $context-ids - Context IDs
197/// @param {bool} $check-head-only [false] - If false, all items will be checked. If true, only the head will be checked.
198///
199/// @throw If assertion fails
200///
201@mixin iro-context-assert-stack-must-not-contain($stack-id, $context-ids, $check-head-only: false) {
202 @if iro-context-stack-contains($stack-id, $context-ids, $check-head-only) {
203 @error 'Must not be called inside of contexts "#{inspect($context-ids)}".';
204 }
205}
206
207///
208/// Check if a context stack contains one of the given context IDs.
209///
210/// @param {string} $stack-id - ID of context stack to use
211/// @param {list} $context-ids - Context IDs
212/// @param {bool} $check-head-only [false] - If false, all items will be checked. If true, only the head will be checked.
213///
214/// @return {bool} `true` if the context stack contains one of the context IDs, otherwise `false`
215///
216@function iro-context-stack-contains($stack-id, $context-ids, $check-head-only: false) {
217 @if not map-has-key($iro-context-stacks, $stack-id) {
218 @error 'Context stack "#{inspect($stack-id)}" does not exist.';
219 }
220
221 $context-stack: map-get($iro-context-stacks, $stack-id);
222
223 @if length($context-stack) == 0 {
224 @return false;
225 }
226
227 $end-idx: if($check-head-only, length($context-stack), 1);
228
229 @for $i from length($context-stack) through $end-idx {
230 $context: nth($context-stack, $i);
231
232 @each $chk-context in $context-ids {
233 @if nth($context, 1) == $chk-context {
234 @return true;
235 }
236 }
237 }
238
239 @return false;
240}
241
242///
243/// Assert that a context stack must contain a number of contexts smaller than $max-count.
244///
245/// @param {string} $stack-id - ID of context stack to use
246/// @param {number} $max-count - Maximum number ofg contexts in context stack
247///
248/// @throw If assertion fails
249///
250@mixin iro-context-assert-stack-count($stack-id, $max-count) {
251 @if iro-context-stack-count($stack-id) > $max-count {
252 @error 'Maximum context count "#{inspect($max-count)}" exceeded.';
253 }
254}
255
256///
257/// Get the number of contexts from a context stack.
258///
259/// @param {string} $stack-id - ID of context stack to use
260///
261/// @return {number} The number of contexts
262///
263@function iro-context-stack-count($stack-id) {
264 @if not map-has-key($iro-context-stacks, $stack-id) {
265 @error 'Context stack "#{inspect($stack-id)}" does not exist.';
266 }
267
268 $context-stack: map-get($iro-context-stacks, $stack-id);
269
270 @return length($context-stack);
271}
272
273///
274/// Get a specific context from the stack.
275///
276/// @param {string} $stack-id - ID of context stack to use
277/// @param {number | string | list} $type-or-level - If this is a number (!= 0), the nth context from the head will be returned. If it is a string, the first context with a matching ID will be returned. If it is a list, the first context that matches one of the IDs in the list will be returned.
278///
279/// @return {list} Null if no match was found, otherwise a list with two items: 1. context ID, 2. context data.
280///
281@function iro-context-get($stack-id, $type-or-level: null) {
282 @if not map-has-key($iro-context-stacks, $stack-id) {
283 @error 'Context stack "#{inspect($stack-id)}" does not exist.';
284 }
285
286 $context-stack: map-get($iro-context-stacks, $stack-id);
287
288 @if length($context-stack) == 0 {
289 @return null;
290 }
291
292 @if type-of($type-or-level) == number {
293 $context: nth($context-stack, -$type-or-level);
294
295 @return $context;
296 } @else {
297 @for $i from -1 through -(length($context-stack)) {
298 $context: nth($context-stack, $i);
299
300 @if type-of($type-or-level) == list {
301 @for $j from 1 through length($type-or-level) {
302 $ctx: nth($type-or-level, $j);
303
304 @if nth($context, 1) == $ctx {
305 @return $context;
306 }
307 }
308 } @else if nth($context, 1) == $type-or-level {
309 @return $context;
310 }
311 }
312 }
313
314 @return null;
315}