Lzh on GitHub
一个在两种状态之间切换的控件。

用法

使用 v-model 指令控制 Switch 的选中状态。

<script setup lang="ts">
const value = ref(true)
</script>

<template>
  <USwitch v-model="value" />
</template>

当你不需要控制其状态时,使用 default-value prop 设置初始值。

<template>
  <USwitch default-value />
</template>

标签

使用 label prop 设置 Switch 的标签。

<template>
  <USwitch label="Check me" />
</template>

当使用 required prop 时,标签旁边会添加一个星号。

<template>
  <USwitch required label="Check me" />
</template>

描述

使用 description prop 设置 Switch 的描述。

This is a checkbox.

<template>
  <USwitch label="Check me" description="This is a checkbox." />
</template>

图标 (Icon)

使用 checked-iconunchecked-icon props 设置 Switch 选中和未选中时的图标。

<template>
  <USwitch
    unchecked-icon="i-lucide-x"
    checked-icon="i-lucide-check"
    default-value
    label="Check me"
  />
</template>

加载中 (Loading)

使用 loading prop 在 Switch 上显示一个加载图标。

<template>
  <USwitch loading default-value label="Check me" />
</template>

加载图标 (Loading Icon)

使用 loading-icon prop 自定义加载图标。默认为 i-lucide-loader-circle

<template>
  <USwitch loading loading-icon="i-lucide-loader" default-value label="Check me" />
</template>
你可以在 app.config.ts 中的 ui.icons.loading 键下全局自定义此图标。
你可以在 vite.config.ts 中的 ui.icons.loading 键下全局自定义此图标。

颜色 (Color)

使用 color prop 更改 Switch 的颜色。

<template>
  <USwitch color="neutral" default-value label="Check me" />
</template>

尺寸 (Size)

使用 size prop 更改 Switch 的尺寸。

<template>
  <USwitch size="xl" default-value label="Check me" />
</template>

禁用 (Disabled)

使用 disabled prop 禁用 Switch。

<template>
  <USwitch disabled label="Check me" />
</template>

API

Props

Prop Default Type
as

'div'

any

The element or component this component should render as.

color

'primary'

"error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"

size

'md'

"md" | "xs" | "sm" | "lg" | "xl"

loading

boolean

When true, the loading icon will be displayed.

loadingIcon

appConfig.ui.icons.loading

string

The icon when the loading prop is true.

checkedIcon

string

Display an icon when the switch is checked.

uncheckedIcon

string

Display an icon when the switch is unchecked.

label

string

description

string

defaultValue

boolean

The state of the switch when it is initially rendered. Use when you do not need to control its state.

disabled

boolean

When true, prevents the user from interacting with the switch.

required

boolean

When true, indicates that the user must set the value before the owning form can be submitted.

name

string

The name of the field. Submitted with its owning form as part of a name/value pair.

value

string

The value given as data when submitted with a name.

id

string

modelValue

boolean

ui

{ root?: ClassNameValue; base?: ClassNameValue; container?: ClassNameValue; thumb?: ClassNameValue; icon?: ClassNameValue; wrapper?: ClassNameValue; label?: ClassNameValue; description?: ClassNameValue; }

Slots

Slot Type
label

{ label?: string | undefined; }

description

{ description?: string | undefined; }

Emits

Event Type
update:modelValue

[value: boolean]

change

[payload: Event]

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    switch: {
      slots: {
        root: 'relative flex items-start',
        base: [
          'inline-flex items-center shrink-0 rounded-full border-2 border-transparent focus-visible:outline-2 focus-visible:outline-offset-2 data-[state=unchecked]:bg-accented',
          'transition-[background] duration-200'
        ],
        container: 'flex items-center',
        thumb: 'group pointer-events-none rounded-full bg-default shadow-lg ring-0 transition-transform duration-200 data-[state=unchecked]:translate-x-0 data-[state=unchecked]:rtl:-translate-x-0 flex items-center justify-center',
        icon: [
          'absolute shrink-0 group-data-[state=unchecked]:text-dimmed opacity-0 size-10/12',
          'transition-[color,opacity] duration-200'
        ],
        wrapper: 'ms-2',
        label: 'block font-medium text-default',
        description: 'text-muted'
      },
      variants: {
        color: {
          primary: {
            base: 'data-[state=checked]:bg-primary focus-visible:outline-primary',
            icon: 'group-data-[state=checked]:text-primary'
          },
          secondary: {
            base: 'data-[state=checked]:bg-secondary focus-visible:outline-secondary',
            icon: 'group-data-[state=checked]:text-secondary'
          },
          success: {
            base: 'data-[state=checked]:bg-success focus-visible:outline-success',
            icon: 'group-data-[state=checked]:text-success'
          },
          info: {
            base: 'data-[state=checked]:bg-info focus-visible:outline-info',
            icon: 'group-data-[state=checked]:text-info'
          },
          warning: {
            base: 'data-[state=checked]:bg-warning focus-visible:outline-warning',
            icon: 'group-data-[state=checked]:text-warning'
          },
          error: {
            base: 'data-[state=checked]:bg-error focus-visible:outline-error',
            icon: 'group-data-[state=checked]:text-error'
          },
          neutral: {
            base: 'data-[state=checked]:bg-inverted focus-visible:outline-inverted',
            icon: 'group-data-[state=checked]:text-highlighted'
          }
        },
        size: {
          xs: {
            base: 'w-7',
            container: 'h-4',
            thumb: 'size-3 data-[state=checked]:translate-x-3 data-[state=checked]:rtl:-translate-x-3',
            wrapper: 'text-xs'
          },
          sm: {
            base: 'w-8',
            container: 'h-4',
            thumb: 'size-3.5 data-[state=checked]:translate-x-3.5 data-[state=checked]:rtl:-translate-x-3.5',
            wrapper: 'text-xs'
          },
          md: {
            base: 'w-9',
            container: 'h-5',
            thumb: 'size-4 data-[state=checked]:translate-x-4 data-[state=checked]:rtl:-translate-x-4',
            wrapper: 'text-sm'
          },
          lg: {
            base: 'w-10',
            container: 'h-5',
            thumb: 'size-4.5 data-[state=checked]:translate-x-4.5 data-[state=checked]:rtl:-translate-x-4.5',
            wrapper: 'text-sm'
          },
          xl: {
            base: 'w-11',
            container: 'h-6',
            thumb: 'size-5 data-[state=checked]:translate-x-5 data-[state=checked]:rtl:-translate-x-5',
            wrapper: 'text-base'
          }
        },
        checked: {
          true: {
            icon: 'group-data-[state=checked]:opacity-100'
          }
        },
        unchecked: {
          true: {
            icon: 'group-data-[state=unchecked]:opacity-100'
          }
        },
        loading: {
          true: {
            icon: 'animate-spin'
          }
        },
        required: {
          true: {
            label: "after:content-['*'] after:ms-0.5 after:text-error"
          }
        },
        disabled: {
          true: {
            base: 'cursor-not-allowed opacity-75',
            label: 'cursor-not-allowed opacity-75',
            description: 'cursor-not-allowed opacity-75'
          }
        }
      },
      defaultVariants: {
        color: 'primary',
        size: 'md'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        switch: {
          slots: {
            root: 'relative flex items-start',
            base: [
              'inline-flex items-center shrink-0 rounded-full border-2 border-transparent focus-visible:outline-2 focus-visible:outline-offset-2 data-[state=unchecked]:bg-accented',
              'transition-[background] duration-200'
            ],
            container: 'flex items-center',
            thumb: 'group pointer-events-none rounded-full bg-default shadow-lg ring-0 transition-transform duration-200 data-[state=unchecked]:translate-x-0 data-[state=unchecked]:rtl:-translate-x-0 flex items-center justify-center',
            icon: [
              'absolute shrink-0 group-data-[state=unchecked]:text-dimmed opacity-0 size-10/12',
              'transition-[color,opacity] duration-200'
            ],
            wrapper: 'ms-2',
            label: 'block font-medium text-default',
            description: 'text-muted'
          },
          variants: {
            color: {
              primary: {
                base: 'data-[state=checked]:bg-primary focus-visible:outline-primary',
                icon: 'group-data-[state=checked]:text-primary'
              },
              secondary: {
                base: 'data-[state=checked]:bg-secondary focus-visible:outline-secondary',
                icon: 'group-data-[state=checked]:text-secondary'
              },
              success: {
                base: 'data-[state=checked]:bg-success focus-visible:outline-success',
                icon: 'group-data-[state=checked]:text-success'
              },
              info: {
                base: 'data-[state=checked]:bg-info focus-visible:outline-info',
                icon: 'group-data-[state=checked]:text-info'
              },
              warning: {
                base: 'data-[state=checked]:bg-warning focus-visible:outline-warning',
                icon: 'group-data-[state=checked]:text-warning'
              },
              error: {
                base: 'data-[state=checked]:bg-error focus-visible:outline-error',
                icon: 'group-data-[state=checked]:text-error'
              },
              neutral: {
                base: 'data-[state=checked]:bg-inverted focus-visible:outline-inverted',
                icon: 'group-data-[state=checked]:text-highlighted'
              }
            },
            size: {
              xs: {
                base: 'w-7',
                container: 'h-4',
                thumb: 'size-3 data-[state=checked]:translate-x-3 data-[state=checked]:rtl:-translate-x-3',
                wrapper: 'text-xs'
              },
              sm: {
                base: 'w-8',
                container: 'h-4',
                thumb: 'size-3.5 data-[state=checked]:translate-x-3.5 data-[state=checked]:rtl:-translate-x-3.5',
                wrapper: 'text-xs'
              },
              md: {
                base: 'w-9',
                container: 'h-5',
                thumb: 'size-4 data-[state=checked]:translate-x-4 data-[state=checked]:rtl:-translate-x-4',
                wrapper: 'text-sm'
              },
              lg: {
                base: 'w-10',
                container: 'h-5',
                thumb: 'size-4.5 data-[state=checked]:translate-x-4.5 data-[state=checked]:rtl:-translate-x-4.5',
                wrapper: 'text-sm'
              },
              xl: {
                base: 'w-11',
                container: 'h-6',
                thumb: 'size-5 data-[state=checked]:translate-x-5 data-[state=checked]:rtl:-translate-x-5',
                wrapper: 'text-base'
              }
            },
            checked: {
              true: {
                icon: 'group-data-[state=checked]:opacity-100'
              }
            },
            unchecked: {
              true: {
                icon: 'group-data-[state=unchecked]:opacity-100'
              }
            },
            loading: {
              true: {
                icon: 'animate-spin'
              }
            },
            required: {
              true: {
                label: "after:content-['*'] after:ms-0.5 after:text-error"
              }
            },
            disabled: {
              true: {
                base: 'cursor-not-allowed opacity-75',
                label: 'cursor-not-allowed opacity-75',
                description: 'cursor-not-allowed opacity-75'
              }
            }
          },
          defaultVariants: {
            color: 'primary',
            size: 'md'
          }
        }
      }
    })
  ]
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'

export default defineConfig({
  plugins: [
    vue(),
    uiPro({
      ui: {
        switch: {
          slots: {
            root: 'relative flex items-start',
            base: [
              'inline-flex items-center shrink-0 rounded-full border-2 border-transparent focus-visible:outline-2 focus-visible:outline-offset-2 data-[state=unchecked]:bg-accented',
              'transition-[background] duration-200'
            ],
            container: 'flex items-center',
            thumb: 'group pointer-events-none rounded-full bg-default shadow-lg ring-0 transition-transform duration-200 data-[state=unchecked]:translate-x-0 data-[state=unchecked]:rtl:-translate-x-0 flex items-center justify-center',
            icon: [
              'absolute shrink-0 group-data-[state=unchecked]:text-dimmed opacity-0 size-10/12',
              'transition-[color,opacity] duration-200'
            ],
            wrapper: 'ms-2',
            label: 'block font-medium text-default',
            description: 'text-muted'
          },
          variants: {
            color: {
              primary: {
                base: 'data-[state=checked]:bg-primary focus-visible:outline-primary',
                icon: 'group-data-[state=checked]:text-primary'
              },
              secondary: {
                base: 'data-[state=checked]:bg-secondary focus-visible:outline-secondary',
                icon: 'group-data-[state=checked]:text-secondary'
              },
              success: {
                base: 'data-[state=checked]:bg-success focus-visible:outline-success',
                icon: 'group-data-[state=checked]:text-success'
              },
              info: {
                base: 'data-[state=checked]:bg-info focus-visible:outline-info',
                icon: 'group-data-[state=checked]:text-info'
              },
              warning: {
                base: 'data-[state=checked]:bg-warning focus-visible:outline-warning',
                icon: 'group-data-[state=checked]:text-warning'
              },
              error: {
                base: 'data-[state=checked]:bg-error focus-visible:outline-error',
                icon: 'group-data-[state=checked]:text-error'
              },
              neutral: {
                base: 'data-[state=checked]:bg-inverted focus-visible:outline-inverted',
                icon: 'group-data-[state=checked]:text-highlighted'
              }
            },
            size: {
              xs: {
                base: 'w-7',
                container: 'h-4',
                thumb: 'size-3 data-[state=checked]:translate-x-3 data-[state=checked]:rtl:-translate-x-3',
                wrapper: 'text-xs'
              },
              sm: {
                base: 'w-8',
                container: 'h-4',
                thumb: 'size-3.5 data-[state=checked]:translate-x-3.5 data-[state=checked]:rtl:-translate-x-3.5',
                wrapper: 'text-xs'
              },
              md: {
                base: 'w-9',
                container: 'h-5',
                thumb: 'size-4 data-[state=checked]:translate-x-4 data-[state=checked]:rtl:-translate-x-4',
                wrapper: 'text-sm'
              },
              lg: {
                base: 'w-10',
                container: 'h-5',
                thumb: 'size-4.5 data-[state=checked]:translate-x-4.5 data-[state=checked]:rtl:-translate-x-4.5',
                wrapper: 'text-sm'
              },
              xl: {
                base: 'w-11',
                container: 'h-6',
                thumb: 'size-5 data-[state=checked]:translate-x-5 data-[state=checked]:rtl:-translate-x-5',
                wrapper: 'text-base'
              }
            },
            checked: {
              true: {
                icon: 'group-data-[state=checked]:opacity-100'
              }
            },
            unchecked: {
              true: {
                icon: 'group-data-[state=unchecked]:opacity-100'
              }
            },
            loading: {
              true: {
                icon: 'animate-spin'
              }
            },
            required: {
              true: {
                label: "after:content-['*'] after:ms-0.5 after:text-error"
              }
            },
            disabled: {
              true: {
                base: 'cursor-not-allowed opacity-75',
                label: 'cursor-not-allowed opacity-75',
                description: 'cursor-not-allowed opacity-75'
              }
            }
          },
          defaultVariants: {
            color: 'primary',
            size: 'md'
          }
        }
      }
    })
  ]
})