-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #289 from sima-land/285-badge-upd
Шаг 2 #285 Badge: доработать в соответствии с новыми дизайн-гайдами
- Loading branch information
Showing
8 changed files
with
232 additions
and
116 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
docs/stories/common/components/badge/10-gradient.stories.meta.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"parameters": { | ||
"sources": { | ||
"extraSources": ["./gradient.m.scss"] | ||
} | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
docs/stories/common/components/badge/10-gradient.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Badge } from '@sima-land/moleculas/common/components/badge'; | ||
import styles from './gradient.m.scss'; | ||
|
||
export const meta = { | ||
category: 'common/Badge', | ||
title: 'Заливка градиентом', | ||
parameters: { | ||
layout: 'padded', | ||
}, | ||
}; | ||
|
||
export default function DifferentStates() { | ||
return ( | ||
// ВАЖНО: для использования градиентов необходимо наличие соответствующих css-переменных выше в DOM | ||
<div className={styles.container}> | ||
<Badge | ||
shape='round' | ||
coloring='fill' | ||
color='gradient/gold' | ||
fields={[ | ||
{ | ||
type: 'svg-url', | ||
value: 'public/images/logo_white.svg', | ||
}, | ||
{ | ||
type: 'text', | ||
value: 'Сделано в Сима-ленд', | ||
}, | ||
]} | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
@use 'pkg:@sima-land/ui-nucleons/colors.scss'; | ||
@use 'pkg:@sima-land/ui-nucleons/gradients.scss'; | ||
|
||
// предоставляем переменные цветов и градиентов (для использования градиентов в Badge это необходимо) | ||
.container { | ||
@include colors.properties; | ||
@include gradients.properties; | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { tokenToCustomProperty } from '../utils'; | ||
|
||
describe('tokenToCustomProperty', () => { | ||
it('should return value if it is not a string', () => { | ||
expect(tokenToCustomProperty(undefined)).toBe(undefined); | ||
}); | ||
|
||
it('should return value if it is not a token', () => { | ||
expect(tokenToCustomProperty('something')).toBe('something'); | ||
expect(tokenToCustomProperty('#000')).toBe('#000'); | ||
}); | ||
|
||
it('should return value if it is color token but color not exist', () => { | ||
expect(tokenToCustomProperty('color/foo')).toBe('color/foo'); | ||
}); | ||
|
||
it('should return css property if it is valid color token', () => { | ||
expect(tokenToCustomProperty('color/basic-blue')).toBe('var(--color-basic-blue)'); | ||
}); | ||
|
||
it('should return value if it is gradient token but gradient not exist', () => { | ||
expect(tokenToCustomProperty('gradient/hello')).toBe('gradient/hello'); | ||
}); | ||
|
||
it('should return css property if it is valid gradient token', () => { | ||
expect(tokenToCustomProperty('gradient/gold')).toBe('var(--gradient-gold)'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import type { ReactNode } from 'react'; | ||
import type { BadgeField } from './types'; | ||
import { COLORS } from '@sima-land/ui-nucleons/colors'; | ||
import { GRADIENTS } from '@sima-land/ui-nucleons/gradients'; | ||
import { Timer } from '@sima-land/ui-nucleons/timer'; | ||
import styles from './badge.m.scss'; | ||
|
||
/** | ||
* Если переданное значение является токеном цвета/градиента, вернёт CSS-значение с соответствующей CSS-переменной. | ||
* Иначе вернет само значение. | ||
* @param value Значение. | ||
* @return Значение или undefined. | ||
*/ | ||
export function tokenToCustomProperty(value: string | undefined): string | undefined { | ||
if (typeof value !== 'string') { | ||
return value; | ||
} | ||
|
||
if (value.startsWith('color/')) { | ||
const colorName = value.replace('color/', ''); | ||
|
||
if (!COLORS.has(colorName as any)) { | ||
return value; | ||
} | ||
|
||
return `var(--color-${colorName})`; | ||
} | ||
|
||
if (value.startsWith('gradient/')) { | ||
const gradientName = value.replace('gradient/', ''); | ||
|
||
if (!GRADIENTS.has(gradientName as any)) { | ||
return value; | ||
} | ||
|
||
return `var(--gradient-${gradientName})`; | ||
} | ||
|
||
return value; | ||
} | ||
|
||
/** | ||
* Сформирует содержимое шильдика на основе списка полей. | ||
* Текстовые поля (type === text | timer) идущие подряд, будут объединены в один span. | ||
* Между текстовыми полями в span будет вставлен 1 пробел. | ||
* @param fields Поля. | ||
* @return СОдержимое шильдика. | ||
*/ | ||
export function renderFields(fields: BadgeField[]): ReactNode[] { | ||
const result: ReactNode[] = []; | ||
|
||
let item: null | Array<ReactNode> = null; | ||
|
||
for (const field of fields) { | ||
if (!item) { | ||
switch (field.type) { | ||
case 'text': { | ||
item = [field.value]; | ||
break; | ||
} | ||
|
||
case 'timer': { | ||
item = [<Timer key={0} date={field.value} timeout={1000 * 60} format={formatDistance} />]; | ||
break; | ||
} | ||
|
||
case 'svg-url': { | ||
result.push(<img key={result.length} className={styles.icon} src={field.value} alt='' />); | ||
break; | ||
} | ||
} | ||
} else { | ||
switch (field.type) { | ||
case 'text': { | ||
item.push(' '); | ||
item.push(field.value); | ||
break; | ||
} | ||
|
||
case 'timer': { | ||
item.push(' '); | ||
item.push( | ||
<Timer | ||
key={item.length} | ||
date={field.value} | ||
timeout={1000 * 60} | ||
format={formatDistance} | ||
/>, | ||
); | ||
break; | ||
} | ||
|
||
case 'svg-url': { | ||
// вложенный span нужен для того чтобы объединить `display: inline-flex` и `text-overflow: ellipsis` | ||
result.push( | ||
<span key={result.length} className={styles.content}> | ||
{item} | ||
</span>, | ||
); | ||
item = null; | ||
result.push(<img key={result.length} className={styles.icon} src={field.value} alt='' />); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (item) { | ||
result.push( | ||
<span key={result.length} className={styles.content}> | ||
{item} | ||
</span>, | ||
); | ||
item = null; | ||
} | ||
|
||
return result; | ||
} | ||
|
||
/** | ||
* Форматирует оставшееся время. | ||
* @param distance Оставшееся время. | ||
* @return Отформатированное время. | ||
*/ | ||
export function formatDistance({ | ||
days, | ||
hours, | ||
minutes, | ||
}: { | ||
days: number; | ||
hours: number; | ||
minutes: number; | ||
}) { | ||
return `${toTimePart(days)}:${toTimePart(hours % 24)}:${toTimePart(minutes % 60)}`; | ||
} | ||
|
||
/** | ||
* Форматирует число добавляя нули при необходимости. | ||
* @param n Число. | ||
* @return Отформатированное число. | ||
*/ | ||
export function toTimePart(n: number) { | ||
return `${n}`.padStart(2, '0'); | ||
} |