mirror of
https://github.com/HoshinoSuzumi/rayine-ui.git
synced 2025-04-10 04:58:51 +08:00
✨ feat(mark): new component RayMark
This commit is contained in:
parent
83f8593391
commit
ddff1ca9c0
83
docs/content/2.components/mark.md
Normal file
83
docs/content/2.components/mark.md
Normal 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"}
|
||||||
|
::
|
90
src/runtime/components/elements/Mark.vue
Normal file
90
src/runtime/components/elements/Mark.vue
Normal 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>
|
@ -4,5 +4,6 @@ export * from './input'
|
|||||||
export * from './textarea'
|
export * from './textarea'
|
||||||
export * from './kbd'
|
export * from './kbd'
|
||||||
export * from './toggle'
|
export * from './toggle'
|
||||||
|
export * from './mark'
|
||||||
|
|
||||||
export * from './utils'
|
export * from './utils'
|
||||||
|
14
src/runtime/types/mark.d.ts
vendored
Normal file
14
src/runtime/types/mark.d.ts
vendored
Normal 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']>
|
51
src/runtime/ui.config/elements/mark.ts
Normal file
51
src/runtime/ui.config/elements/mark.ts
Normal 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',
|
||||||
|
},
|
||||||
|
}
|
@ -4,6 +4,7 @@ export { default as standard } from './standard'
|
|||||||
// elements
|
// elements
|
||||||
export { default as button } from './elements/button'
|
export { default as button } from './elements/button'
|
||||||
export { default as kbd } from './elements/kbd'
|
export { default as kbd } from './elements/kbd'
|
||||||
|
export { default as mark } from './elements/mark'
|
||||||
|
|
||||||
// forms
|
// forms
|
||||||
export { default as input } from './forms/input'
|
export { default as input } from './forms/input'
|
||||||
|
Loading…
Reference in New Issue
Block a user