1
- import { computed , DefineComponent , defineComponent , h , PropType } from 'vue'
1
+ import {
2
+ Component ,
3
+ computed ,
4
+ DefineComponent ,
5
+ defineComponent ,
6
+ h ,
7
+ HTMLAttributes ,
8
+ PropType ,
9
+ } from 'vue'
2
10
import {
3
11
css ,
4
12
ResponsiveValue ,
13
+ SystemCSSProperties ,
5
14
SystemProps ,
6
15
SystemStyleObject ,
7
16
} from '@chakra-ui/styled-system'
8
- import { cx , memoizedGet as get , objectAssign } from '@chakra-ui/vue-utils'
17
+ import {
18
+ cx ,
19
+ isFunction ,
20
+ memoizedGet as get ,
21
+ objectAssign ,
22
+ } from '@chakra-ui/vue-utils'
9
23
import { css as _css , CSSObject } from '@emotion/css'
10
24
import { extractStyleAttrs } from './system.attrs'
11
25
import { domElements , DOMElements } from './system.utils'
12
26
import { useTheme } from './composables/use-chakra'
27
+ import { Theme } from '@chakra-ui/vue-theme'
13
28
14
29
interface StyleResolverProps extends SystemProps {
15
30
__css ?: SystemStyleObject
@@ -21,19 +36,27 @@ interface StyleResolverProps extends SystemProps {
21
36
textStyle ?: string
22
37
apply ?: ResponsiveValue < string >
23
38
componentName ?: String
24
- }
25
-
26
- interface ChakraFactoryOptions extends StyleResolverProps {
27
39
label ?: string
28
40
baseStyle ?: SystemStyleObject
41
+ /**
42
+ * User provided styles from component/chakra API
43
+ */
44
+ styles ?: SystemStyleObject
45
+ }
46
+
47
+ interface StyleResolverOptions extends StyleResolverProps {
48
+ truncateStyle ?: CSSObject
49
+ theme : Theme
29
50
}
30
51
52
+ interface ChakraFactoryOptions extends StyleResolverProps { }
53
+
31
54
const chakraProps = {
32
55
__css : Object as PropType < StyleResolverProps [ '__css' ] > ,
33
56
sx : Object as PropType < StyleResolverProps [ 'sx' ] > ,
34
57
css : Object as PropType < StyleResolverProps [ 'css' ] > ,
35
58
noOfLines : Number as PropType < StyleResolverProps [ 'noOfLines' ] > ,
36
- baseStyle : Object as PropType < ChakraFactoryOptions [ 'baseStyle' ] > ,
59
+ baseStyle : Object as PropType < StyleResolverProps [ 'baseStyle' ] > ,
37
60
isTruncated : Boolean as PropType < StyleResolverProps [ 'isTruncated' ] > ,
38
61
layerStyle : String as PropType < StyleResolverProps [ 'layerStyle' ] > ,
39
62
textStyle : String as PropType < StyleResolverProps [ 'textStyle' ] > ,
@@ -43,76 +66,124 @@ const chakraProps = {
43
66
export type ChakraBaseComponentProps = typeof chakraProps
44
67
45
68
/**
46
- * Creates a Chakra UI Vue component
47
- * @param tag Tag
48
- * @param componentName Component name
69
+ * Chakra factory serves as an object of chakra enabled HTML elements,
70
+ * and also a function that can be used to enable custom component receive chakra's style props.
71
+ * @param tag Tag or Component
72
+ * @param options resolver options
73
+ *
74
+ * How does it work?
75
+ *
76
+ * 1. Components returned from the chakra factory can be styled after consuming them
77
+ * @example
78
+ * ```js
79
+ * const Form = chakra('form') // returns a VNode you can use in the template directly
80
+ * ```
81
+ *
82
+ * 2. Chakra components can directly be styled upon creation using the options object of type `StyleResolverProps`
83
+ * This resolves style object for component styles defined in the theme.
84
+ *
85
+ * Styling components using the chakra factory function can be done using the following keys from the theme:
86
+ * - `baseStyle`
87
+ * - `layerStyle`
88
+ * - `textStyle`
89
+ *
90
+ * @example
91
+ * ```js
92
+ * const MyCustomButton = chakra('button', {
93
+ * baseStyle: {
94
+ bg: 'papayawhip,
95
+ color: 'red.500,
96
+ px: 4,
97
+ py: 3
98
+ }
99
+ * })
100
+ * ```
101
+ * ```html
102
+ * <my-custom-button>Hello Papaya Button</my-custom-button>
103
+ * ```
104
+ *
105
+ * See more about the style resolution in the `resolveStyles` function.
106
+ *
107
+ * 3. Chakra components created and styled using the `chakra` factory can be overriden in the template by applying
108
+ * style properties directly
109
+ *
110
+ * @example
111
+ * ```html
112
+ * <my-custom-button bg="blue.400">
113
+ * Papaya button goes blue
114
+ * </my-custom-button>
115
+ * ```
49
116
*/
50
117
// @ts -expect-error
51
118
export const chakra : IChakraFactory = (
52
- tag : DOMElements ,
53
- options ?: ChakraFactoryOptions
119
+ tag : DOMElements & Component ,
120
+ options : StyleResolverProps
54
121
) : DefineComponent => {
55
122
return defineComponent ( {
56
123
inheritAttrs : false ,
57
124
props : chakraProps ,
58
125
setup ( props , { slots, attrs } ) {
59
- const theme = useTheme ( )
126
+ const { class : inheritedClass , ...rest } = attrs
127
+ const {
128
+ layerStyle,
129
+ baseStyle,
130
+ textStyle,
131
+ noOfLines,
132
+ isTruncated,
133
+ __css,
134
+ css,
135
+ sx,
136
+ apply,
137
+ ...otherStyles
138
+ } = options
139
+
60
140
// Separate component style attributes from raw HTML attributes
141
+ const { styles, attrs : elementAttributes } = extractStyleAttrs <
142
+ any ,
143
+ HTMLAttributes
144
+ > ( {
145
+ ...rest ,
146
+ ...otherStyles ,
147
+ } )
148
+
149
+ const theme = useTheme ( ) as Theme
61
150
62
- const layerStyle = computed ( ( ) => props . layerStyle || options ?. layerStyle )
63
- const textStyle = computed ( ( ) => props . textStyle || options ?. textStyle )
64
- const baseStyle = computed ( ( ) => props . baseStyle || options ?. baseStyle )
65
- const noOfLines = computed ( ( ) => props . noOfLines || options ?. noOfLines )
66
- const isTruncated = computed (
151
+ const layerStyle$ = computed (
152
+ ( ) => props . layerStyle || options ?. layerStyle
153
+ )
154
+ const textStyle$ = computed ( ( ) => props . textStyle || options ?. textStyle )
155
+ const baseStyle$ = computed ( ( ) => props . baseStyle || options ?. baseStyle )
156
+ const noOfLines$ = computed ( ( ) => props . noOfLines || options ?. noOfLines )
157
+ const isTruncated$ = computed (
67
158
( ) => props . isTruncated || options ?. isTruncated
68
159
)
69
- const __css = computed ( ( ) => props . __css || options ?. __css )
70
- const sx = computed ( ( ) => props . sx || options ?. sx )
71
- const apply = computed ( ( ) => props . apply || options ?. apply )
160
+ const __css$ = computed ( ( ) => props . __css || options ?. __css )
161
+ const css$ = computed ( ( ) => props . css || options ?. css )
162
+ const sx$ = computed ( ( ) => props . sx || options ?. sx )
163
+ const apply$ = computed ( ( ) => props . apply || options ?. apply )
72
164
73
- const { class : inheritedClass , ...rest } = attrs
74
- const _layerStyle = get ( theme , `layerStyles.${ layerStyle . value } ` , { } )
75
- const _textStyle = get ( theme , `textStyles.${ textStyle . value } ` , { } )
76
- const { styles, attrs : elementAttributes } = extractStyleAttrs ( rest )
77
-
78
- let truncateStyle : any = { }
79
- if ( noOfLines . value != null ) {
80
- truncateStyle = {
81
- overflow : 'hidden' ,
82
- textOverflow : 'ellipsis' ,
83
- display : '-webkit-box' ,
84
- WebkitBoxOrient : 'vertical' ,
85
- WebkitLineClamp : noOfLines . value ,
86
- }
87
- } else if ( isTruncated . value ) {
88
- truncateStyle = {
89
- overflow : 'hidden' ,
90
- textOverflow : 'ellipsis' ,
91
- whiteSpace : 'nowrap' ,
92
- }
93
- }
94
-
95
- const finalStyles = objectAssign (
96
- { } ,
97
- __css . value ,
98
- baseStyle . value ,
99
- { apply : apply . value } ,
100
- _layerStyle ,
101
- _textStyle ,
102
- truncateStyle ,
103
- styles ,
104
- sx . value
105
- )
165
+ const resolvedComponentStyles = resolveStyles ( {
166
+ __css : __css$ . value ,
167
+ baseStyle : baseStyle$ . value ,
168
+ apply : apply$ . value ,
169
+ layerStyle : layerStyle$ . value ,
170
+ noOfLines : noOfLines$ . value ,
171
+ isTruncated : isTruncated$ . value ,
172
+ textStyle : textStyle$ . value ,
173
+ sx : sx$ . value ,
174
+ css : css$ . value ,
175
+ ...( styles as SystemProps ) ,
176
+ theme,
177
+ } )
106
178
107
- const className = _css ( css ( finalStyles ) ( { theme } ) )
179
+ const className = _css ( resolvedComponentStyles )
108
180
const _componentName = options ?. label ? `chakra-${ options ?. label } ` : ''
109
181
110
182
return ( ) =>
111
183
h (
112
184
tag ,
113
185
{
114
186
class : cx ( inheritedClass , _componentName , className ) ,
115
- ...props ,
116
187
...elementAttributes ,
117
188
} ,
118
189
slots
@@ -121,14 +192,73 @@ export const chakra: IChakraFactory = (
121
192
} )
122
193
}
123
194
195
+ export const resolveStyles = (
196
+ resolvers = { } as StyleResolverOptions
197
+ ) : CSSObject => {
198
+ const {
199
+ layerStyle,
200
+ baseStyle,
201
+ textStyle,
202
+ noOfLines,
203
+ isTruncated,
204
+ __css,
205
+ css : cssProp ,
206
+ sx,
207
+ apply,
208
+ theme,
209
+ ...otherStyles
210
+ } = resolvers
211
+
212
+ const _layerStyle = get ( theme , `layerStyles.${ layerStyle } ` , { } )
213
+ const _textStyle = get ( theme , `textStyles.${ textStyle } ` , { } )
214
+
215
+ let truncateStyle : any = { }
216
+ if ( noOfLines != null ) {
217
+ truncateStyle = {
218
+ overflow : 'hidden' ,
219
+ textOverflow : 'ellipsis' ,
220
+ display : '-webkit-box' ,
221
+ WebkitBoxOrient : 'vertical' ,
222
+ WebkitLineClamp : noOfLines ,
223
+ }
224
+ } else if ( isTruncated ) {
225
+ truncateStyle = {
226
+ overflow : 'hidden' ,
227
+ textOverflow : 'ellipsis' ,
228
+ whiteSpace : 'nowrap' ,
229
+ }
230
+ }
231
+
232
+ const finalStyles = css (
233
+ objectAssign (
234
+ { } ,
235
+ __css ,
236
+ baseStyle ,
237
+ { apply : apply } ,
238
+ _layerStyle ,
239
+ _textStyle ,
240
+ truncateStyle ,
241
+ otherStyles ,
242
+ sx
243
+ )
244
+ ) ( theme )
245
+
246
+ const cssObject : CSSObject = objectAssign (
247
+ finalStyles ,
248
+ isFunction ( cssProp ) ? cssProp ( theme ) : cssProp
249
+ )
250
+
251
+ return cssObject
252
+ }
253
+
124
254
type IChakraFactory = {
125
255
[ key in DOMElements ] : DefineComponent
126
256
} & {
127
- ( tag : DOMElements , componentName ?: string ) : DefineComponent
257
+ ( tag : DOMElements , options : StyleResolverProps ) : DefineComponent
128
258
}
129
259
130
260
domElements . forEach ( ( tag ) => {
131
- chakra [ tag ] = chakra ( tag )
261
+ chakra [ tag ] = chakra ( tag , { } )
132
262
} )
133
263
134
264
export { domElements }
0 commit comments