mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Add color indicator component (#10209)
Co-authored-by: Mark Stacey <markjstacey@gmail.com>
This commit is contained in:
parent
7f1f68399a
commit
a036b0ebcd
@ -203,7 +203,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__check-mark-icon {
|
&__check-mark-icon {
|
||||||
background-image: url("images/check-white.svg");
|
background-image: url("/images/check-white.svg");
|
||||||
height: 18px;
|
height: 18px;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
50
ui/app/components/ui/color-indicator/color-indicator.js
Normal file
50
ui/app/components/ui/color-indicator/color-indicator.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { COLORS } from '../../../helpers/constants/design-system'
|
||||||
|
|
||||||
|
export default function ColorIndicator({
|
||||||
|
size = 'small',
|
||||||
|
type = 'outlined',
|
||||||
|
color = COLORS.UI4,
|
||||||
|
borderColor,
|
||||||
|
iconClassName,
|
||||||
|
}) {
|
||||||
|
const colorIndicatorClassName = classnames('color-indicator', {
|
||||||
|
'color-indicator--filled': type === 'filled' || Boolean(iconClassName),
|
||||||
|
'color-indicator--partial-filled': type === 'partial-filled',
|
||||||
|
[`color-indicator--border-color-${borderColor}`]: Boolean(borderColor),
|
||||||
|
[`color-indicator--color-${color}`]: true,
|
||||||
|
[`color-indicator--size-${size}`]: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={colorIndicatorClassName}>
|
||||||
|
{iconClassName ? (
|
||||||
|
<i className={classnames('color-indicator__icon', iconClassName)} />
|
||||||
|
) : (
|
||||||
|
<span className="color-indicator__inner-circle" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorIndicator.SIZES = {
|
||||||
|
LARGE: 'large',
|
||||||
|
MEDIUM: 'medium',
|
||||||
|
SMALL: 'small,',
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorIndicator.TYPES = {
|
||||||
|
FILLED: 'filled',
|
||||||
|
PARTIAL: 'partial-filled',
|
||||||
|
OUTLINE: 'outline',
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorIndicator.propTypes = {
|
||||||
|
color: PropTypes.oneOf(Object.values(COLORS)),
|
||||||
|
borderColor: PropTypes.oneOf(Object.values(COLORS)),
|
||||||
|
size: PropTypes.oneOf(Object.values(ColorIndicator.SIZES)),
|
||||||
|
iconClassName: PropTypes.string,
|
||||||
|
type: PropTypes.oneOf(Object.values(ColorIndicator.TYPES)),
|
||||||
|
}
|
61
ui/app/components/ui/color-indicator/color-indicator.scss
Normal file
61
ui/app/components/ui/color-indicator/color-indicator.scss
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
@use "utilities";
|
||||||
|
@use "design-system";
|
||||||
|
|
||||||
|
$sizes: (
|
||||||
|
'large': 6,
|
||||||
|
'medium': 5,
|
||||||
|
'small': 4,
|
||||||
|
);
|
||||||
|
|
||||||
|
.color-indicator {
|
||||||
|
$self: &;
|
||||||
|
|
||||||
|
border: 1px solid transparent;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&__inner-circle {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $variant, $size in $sizes {
|
||||||
|
&--size-#{$variant} {
|
||||||
|
height: #{2 * $size}px;
|
||||||
|
width: #{2 * $size}px;
|
||||||
|
border-radius: #{$size}px;
|
||||||
|
|
||||||
|
#{$self}__inner-circle {
|
||||||
|
border-radius: #{$size}px;
|
||||||
|
height: #{$size}px;
|
||||||
|
width: #{$size}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$self}__icon {
|
||||||
|
font-size: #{1.25 * $size}px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $variant, $color in design-system.$color-map {
|
||||||
|
&--color-#{$variant} {
|
||||||
|
border-color: $color;
|
||||||
|
&#{$self}--partial-filled #{$self}__inner-circle {
|
||||||
|
background-color: $color;
|
||||||
|
}
|
||||||
|
&#{$self}--filled {
|
||||||
|
background-color: $color;
|
||||||
|
}
|
||||||
|
& #{$self}__icon {
|
||||||
|
color: #{utilities.choose-contrast-color($color)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// separate iterator to ensure borderColor takes precedence
|
||||||
|
@each $variant, $color in design-system.$color-map {
|
||||||
|
&--border-color-#{$variant} {
|
||||||
|
border-color: $color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { select } from '@storybook/addon-knobs'
|
||||||
|
import { COLORS } from '../../../helpers/constants/design-system'
|
||||||
|
import ColorIndicator from './color-indicator'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'ColorIndicator',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const colorIndicator = () => (
|
||||||
|
<ColorIndicator
|
||||||
|
size={select('size', ColorIndicator.SIZES, ColorIndicator.SIZES.LARGE)}
|
||||||
|
type={select('type', ColorIndicator.TYPES, ColorIndicator.TYPES.FILLED)}
|
||||||
|
color={select('color', COLORS, COLORS.PRIMARY1)}
|
||||||
|
borderColor={select('borderColor', { NONE: undefined, ...COLORS })}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const withIcon = () => (
|
||||||
|
<ColorIndicator
|
||||||
|
size={select('size', ColorIndicator.SIZES, ColorIndicator.SIZES.LARGE)}
|
||||||
|
type={select('type', ColorIndicator.TYPES, ColorIndicator.TYPES.FILLED)}
|
||||||
|
color={select('color', COLORS, COLORS.PRIMARY1)}
|
||||||
|
iconClassName="fa fa-question"
|
||||||
|
borderColor={select('borderColor', { NONE: undefined, ...COLORS })}
|
||||||
|
/>
|
||||||
|
)
|
1
ui/app/components/ui/color-indicator/index.js
Normal file
1
ui/app/components/ui/color-indicator/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './color-indicator'
|
@ -9,6 +9,7 @@
|
|||||||
@import 'check-box/index';
|
@import 'check-box/index';
|
||||||
@import 'chip/chip';
|
@import 'chip/chip';
|
||||||
@import 'circle-icon/index';
|
@import 'circle-icon/index';
|
||||||
|
@import 'color-indicator/color-indicator';
|
||||||
@import 'currency-display/index';
|
@import 'currency-display/index';
|
||||||
@import 'currency-input/index';
|
@import 'currency-input/index';
|
||||||
@import 'dialog/dialog';
|
@import 'dialog/dialog';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
$fa-font-path: 'fonts/fontawesome';
|
$fa-font-path: '/fonts/fontawesome';
|
||||||
|
|
||||||
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome';
|
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome';
|
||||||
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/solid';
|
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/solid';
|
||||||
@ -8,63 +8,63 @@ $fa-font-path: 'fonts/fontawesome';
|
|||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
|
src: local('Roboto Thin'), local('Roboto-Thin'), url('/fonts/Roboto/Roboto-Thin.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
|
src: local('Roboto Light'), local('Roboto-Light'), url('/fonts/Roboto/Roboto-Light.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
|
src: local('Roboto'), local('Roboto-Regular'), url('/fonts/Roboto/Roboto-Regular.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
|
src: local('Roboto Medium'), local('Roboto-Medium'), url('/fonts/Roboto/Roboto-Medium.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
|
src: local('Roboto Bold'), local('Roboto-Bold'), url('/fonts/Roboto/Roboto-Bold.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
|
src: local('Roboto Black'), local('Roboto-Black'), url('/fonts/Roboto/Roboto-Black.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Euclid';
|
font-family: 'Euclid';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: url('fonts/Euclid/EuclidCircularB-Regular-WebXL.ttf') format('truetype');
|
src: url('/fonts/Euclid/EuclidCircularB-Regular-WebXL.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Euclid';
|
font-family: 'Euclid';
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: url('fonts/Euclid/EuclidCircularB-RegularItalic-WebXL.ttf') format('truetype');
|
src: url('/fonts/Euclid/EuclidCircularB-RegularItalic-WebXL.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Euclid';
|
font-family: 'Euclid';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: url('fonts/Euclid/EuclidCircularB-Bold-WebXL.ttf') format('truetype');
|
src: url('/fonts/Euclid/EuclidCircularB-Bold-WebXL.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
$font-family: Euclid, Roboto, Helvetica, Arial, sans-serif;
|
$font-family: Euclid, Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
75
ui/app/css/utilities/_colors.scss
Normal file
75
ui/app/css/utilities/_colors.scss
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
@use "sass:math";
|
||||||
|
@use "sass:map";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the luminance for a color.
|
||||||
|
* See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
|
||||||
|
*
|
||||||
|
* Note:
|
||||||
|
* @Gudahtt and @brad-decker have identified a potential performance concern
|
||||||
|
* that **might** impact build times if this function becomes widely used. It
|
||||||
|
* has not been validated, and is predicated on user-land math.pow functions
|
||||||
|
* prior to dart-sass 1.25.0. However, if the build times of sass raise
|
||||||
|
* exponentially a lookup table of all possible values for the math.pow call
|
||||||
|
* can be used to bypass the issue. This will require some logic updates as
|
||||||
|
* well. For reference, the blog post where the performance concern was
|
||||||
|
* originally made is here https://bit.ly/3c3qXzq (css-tricks)
|
||||||
|
*/
|
||||||
|
@function luminance($color) {
|
||||||
|
$channels: (
|
||||||
|
'red': red($color),
|
||||||
|
'green': green($color),
|
||||||
|
'blue': blue($color),
|
||||||
|
);
|
||||||
|
|
||||||
|
$values: (
|
||||||
|
'red': map.get($channels, 'red') / 255,
|
||||||
|
'blue': map.get($channels, 'red') / 255,
|
||||||
|
'green': map.get($channels, 'red') / 255,
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $name, $value in $values {
|
||||||
|
@if $value <= 0.03928 {
|
||||||
|
$value: $value / 12.92;
|
||||||
|
}
|
||||||
|
|
||||||
|
@else {
|
||||||
|
$value: ($value + 0.055) / 1.055;
|
||||||
|
$value: math.pow($value, 2.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
$values: map.merge($values, ($name: $value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@return (0.2126 * map.get($values, 'red'))
|
||||||
|
+ (0.7152 * map.get($values, 'green'))
|
||||||
|
+ (0.0722 * map.get($values, 'blue'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the contrast ratio between two colors.
|
||||||
|
* See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
|
||||||
|
*/
|
||||||
|
@function contrast($back, $front) {
|
||||||
|
$backLum: luminance($back) + 0.05;
|
||||||
|
$foreLum: luminance($front) + 0.05;
|
||||||
|
|
||||||
|
@return max($backLum, $foreLum) / min($backLum, $foreLum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether to use dark or light text on top of given color.
|
||||||
|
* Returns black for dark text and white for light text.
|
||||||
|
*/
|
||||||
|
@function choose-contrast-color($color) {
|
||||||
|
$lightContrast: contrast($color, white);
|
||||||
|
$darkContrast: contrast($color, black);
|
||||||
|
|
||||||
|
@if ($lightContrast > $darkContrast) {
|
||||||
|
@return white;
|
||||||
|
}
|
||||||
|
|
||||||
|
@else {
|
||||||
|
@return black;
|
||||||
|
}
|
||||||
|
}
|
1
ui/app/css/utilities/index.scss
Normal file
1
ui/app/css/utilities/index.scss
Normal file
@ -0,0 +1 @@
|
|||||||
|
@forward 'colors';
|
Loading…
x
Reference in New Issue
Block a user