mirror of
https://github.com/HoshinoSuzumi/rayine-ui.git
synced 2025-04-10 04:58:51 +08:00
✨ feat(toggle): new component
This commit is contained in:
parent
177c4cfd38
commit
2729812a3c
69
docs/content/2.components/toggle.md
Normal file
69
docs/content/2.components/toggle.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
description: Get a dynamic switch component
|
||||||
|
since: 1.3.4
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Use the `v-model` directive to make it reactive.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
v-model: checked
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Colors
|
||||||
|
|
||||||
|
The `color` prop affects the background color of the toggle.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
modelValue: true
|
||||||
|
props:
|
||||||
|
color: primary
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Sizes
|
||||||
|
|
||||||
|
The default size of the toggle is `md`.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
size: md
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Rounded
|
||||||
|
|
||||||
|
You can make the toggle rounded by setting the `rounded` prop to `true`.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
props:
|
||||||
|
rounded: true
|
||||||
|
size: md
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
### Disabled
|
||||||
|
|
||||||
|
Disable it.
|
||||||
|
|
||||||
|
::ComponentPreview
|
||||||
|
---
|
||||||
|
privateProps:
|
||||||
|
modelValue: true
|
||||||
|
props:
|
||||||
|
disabled: true
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
::ComponentDefaults
|
||||||
|
::
|
100
src/runtime/components/forms/Toggle.vue
Normal file
100
src/runtime/components/forms/Toggle.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, toRef, type PropType } from 'vue'
|
||||||
|
import { twJoin, twMerge } from 'tailwind-merge'
|
||||||
|
import { toggle } from '../../ui.config'
|
||||||
|
import type { DeepPartial, Strategy, ToggleColor, ToggleSize } from '../../types'
|
||||||
|
import { useRayUI } from '#build/imports'
|
||||||
|
|
||||||
|
const config = toggle
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean as PropType<boolean | null>,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
rounded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String as PropType<ToggleSize>,
|
||||||
|
default: config.default.size,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String as PropType<ToggleColor>,
|
||||||
|
default: config.default.color,
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
'update:modelValue',
|
||||||
|
'change',
|
||||||
|
],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { ui, attrs } = useRayUI('toggle', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
|
const checked = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (value: boolean) => {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
emit('change', value)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const toggleClass = computed(() => {
|
||||||
|
return twMerge(twJoin(
|
||||||
|
ui.value.base,
|
||||||
|
props.rounded ? 'rounded-full' : ui.value.rounded,
|
||||||
|
ui.value.size[props.size],
|
||||||
|
ui.value.ring.replaceAll('{color}', props.color),
|
||||||
|
checked.value ? ui.value.active.replaceAll('{color}', props.color) : ui.value.inactive,
|
||||||
|
), props.class)
|
||||||
|
})
|
||||||
|
|
||||||
|
const bulletClass = computed(() => {
|
||||||
|
return twJoin(
|
||||||
|
ui.value.bullet.base,
|
||||||
|
props.rounded ? 'rounded-full' : ui.value.bullet.rounded,
|
||||||
|
ui.value.bullet.shadow,
|
||||||
|
ui.value.bullet.size[props.size],
|
||||||
|
!props.disabled && ui.value.bullet.translate[props.size],
|
||||||
|
checked.value ? ui.value.bullet.active[props.size] : ui.value.bullet.inactive,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (!props.disabled) {
|
||||||
|
checked.value = !checked.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
|
attrs,
|
||||||
|
checked,
|
||||||
|
toggleClass,
|
||||||
|
bulletClass,
|
||||||
|
handleClick,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button :class="toggleClass" :disabled="disabled" v-bind="attrs" @click="handleClick">
|
||||||
|
<span :class="bulletClass" />
|
||||||
|
</button>
|
||||||
|
</template>
|
@ -2,5 +2,6 @@ export * from './button'
|
|||||||
export * from './message'
|
export * from './message'
|
||||||
export * from './input'
|
export * from './input'
|
||||||
export * from './kbd'
|
export * from './kbd'
|
||||||
|
export * from './toggle'
|
||||||
|
|
||||||
export * from './utils'
|
export * from './utils'
|
||||||
|
9
src/runtime/types/toggle.ts
Normal file
9
src/runtime/types/toggle.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import type { AppConfig } from '@nuxt/schema'
|
||||||
|
import type { toggle } from '../ui.config'
|
||||||
|
import type { ExtractDeepKey } from './utils'
|
||||||
|
import type colors from '#ray-colors'
|
||||||
|
|
||||||
|
export type ToggleSize =
|
||||||
|
| keyof typeof toggle.size
|
||||||
|
| ExtractDeepKey<AppConfig, ['rayui', 'toggle', 'size']>
|
||||||
|
export type ToggleColor = (typeof colors)[number]
|
53
src/runtime/ui.config/forms/toggle.ts
Normal file
53
src/runtime/ui.config/forms/toggle.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
export default {
|
||||||
|
base: 'relative inline-flex flex-shrink-0 shadow-inner disabled:cursor-not-allowed disabled:opacity-50 focus:outline-none transition ease-in-out duration-200 group',
|
||||||
|
rounded: 'rounded-md',
|
||||||
|
ring: 'focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-{color}-500 dark:focus-visible:ring-{color}-400 focus-visible:ring-offset-white dark:focus-visible:ring-offset-gray-900',
|
||||||
|
active: 'bg-{color}-500 dark:bg-{color}-400',
|
||||||
|
inactive: 'bg-gray-100 dark:bg-gray-800',
|
||||||
|
size: {
|
||||||
|
'2xs': 'h-3 w-5',
|
||||||
|
'xs': 'h-3.5 w-6',
|
||||||
|
'sm': 'h-4 w-7',
|
||||||
|
'md': 'h-5 w-9',
|
||||||
|
'lg': 'h-6 w-11',
|
||||||
|
'xl': 'h-7 w-[3.25rem]',
|
||||||
|
'2xl': 'h-8 w-[3.75rem]',
|
||||||
|
},
|
||||||
|
bullet: {
|
||||||
|
base: 'relative inline-block m-0.5 bg-white dark:bg-gray-900 pointer-events-none transform transition ease-in-out duration-300 group-active:scale-90 group-disabled:scale-100',
|
||||||
|
shadow: 'shadow',
|
||||||
|
rounded: 'rounded',
|
||||||
|
size: {
|
||||||
|
'2xs': 'h-2 w-2',
|
||||||
|
'xs': 'h-2.5 w-2.5',
|
||||||
|
'sm': 'h-3 w-3',
|
||||||
|
'md': 'h-4 w-4',
|
||||||
|
'lg': 'h-5 w-5',
|
||||||
|
'xl': 'h-6 w-6',
|
||||||
|
'2xl': 'h-7 w-7',
|
||||||
|
},
|
||||||
|
translate: {
|
||||||
|
'2xs': 'group-active:translate-x-1',
|
||||||
|
'xs': 'group-active:translate-x-1.5',
|
||||||
|
'sm': 'group-active:translate-x-2',
|
||||||
|
'md': 'group-active:translate-x-2.5',
|
||||||
|
'lg': 'group-active:translate-x-3',
|
||||||
|
'xl': 'group-active:translate-x-3.5',
|
||||||
|
'2xl': 'group-active:translate-x-4',
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
'2xs': 'translate-x-2',
|
||||||
|
'xs': 'translate-x-2.5',
|
||||||
|
'sm': 'translate-x-3',
|
||||||
|
'md': 'translate-x-4',
|
||||||
|
'lg': 'translate-x-5',
|
||||||
|
'xl': 'translate-x-6',
|
||||||
|
'2xl': 'translate-x-7',
|
||||||
|
},
|
||||||
|
inactive: 'translate-x-0',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
size: 'md',
|
||||||
|
color: 'primary',
|
||||||
|
},
|
||||||
|
}
|
@ -7,6 +7,7 @@ export { default as kbd } from './elements/kbd'
|
|||||||
|
|
||||||
// forms
|
// forms
|
||||||
export { default as input } from './forms/input'
|
export { default as input } from './forms/input'
|
||||||
|
export { default as toggle } from './forms/toggle'
|
||||||
|
|
||||||
// overlays
|
// overlays
|
||||||
export { default as message } from './overlays/message'
|
export { default as message } from './overlays/message'
|
||||||
|
@ -179,6 +179,16 @@ const safelistForComponent: Record<
|
|||||||
variants: ['focus'],
|
variants: ['focus'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
toggle: colorsToRegex => [
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^ring-(${colorsToRegex})-400$`),
|
||||||
|
variants: ['focus-visible', 'dark'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^ring-(${colorsToRegex})-500$`),
|
||||||
|
variants: ['focus-visible'],
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateSafelist = (colors: string[], globalColors: string[]) => {
|
export const generateSafelist = (colors: string[], globalColors: string[]) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user