Skip to content

Commit

Permalink
feat(app): add Avatar
Browse files Browse the repository at this point in the history
This pull request is intended to add the Avatar component.
  • Loading branch information
Selemondev authored and Selemondev committed Jul 27, 2023
1 parent 3087bec commit 97843b2
Show file tree
Hide file tree
Showing 13 changed files with 275 additions and 10 deletions.
12 changes: 7 additions & 5 deletions example/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
<script setup lang='ts'>
import { ref } from "vue"
import { ref } from 'vue'
const show = ref(true)
const handleClose = () => {
function handleClose() {
return show.value = false
}
</script>

<template>
<div class="mt-48 p-4 space-y-2">
<WIcon name="ph:sun" size="100" />
<WAlert :is-visible="show" variant="primary-light" transition="scale" title="Alert" closable @close="handleClose">
<WAvatar src="https://github.cm/selemondev.png" name="Selemon" chip-position="top-right" initials="SB" chip-color="red" />

<WAlert :is-visible="show" icon="ph:sun" :trailing="false" variant="success-light" transition="slideRight" title="Alert" closable @close="handleClose">
<WAlertDescription>
Hello from alert
</WAlertDescription>
Expand All @@ -28,4 +30,4 @@ const handleClose = () => {
</WAlertDescription>
</WAlert>
</div>
</template>
</template>
40 changes: 40 additions & 0 deletions example/src/theme/myTheme.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,44 @@
export default {
WAvatar: {
base: {
'root': 'relative cursor-pointer inline-flex items-center justify-center bg-gray-100 rounded-full',
'rounded': 'rounded-full',
'placeholderClass': 'font-medium cursor-pointer text-gray-600 upperCase',
'xs': 'h-6 w-6 text-[11px]',
'sm': 'h-8 w-8 text-xs',
'md': 'h-10 w-10 text-sm',
'lg': 'h-12 w-12 text-base',
'xl': 'h-14 w-14 text-lg',
'2xl': 'h-16 w-16 text-xl',
'3xl': 'h-20 w-20 text-2xl',
'avatarChipClass': 'flex justify-center items-center absolute rounded-full !text-white cursor-pointer ring-1 !ring-white dark:ring-gray-600 text-white dark:text-gray-900',
'avatarChipPosition': {
'top-right': 'top-0 right-0',
'bottom-right': 'bottom-0 right-0',
'top-left': 'top-0 left-0',
'bottom-left': 'bottom-0 left-0',
},
'avatarChipSize': {
'xs': 'h-1.5 min-w-[0.375rem] text-[6px] p-px',
'sm': 'h-2 min-w-[0.5rem] text-[7px] p-0.5',
'md': 'h-2.5 min-w-[0.625rem] text-[8px] p-0.5',
'lg': 'h-3 min-w-[0.75rem] text-[10px] p-0.5',
'xl': 'h-3.5 min-w-[0.875rem] text-[11px] p-1',
'2xl': 'h-4 min-w-[1rem] text-[12px] p-1',
'3xl': 'h-5 min-w-[1.25rem] text-[14px] p-1',
},

},

variants: {
default: {
root: 'relative cursor-pointer inline-flex items-center justify-center bg-gray-100 rounded-full',
rounded: 'rounded-full',
placeholderClass: 'font-medium cursor-pointer text-gray-600 upperCase',
},
},

},
WAlert: {
base: {
root: 'relative space-y-1 p-4 max-h-lg w-full cursor-pointer rounded-md space-x-3 block',
Expand Down
13 changes: 13 additions & 0 deletions packages/windi/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions packages/windi/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ function handleClose() {

<template>
<div class="mt-48 p-4 space-y-2">
<WAvatar src="https://github.cm/selemondev.png" name="Selemon" chip-position="top-right" initials="SB" chip-color="red" />

<WAlert :is-visible="show" icon="ph:sun" :trailing="false" variant="danger-light" transition="slideRight" title="Alert" closable @close="handleClose">
<WAlertDescription>
Hello from alert
Expand Down
17 changes: 17 additions & 0 deletions packages/windi/src/Types/componentsTypes/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export interface WAlert extends WComponentRoot {
closeButtonClass?: string
flexBetween?: string
body?: string
isLeading?: string
isNotLeading?: string
closeIcon?: string
trailingClass?: string
leadingClass?: string
Expand All @@ -18,4 +20,19 @@ export interface WIcon extends WComponentRoot {
size?: string
}

export interface WAvatar extends WComponentRoot {
wrapper?: string
chipClass?: string
avatarSize?: string
rounded?: string
avatarClass?: string
avatarChipSize?: string
avatarChipPosition?: string
placeholderClass?: string
}

export type WAlertVariants = WithVariantProps<WAlert>

export type WAvatarVariants = WithVariantProps<WAvatar>

export type WIconVariants = WithVariantProps<WIcon>
1 change: 1 addition & 0 deletions packages/windi/src/Types/enums/Components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum Components {
WAlert = 'WAlert',
WAlertDescription = 'WAlertDescription',
WIcon = 'WIcon',
WAvatar = 'WAvatar',
}
5 changes: 4 additions & 1 deletion packages/windi/src/Types/variant.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Components } from './enums/Components'
import type { WAlertVariants } from './componentsTypes/components'
import type { WAlertVariants, WAvatarVariants, WIconVariants } from './componentsTypes/components'

export declare interface CSSClassKeyValuePair {
[key: string]: any
Expand Down Expand Up @@ -31,4 +31,7 @@ export interface Variants<P> {
export interface WindiUIConfiguration {
transitions: Record<string, Record<string, string>>
[Components.WAlert]?: WAlertVariants
[Components.WAvatar]?: WAvatarVariants
[Components.WIcon]?: WIconVariants

}
2 changes: 1 addition & 1 deletion packages/windi/src/components/Alert/WAlert.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default defineComponent({
<Transition v-bind="transition" mode="out-in">
<div v-if="props.isVisible" :class="variant.root">
<div :class="variant.flexBetween">
<div :class="[$slots.leading || isLeading ? 'space-x-4' : 'space-x-2']" class="flex items-center">
<div :class="[$slots.leading || isLeading ? variant.isLeading : variant.isNotLeading]" class="flex items-center">
<div class="shrink-0">
<span v-if="(isLeading && leadingIconName) || $slots.leading" class="px-2">
<slot name="leading">
Expand Down
130 changes: 130 additions & 0 deletions packages/windi/src/components/Avatar/WAvatar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<script setup lang='ts'>
import type { PropType } from 'vue'
import { computed, defineComponent, ref, watchEffect } from 'vue'
import classNames from 'classnames'
import { getVariantPropsWithClassesList } from '../../utils/getVariantProps'
import type { WAvatar } from '../../Types/componentsTypes/components'
import { Components } from '../../Types/enums/Components'
import { useVariants } from '../../composables/useVariants'
import windiTheme from '../../theme/windiTheme'
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl'
export type AvatarChipPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
const props = defineProps({
...getVariantPropsWithClassesList<WAvatar>(),
name: {
type: String,
required: true,
default: '?',
},
src: {
type: String,
default: '',
},
size: {
type: String as PropType<AvatarSize>,
default: 'md',
},
initials: {
type: String,
default: null,
},
chipColor: {
type: String,
default: '',
},
chipSize: {
type: String,
default: 'md',
},
chipPosition: {
type: String as PropType<AvatarChipPosition>,
default: 'bottom-right',
},
chipText: {
type: String,
default: '',
},
})
const avatarUrl = ref('')
watchEffect(() => {
const img = new Image()
img.src = props.src
img.decode().then(() => (avatarUrl.value = props.src)).catch((err: string) => {
avatarUrl.value = ''
throw err
})
})
const fallback = computed(() => {
return props.initials || props.name.charAt(0) || '?'
})
const variant = useVariants<WAvatar>(Components.WAvatar, props)
const avatarWrapperClasses = computed<string>(() => {
return classNames(
variant.root,
variant.avatarSize && variant[props.size],
)
})
const avatarClasses = computed(() => {
let sizeClass = ''
if (typeof props.size === 'number') {
return props.size
}
else {
sizeClass += variant[props.size] || ''
return classNames(variant.rounded, sizeClass, variant.root)
}
})
const avatarChipSize = computed(() => {
let sizeClass = ''
if (typeof props.size === 'number') {
return props.size
}
else {
sizeClass += windiTheme.WAvatar.base.avatarChipSize[props.size] || ''
return classNames(sizeClass)
}
})
const avatarChipClass = computed(() => {
return classNames(
variant.avatarChipClass,
windiTheme.WAvatar.base.avatarChipPosition[props.chipPosition],
)
})
const avatarChipColorStyles = computed(() => ({
'background-color': props.chipColor || '',
}))
</script>

<script lang="ts">
export default defineComponent({
name: Components.WAvatar,
})
</script>

<template>
<span :class="[avatarWrapperClasses, avatarClasses]" :title="props.name">
<img v-if="avatarUrl" :class="avatarClasses" :src="avatarUrl" :alt="props.name">
<span v-else-if="!avatarUrl" :class="variant.placeholderClass">{{ fallback }}</span>
<span v-if="props.chipColor" :style="avatarChipColorStyles" :class="[avatarChipClass, avatarChipSize]">
{{ chipText }}
</span>
<slot />
</span>
</template>
5 changes: 5 additions & 0 deletions packages/windi/src/components/Avatar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Avatar from './WAvatar.vue'

export default {
Avatar,
}
6 changes: 3 additions & 3 deletions packages/windi/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { App, defineComponent } from 'vue'
import './assets/css/tailwind.css'
import Alert from './components/Alert/WAlert.vue'
import AlertDescription from './components/Alert/WAlertDescription.vue'
import Avatar from './components/Avatar/WAvatar.vue'
import Icon from './components/Icon/WIcon.vue'
import type { WindiUIConfiguration } from './Types/variant'
import windiTheme from './theme/windiTheme'
Expand All @@ -10,14 +11,12 @@ const components: Record<string, ReturnType<typeof defineComponent>> = {
Alert,
AlertDescription,
Icon,
Avatar,
}

function install(app: App, configuration: WindiUIConfiguration) {
for (const component in components)
app.component(components[component].name, components[component])
// app.component(Alert.name, Alert)
// app.component(AlertDescription.name, AlertDescription)
// app.component(Icon.name, Icon)
app.provide('config', configuration)
}

Expand All @@ -26,3 +25,4 @@ export default { install, windiTheme }
export { default as Alert } from './components/Alert'
export { default as AlertDescription } from './components/Alert'
export { default as Icon } from './components/Icon'
export { default as Avatar } from './components/Avatar'
9 changes: 9 additions & 0 deletions packages/windi/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import './assets/css/tailwind.css'
import { createApp } from 'vue'
import App from './App.vue'
import config from './theme/windiTheme'
import install from './index'

const app = createApp(App)
app.use(install, config)
app.mount('#app')
43 changes: 43 additions & 0 deletions packages/windi/src/theme/windiTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export default {
flexBetween: 'flex justify-between items-center w-full',
body: 'text-sm leading-none mt-2.5',
closeIcon: 'w-6 h-6 block',
isLeading: 'space-x-4',
isNotLeading: 'space-x-2',
closeButtonClass: 'p-0.5 rounded-md -m-1 block transition duration-200 ease-in',
leadingClass: 'absolute left-2.5 inset-y-0 cursor-pointer flex items-center overflow-hidden',
trailingClass: 'absolute right-4 inset-y-0 cursor-pointer flex items-center overflow-hidden',
Expand Down Expand Up @@ -133,6 +135,47 @@ export default {
},
},
},

WAvatar: {
base: {
'root': 'relative cursor-pointer inline-flex items-center justify-center bg-gray-100 rounded-full',
'rounded': 'rounded-full',
'placeholderClass': 'font-medium cursor-pointer text-gray-600 upperCase',
'xs': 'h-6 w-6 text-[11px]',
'sm': 'h-8 w-8 text-xs',
'md': 'h-10 w-10 text-sm',
'lg': 'h-12 w-12 text-base',
'xl': 'h-14 w-14 text-lg',
'2xl': 'h-16 w-16 text-xl',
'3xl': 'h-20 w-20 text-2xl',
'avatarChipClass': 'flex justify-center items-center absolute rounded-full !text-white cursor-pointer ring-1 !ring-white dark:ring-gray-600 text-white dark:text-gray-900',
'avatarChipPosition': {
'top-right': 'top-0 right-0',
'bottom-right': 'bottom-0 right-0',
'top-left': 'top-0 left-0',
'bottom-left': 'bottom-0 left-0',
},
'avatarChipSize': {
'xs': 'h-1.5 min-w-[0.375rem] text-[6px] p-px',
'sm': 'h-2 min-w-[0.5rem] text-[7px] p-0.5',
'md': 'h-2.5 min-w-[0.625rem] text-[8px] p-0.5',
'lg': 'h-3 min-w-[0.75rem] text-[10px] p-0.5',
'xl': 'h-3.5 min-w-[0.875rem] text-[11px] p-1',
'2xl': 'h-4 min-w-[1rem] text-[12px] p-1',
'3xl': 'h-5 min-w-[1.25rem] text-[14px] p-1',
},

},

variants: {
default: {
root: 'relative cursor-pointer inline-flex items-center justify-center bg-gray-100 rounded-full',
rounded: 'rounded-full',
placeholderClass: 'font-medium cursor-pointer text-gray-600 upperCase',
},
},

},
transitions: {
scale: {
'enter-active-class': 'duration-200 ease-[cubic-bezier(0.175,0.885,0.32,1.475)]',
Expand Down

0 comments on commit 97843b2

Please sign in to comment.