Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions src/components/NcFormBoxSwitch/NcFormBoxSwitch.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<!--
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup lang="ts">
import type { Slot } from 'vue'

import { mdiToggleSwitch, mdiToggleSwitchOff } from '@mdi/js'
import { watch } from 'vue'
import NcFormBoxItem from '../NcFormBox/NcFormBoxItem.vue'
import NcIconSvgWrapper from '../NcIconSvgWrapper/NcIconSvgWrapper.vue'
import { createElementId } from '../../utils/createElementId.ts'

/** Switch toggle model value */
const modelValue = defineModel<boolean>({ required: true })

const {
label = undefined,
description = undefined,
disabled = false,
} = defineProps<{
/** Main label */
label?: string
/** Optional description below the label, also used for the aria-describedby */
description?: string
/** Disabled state */
disabled?: boolean
}>()

const emit = defineEmits<{
/** Switch is toggled ON */
enable: []
/** Switch is toggled OFF */
disable: []
}>()

defineSlots<{
/** Custom label content */
default?: Slot
/** Custom description content */
description?: Slot
}>()

const inputId = createElementId()

watch(modelValue, () => {
if (modelValue.value) {
emit('enable')
} else {
emit('disable')
}
}, {
// defineModel emits update:modelValue synchronously
// Watching it synchronously to emit the enable/disable events together with the update:modelValue event
flush: 'sync',
})
</script>

<template>
<NcFormBoxItem
tag="label"
:for="inputId">
<template v-if="$slots.default || label" #default>
<slot>
{{ label }}
</slot>
</template>
<template v-if="$slots.description || description" #description>
<slot name="description">
{{ description }}
</slot>
</template>
<template #icon="{ descriptionId }">
<input
:id="inputId"
v-model="modelValue"
:class="$style.formBoxSwitch__input"
type="checkbox"
role="switch"
:aria-describedby="descriptionId"
:disabled>
<NcIconSvgWrapper
:path="modelValue ? mdiToggleSwitch : mdiToggleSwitchOff"
:class="$style.formBoxSwitch__icon"
:size="34 /* --default-clickable-area */"
inline />
</template>
</NcFormBoxItem>
</template>

<style lang="scss" module>
input.formBoxSwitch__input {
margin: 0;
width: var(--default-clickable-area);
/* Keep it visually hidden but on the position of visual switch icon */
position: absolute;
inset-block: 0;
inset-inline-end: var(--form-element-label-offset);
z-index: -1;
opacity: 0 !important;
/* Override server styles */
height: auto;
cursor: inherit;
}

.formBoxSwitch__icon {
color: var(--color-text-maxcontrast);
}

input:checked + .formBoxSwitch__icon {
color: var(--color-primary-element);
}
</style>

<docs>
### General

A toggle switch to be used within `<NcFormBox>`.

Like other form box items, it has a label and an optional description.

In addition to the standard `v-model` binding, it emits `enable` and `disable` events when toggled **on** or **off**.

```vue
<script>
export default {
data() {
return {
switchValue: false,
}
}
}
</script>

<template>
<NcFormBox>
<NcFormBoxSwitch v-model="switchValue" label="Turn camera and microphone off by default" />
<NcFormBoxSwitch
v-model="switchValue"
label="Blur camera background by default"
disabled />
<NcFormBoxSwitch
v-model="switchValue"
label="Skip device preview before joining a call"
description="Will always show if recording consent is required"
@update:modelValue="console.log('Switch toggled', $event, switchValue)"
@enable="console.log('Switch enabled', switchValue)"
@disable="console.log('Switch disabled', switchValue)"
/>
<NcFormBoxSwitch v-model="switchValue" />
</NcFormBox>
</template>
```
</docs>
6 changes: 6 additions & 0 deletions src/components/NcFormBoxSwitch/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*!
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

export { default } from './NcFormBoxSwitch.vue'
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export { default as NcEmojiPicker } from './NcEmojiPicker/index.js'
export { default as NcEmptyContent } from './NcEmptyContent/index.ts'
export { default as NcFormBox } from './NcFormBox/index.ts'
export { default as NcFormBoxButton } from './NcFormBoxButton/index.ts'
export { default as NcFormBoxSwitch } from './NcFormBoxSwitch/index.ts'
export { default as NcFormGroup } from './NcFormGroup/index.ts'
export { default as NcGuestContent } from './NcGuestContent/index.ts'
export { default as NcHeaderButton } from './NcHeaderButton/index.ts'
Expand Down
Loading