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

Commit 85f96a8

Browse files
Merge pull request #265 from chakra-ui/feat/inline-icons
feat/inline icons
2 parents 1a1a6b4 + 408e236 commit 85f96a8

File tree

12 files changed

+213
-22
lines changed

12 files changed

+213
-22
lines changed

.changeset/nasty-plums-greet.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
"@chakra-ui/c-visually-hidden": minor
3+
"@chakra-ui/c-theme-provider": minor
4+
"@chakra-ui/vue-composables": minor
5+
"@chakra-ui/c-close-button": minor
6+
"@chakra-ui/c-form-control": minor
7+
"@chakra-ui/c-media-query": minor
8+
"@chakra-ui/c-scroll-lock": minor
9+
"@chakra-ui/c-breadcrumb": minor
10+
"@chakra-ui/c-color-mode": minor
11+
"@chakra-ui/c-focus-lock": minor
12+
"@chakra-ui/c-accordion": minor
13+
"@chakra-ui/c-pin-input": minor
14+
"@chakra-ui/c-checkbox": minor
15+
"@chakra-ui/c-skip-nav": minor
16+
"@chakra-ui/vue-test-utils": minor
17+
"@chakra-ui/vue-auto-import": minor
18+
"@chakra-ui/c-spinner": minor
19+
"@chakra-ui/c-avatar": minor
20+
"@chakra-ui/c-button": minor
21+
"@chakra-ui/c-motion": minor
22+
"@chakra-ui/c-popper": minor
23+
"@chakra-ui/c-portal": minor
24+
"@chakra-ui/vue-accessibilty": minor
25+
"@chakra-ui/c-alert": minor
26+
"@chakra-ui/c-image": minor
27+
"@chakra-ui/c-input": minor
28+
"@chakra-ui/c-modal": minor
29+
"@chakra-ui/c-reset": minor
30+
"@chakra-ui/c-toast": minor
31+
"@chakra-ui/c-code": minor
32+
"@chakra-ui/c-icon": minor
33+
"@chakra-ui/c-menu": minor
34+
"@chakra-ui/vue-layout": minor
35+
"@chakra-ui/vue-styled": minor
36+
"@chakra-ui/vue-system": minor
37+
"@chakra-ui/c-tag": minor
38+
"@chakra-ui/vue-utils": minor
39+
"@chakra-ui/nuxt-next": minor
40+
"@chakra-ui/vue-next": minor
41+
---
42+
43+
adds `createIcon` api to allow consumers to inline icons they want to use o the
44+
fly"

modules/nuxt/src/module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export default defineNuxtModule<ChakraModuleOptions>({
117117
// Include all internal lodash modules
118118
// to the optimized dependencies since they do not
119119
// natively export ESM modules.
120+
// We should still preserve user-provided option
120121
const viteConfig = nuxt.options.vite || {}
121122
const extendedViteConfigOptions = {
122123
optimizeDeps: {

modules/nuxt/src/runtime/chakra.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import Chakra, {
77
ColorModeConstants,
88
extendTheme,
99
ColorModeScriptProps,
10+
domElements,
1011
} from "@chakra-ui/vue-next"
11-
import { domElements } from "@chakra-ui/vue-system"
1212
import { parseCookies } from "h3"
1313
import type { ChakraModuleOptions } from "../module"
1414

@@ -70,6 +70,7 @@ export default defineNuxtPlugin((nuxtApp) => {
7070
colorModeManager: cookieStorageManagerSSR(
7171
ColorModeConstants.CookieStorageKey
7272
),
73+
icons: chakraConfig.icons,
7374
})
7475
)
7576

packages/c-button/src/button.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
h,
66
Fragment,
77
SetupContext,
8+
DefineComponent,
89
} from "vue"
910
import {
1011
chakra,
@@ -58,15 +59,15 @@ const CButtonSpinner = defineComponent({
5859
})
5960

6061
interface CButtonContentProps {
61-
leftIcon?: string
62-
rightIcon?: string
62+
leftIcon?: string | object | typeof chakra.svg | DefineComponent
63+
rightIcon?: string | object | typeof chakra.svg | DefineComponent
6364
iconSpacing?: CButtonSpinnerProps["spacing"]
6465
}
6566
const CButtonContent = defineComponent({
6667
name: "CButtonContent",
6768
props: {
68-
leftIcon: String as PropType<CButtonContentProps["leftIcon"]>,
69-
rightIcon: String as PropType<CButtonContentProps["rightIcon"]>,
69+
leftIcon: [String, Object] as PropType<CButtonContentProps["leftIcon"]>,
70+
rightIcon: [String, Object] as PropType<CButtonContentProps["rightIcon"]>,
7071
iconSpacing: String as PropType<CButtonContentProps["iconSpacing"]>,
7172
},
7273
setup(props, { slots }) {
@@ -92,10 +93,18 @@ const CButtonContent = defineComponent({
9293
const CButtonIcon = defineComponent({
9394
name: "CButtonIcon",
9495
props: {
95-
icon: String as PropType<string>,
96+
icon: [String, Object] as PropType<string | object | DefineComponent>,
9697
},
9798
setup(props, { attrs }) {
98-
return () => <CIcon __label="button__icon" name={props.icon} {...attrs} />
99+
return () =>
100+
typeof props.icon === "string" ? (
101+
<CIcon __label="button__icon" name={props.icon} {...attrs} />
102+
) : (
103+
<CIcon __label="button__icon" {...attrs}>
104+
{/* @ts-ignore */}
105+
<props.icon />
106+
</CIcon>
107+
)
99108
},
100109
})
101110

@@ -130,10 +139,10 @@ export const CButton = defineComponent({
130139
type: String as PropType<ButtonProps["type"]>,
131140
},
132141
leftIcon: {
133-
type: String as PropType<ButtonProps["leftIcon"]>,
142+
type: [String, Object] as PropType<ButtonProps["leftIcon"]>,
134143
},
135144
rightIcon: {
136-
type: String as PropType<ButtonProps["rightIcon"]>,
145+
type: [String, Object] as PropType<ButtonProps["rightIcon"]>,
137146
},
138147
iconSpacing: {
139148
type: SNAO as PropType<ButtonProps["iconSpacing"]>,

packages/c-button/src/button.utils.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { SystemProps } from "@chakra-ui/styled-system"
22
import { BaseThemedComponentProps } from "@chakra-ui/vue-utils"
3-
import { DOMElements } from "@chakra-ui/vue-system"
3+
import { DOMElements, chakra } from "@chakra-ui/vue-system"
4+
import type { DefineComponent } from "vue"
45

56
type ButtonTypes = "button" | "reset" | "submit"
67

@@ -12,8 +13,8 @@ export interface ButtonProps extends BaseThemedComponentProps {
1213
loadingText?: string
1314
isFullWidth?: boolean
1415
type?: ButtonTypes
15-
leftIcon?: string
16-
rightIcon?: string
16+
leftIcon?: string | object | typeof chakra.svg | DefineComponent
17+
rightIcon?: string | object | typeof chakra.svg | DefineComponent
1718
spinnerPlacement?: "start" | "end"
1819
iconSpacing?: SystemProps["marginRight"]
1920
}

packages/c-icon/README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,25 @@ yarn add @chakra-ui/c-icon
1010
npm i @chakra-ui/c-icon
1111
```
1212

13-
### Adding custom icons
13+
### Creating inline icons
14+
If you with to create new icons on the fly, you can create an icon component which accepts Chakra UI style props using the `createIcon` API as shown below:
15+
```ts
16+
17+
const DocumentationIcon = createIcon({
18+
name: "Documentation",
19+
viewBox: "0 2.4 24 24",
20+
path: `<path fill="currentColor" d="M6 24.4q-.825 0-1.413-.587T4 22.4v-16q0-.825.588-1.412T6 4.4h12q.825 0 1.413.588T20 6.4v16q0 .825-.588 1.413T18 24.4H6Zm0-2h12v-16h-2v6.125q0 .3-.25.438t-.5-.013L13.5 11.9l-1.75 1.05q-.25.15-.5.013t-.25-.438V6.4H6v16Zm5-16h5h-5Zm-5 0h12H6Z"/>`,
21+
})
22+
23+
```
24+
This creates a `DocumentationIcon` component that you can style with Chakra UI props as shown in the template below:
25+
```vue
26+
<template>
27+
<DocumentationIcon :box-size="20" color="tomato" />
28+
</template>
29+
```
30+
31+
### Adding custom icons to theme
1432
All Chakra icons are registered in the Chakra plugin under the `icons.extend` key. So you
1533
can extend this object to add your own icons. Here are the steps:
1634

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
<template>
22
<div>
3-
<c-icon size="10" name="discord" />
3+
<c-h-stack>
4+
<c-icon size="10" name="discord" />
5+
<InlineDocumentationIcon :box-size="20" />
6+
</c-h-stack>
47
</div>
58
</template>
9+
10+
<script setup lang="ts">
11+
import { createIcon } from "../src"
12+
const InlineDocumentationIcon = createIcon({
13+
name: "Documentation",
14+
viewBox: "0 2.4 24 24",
15+
path: `<path fill="currentColor" d="M6 24.4q-.825 0-1.413-.587T4 22.4v-16q0-.825.588-1.412T6 4.4h12q.825 0 1.413.588T20 6.4v16q0 .825-.588 1.413T18 24.4H6Zm0-2h12v-16h-2v6.125q0 .3-.25.438t-.5-.013L13.5 11.9l-1.75 1.05q-.25.15-.5.013t-.25-.438V6.4H6v16Zm5-16h5h-5Zm-5 0h12H6Z"/>`,
16+
})
17+
</script>

packages/c-icon/src/icon.tsx

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,12 @@ import {
55
inject,
66
SVGAttributes,
77
PropType,
8-
Fragment,
98
DefineComponent,
109
} from "vue"
11-
import {
12-
chakra,
13-
ChakraProps,
14-
ComponentWithProps,
15-
DeepPartial,
16-
HTMLChakraProps,
17-
} from "@chakra-ui/vue-system"
10+
import { chakra, ChakraProps } from "@chakra-ui/vue-system"
1811
import type {} from "@vue/runtime-core"
1912
import { SNAO, camelCase, mergeWith } from "@chakra-ui/vue-utils"
13+
import { omit } from "@chakra-ui/utils"
2014

2115
const fallbackIcon = {
2216
path: `
@@ -113,3 +107,41 @@ export function createIconComponent(name: string) {
113107
iconComponent.name = componentName
114108
return iconComponent
115109
}
110+
111+
export interface CreateIconOptions {
112+
name: string
113+
path: string
114+
viewBox?: string
115+
}
116+
117+
const createIconProps = omit(_iconProps, ["name"])
118+
export function createIcon(options: CreateIconOptions) {
119+
const componentName = camelCase(options.name)
120+
const iconComponent = defineComponent({
121+
name: componentName,
122+
props: createIconProps,
123+
setup(props, { slots, attrs }) {
124+
const hasDefaultSlot = computed(() => slots?.default?.()?.length)
125+
const vnodeProps = computed(() => ({
126+
w: props.size,
127+
h: props.size,
128+
display: "inline-block",
129+
lineHeight: "1em",
130+
flexShrink: 0,
131+
color: "currentColor",
132+
...(!hasDefaultSlot.value && {
133+
innerHTML: options.path,
134+
}),
135+
focusable: false,
136+
viewBox: options.viewBox || fallbackIcon.viewBox,
137+
}))
138+
139+
return () => (
140+
<chakra.svg __label="icon" {...vnodeProps.value} {...attrs}>
141+
{slots.default?.()}
142+
</chakra.svg>
143+
)
144+
},
145+
})
146+
return iconComponent
147+
}

scripts/utils/exports/inspector.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import * as ts from "typescript"
2+
import { resolve } from "path"
3+
4+
function findExports(fileNames: string[], options: ts.CompilerOptions): void {
5+
let program = ts.createProgram(fileNames, options)
6+
7+
const checker = program.getTypeChecker()
8+
9+
const namedExports = new Map<string, ts.Symbol["escapedName"][]>()
10+
11+
fileNames.forEach((file) => {
12+
const sourceFile = program.getSourceFile(file)
13+
if (!sourceFile) return console.info("No sourceFile found at ", file)
14+
15+
const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile)!
16+
const exports = checker
17+
.getExportsOfModule(sourceFileSymbol)
18+
.filter((symbol) => symbol.escapedName !== "default")
19+
.map((symbol) => {
20+
console.log("symbol", symbol)
21+
22+
return symbol.escapedName
23+
})
24+
25+
namedExports.set(file, exports)
26+
})
27+
28+
namedExports.forEach((exports, file) => {
29+
console.log(`Named exports from ${file}:`)
30+
exports.forEach((exportName) => {
31+
console.log(`- ${exportName}`)
32+
})
33+
})
34+
}
35+
36+
findExports([resolve(__dirname, "./lib-example/index.ts")], {
37+
noEmitOnError: true,
38+
noImplicitAny: true,
39+
target: ts.ScriptTarget.ES5,
40+
module: ts.ModuleKind.CommonJS,
41+
})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { defineComponent, h } from "vue"
2+
3+
export interface CAccordionProps {
4+
name: string
5+
activeIndex: number
6+
}
7+
export const CAccordion = defineComponent((props: CAccordionProps) => {
8+
return () => h("div", "Hello Accordion")
9+
})
10+
11+
const CAccordionDefaultExport = {
12+
some: "random",
13+
object: true,
14+
}
15+
export default CAccordionDefaultExport

0 commit comments

Comments
 (0)