@use 'sass:map';
@use 'sass:list';
@use 'sass:string';
@use 'sass:meta';
@use 'sass:selector';
@use 'sass:math';
@use 'functions' as *;
@use 'breakpoints' as *;

// Transform a map into a list of custom props
@mixin spread-map-into-props($map: (), $prefix: '', $old-key: '', $deep: true) {
	@each $key, $value in $map {
		$name: meta.inspect($key);

		@if $name == meta.inspect($old-key) {
			$name: $prefix;
		} @else if $prefix != '' and meta.inspect($prefix) != meta.inspect($key)
		{
			$name: $prefix + '-' + $key;
		}

		// fix name
		$name: str-replace($name, '\\/', '-');

		@if string.slice(meta.inspect($key), 1, 1) !=
			'_' and
			string.slice(meta.inspect($key), 1, 2) !=
			'--' and
			string.slice(meta.inspect($key), 1, 1) !=
			'['
		{
			@if meta.type-of($value) == 'map' {
				@if $deep {
					@include spread-map-into-props($value, $name, $key);
				}
			} @else {
				#{'--' + $name}: #{$value};
			}
		}
	}
}

// Transform a map into a list of utilities
@mixin spread-map-into-utilities(
	$map: (),
	$class: '',
	$attribute: '',
	$prop: '',
	$name: '',
	$deep: true,
	$bps: (
		xxs: 0,
	),
	$prefix: '',
	$zero-specificity: false
) {
	@if (meta.type-of($map) == 'map' or meta.type-of($map) == 'list') and
		$attribute !=
		''
	{
		@each $breakpoint, $breakpoint-value in $bps {
			@if $breakpoint-value > 0 {
				$prefix: $breakpoint + '\\:';
			}

			@each $key, $value in $map {
				@if $name != '' and meta.inspect($key) != meta.inspect($name) {
					$key: #{$name}-#{meta.inspect($key)};
				}

				@if meta.type-of($value) == 'map' and $deep {
					@include spread-map-into-utilities(
						$map: $value,
						$class: $class,
						$attribute: $attribute,
						$prop: $prop,
						$name: $key,
						$bps: $bps,
						$prefix: $prefix,
						$zero-specificity: $zero-specificity
					);
				} @else {
					$default: null;
					$class-name: $class;
					$key: meta.inspect($key);
					$var: $prop;

					// fix class name
					@if $class-name != $key and $prop != $key {
						@if $class-name != '' {
							$class-name: $class-name + '-';
						}

						$class-name: $class-name + $key;
					}

					// fix variable name
					@if $var != '' and $var != $key {
						$var: $var + '-' + $key;
					}

					$var: str-replace($var, '\\/', '-');

					// check if value is not a map (spacing-dynamic)
					@if meta.type-of($value) != 'map' {
						$default: $value;
					}

					// check if map is a list
					@if meta.type-of($map) ==
						'list' and
						(not $default or $default == '')
					{
						$default: $key;
					}

					@if meta.type-of($default) == 'string' {
						$default: string.unquote($default);
					}

					%#{$prefix}#{$class-name} {
						@include media-breakpoint-up($breakpoint, $bps) {
							@if meta.type-of($attribute) != 'list' {
								$attribute: ($attribute);
							}

							@each $attr in $attribute {
								@if $default {
									@if $var != '' {
										#{$attr}: var(--#{$var}, $default);
									} @else {
										#{$attr}: $default;
									}
								} @else if $var != '' {
									#{$attr}: var(--#{$var});
								}
							}
						}
					}

					@include wrap-with-where(
						$selector: '.' + $prefix + $class-name,
						$enabled: $zero-specificity
					) {
						@extend %#{$prefix}#{$class-name};
					}
				}
			}
		}
	}
}

// Transform a map into a list of modifiers
@mixin spread-map-into-modifiers(
	$original: null,
	$map: (),
	$prefix: '',
	$key: 'modifier',
	$block: null,
	$modifier: '',
	$props: true,
	$zero-specificity: true,
	$bps: (
		xxs: 0,
	)
) {
	@if not $original {
		$original: $map;
	}

	@if not $block {
		$block: &;
	}

	@if meta.type-of(map.get($map, $key)) == 'map' {
		@each $item, $value in map.get($map, $key) {
			$selector: $block + '--' + $item;

			%#{$prefix + '--' + $item} {
				@include spread-map-into-attrs(
					$original: $original,
					$map: $value,
					$prefix: $prefix,
					$variant: #{$key}-#{$item},
					$deep: true,
					$ignore: (
						'state',
						'breakpoint',
						'element',
						'modifier',
					),
					$props: $props
				);
			}

			// map props and attributes
			@include wrap-with-where(
				$selector: $modifier + $selector + $block,
				$enabled: $zero-specificity
			) {
				@extend %#{$prefix + '--' + $item};
			}

			// map elements
			@if map.get($value, 'element') {
				@include spread-map-into-elements(
					$original: $original,
					$map: $value,
					$prefix: #{$prefix}-#{$key}-#{$item},
					$key: 'element',
					$block: $block,
					$modifier: $modifier + $selector,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}

			// map states
			@if map.get($value, 'state') {
				@include spread-map-into-states(
					$original: $original,
					$map: $value,
					$prefix: #{$prefix}-#{$key}-#{$item},
					$key: 'state',
					$block: $block,
					$modifier: $modifier + $selector,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}

			// map breakpoints
			@if map.get($value, 'breakpoint') {
				@include spread-map-into-breakpoints(
					$map: $value,
					$prefix: #{$prefix}-#{$key}-#{$item},
					$key: 'breakpoint',
					$block: $block,
					$modifier: $modifier + $selector,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}

			// map modifiers
			@if map.get($value, 'modifier') {
				@include spread-map-into-modifiers(
					$original: $original,
					$map: $value,
					$prefix: #{$prefix}-#{$key}-#{$item},
					$key: 'modifier',
					$block: $block,
					$modifier: $modifier + $selector,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}
		}
	}
}

// Transform a map into a list of BEM elements
@mixin spread-map-into-elements(
	$original: (),
	$map: (),
	$prefix: '',
	$key: 'element',
	$block: null,
	$modifier: null,
	$props: true,
	$zero-specificity: true,
	$bps: (
		xxs: 0,
	)
) {
	@if not $block {
		$block: &;
	}

	@if meta.type-of(map.get($map, $key)) == 'map' {
		@each $element, $value in map.get($map, $key) {
			$selector: $block + '__' + $element;

			// combinator
			$combinator: map.get($value, '_combinator');

			@if not $combinator {
				$combinator: map.get($original, $key, $element, '_combinator');
			}

			@if $modifier {
				$selectors: ();

				@each $item in selector.parse($modifier) {
					@if $combinator {
						$item: '#{$item} #{$combinator}';
					}

					$selectors: list.join(
						$selectors,
						selector.nest($item, $selector)
					);
				}

				$selector: list-to-string($selectors);
			}

			// alias
			$alias: map.get($value, '_alias');

			@if not $alias {
				$alias: map.get($original, $key, $element, '_alias');
			}

			@if $alias {
				@if $modifier {
					$aliases: ();

					@each $item in selector.parse($modifier) {
						$aliases: list.join(
							$aliases,
							selector.nest($item, $alias)
						);
					}

					$alias: list-to-string($aliases);
				} @else {
					$alias: $block + ' ' + $alias;
				}
			}

			%#{$prefix + '__' + $element} {
				@include spread-map-into-attrs(
					$map: $value,
					$prefix: $prefix,
					$variant: #{$key}-#{$element},
					$deep: true,
					$ignore: (
						'state',
						'breakpoint',
					),
					$props: $props
				);
			}

			// attributes
			@include wrap-with-where(
				$selector: list-to-string(($selector, $alias)),
				$enabled: $zero-specificity
			) {
				@extend %#{$prefix + '__' + $element};
			}

			// states
			@if map.get($value, 'state') {
				@include spread-map-into-states(
					$map: $value,
					$prefix: #{$prefix}-#{$key}-#{$element},
					$key: 'state',
					$block: $selector,
					$alias: $alias,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}

			// breakpoints
			@if map.get($value, 'breakpoint') {
				@include spread-map-into-breakpoints(
					$map: $value,
					$prefix: #{$prefix}-#{$key}-#{$element},
					$key: 'breakpoint',
					$block: list-to-string(($selector, $alias)),
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}
		}
	}
}

// Transform a map into a list of states
@mixin spread-map-into-states(
	$original: null,
	$map: (),
	$prefix: '',
	$key: 'state',
	$block: null,
	$modifier: '',
	$alias: null,
	$props: true,
	$zero-specificity: true,
	$bps: (
		xxs: 0,
	)
) {
	@if not $original {
		$original: $map;
	}

	@if not $block {
		$blocks: &;
	}

	@if meta.type-of(map.get($map, $key)) == 'map' {
		@each $state, $value in map.get($map, $key) {
			@if list.index(
				(
					active,
					focus,
					hover,
					focus-within,
					target,
					visited,
					focus-visible,
					disabled,
					checked,
					checked-within,
					multiple,
					selected,
					readonly,
					empty,
					placeholder-shown,
					dirty,
					valid,
					invalid,
					indeterminate,
					first-child,
					last-child,
					open,
					close,
					determinate,
					indeterminate,
					pressed,
					current
				),
				$state
			)
			{
				$selectors: ();

				@each $block-item in selector.parse($block) {
					$selectors: list.join(
						$selectors,
						(
							'#{$modifier}#{$block-item}--#{$state}',
							'#{$modifier}#{$block-item}.#{$state}'
						)
					);

					@if $alias {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$alias}.#{$state}'
						);
					}

					@if $state == 'checked-within' {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}:has(input:checked)'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}:has(input:checked)'
							);
						}
					} @else if $state == 'open' {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}[open]'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}[open]'
							);
						}
					} @else if $state == 'close' {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}:not([open])'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}:not([open])'
							);
						}
					} @else if $state == 'dirty' {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}:not(:has(*:placeholder-shown))'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}:not(:has(*:placeholder-shown))'
							);
						}
					} @else if
						$state ==
						'disabled' or
						$state ==
						'readonly' or
						$state ==
						'multiple'
					{
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}[#{$state}]'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}[#{$state}]'
							);
						}

						@if $state == 'disabled' {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$block-item}[aria-disabled="true"]'
							);

							@if $alias {
								$selectors: list.join(
									$selectors,
									'#{$modifier}#{$alias}[aria-disabled="true"]'
								);
							}
						}
					} @else if $state == 'focus-visible' {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}:not(:active):not([disabled]):not([tabindex="-1"]:not([aria-disabled="true"])):#{$state}'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}:not(:active):not([disabled]):not([tabindex="-1"]:not([aria-disabled="true"])):#{$state}'
							);
						}
					} @else if $state == 'checked' {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}:#{$state}'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}:#{$state}'
							);
						}
					} @else if $state == 'pressed' {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}[aria-pressed="true"]'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}[aria-pressed="true"]'
							);
						}
					} @else if $state == 'selected' {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}[aria-selected="true"]'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}[aria-selected="true"]'
							);
						}
					} @else if $state != 'current' {
						$selectors: list.join(
							$selectors,
							'#{$modifier}#{$block-item}:not([disabled]):#{$state}'
						);

						@if $alias {
							$selectors: list.join(
								$selectors,
								'#{$modifier}#{$alias}:not([disabled]):#{$state}'
							);
						}
					}
				}

				$selector: list-to-string($selectors);

				%#{$prefix + '--' + $state} {
					@include spread-map-into-attrs(
						$original: $original,
						$map: $value,
						$prefix: $prefix,
						$variant: #{$key}-#{$state},
						$ignore: (
							'element',
						),
						$deep: true,
						$props: $props
					);
				}

				@include wrap-with-where(
					$selector: $selector,
					$enabled: $zero-specificity
				) {
					@extend %#{$prefix + '--' + $state};
				}

				// map elements
				@if map.get($value, 'element') {
					@include spread-map-into-elements(
						$original: $original,
						$map: $value,
						$prefix: #{$prefix}-#{$key}-#{$state},
						$key: 'element',
						$block: $block,
						$modifier: $selector,
						$props: $props,
						$zero-specificity: $zero-specificity,
						$bps: $bps
					);
				}

				// breakpoints
				@if map.get($value, 'breakpoint') {
					@include spread-map-into-breakpoints(
						$original: $original,
						$map: $value,
						$prefix: #{$prefix}-#{$key}-#{$state},
						$key: 'breakpoint',
						$block: $block,
						$modifier: $selector,
						$props: $props,
						$zero-specificity: $zero-specificity,
						$bps: $bps
					);
				}
			}
		}
	}
}

// Transform a map into a list of breakpoints
@mixin spread-map-into-breakpoints(
	$original: null,
	$map: (),
	$prefix: '',
	$key: 'breakpoint',
	$block: null,
	$modifier: '',
	$props: true,
	$zero-specificity: true,
	$bps: (
		xxs: 0,
	)
) {
	@if not $original {
		$original: $map;
	}

	@if not $block {
		$block: &;
	}

	@if meta.type-of(map.get($map, $key)) == 'map' {
		@each $breakpoint, $value in map.get($map, $key) {
			@if string.index($breakpoint, '-') {
				$operator: list.nth(string.split($breakpoint, '-'), 1);
				$bp: list.nth(string.split($breakpoint, '-'), 2);

				@if map.has-key($bps, $bp) {
					@if $operator == 'up' {
						@include wrap-with-where(
							$selector: '#{$modifier}#{$block}',
							$enabled: $zero-specificity
						) {
							@include media-breakpoint-up($bp, $bps) {
								@include spread-map-into-attrs(
									$original: $original,
									$map: $value,
									$prefix: $prefix,
									$variant: #{$key}-#{$breakpoint},
									$deep: true,
									$props: $props
								);
							}
						}
					}

					@if $operator == 'down' {
						@include wrap-with-where(
							$selector: '#{$modifier}#{$block}',
							$enabled: $zero-specificity
						) {
							@include media-breakpoint-down($bp, $bps) {
								@include spread-map-into-attrs(
									$original: $original,
									$map: $value,
									$prefix: $prefix,
									$variant: #{$key}-#{$breakpoint},
									$deep: true,
									$props: $props
								);
							}
						}
					}

					@if map.has-key($bps, $operator) {
						@include wrap-with-where(
							$selector: '#{$modifier}#{$block}',
							$enabled: $zero-specificity
						) {
							@include media-breakpoint-between(
								$operator,
								$bp,
								$bps
							) {
								@include spread-map-into-attrs(
									$original: $original,
									$map: $value,
									$prefix: $prefix,
									$variant: #{$key}-#{$breakpoint},
									$deep: true,
									$props: $props
								);
							}
						}
					}
				}
			} @else if map.has-key($bps, $breakpoint) {
				@include wrap-with-where(
					$selector: '#{$modifier}#{$block}',
					$enabled: $zero-specificity
				) {
					@include media-breakpoint-up($breakpoint, $bps) {
						@include spread-map-into-attrs(
							$original: $original,
							$map: $value,
							$prefix: $prefix,
							$variant: #{$key}-#{$breakpoint},
							$deep: true,
							$props: $props
						);
					}
				}
			}
		}
	}
}

@mixin pseudo-element-placehoder($pseudo-element) {
	@if list.index(
		(
			calendar-picker-indicator,
			search-cancel-button,
			color-swatch-wrapper,
			color-swatch,
			inner-spin-button,
			outer-spin-button,
			progress-bar,
			progress-value,
			progress-inner-element,
			datetime-edit,
			datetime-edit-fields-wrapper,
			datetime-edit-day-field
		),
		$pseudo-element
	)
	{
		&::-webkit-#{$pseudo-element} {
			@content;
		}
		&::-moz-#{$pseudo-element} {
			@content;
		}
	} @else {
		&::#{$pseudo-element} {
			@content;
		}
	}
}

// Transform a map into a list of CSS attributes
@mixin spread-map-into-attrs(
	$original: (),
	$map: (),
	$prefix: '',
	$variant: '',
	$postfix: '',
	$deep: false,
	$ignore: (),
	$props: true,
	$bps: (
		xxs: 0,
	)
) {
	@if meta.type-of($map) == 'map' and $prefix != '' {
		@each $key, $value in $map {
			@if not list.index($ignore, $key) {
				@if meta.type-of($value) ==
					'map' and
					($deep or $key == 'pseudo')
				{
					$original: map.get($original, $key);

					@if not $original {
						$original: ();
					}

					@if $postfix != '' {
						$key: #{$postfix}-#{$key};
					}

					@if $key == 'pseudo' {
						@each $pseudo-element, $pseudo-map in $value {
							@if meta.type-of($pseudo-map) == 'map' {
								@if list.index(
									(
										before,
										after,
										placeholder,
										calendar-picker-indicator,
										search-cancel-button,
										color-swatch-wrapper,
										color-swatch,
										inner-spin-button,
										outer-spin-button,
										progress-bar,
										progress-value,
										inner-element,
										marker,
										datetime-edit,
										datetime-edit-fields-wrapper,
										datetime-edit-day-field,
										backdrop,
										file-selector-button,
										-webkit-progress-bar,
										-webkit-progress-value,
										-webkit-inner-element,
										-moz-progress-bar,
										-moz-progress-value,
										-moz-inner-element,
										-webkit-details-marker,
										-webkit-scrollbar,
										-webkit-scrollbar-track,
										-webkit-scrollbar-thumb
									),
									$pseudo-element
								)
								{
									$original: map.get(
										$original,
										$pseudo-element
									);
									$key: pseudo-#{$pseudo-element};

									@include pseudo-element-placehoder(
										$pseudo-element
									) {
										@if not $original {
											@if not(
												map.has-key(
													$pseudo-map,
													'content'
												)
											)
											{
												content: '';
											}

											$original: ();
										}

										@include spread-map-into-attrs(
											$original: $original,
											$map: $pseudo-map,
											$prefix: $prefix,
											$variant: $variant,
											$postfix: $key,
											$deep: true,
											$props: $props
										);
									}
								}
							}
						}
					} @else {
						@include spread-map-into-attrs(
							$original: $original,
							$map: $value,
							$prefix: $prefix,
							$variant: $variant,
							$postfix: $key,
							$deep: true,
							$props: $props
						);
					}
				} @else if
					meta.type-of($value) !=
					'map' and
					(string.slice(meta.inspect($key), 1, 1) != '_')
				{
					@if $props and
						$key !=
						'content' and
						(string.slice(meta.inspect($key), 1, 2) != '--') and
						(string.slice(meta.inspect($key), 1, 1) != '[')
					{
						$prop-to-replace: --#{$prefix};
						$prop-to-replace-with: --#{$prefix};

						// add variant
						@if $variant != '' {
							$prop-to-replace-with: #{$prop-to-replace-with}-#{$variant};
						}

						// add postfix
						@if $postfix != '' {
							$prop-to-replace: #{$prop-to-replace}-#{$postfix};
							$prop-to-replace-with: #{$prop-to-replace-with}-#{$postfix};
						}

						// add key
						$prop-to-replace: #{$prop-to-replace}-#{$key};
						$prop-to-replace-with: #{$prop-to-replace-with}-#{$key};

						@if map.get($original, $key) {
							#{$prop-to-replace}: var(#{$prop-to-replace-with});
						} @else {
							#{$key}: var(#{$prop-to-replace-with});
						}
					} @else {
						@if string.slice(meta.inspect($key), 1, 1) == '[' {
							$key: string.slice(
								meta.inspect($key),
								2,
								string.length(meta.inspect($key)) - 1
							);
						}

						#{$key}: $value;
					}
				}
			}
		}
	}
}

// Wrap selector with :where()
@mixin wrap-with-where($selector, $enabled: true) {
	@if meta.type-of($selector) == 'map' or meta.type-of($selector) == 'list' {
		$selector: #{$selector};
	}

	@if $enabled {
		$selector: ':where(#{string.unquote($selector)})';
	}

	#{$selector} {
		@content;
	}
}

// Transform a map into a BEM component
@mixin spread-map-into-bem(
	$map,
	$block: '',
	$elements-key: 'element',
	$modifiers-key: 'modifier',
	$states-key: 'state',
	$breakpoints-key: 'breakpoint',
	$transitions-key: 'transition',
	$props: true,
	$zero-specificity: true,
	$custom-props: false,
	$custom-block: false,
	$custom-elements: false,
	$custom-modifiers: false,
	$custom-states: false,
	$custom-breakpoints: false,
	$custom-transitions: false,
	$bps: (xxs: 0)
) {
	@if meta.type-of($map) == 'map' and $block != '' {
		$selector: '.' + $block;

		// custom props
		@if not $custom-props {
			@if $props {
				:where(:host, :root, .theme) {
					@include spread-map-into-props(
						$map: $map,
						$prefix: $block,
						$deep: true
					);
				}
			}
		}

		// block
		@if not $custom-block {
			%#{$block} {
				@include spread-map-into-attrs(
					$map: $map,
					$prefix: $block,
					$props: $props
				);

				// content
				@content;
			}

			@include wrap-with-where(
				$selector: $selector,
				$enabled: $zero-specificity
			) {
				@extend %#{$block};
			}
		} @else {
			// content
			@content;
		}

		// elements
		@if not $custom-elements {
			@if map.has-key($map, $elements-key) {
				@include spread-map-into-elements(
					$map: $map,
					$prefix: $block,
					$key: $elements-key,
					$block: $selector,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}
		}

		// states
		@if not $custom-states {
			@if map.has-key($map, $states-key) {
				@include spread-map-into-states(
					$map: $map,
					$prefix: $block,
					$key: $states-key,
					$block: $selector,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}
		}

		// modifiers
		@if not $custom-modifiers {
			@if map.has-key($map, $modifiers-key) {
				@include spread-map-into-modifiers(
					$map: $map,
					$prefix: $block,
					$key: $modifiers-key,
					$block: $selector,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}
		}

		// transition
		@if not $custom-transitions {
			@if map.has-key($map, $transitions-key) {
				@include spread-map-into-transitions(
					$map: $map,
					$prefix: $block,
					$key: $transitions-key,
					$block: $selector,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}
		}

		// breakpoints
		@if not $custom-breakpoints {
			@if map.has-key($map, $breakpoints-key) {
				@include spread-map-into-breakpoints(
					$map: $map,
					$prefix: $block,
					$key: $breakpoints-key,
					$block: $selector,
					$props: $props,
					$zero-specificity: $zero-specificity,
					$bps: $bps
				);
			}
		}
	}
}

// Transform a map into a list of transitions
@mixin spread-map-into-transitions(
	$map: (),
	$prefix: '',
	$key: 'transition',
	$block: null,
	$props: true,
	$zero-specificity: true,
	$bps: (
		xxs: 0,
	)
) {
	@if not $block {
		$block: &;
	}

	@if meta.type-of(map.get($map, $key)) == 'map' {
		@each $transition, $states in map.get($map, $key) {
			@each $state, $value in $states {
				@if $state in [ 'enter-from', 'enter-active', 'enter-to',
					'leave-from', 'leave-active', 'leave-to', 'active',
					'inert'] and meta.type-of($value) == 'map'
				{
					$selector: '.#{$transition}';

					@if $prefix != '' {
						$selector: '.#{$prefix}--#{$transition}';
					}

					@if $state == 'active' {
						$selector: (
							'#{$selector}-enter-active',
							'#{$selector}-leave-active'
						);
					} @else if $state == 'inert' {
						$selector: (
							'#{$selector}-enter-from',
							'#{$selector}-leave-to'
						);
					} @else {
						$selector: '#{$selector}-#{$state}';
					}

					// map props and attributes
					@include wrap-with-where(
						$selector,
						$enabled: $zero-specificity
					) {
						@include spread-map-into-attrs(
							$map: $value,
							$prefix: $prefix,
							$variant: #{$key}-#{$transition}-#{$state},
							$deep: true,
							$ignore: (
								'element',
							),
							$props: $props,
							$bps: $bps
						);
					}

					// map elements
					@if map.get($value, 'element') {
						@include spread-map-into-elements(
							$original: $map,
							$map: $value,
							$prefix: #{$prefix}-#{$key}-#{$transition}-#{$state},
							$key: 'element',
							$block: $block,
							$modifier: $selector,
							$props: $props,
							$zero-specificity: $zero-specificity,
							$bps: $bps
						);
					}

					// map breakpoints
					@if map.get($value, 'breakpoint') {
						@include spread-map-into-breakpoints(
							$original: $map,
							$map: $value,
							$prefix: #{$prefix}-#{$key}-#{$transition}-#{$state},
							$key: 'breakpoint',
							$block: $block,
							$modifier: $selector,
							$props: $props,
							$zero-specificity: $zero-specificity,
							$bps: $bps
						);
					}
				}
			}
		}
	}
}
