Skip to content

Commit f515c3e

Browse files
authored
Merge pull request #7795 from nextcloud-libraries/backport/7781/stable8
[stable8] feat: add NcFormBoxSwitch
2 parents 085edf3 + dd5b1a9 commit f515c3e

File tree

4 files changed

+172
-1
lines changed

4 files changed

+172
-1
lines changed

src/components/NcFormBoxCopyButton/NcFormBoxCopyButton.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ const props = withDefaults(defineProps<{
2525
})
2626
2727
const emit = defineEmits<{
28-
copy: () => void
28+
/** Value has been successfully copied */
29+
(event: 'copy'): void
2930
}>()
3031
3132
const { isCopied, copy } = useCopy(() => props.value)
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<!--
2+
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
- SPDX-License-Identifier: AGPL-3.0-or-later
4+
-->
5+
6+
<script lang="ts">
7+
export default {
8+
model: {
9+
prop: 'modelValue',
10+
event: 'update:modelValue',
11+
},
12+
}
13+
</script>
14+
15+
<script setup lang="ts">
16+
import { mdiToggleSwitch, mdiToggleSwitchOff } from '@mdi/js'
17+
import { useVModel } from '@vueuse/core'
18+
import { watch } from 'vue'
19+
import NcFormBoxItem from '../NcFormBox/NcFormBoxItem.vue'
20+
import NcIconSvgWrapper from '../NcIconSvgWrapper/NcIconSvgWrapper.vue'
21+
import { createElementId } from '../../utils/createElementId.ts'
22+
23+
const props = withDefaults(defineProps<{
24+
/** Main label */
25+
label?: string
26+
/** Optional description below the label, also used for the aria-describedby */
27+
description?: string
28+
/** Disabled state */
29+
disabled?: boolean
30+
/** Switch toggle model value */
31+
modelValue: boolean // eslint-disable-line vue/no-unused-properties
32+
}>(), {
33+
label: undefined,
34+
description: undefined,
35+
disabled: false,
36+
})
37+
38+
const emit = defineEmits<{
39+
/** Switch is toggled ON */
40+
(event: 'enable'): void
41+
/** Switch is toggled OFF */
42+
(event: 'disable'): void
43+
(event: 'update:modelValue', value: boolean): void
44+
}>()
45+
46+
const model = useVModel(props, 'modelValue', emit)
47+
48+
const inputId = createElementId()
49+
50+
watch(model, () => {
51+
if (model.value) {
52+
emit('enable')
53+
} else {
54+
emit('disable')
55+
}
56+
}, {
57+
// defineModel emits update:modelValue synchronously
58+
// Watching it synchronously to emit the enable/disable events together with the update:modelValue event
59+
flush: 'sync',
60+
})
61+
</script>
62+
63+
<template>
64+
<NcFormBoxItem
65+
tag="label"
66+
:for="inputId">
67+
<template v-if="$slots.default || label" #default>
68+
<!-- @slot Custom label content -->
69+
<slot>
70+
{{ label }}
71+
</slot>
72+
</template>
73+
<template v-if="$slots.description || description" #description>
74+
<!-- @slot Custom description content -->
75+
<slot name="description">
76+
{{ description }}
77+
</slot>
78+
</template>
79+
<template #icon="{ descriptionId }">
80+
<input
81+
:id="inputId"
82+
v-model="model"
83+
:class="$style.formBoxSwitch__input"
84+
type="checkbox"
85+
role="switch"
86+
:aria-describedby="descriptionId"
87+
:disabled="disabled">
88+
<NcIconSvgWrapper
89+
:path="model ? mdiToggleSwitch : mdiToggleSwitchOff"
90+
:class="$style.formBoxSwitch__icon"
91+
:size="34 /* --default-clickable-area */"
92+
inline />
93+
</template>
94+
</NcFormBoxItem>
95+
</template>
96+
97+
<style lang="scss" module>
98+
input.formBoxSwitch__input {
99+
margin: 0;
100+
width: var(--default-clickable-area);
101+
/* Keep it visually hidden but on the position of visual switch icon */
102+
position: absolute;
103+
inset-block: 0;
104+
inset-inline-end: var(--form-element-label-offset);
105+
z-index: -1;
106+
opacity: 0 !important;
107+
/* Override server styles */
108+
height: auto;
109+
cursor: inherit;
110+
}
111+
112+
.formBoxSwitch__icon {
113+
color: var(--color-text-maxcontrast);
114+
}
115+
116+
input:checked + .formBoxSwitch__icon {
117+
color: var(--color-primary-element);
118+
}
119+
</style>
120+
121+
<docs>
122+
### General
123+
124+
A toggle switch to be used within `<NcFormBox>`.
125+
126+
Like other form box items, it has a label and an optional description.
127+
128+
In addition to the standard `v-model` binding, it emits `enable` and `disable` events when toggled **on** or **off**.
129+
130+
```vue
131+
<script>
132+
export default {
133+
data() {
134+
return {
135+
switchValue: false,
136+
}
137+
},
138+
methods: {
139+
log: console.log,
140+
},
141+
}
142+
</script>
143+
144+
<template>
145+
<NcFormBox>
146+
<NcFormBoxSwitch v-model="switchValue" label="Turn camera and microphone off by default" />
147+
<NcFormBoxSwitch
148+
v-model="switchValue"
149+
label="Blur camera background by default"
150+
disabled />
151+
<NcFormBoxSwitch
152+
v-model="switchValue"
153+
label="Skip device preview before joining a call"
154+
description="Will always show if recording consent is required"
155+
@update:modelValue="log('Switch toggled', $event, switchValue)"
156+
@enable="log('Switch enabled', switchValue)"
157+
@disable="log('Switch disabled', switchValue)"
158+
/>
159+
<NcFormBoxSwitch v-model="switchValue" />
160+
</NcFormBox>
161+
</template>
162+
```
163+
</docs>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*!
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
export { default } from './NcFormBoxSwitch.vue'

src/components/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export { default as NcEmptyContent } from './NcEmptyContent/index.js'
6060
export { default as NcFormBox } from './NcFormBox/index.ts'
6161
export { default as NcFormBoxButton } from './NcFormBoxButton/index.ts'
6262
export { default as NcFormBoxCopyButton } from './NcFormBoxCopyButton/index.ts'
63+
export { default as NcFormBoxSwitch } from './NcFormBoxSwitch/index.ts'
6364
export { default as NcFormGroup } from './NcFormGroup/index.ts'
6465
export { default as NcGuestContent } from './NcGuestContent/index.js'
6566
export { default as NcHeaderButton } from './NcHeaderButton/index.js'

0 commit comments

Comments
 (0)