mirror of
https://github.com/HoshinoSuzumi/rayine-ui.git
synced 2025-04-07 12:48:50 +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 './input'
|
||||
export * from './kbd'
|
||||
export * from './toggle'
|
||||
|
||||
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
|
||||
export { default as input } from './forms/input'
|
||||
export { default as toggle } from './forms/toggle'
|
||||
|
||||
// overlays
|
||||
export { default as message } from './overlays/message'
|
||||
|
@ -179,6 +179,16 @@ const safelistForComponent: Record<
|
||||
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[]) => {
|
||||
|
Loading…
Reference in New Issue
Block a user