|
1 | 1 | <script lang="ts"> |
2 | | -import { defineComponent, PropType, capitalize as capitalizeFirstLetter } from 'vue' |
3 | | -import icons, { IconSize, DEPRECATED_ICONS } from './icons' |
| 2 | +export type { |
| 3 | + IconSize, |
| 4 | + IconName, |
| 5 | + SmIcon, |
| 6 | + MdIcon, |
| 7 | + LgIcon, |
| 8 | + OtherIcon, |
| 9 | + AnyIcon, |
| 10 | + AnyIconName, |
| 11 | + IconIdentifier, |
| 12 | + SmIconId, |
| 13 | + MdIconId, |
| 14 | + LgIconId, |
| 15 | + OtherIconId |
| 16 | +} from './icons/types' |
| 17 | +</script> |
| 18 | + |
| 19 | +<script setup lang="ts"> |
| 20 | +import { computed } from 'vue' |
| 21 | +import icons, { DEPRECATED_ICONS } from './icons' |
| 22 | +import { type IconSize, type IconIdentifier, type AnyIconName } from './icons/types' |
| 23 | +
|
| 24 | +defineOptions({ |
| 25 | + name: 'AIcon' |
| 26 | +}) |
4 | 27 |
|
5 | | -export default defineComponent({ |
6 | | - name: 'AIcon', |
7 | | - props: { |
| 28 | +const props = withDefaults( |
| 29 | + defineProps<{ |
8 | 30 | /** |
9 | | - * icon name in PascalCase (e.g. ArrowDown, Warning) or camelCase (e.g. arrowDown, warning) |
10 | | - * together with the size identifies the correct icon to load |
| 31 | + * Type-safe icon identifier in format "name-size" (e.g., "search-sm", "check-md"). |
| 32 | + * This is the recommended way to specify icons as it enforces valid name+size combinations. |
11 | 33 | */ |
12 | | - name: { |
13 | | - type: String, |
14 | | - required: true |
15 | | - }, |
| 34 | + icon?: IconIdentifier |
16 | 35 | /** |
17 | | - * icon size - used to set width and height on an `svg` element |
18 | | - * currently used sizes: `sm`: 1rem/16px, `md` (default): 2rem/32px, `lg`: 2.5rem/40px, `other`: various sizes for special cases |
| 36 | + * @deprecated Use `icon` prop instead (e.g., icon="search-sm"). |
| 37 | + * Icon name in PascalCase (e.g. ArrowDown, Warning) or camelCase (e.g. arrowDown, warning). |
19 | 38 | */ |
20 | | - size: { |
21 | | - type: String as PropType<IconSize>, |
22 | | - default: 'md' |
23 | | - } |
24 | | - }, |
25 | | - computed: { |
26 | | - iconComponent() { |
27 | | - // need to use the props directly inside the computed function, |
28 | | - // so that Vue tracks changes |
29 | | - // https://stackoverflow.com/a/58577362/6917800 |
30 | | - const size = this.size |
31 | | - const name = capitalizeFirstLetter(this.name) // allow camelCase name |
32 | | - if (DEPRECATED_ICONS[size].includes(name)) { |
33 | | - console.warn( |
34 | | - `Icon "${name}" in size "${size}" is deprecated and will be removed in the next major version. |
35 | | - Use another supported size or alternative icon, see storybook docs https://honeycomb.archilogic.com` |
36 | | - ) |
37 | | - } |
38 | | - if (icons[size][name]) { |
39 | | - return icons[size][name] |
40 | | - } else { |
41 | | - console.error(`Icon ${name} of size ${size} does not exist.`, 'Available icons', icons) |
42 | | - return null |
43 | | - } |
44 | | - } |
| 39 | + name?: AnyIconName |
| 40 | + /** |
| 41 | + * @deprecated Use `icon` prop instead (e.g., icon="search-sm"). |
| 42 | + * Icon size - used to set width and height on an `svg` element. |
| 43 | + */ |
| 44 | + size?: IconSize |
| 45 | + }>(), |
| 46 | + { |
| 47 | + icon: undefined, |
| 48 | + name: undefined, |
| 49 | + size: 'md' |
| 50 | + } |
| 51 | +) |
| 52 | +
|
| 53 | +const iconComponent = computed(() => { |
| 54 | + let size: IconSize |
| 55 | + let name: string |
| 56 | +
|
| 57 | + if (props.icon) { |
| 58 | + const lastDash = props.icon.lastIndexOf('-') |
| 59 | + size = props.icon.slice(lastDash + 1) as IconSize |
| 60 | + name = props.icon |
| 61 | + .slice(0, lastDash) |
| 62 | + .split('-') |
| 63 | + .map(s => s.charAt(0).toUpperCase() + s.slice(1)) |
| 64 | + .join('') |
| 65 | + } else if (props.name) { |
| 66 | + size = props.size |
| 67 | + name = props.name.charAt(0).toUpperCase() + props.name.slice(1) |
| 68 | + } else { |
| 69 | + console.error('[Honeycomb] a-icon: either icon or name prop is required') |
| 70 | + return null |
| 71 | + } |
| 72 | +
|
| 73 | + if (DEPRECATED_ICONS[size].includes(name)) { |
| 74 | + console.warn( |
| 75 | + `Icon "${name}" in size "${size}" is deprecated and will be removed in the next major version. |
| 76 | + Use another supported size or alternative icon, see storybook docs https://honeycomb.archilogic.com` |
| 77 | + ) |
| 78 | + } |
| 79 | +
|
| 80 | + if (icons[size][name]) { |
| 81 | + return icons[size][name] |
| 82 | + } else { |
| 83 | + console.error(`Icon ${name} of size ${size} does not exist.`, 'Available icons', icons) |
| 84 | + return null |
45 | 85 | } |
46 | 86 | }) |
47 | 87 | </script> |
| 88 | + |
48 | 89 | <template> |
49 | 90 | <component :is="iconComponent" v-if="iconComponent" aria-hidden class="flex-shrink-0" /> |
50 | 91 | </template> |
0 commit comments