@import ( reference ) '@wikimedia/codex-icons/codex-icon-paths.less';

//
// To create a CSS-only icon you can do one of the following:
// 1. Apply the .cdx-mixin-css-icon() mixin to an empty <span>, passing in at least the icon param.
//    This method should suffice for any square icon that can exist as a standalone element. This
//    mixin applies all of the other mixins inside this file. See Message.vue for sample usage.
// 2. Apply the individual CSS icon mixins for background, size, alignment, and/or background-image
//    rules to any element. This can be used to apply an icon within another element, like the
//    <select> handle. See Select.vue for sample usage.
//
// These mixins account for icons that vary by reading direction or language.
//

// Get the associated min-size-icon from a size-icon token.
.get-calculated-min-size-icon( @param-size-icon ) when ( @param-size-icon = @size-icon-medium ) {
	@calculated-min-size-icon: @min-size-icon-medium;
}
.get-calculated-min-size-icon( @param-size-icon ) when ( @param-size-icon = @size-icon-small ) {
	@calculated-min-size-icon: @min-size-icon-small;
}
.get-calculated-min-size-icon( @param-size-icon ) when ( @param-size-icon = @size-icon-x-small ) {
	@calculated-min-size-icon: @min-size-icon-x-small;
}

//
// Get background rules for a CSS icon (or mask rules for icons in buttons).
//
// @param {string} size-icon - The size of the icon, used to set background-size
// @param {string} background-position - The background position value
// @param {boolean} is-button-icon: Whether this icon is inside of a <button> element.
//
.cdx-mixin-css-icon-background( @param-size-icon: @size-icon-medium, @param-background-position: @background-position-base, @param-is-button-icon: false ) {
	.get-calculated-min-size-icon( @param-size-icon );

	.set-background-rules() {
		background-position: @param-background-position;
		background-repeat: no-repeat;
		// Set background size to the relative @param-size-icon or to @calculated-min-size-icon, whichever is larger.
		// This ensures that the icon will never appear smaller than @calculated-min-size-icon.
		// Escape the max() call to prevent older Less versions from trying to do the max() calculation at compile time.
		background-size: calc( ~'max( @{param-size-icon}, @{calculated-min-size-icon} )' );
	}

	& when ( @param-is-button-icon = false ) {
		.set-background-rules();
	}

	& when ( @param-is-button-icon = true ) {
		// Fall back to background rules.
		@supports not ( mask-image: none ) and ( -webkit-mask-image: none ) {
			.set-background-rules();
		}

		@supports ( mask-image: none ) or ( -webkit-mask-image: none ) {
			/* stylelint-disable plugin/no-unsupported-browser-features */
			// Support Chrome
			-webkit-mask-size: calc( ~'max( @{param-size-icon}, @{calculated-min-size-icon} )' );
			mask-size: calc( ~'max( @{param-size-icon}, @{calculated-min-size-icon} )' );
			// Support Chrome
			-webkit-mask-repeat: no-repeat;
			mask-repeat: no-repeat;
			// Support Chrome
			-webkit-mask-position: @param-background-position;
			mask-position: @param-background-position;
			/* stylelint-enable plugin/no-unsupported-browser-features */
		}
	}
}

//
// Get size styles for a CSS icon.
//
// This sets min-width, min-height, width, and height for a square icon.
//
// @param {string} size-icon: The size of the icon (base, small, x-small, or indicator)
//
.cdx-mixin-css-icon-size( @param-size-icon: @size-icon-medium ) {
	.get-calculated-min-size-icon( @param-size-icon );
	// Set the default icon size.
	min-width: @calculated-min-size-icon;
	min-height: @calculated-min-size-icon;
	// Scale width/height of the span with font size.
	width: @param-size-icon;
	height: @param-size-icon;
}

//
// Get alignment styles for a CSS icon.
//
// @param {string} vertical-align: The vertical-align value
//
.cdx-mixin-css-icon-alignment( @param-vertical-align: text-bottom ) {
	display: inline-block;
	// Vertically align surrounding text in inline, inline-block, and table contexts.
	vertical-align: @param-vertical-align;
}

//
// Handle an icon within a button.
//
// For browsers that support mask-image, this mixin will apply the background image as a mask, so
// that background-color rules in the Button styles can set the icon color.
//
// For browsers that don't support mask-image, this mixin sets the icon as a background image with
// the color set via opacity.
//
// @param {string} icon - The icon to show
//
.cdx-mixin-css-icon-button-icon-background-image( @param-icon ) {
	// TODO: remove in favor of a token.
	@color-black: #000000;
	// TODO: update variable name to match new token once it exists.
	// Older versions of Less don't support using escape() on colors, so use %A instead
	@escaped-color-black: %( '%A', @color-black );
	@background-image-url: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="@{escaped-color-black}">@{param-icon}</svg>';

	// Chrome requires the -webkit prefix, so we must use it in all @supports queries.
	@supports not ( mask-image: none ) and ( -webkit-mask-image: none ) {
		// Support Firefox v39-52: Fallback for @color-base icons.
		background-image: url( @background-image-url );
		// Set icon color close to @color-base.
		opacity: @opacity-icon-base;

		// Support Firefox v39-52: Fallback for @color-inverted icons.
		.cdx-button:not( .cdx-button--type-quiet ):disabled &,
		.cdx-button--type-primary.cdx-button--action-progressive &,
		.cdx-button--type-primary.cdx-button--action-destructive & {
			// Set icon color close to @color-inverted.
			filter: invert( 1 );
		}
	}

	// For browsers that support it, use mask-image to set the SVG so we can change the color with
	// much more granularity in the Button styles via the background color.
	@supports ( mask-image: none ) or ( -webkit-mask-image: none ) {
		background-image: none;
		// Support Chrome
		/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
		-webkit-mask-image: url( @background-image-url );
		/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
		mask-image: url( @background-image-url );
		transition-property: @transition-property-icon-css-only;
		transition-duration: @transition-duration-base;
	}
}

//
// Get a background image rule for an icon.
//
// Note that in RTL contexts, this mixin requires `dir="rtl"` either on the icon element itself
// or on the <html> element.
//
// This mixin takes in an icon, which is really a Less variable generated by the codex-icons
// package. These variables are lists of icon data that contain:
// 1. The default icon path (a string)
// 2. Whether the icon should flip in RTL ('true' or 'false')
// 3. Exceptions to the flip rule ('false' or a selector string that will rule out languages)
// 4. RTL-specific icon path ('false' or the path string)
// 5. Whether the icon has language-specific variants ('true' or 'false')
// 6+ If there are language-specific variants, they will be included as string pairs after the other
//   icon data. The first item in the pair is a lang code and the second is the icon path for that
//   language.
//
// @param {string} icon - The icon to show (follows the pattern @cdx-icon-icon-name, e.g. @cdx-icon-info-filled )
// @param {hex} fill-color - The fill color of the icon (defaults to @color-base)
//
.cdx-mixin-css-icon-background-image( @param-icon, @param-fill-color: @color-base, @param-is-button-icon: false ) {
	// Extract icon data from the list.
	@default-icon: extract( @param-icon, 1 );
	@should-flip: extract( @param-icon, 2 );
	@flip-exceptions: extract( @param-icon, 3 );
	@rtl-icon: extract( @param-icon, 4 );
	@has-lang-variants: extract( @param-icon, 5 );

	// Escape the hash in the hex code before we use it in the background-image rule.
	// Older versions of Less don't support using escape() on colors, so use %A instead
	@escaped-hex-code: %( '%A', @param-fill-color );

	// Add default image.
	& when not ( @param-is-button-icon ) {
		background-image: url( 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="@{escaped-hex-code}">@{default-icon}</svg>' );
	}
	& when ( @param-is-button-icon ) {
		.cdx-mixin-css-icon-button-icon-background-image( @default-icon );
	}

	// Flip icons with no shouldFlip exceptions.
	& when ( @should-flip = 'true' ) and ( @flip-exceptions = 'false' ) {
		&[ dir='rtl' ],
		html[ dir='rtl' ] &:not( [ dir='ltr' ] ) {
			transform: scaleX( -1 );
		}
	}

	// Flip icons with shouldFlip exceptions.
	& when ( @should-flip = 'true' ) and not ( @flip-exceptions = 'false' ) {
		// Create a selector string out of each exception lang code.
		// Final selector will look like `:not( :lang( he ) ):not( :lang( yi ) )`
		@exceptions-selector: e( replace( @flip-exceptions, '(^| )([^ ]+)', ':not( :lang( $2 ) )', 'g' ) );

		&[ dir='rtl' ],
		html[ dir='rtl' ] &:not( [ dir='ltr' ] ) {
			&@{exceptions-selector} {
				transform: scaleX( -1 );
			}
		}
	}

	// If an icon has an RTL-specific icon, apply it.
	& when not ( @rtl-icon = 'false' ) {
		&[ dir='rtl' ],
		html[ dir='rtl' ] &:not( [ dir='ltr' ] ) {
			& when not ( @param-is-button-icon ) {
				background-image: url( 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="@{escaped-hex-code}">@{rtl-icon}</svg>' );
			}
			& when ( @param-is-button-icon ) {
				.cdx-mixin-css-icon-button-icon-background-image( @rtl-icon );
			}
		}
	}

	// Set language-specific icons.
	& when ( @has-lang-variants = 'true' ) {
		@icon-list-length: length( @param-icon );

		// Language-specific icons are represented by list items in @param-icon. They consist of a
		// lang code, e.g. ar, and an icon path.
		// Since we can't use modern Less features in MediaWiki, we need a recursive mixin.
		.get-lang-rules( @i: 6 ) when ( @i <= @icon-list-length ) {
			@lang-data: extract( @param-icon, @i );
			@lang-code: extract( @lang-data, 1 );
			@lang-icon: extract( @lang-data, 2 );

			&:lang( @{lang-code} ) {
				& when not ( @param-is-button-icon ) {
					background-image: url( 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="@{escaped-hex-code}">@{lang-icon}</svg>' );
				}
				& when ( @param-is-button-icon ) {
					.cdx-mixin-css-icon-button-icon-background-image( @lang-icon );
				}
			}
			.get-lang-rules( @i + 1 );
		}

		.get-lang-rules();
	}
}

//
// Create a square, standalone CSS icon.
//
// @param {string} icon - The icon to show (follows the pattern @cdx-icon-icon-name, e.g. @cdx-icon-info-filled )
// @param {hex} fill-color - The fill color of the icon
// @param {string} size-icon: The size of the icon
// @param {boolean} is-button-icon: Whether this icon is inside of a <button> element.
// @param {string} background-position - The background position value
// @param {string} vertical-align: The vertical-align value
//
/* stylelint-disable stylistic/indentation */
.cdx-mixin-css-icon(
	@param-icon,
	@param-fill-color: @color-base,
	@param-size-icon: @size-icon-medium,
	@param-is-button-icon: false,
	@param-background-position: @background-position-base,
	@param-vertical-align: text-bottom
) {
/* stylelint-enable stylistic/indentation */
	.cdx-mixin-css-icon-background( @param-size-icon, @param-background-position, @param-is-button-icon );
	.cdx-mixin-css-icon-size( @param-size-icon );
	.cdx-mixin-css-icon-alignment( @param-vertical-align );
	.cdx-mixin-css-icon-background-image( @param-icon, @param-fill-color, @param-is-button-icon );
}
