Skip to content

Commit f821a8c

Browse files
committed
add animation attribute to theme
1 parent 6736ba6 commit f821a8c

File tree

3 files changed

+63
-27
lines changed

3 files changed

+63
-27
lines changed

src/components/Button.tsx

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import React from 'react'
1+
import React, { useState } from 'react'
22
import { CSSProperties } from 'styled-components'
3-
import { getThemeContext, AppTheme } from '../theme/theme'
3+
import { useDebouncedCallback } from 'use-debounce/lib'
4+
import { P } from '../html'
5+
import { AppTheme, getThemeContext } from '../theme/theme'
46
import { createStyled, useCSSStyles, useInlineStyle } from '../theme/util'
5-
import { IconProps, Icon } from './Icon'
7+
import { Icon, IconProps } from './Icon'
68
import { Loading } from './Loading'
7-
import { P } from '../html'
9+
810

911
const ButtonWrapper = createStyled('button')
1012

@@ -35,21 +37,39 @@ export type Props = {
3537
}
3638

3739
export const Button = (props: Props) => {
38-
const { disabled } = props
40+
/* Style hooks */
3941
const type = props.type || ButtonType.Secondary
4042
const theme = React.useContext(getThemeContext())
4143

4244
const getStyle = useInlineStyle(theme, 'button')(props.style)
4345
const getCSSStyle = useCSSStyles(theme, 'button')(props.style)
4446

47+
/* Click handler */
48+
const [isClicked, setIsClicked] = useState(false)
49+
50+
const handleClicked =
51+
props.disabled || props.loading
52+
? undefined
53+
: () => {
54+
setIsClicked(true)
55+
onClicked()
56+
}
57+
58+
const onClicked = useDebouncedCallback(() => {
59+
props.onClick()
60+
setIsClicked(false)
61+
}, 300)
62+
4563
return (
4664
<ButtonWrapper
65+
className={isClicked ? 'animate' : ''}
4766
data-test-id={props.dataTestId}
48-
onClick={disabled || props.loading ? undefined : props.onClick}
49-
cssStyles={getCSSStyle(['common', mapTypeToStyleKey[type]], {
50-
...(disabled ? { opacity: 0.4, pointerEvents: 'none' } : {}),
51-
...(props.override || {}),
52-
})}
67+
onClick={handleClicked}
68+
disabled={props.disabled}
69+
cssStyles={getCSSStyle(
70+
['common', mapTypeToStyleKey[type]],
71+
props.override,
72+
)}
5373
>
5474
{props.icon && <Icon {...props.icon} />}
5575
<P

src/theme/theme.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export type CSSProperties = sc_CSSProperties & {
88
':disabled'?: CSSProperties
99
':readonly'?: CSSProperties
1010
'@media-mobile'?: CSSProperties
11+
'@animation'?: string
1112
}
1213

1314
export type AppTheme = {

src/theme/util.ts

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import styled, { css, CSSProperties } from 'styled-components'
12
import { AppTheme, MediaQuery } from './theme'
2-
import styled, { CSSProperties, css } from 'styled-components'
33

44
export const omitKeys = <T extends { [k: string]: unknown }>(
55
obj: T,
@@ -20,14 +20,24 @@ export const dynamicStyleKeys = [
2020
'@media-mobile',
2121
]
2222

23-
const nonPixelKeys = ['flexGrow', 'flexShrink', 'lineHeight', 'fontWeight']
23+
const animationKeys = ['@animation']
2424

25-
export const mapStylesToCSS = style =>
25+
const nonPixelKeys = [
26+
'flexGrow',
27+
'flexShrink',
28+
'fontWeight',
29+
'lineHeight',
30+
'opacity',
31+
]
32+
33+
export const mapStylesToCSS = (style) =>
2634
Object.entries(style)
2735
.map(
2836
([cssKey, cssValue]) =>
29-
`${cssKey.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)}: ${
30-
isNaN(cssValue as any) || nonPixelKeys.includes(cssKey) ? cssValue : `${cssValue}px`
37+
`${cssKey.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`)}: ${
38+
isNaN(cssValue as any) || nonPixelKeys.includes(cssKey)
39+
? cssValue
40+
: `${cssValue}px`
3141
};`,
3242
)
3343
.join(' ')
@@ -36,15 +46,6 @@ export const mapStylesToCSS = style =>
3646
* Creates a styled component with support for pseudo elements and media query styles provided from a style object
3747
*/
3848

39-
export type StyledCSSProperties = {
40-
css: CSSProperties
41-
':hover'?: CSSProperties
42-
':focus'?: CSSProperties
43-
':invalid'?: CSSProperties
44-
':readonly'?: CSSProperties
45-
':disabled'?: CSSProperties
46-
'@media-mobile'?: CSSProperties
47-
}
4849
export const createStyled = (type: any) =>
4950
typeof type === 'string'
5051
? styled[type]`
@@ -93,10 +94,14 @@ export const getUseCSSStyles = <Theme>() => <C extends keyof Theme>(
9394
elementKeys: Array<K> | K,
9495
internalOverride?: CSSProperties,
9596
): string => {
97+
const keys = Array.isArray(elementKeys)
98+
? elementKeys
99+
: ([elementKeys] as Array<K>)
100+
96101
const styles = {
97102
css: omitKeys(
98103
{
99-
...((Array.isArray(elementKeys) ? elementKeys : [elementKeys]).reduce(
104+
...(keys.reduce(
100105
(obj, elementKey) => ({
101106
...obj,
102107
...theme[componentKey][elementKey],
@@ -106,12 +111,12 @@ export const getUseCSSStyles = <Theme>() => <C extends keyof Theme>(
106111
) as any),
107112
...(internalOverride || {}),
108113
},
109-
dynamicStyleKeys as any,
114+
dynamicStyleKeys.concat(animationKeys) as any,
110115
),
111116
...dynamicStyleKeys.reduce(
112117
(obj, k) => ({
113118
...obj,
114-
[k]: (Array.isArray(elementKeys) ? elementKeys : [elementKeys]).reduce(
119+
[k]: keys.reduce(
115120
(obj, elementKey) => ({
116121
...obj,
117122
...(theme[componentKey][elementKey][k] || {}),
@@ -126,6 +131,14 @@ export const getUseCSSStyles = <Theme>() => <C extends keyof Theme>(
126131
),
127132
}
128133

134+
const animationKey = keys.find(
135+
(elementKey) => !!theme[componentKey][elementKey]['@animation'],
136+
)
137+
let animation
138+
if (animationKey) {
139+
animation = theme[componentKey][animationKey]['@animation']
140+
}
141+
129142
const cssStyles = `
130143
${mapStylesToCSS(styles.css || {})}
131144
@@ -152,6 +165,8 @@ export const getUseCSSStyles = <Theme>() => <C extends keyof Theme>(
152165
@media ${MediaQuery.Mobile} {
153166
${mapStylesToCSS(styles['@media-mobile'] || {})}
154167
}
168+
169+
${animation ? `&.animate { animation: ${animation}; }` : ''}
155170
`
156171

157172
return cssStyles

0 commit comments

Comments
 (0)