feat(mark): new component RayMark

This commit is contained in:
Timothy Yin 2024-11-27 17:29:38 +08:00
parent 83f8593391
commit ddff1ca9c0
6 changed files with 240 additions and 0 deletions

View File

@ -0,0 +1,83 @@
---
description: Display a indicator with or without counts on any component
---
## Usage
Use the default slot to add any component you want to display the indicator on.
::ComponentPreview
---
slots:
default: |
<RayButton icon="tabler:message" label="messages" color="invert" />
---
#default
:RayButton{icon="tabler:message" label="messages" color="invert"}
::
### Styles
You can change the color and size of the indicator by using the `color` and `size` props.
::ComponentPreview
---
props:
color: amber
size: sm
slots:
default: |
<RayButton icon="tabler:message" label="messages" color="invert" />
---
#default
:RayButton{icon="tabler:message" label="messages" color="invert"}
::
### Position
Use the `position` prop to change the position of the indicator.
::ComponentPreview
---
props:
position: top-right
slots:
default: |
<RayButton icon="tabler:message" label="messages" color="invert" />
---
#default
:RayButton{icon="tabler:message" label="messages" color="invert"}
::
### Count
Add a count to the indicator by using the `value` prop.
::ComponentPreview
---
props:
value: 5
slots:
default: |
<RayButton icon="tabler:message" label="messages" color="invert" />
---
#default
:RayButton{icon="tabler:message" label="messages" color="invert"}
::
#### Overflow
Set `max` prop to handle overflow situation.
::ComponentPreview
---
props:
value: 110
max: 99
slots:
default: |
<RayButton icon="tabler:message" label="messages" color="invert" />
---
#default
:RayButton{icon="tabler:message" label="messages" color="invert"}
::

View File

@ -0,0 +1,90 @@
<script lang="ts">
import { computed, defineComponent, toRef, type PropType } from 'vue'
import { twJoin, twMerge } from 'tailwind-merge'
import { mark } from '../../ui.config'
import type { MarkColor, MarkPosition, MarkSize } from '../../types'
import { useRayUI } from '#build/imports'
const config = mark
export default defineComponent({
inheritAttrs: false,
props: {
value: {
type: [Number, String],
default: null,
},
max: {
type: Number,
default: null,
},
size: {
type: String as PropType<MarkSize>,
default: config.default.size,
},
color: {
type: String as PropType<MarkColor>,
default: config.default.color,
},
position: {
type: String as PropType<MarkPosition>,
default: config.default.position,
},
ui: {
type: Object as PropType<typeof config>,
default: () => ({}),
},
class: {
type: String,
default: '',
},
},
setup(props) {
const { ui, attrs } = useRayUI('mark', toRef(props, 'ui'), config)
const markClass = computed(() => {
return twMerge(twJoin(
ui.value.base,
ui.value.rounded,
ui.value.ring,
ui.value.position[props.position],
ui.value.background.replaceAll('{color}', props.color),
props.value ? ui.value.value.size[props.size] : ui.value.size[props.size],
props.value ? ui.value.value.translate[props.position] : ui.value.translate[props.position],
), props.class)
})
const isOverMax = computed(() => {
if (props.max === null) return false
if (typeof props.value === 'string') return false
return props.value > props.max
})
// consider string value
const countValue = computed(() => {
if (typeof props.value === 'string') return props.value
return isOverMax.value ? `${props.max}+` : props.value
})
return {
// eslint-disable-next-line vue/no-dupe-keys
ui,
attrs,
markClass,
isOverMax,
countValue,
}
},
})
</script>
<template>
<div :class="ui.wrapper">
<span :class="markClass">
<Transition v-bind="ui.transition">
<span v-if="value" :key="countValue" class="leading-none">{{ countValue }}</span>
</Transition>
</span>
<slot />
</div>
</template>

View File

@ -4,5 +4,6 @@ export * from './input'
export * from './textarea'
export * from './kbd'
export * from './toggle'
export * from './mark'
export * from './utils'

14
src/runtime/types/mark.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
import type { AppConfig } from '@nuxt/schema'
import type { mark } from '../ui.config'
import type { ExtractDeepKey } from './utils'
import type colors from '#ray-colors'
export type MarkSize =
| keyof typeof mark.size
| ExtractDeepKey<AppConfig, ['rayui', 'mark', 'size']>
export type MarkColor =
| ExtractDeepKey<AppConfig, ['rayui', 'mark', 'color']>
| (typeof colors)[number]
export type MarkPosition =
| keyof typeof mark.position
| ExtractDeepKey<AppConfig, ['rayui', 'mark', 'position']>

View File

@ -0,0 +1,51 @@
export default {
wrapper: 'relative',
base: 'absolute text-white rounded-full inline-flex justify-center items-center',
ring: 'ring-2 ring-white dark:ring-gray-900',
rounded: 'rounded-full',
background: 'bg-{color}-500',
position: {
'top-left': 'top-0 left-0',
'top-right': 'top-0 right-0',
'bottom-left': 'bottom-0 left-0',
'bottom-right': 'bottom-0 right-0',
},
translate: {
'top-left': '-translate-x-0.5 -translate-y-0.5',
'top-right': 'translate-x-0.5 -translate-y-0.5',
'bottom-left': '-translate-x-0.5 translate-y-0.5',
'bottom-right': 'translate-x-0.5 translate-y-0.5',
},
size: {
xs: 'w-1.5 h-1.5',
sm: 'w-2 h-2',
md: 'w-2.5 h-2.5',
},
value: {
size: {
xs: 'px-1 h-3 leading-none text-xs',
sm: 'px-1.5 h-4 leading-none text-xs',
md: 'px-2 h-5 leading-none text-sm',
},
translate: {
'top-left': '-translate-x-1/3 -translate-y-1/3',
'top-right': 'translate-x-1/3 -translate-y-1/3',
'bottom-left': '-translate-x-1/3 translate-y-1/3',
'bottom-right': 'translate-x-1/3 translate-y-1/3',
},
},
transition: {
moveClass: 'transform ease-out duration-300 transition',
enterActiveClass: 'transform ease-out duration-300 transition',
leaveActiveClass: 'transform ease-out duration-300 transition absolute',
enterFromClass: 'translate-y-2 opacity-0',
enterToClass: 'translate-y-0 opacity-100',
leaveFromClass: 'translate-y-0 opacity-100',
leaveToClass: '-translate-y-2 opacity-0',
},
default: {
size: 'sm',
color: 'primary',
position: 'top-right',
},
}

View File

@ -4,6 +4,7 @@ export { default as standard } from './standard'
// elements
export { default as button } from './elements/button'
export { default as kbd } from './elements/kbd'
export { default as mark } from './elements/mark'
// forms
export { default as input } from './forms/input'