-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(plasma-new-hope): add Note component
- Loading branch information
1 parent
e450fce
commit 6e4c90e
Showing
20 changed files
with
894 additions
and
1 deletion.
There are no files selected for viewing
37 changes: 37 additions & 0 deletions
37
packages/plasma-new-hope/src/components/Note/Note.styles.ts
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,37 @@ | ||
import { styled } from '@linaria/react'; | ||
import { css } from '@linaria/core'; | ||
|
||
export const base = css` | ||
position: relative; | ||
display: flex; | ||
box-sizing: border-box; | ||
`; | ||
|
||
export const ContentBefore = styled.div``; | ||
|
||
export const ContentWrapper = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
box-sizing: border-box; | ||
`; | ||
|
||
export const Title = styled.div``; | ||
export const TitleHelper = styled.div` | ||
visibility: hidden; | ||
position: absolute; | ||
z-index: -9999; | ||
opacity: 0; | ||
`; | ||
|
||
export const Text = styled.span` | ||
display: block; | ||
position: relative; | ||
`; | ||
export const TextHelper = styled.span` | ||
visibility: hidden; | ||
position: absolute; | ||
z-index: -9999; | ||
opacity: 0; | ||
top: 0; | ||
left: 0; | ||
`; |
41 changes: 41 additions & 0 deletions
41
packages/plasma-new-hope/src/components/Note/Note.tokens.ts
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,41 @@ | ||
export const classes = { | ||
stretch: 'note-stretch', | ||
contentAlignCenter: 'note-content-align-center', | ||
}; | ||
|
||
export const privateTokens = { | ||
width: '--plasma_private-note-width', | ||
height: '--plasma_private-note-height', | ||
contentWidthWithOffset: '--plasma_private-note-content-width-with-offset', | ||
}; | ||
|
||
export const tokens = { | ||
background: '--plasma-note-background', | ||
color: '--plasma-note-color', | ||
contentBeforeColor: '--plasma-note-content-before-color', | ||
|
||
padding: '--plasma-note-padding', | ||
paddingScalable: '--plasma-note-padding-scalable', | ||
borderRadius: '--plasma-note-border-radius', | ||
gap: '--plasma-note-gap', | ||
gapScalable: '--plasma-note-gap-scalable', | ||
contentGap: '--plasma-note-content-gap', | ||
|
||
fixedContentBeforeWidth: '--plasma-note-fixed-content-before-width', | ||
fixedContentBeforeHeight: '--plasma-note-fixed-content-before-height', | ||
fixedContentBeforePadding: '--plasma-note-fixed-content-before-padding', | ||
|
||
titleFontFamily: '--plasma-note-title-font-family', | ||
titleFontSize: '--plasma-note-title-font-size', | ||
titleFontStyle: '--plasma-note-title-font-style', | ||
titleFontWeight: '--plasma-note-title-font-weight', | ||
titleLetterSpacing: '--plasma-note-title-letter-spacing', | ||
titleLineHeight: '--plasma-note-title-line-height', | ||
|
||
textFontFamily: '--plasma-note-text-font-family', | ||
textFontSize: '--plasma-note-text-font-size', | ||
textFontStyle: '--plasma-note-text-font-style', | ||
textFontWeight: '--plasma-note-text-font-weight', | ||
textLetterSpacing: '--plasma-note-text-letter-spacing', | ||
textLineHeight: '--plasma-note-text-line-height', | ||
}; |
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,167 @@ | ||
import React, { forwardRef, useEffect, useLayoutEffect, useRef, useState } from 'react'; | ||
import type { CSSProperties } from 'react'; | ||
import { useResizeObserver } from '@salutejs/plasma-core'; | ||
|
||
import { canUseDOM, cx, getSizeValueFromProp } from '../../utils'; | ||
import type { RootProps } from '../../engines'; | ||
|
||
import type { NoteProps } from './Note.types'; | ||
import { base as viewCSS } from './variations/_view/base'; | ||
import { base as sizeCSS } from './variations/_size/base'; | ||
import { base, ContentBefore, ContentWrapper, Text, TextHelper, Title, TitleHelper } from './Note.styles'; | ||
import { classes, privateTokens, tokens } from './Note.tokens'; | ||
|
||
export const noteRoot = (Root: RootProps<HTMLDivElement, NoteProps>) => | ||
forwardRef<HTMLDivElement, NoteProps>( | ||
( | ||
{ | ||
className, | ||
style, | ||
title, | ||
text, | ||
contentBefore, | ||
contentBeforeSizing = 'fixed', | ||
size, | ||
view, | ||
stretch, | ||
width, | ||
height, | ||
...rest | ||
}, | ||
outerRef, | ||
) => { | ||
const [innerText, setInnerText] = useState(text); | ||
const [contentBeforeWidth, setContentBeforeWidth] = useState( | ||
contentBefore ? `var(${tokens.fixedContentBeforeWidth})` : '0', | ||
); | ||
|
||
const contentWrapperRef = useRef<HTMLDivElement>(null); | ||
const contentBeforeRef = useRef<HTMLDivElement>(null); | ||
const titleRef = useRef<HTMLDivElement>(null); | ||
const titleHelperRef = useRef<HTMLDivElement>(null); | ||
const textRef = useRef<HTMLSpanElement>(null); | ||
const textHelperRef = useRef<HTMLSpanElement>(null); | ||
const textRenderHelperRef = useRef<HTMLSpanElement>(null); | ||
|
||
const innerWidth = width ? getSizeValueFromProp(width) : 'fit-content'; | ||
const innerHeight = height ? getSizeValueFromProp(height) : 'fit-content'; | ||
|
||
const contentGapToken = contentBeforeSizing === 'scalable' ? tokens.gapScalable : tokens.gap; | ||
const contentWidthWithOffsetToken = contentBefore | ||
? `calc(100% - ${contentBeforeWidth} - var(${contentGapToken}))` | ||
: '100%'; | ||
|
||
const getTruncatedText = () => { | ||
if (!canUseDOM || !text || !contentWrapperRef?.current || !textRenderHelperRef?.current) { | ||
return; | ||
} | ||
|
||
const contentHeight = contentWrapperRef.current.offsetHeight; | ||
const titleHeight = titleHelperRef.current?.offsetHeight || 0; | ||
|
||
const contentGap = Number(window.getComputedStyle(contentWrapperRef.current).rowGap.replace('px', '')); | ||
|
||
const textAvailableHeight = contentHeight - titleHeight - contentGap; | ||
|
||
textRenderHelperRef.current.textContent = text; | ||
|
||
if (textRenderHelperRef.current.offsetHeight <= textAvailableHeight) { | ||
setInnerText(text); | ||
return; | ||
} | ||
|
||
let fullText = `${text.slice(0, -3)}...`; | ||
|
||
for (let i = text.length - 1; i >= 0; i -= 1) { | ||
textRenderHelperRef.current.textContent = fullText; | ||
|
||
if (textRenderHelperRef.current.offsetHeight <= textAvailableHeight) { | ||
break; | ||
} | ||
|
||
fullText = `${fullText.slice(0, i)}...`; | ||
} | ||
|
||
setInnerText(fullText); | ||
}; | ||
|
||
useResizeObserver(contentWrapperRef, getTruncatedText); | ||
|
||
useLayoutEffect(() => { | ||
getTruncatedText(); | ||
}, [text, contentBefore, contentBeforeSizing, stretch]); | ||
|
||
useEffect(() => { | ||
if (!contentBeforeRef?.current) { | ||
return; | ||
} | ||
|
||
if (contentBeforeSizing === 'scalable') { | ||
setContentBeforeWidth(`${contentBeforeRef.current.offsetWidth}px`); | ||
return; | ||
} | ||
|
||
setContentBeforeWidth(`var(${tokens.fixedContentBeforeWidth})`); | ||
}, [contentBefore, contentBeforeSizing]); | ||
|
||
return ( | ||
<Root | ||
ref={outerRef} | ||
className={cx( | ||
className, | ||
stretch && classes.stretch, | ||
contentBeforeSizing === 'scalable' && classes.contentAlignCenter, | ||
)} | ||
view={view} | ||
size={size} | ||
style={ | ||
{ | ||
...style, | ||
[privateTokens.width]: innerWidth, | ||
[privateTokens.height]: innerHeight, | ||
[privateTokens.contentWidthWithOffset]: contentWidthWithOffsetToken, | ||
} as CSSProperties | ||
} | ||
{...rest} | ||
> | ||
{contentBefore && <ContentBefore ref={contentBeforeRef}>{contentBefore}</ContentBefore>} | ||
<ContentWrapper ref={contentWrapperRef}> | ||
{title && ( | ||
<> | ||
<Title ref={titleRef}>{title}</Title> | ||
<TitleHelper ref={titleHelperRef}>C</TitleHelper> | ||
</> | ||
)} | ||
{innerText && ( | ||
<> | ||
<Text ref={textRef}> | ||
{innerText} | ||
<TextHelper ref={textRenderHelperRef}>C</TextHelper> | ||
</Text> | ||
<TextHelper ref={textHelperRef}>C</TextHelper> | ||
</> | ||
)} | ||
</ContentWrapper> | ||
</Root> | ||
); | ||
}, | ||
); | ||
|
||
export const noteConfig = { | ||
name: 'Note', | ||
tag: 'div', | ||
layout: noteRoot, | ||
base, | ||
variations: { | ||
view: { | ||
css: viewCSS, | ||
}, | ||
size: { | ||
css: sizeCSS, | ||
}, | ||
}, | ||
defaults: { | ||
view: 'default', | ||
size: 'm', | ||
}, | ||
}; |
41 changes: 41 additions & 0 deletions
41
packages/plasma-new-hope/src/components/Note/Note.types.ts
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,41 @@ | ||
import type { HTMLAttributes, ReactNode } from 'react'; | ||
|
||
export type NoteProps = { | ||
/** | ||
* Заголовок к Note. | ||
*/ | ||
title?: string; | ||
/** | ||
* Текст к Note. | ||
*/ | ||
text?: string; | ||
/** | ||
* Слот под иконку слева от контента. | ||
*/ | ||
contentBefore?: ReactNode; | ||
/** | ||
* Размерность слота под иконку. | ||
* @default 'fixed' | ||
*/ | ||
contentBeforeSizing?: 'fixed' | 'scalable'; | ||
/** | ||
* Компонент растягивается на всю доступную ширину и высоту. | ||
*/ | ||
stretch?: boolean; | ||
/** | ||
* Ширина компонента. | ||
*/ | ||
width?: string | number; | ||
/** | ||
* Высота компонента. | ||
*/ | ||
height?: string | number; | ||
/** | ||
* Вид компонента. | ||
*/ | ||
view?: string; | ||
/** | ||
* Размер компонента. | ||
*/ | ||
size?: string; | ||
} & HTMLAttributes<HTMLDivElement>; |
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,2 @@ | ||
export { noteRoot, noteConfig } from './Note'; | ||
export { tokens as noteTokens, classes as noteClasses } from './Note.tokens'; |
67 changes: 67 additions & 0 deletions
67
packages/plasma-new-hope/src/components/Note/variations/_size/base.ts
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,67 @@ | ||
import { css } from '@linaria/core'; | ||
|
||
import { classes, privateTokens, tokens } from '../../Note.tokens'; | ||
import { ContentBefore, ContentWrapper, Text, TextHelper, Title, TitleHelper } from '../../Note.styles'; | ||
import { applyEllipsis } from '../../../../mixins'; | ||
|
||
export const base = css` | ||
padding: var(${tokens.padding}); | ||
border-radius: var(${tokens.borderRadius}); | ||
height: var(${privateTokens.height}); | ||
width: var(${privateTokens.width}); | ||
gap: var(${tokens.gap}); | ||
&.${classes.stretch} { | ||
width: 100%; | ||
height: 100%; | ||
} | ||
${ContentBefore} { | ||
width: var(${tokens.fixedContentBeforeWidth}); | ||
height: var(${tokens.fixedContentBeforeHeight}); | ||
padding: var(${tokens.fixedContentBeforePadding}); | ||
box-sizing: border-box; | ||
} | ||
&.${classes.contentAlignCenter} { | ||
align-items: center; | ||
padding: var(${tokens.paddingScalable}); | ||
gap: var(${tokens.gapScalable}); | ||
${ContentBefore} { | ||
width: unset; | ||
height: unset; | ||
padding: unset; | ||
} | ||
} | ||
${ContentWrapper} { | ||
width: var(${privateTokens.contentWidthWithOffset}); | ||
gap: var(${tokens.contentGap}); | ||
} | ||
${Title}, ${TitleHelper} { | ||
width: 100%; | ||
min-height: var(${tokens.titleLineHeight}); | ||
font-family: var(${tokens.titleFontFamily}); | ||
font-size: var(${tokens.titleFontSize}); | ||
font-style: var(${tokens.titleFontStyle}); | ||
font-weight: var(${tokens.titleFontWeight}); | ||
letter-spacing: var(${tokens.titleLetterSpacing}); | ||
line-height: var(${tokens.titleLineHeight}); | ||
${applyEllipsis()} | ||
} | ||
${Text}, ${TextHelper} { | ||
font-family: var(${tokens.textFontFamily}); | ||
font-size: var(${tokens.textFontSize}); | ||
font-style: var(${tokens.textFontStyle}); | ||
font-weight: var(${tokens.textFontWeight}); | ||
letter-spacing: var(${tokens.textLetterSpacing}); | ||
line-height: var(${tokens.textLineHeight}); | ||
word-break: break-all; | ||
} | ||
`; |
23 changes: 23 additions & 0 deletions
23
packages/plasma-new-hope/src/components/Note/variations/_size/tokens.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,23 @@ | ||
[ | ||
"--plasma-note-padding", | ||
"--plasma-note-padding-scalable", | ||
"--plasma-note-border-radius", | ||
"--plasma-note-gap", | ||
"--plasma-note-gap-scalable", | ||
"--plasma-note-content-gap", | ||
"--plasma-note-fixed-content-before-width", | ||
"--plasma-note-fixed-content-before-height", | ||
"--plasma-note-fixed-content-before-padding", | ||
"--plasma-note-title-font-family", | ||
"--plasma-note-title-font-size", | ||
"--plasma-note-title-font-style", | ||
"--plasma-note-title-font-weight", | ||
"--plasma-note-title-letter-spacing", | ||
"--plasma-note-title-line-height", | ||
"--plasma-note-text-font-family", | ||
"--plasma-note-text-font-size", | ||
"--plasma-note-text-font-style", | ||
"--plasma-note-text-font-weight", | ||
"--plasma-note-text-letter-spacing", | ||
"--plasma-note-text-line-height" | ||
] |
14 changes: 14 additions & 0 deletions
14
packages/plasma-new-hope/src/components/Note/variations/_view/base.ts
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,14 @@ | ||
import { css } from '@linaria/core'; | ||
|
||
import { tokens } from '../../Note.tokens'; | ||
import { ContentBefore } from '../../Note.styles'; | ||
|
||
export const base = css` | ||
background: var(${tokens.background}); | ||
color: var(${tokens.color}); | ||
${ContentBefore} { | ||
color: var(${tokens.contentBeforeColor}); | ||
fill: var(${tokens.contentBeforeColor}); | ||
} | ||
`; |
Oops, something went wrong.