Skip to content
This repository was archived by the owner on Sep 20, 2024. It is now read-only.

Commit 6f8e1c1

Browse files
committed
feat(system): improve overide api and write tests
1 parent 911cf58 commit 6f8e1c1

File tree

7 files changed

+310
-64
lines changed

7 files changed

+310
-64
lines changed

packages/c-accordion/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ const CAccordion = defineComponent({
99
},
1010
},
1111
setup(props, { slots, attrs }) {
12-
return () => h(chakra(props.as), { ...attrs }, slots)
12+
return () =>
13+
h(chakra(props.as, { label: 'accordion' }), { ...attrs }, slots)
1314
},
1415
})
1516

packages/c-alert/src/alert.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export const CAlert = defineComponent({
9696

9797
return () =>
9898
h(
99-
chakra(props.as, 'alert'),
99+
chakra(props.as, { label: 'alert' }),
100100
{
101101
role: 'alert',
102102
...alertStyles,
@@ -119,7 +119,7 @@ export const CAlertTitle = defineComponent({
119119

120120
return () =>
121121
h(
122-
chakra('div', 'alert__title'),
122+
chakra('div', { label: 'alert__title' }),
123123
{
124124
...styles.title,
125125
...attrs,
@@ -141,7 +141,7 @@ export const CAlertDescription = defineComponent({
141141

142142
return () =>
143143
h(
144-
chakra('div', 'alert__description'),
144+
chakra('div', { label: 'alert__description' }),
145145
{
146146
...styles.description,
147147
...attrs,

packages/c-button/src/button.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ const CButton = defineComponent({
7979

8080
return () =>
8181
h(
82-
chakra(props.as, 'button'),
82+
chakra(props.as, { label: 'button' }),
8383
{
8484
disabled: props.isDisabled || props.isLoading,
8585
type: props.as === 'button' ? undefined : props.type,

packages/c-icon/src/icon.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const CIcon = defineComponent({
5151

5252
return () =>
5353
h(
54-
chakra(props.as, 'icon'),
54+
chakra(props.as, { label: 'icon' }),
5555
{ ...vnodeProps.value, ...(icon.value.attrs || {}), ...attrs },
5656
slots
5757
)

packages/system/src/chakra.ts

Lines changed: 187 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
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'
210
import {
311
css,
412
ResponsiveValue,
13+
SystemCSSProperties,
514
SystemProps,
615
SystemStyleObject,
716
} 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'
923
import { css as _css, CSSObject } from '@emotion/css'
1024
import { extractStyleAttrs } from './system.attrs'
1125
import { domElements, DOMElements } from './system.utils'
1226
import { useTheme } from './composables/use-chakra'
27+
import { Theme } from '@chakra-ui/vue-theme'
1328

1429
interface StyleResolverProps extends SystemProps {
1530
__css?: SystemStyleObject
@@ -21,19 +36,27 @@ interface StyleResolverProps extends SystemProps {
2136
textStyle?: string
2237
apply?: ResponsiveValue<string>
2338
componentName?: String
24-
}
25-
26-
interface ChakraFactoryOptions extends StyleResolverProps {
2739
label?: string
2840
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
2950
}
3051

52+
interface ChakraFactoryOptions extends StyleResolverProps {}
53+
3154
const chakraProps = {
3255
__css: Object as PropType<StyleResolverProps['__css']>,
3356
sx: Object as PropType<StyleResolverProps['sx']>,
3457
css: Object as PropType<StyleResolverProps['css']>,
3558
noOfLines: Number as PropType<StyleResolverProps['noOfLines']>,
36-
baseStyle: Object as PropType<ChakraFactoryOptions['baseStyle']>,
59+
baseStyle: Object as PropType<StyleResolverProps['baseStyle']>,
3760
isTruncated: Boolean as PropType<StyleResolverProps['isTruncated']>,
3861
layerStyle: String as PropType<StyleResolverProps['layerStyle']>,
3962
textStyle: String as PropType<StyleResolverProps['textStyle']>,
@@ -43,76 +66,124 @@ const chakraProps = {
4366
export type ChakraBaseComponentProps = typeof chakraProps
4467

4568
/**
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+
* ```
49116
*/
50117
// @ts-expect-error
51118
export const chakra: IChakraFactory = (
52-
tag: DOMElements,
53-
options?: ChakraFactoryOptions
119+
tag: DOMElements & Component,
120+
options: StyleResolverProps
54121
): DefineComponent => {
55122
return defineComponent({
56123
inheritAttrs: false,
57124
props: chakraProps,
58125
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+
60140
// 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
61150

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(
67158
() => props.isTruncated || options?.isTruncated
68159
)
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)
72164

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+
})
106178

107-
const className = _css(css(finalStyles)({ theme }))
179+
const className = _css(resolvedComponentStyles)
108180
const _componentName = options?.label ? `chakra-${options?.label}` : ''
109181

110182
return () =>
111183
h(
112184
tag,
113185
{
114186
class: cx(inheritedClass, _componentName, className),
115-
...props,
116187
...elementAttributes,
117188
},
118189
slots
@@ -121,14 +192,73 @@ export const chakra: IChakraFactory = (
121192
})
122193
}
123194

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+
124254
type IChakraFactory = {
125255
[key in DOMElements]: DefineComponent
126256
} & {
127-
(tag: DOMElements, componentName?: string): DefineComponent
257+
(tag: DOMElements, options: StyleResolverProps): DefineComponent
128258
}
129259

130260
domElements.forEach((tag) => {
131-
chakra[tag] = chakra(tag)
261+
chakra[tag] = chakra(tag, {})
132262
})
133263

134264
export { domElements }

0 commit comments

Comments
 (0)