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 {
|
||||
background-image: url("images/check-white.svg");
|
||||
background-image: url("/images/check-white.svg");
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
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 'chip/chip';
|
||||
@import 'circle-icon/index';
|
||||
@import 'color-indicator/color-indicator';
|
||||
@import 'currency-display/index';
|
||||
@import 'currency-input/index';
|
||||
@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/solid';
|
||||
@ -8,63 +8,63 @@ $fa-font-path: 'fonts/fontawesome';
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
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-family: 'Roboto';
|
||||
font-style: normal;
|
||||
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-family: 'Roboto';
|
||||
font-style: normal;
|
||||
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-family: 'Roboto';
|
||||
font-style: normal;
|
||||
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-family: 'Roboto';
|
||||
font-style: normal;
|
||||
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-family: 'Roboto';
|
||||
font-style: normal;
|
||||
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-family: 'Euclid';
|
||||
font-style: normal;
|
||||
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-family: 'Euclid';
|
||||
font-style: italic;
|
||||
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-family: 'Euclid';
|
||||
font-style: normal;
|
||||
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;
|
||||
|
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…
Reference in New Issue
Block a user