Skip to content

Commit e31b06d

Browse files
authored
Merge pull request #8060 from nextcloud-libraries/backport/8020/stable8
[stable8] fix(NcActions): migrate to custom icons in Checkbox and Radio actions
2 parents b2522d8 + 7d3eb04 commit e31b06d

2 files changed

Lines changed: 94 additions & 200 deletions

File tree

src/components/NcActionCheckbox/NcActionCheckbox.vue

Lines changed: 55 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -9,61 +9,64 @@ This component is made to be used inside of the [NcActions](#NcActions) componen
99
```vue
1010
<template>
1111
<NcActions>
12-
<NcActionCheckbox @change="alert('(un)checked !')">First choice</NcActionCheckbox>
13-
<NcActionCheckbox v-model="checkboxValue" value="second" @change="alert('(un)checked !')">Second choice (v-model)</NcActionCheckbox>
14-
<NcActionCheckbox :model-value="checkboxValue" @change="alert('(un)checked !')">Third choice (checked)</NcActionCheckbox>
15-
<NcActionCheckbox :disabled="true" @change="alert('(un)checked !')">Fourth choice (disabled)</NcActionCheckbox>
12+
<NcActionCheckbox @change="log('(un)checked !')">First choice</NcActionCheckbox>
13+
<NcActionCheckbox value="second" @change="log('(un)checked !')">Second choice</NcActionCheckbox>
14+
<NcActionCheckbox v-model="checkboxValue" @change="log('(un)checked !')">Third choice (checked)</NcActionCheckbox>
15+
<NcActionCheckbox :disabled="true" @change="log('(un)checked !')">Second choice (disabled)</NcActionCheckbox>
1616
</NcActions>
1717
</template>
1818

1919
<script>
2020
export default {
2121
data() {
2222
return {
23-
checkboxValue: false,
23+
checkboxValue: true,
2424
}
2525
},
2626

2727
methods: {
28-
alert(message) {
29-
alert(message)
30-
}
31-
}
28+
log: console.log,
29+
},
3230
}
3331
</script>
3432
```
3533
</docs>
3634

3735
<template>
3836
<li class="action" :class="{ 'action--disabled': disabled }" :role="isInSemanticMenu && 'presentation'">
39-
<span class="action-checkbox" :role="isInSemanticMenu && 'menuitemcheckbox'" :aria-checked="ariaChecked">
40-
<input
41-
:id="id"
42-
ref="checkbox"
43-
:disabled="disabled"
44-
:checked="model"
45-
:value="value"
46-
:class="{ focusable: isFocusable }"
47-
type="checkbox"
48-
class="checkbox action-checkbox__checkbox"
49-
@keydown.enter.exact.prevent="checkInput"
50-
@change="onChange">
51-
<label ref="label" :for="id" class="action-checkbox__label">{{ text }}</label>
52-
53-
<!-- fake slot to gather inner text -->
54-
<slot v-if="false" />
55-
</span>
37+
<label class="action-checkbox" :role="isInSemanticMenu && 'menuitemcheckbox'" :aria-checked="isInSemanticMenu && localModel.toString()">
38+
<span class="action-checkbox__icon">
39+
<input
40+
:id="id"
41+
v-model="localModel"
42+
type="checkbox"
43+
class="action-checkbox__input"
44+
:class="{ focusable: !disabled }"
45+
:value="value"
46+
:disabled="disabled"
47+
@change="onChange">
48+
<NcIconSvgWrapper :path="localModel ? mdiCheckboxMarked : mdiCheckboxBlankOutline" :size="20" />
49+
</span>
50+
<span class="action-checkbox__text">{{ text }}</span>
51+
</label>
5652
</li>
5753
</template>
5854

5955
<script>
56+
import { mdiCheckboxBlankOutline, mdiCheckboxMarked } from '@mdi/js'
57+
import { ref, watch } from 'vue'
58+
import NcIconSvgWrapper from '../NcIconSvgWrapper/NcIconSvgWrapper.vue'
6059
import { useModelMigration } from '../../composables/useModelMigration.ts'
6160
import ActionGlobalMixin from '../../mixins/actionGlobal.js'
6261
import GenRandomId from '../../utils/GenRandomId.js'
6362
6463
export default {
6564
name: 'NcActionCheckbox',
6665
66+
components: {
67+
NcIconSvgWrapper,
68+
},
69+
6770
mixins: [ActionGlobalMixin],
6871
6972
inject: {
@@ -125,8 +128,11 @@ export default {
125128
},
126129
127130
emits: [
131+
/** Native change event */
128132
'change',
133+
/** Checkbox is checked */
129134
'check',
135+
/** Checkbox is unchecked */
130136
'uncheck',
131137
/**
132138
* Removed in v9 - use `update:modelValue` (`v-model`) instead
@@ -146,63 +152,25 @@ export default {
146152
147153
setup() {
148154
const model = useModelMigration('checked', 'update:checked')
149-
return {
150-
model,
151-
}
152-
},
153155
154-
computed: {
155-
/**
156-
* determines if the action is focusable
157-
*
158-
* @return {boolean} is the action focusable ?
159-
*/
160-
isFocusable() {
161-
return !this.disabled
162-
},
156+
// For backward compatibility to support using without v-model binding (passive model)
157+
const localModel = ref(model.value)
158+
watch(model, (newValue) => (localModel.value = newValue), { flush: 'sync' })
159+
watch(localModel, (newValue) => (model.value = newValue), { flush: 'sync' })
163160
164-
/**
165-
* aria-checked attribute for role="menuitemcheckbox"
166-
*
167-
* @return {'true'|'false'|undefined} aria-checked value if needed
168-
*/
169-
ariaChecked() {
170-
if (this.isInSemanticMenu) {
171-
return this.model ? 'true' : 'false'
172-
}
173-
return undefined
174-
},
161+
return {
162+
localModel,
163+
mdiCheckboxBlankOutline,
164+
mdiCheckboxMarked,
165+
}
175166
},
176167
177168
methods: {
178-
checkInput() {
179-
// by clicking we also trigger the change event
180-
this.$refs.label.click()
181-
},
182-
183169
onChange(event) {
184-
this.model = this.$refs.checkbox.checked
185-
186-
/**
187-
* Emitted when the checkbox state is changed
188-
*
189-
* @type {Event}
190-
*/
191170
this.$emit('change', event)
192-
193-
if (this.$refs.checkbox.checked) {
194-
/**
195-
* Emitted when the checkbox is checked
196-
*
197-
* @type {Event}
198-
*/
171+
if (event.target.checked) {
199172
this.$emit('check')
200173
} else {
201-
/**
202-
* Emitted when the checkbox is unchecked
203-
*
204-
* @type {Event}
205-
*/
206174
this.$emit('uncheck')
207175
}
208176
},
@@ -214,56 +182,24 @@ export default {
214182
@use '../../assets/action.scss' as *;
215183
@include action-active;
216184
@include action--disabled;
185+
@include action-item('checkbox');
217186
218-
.action-checkbox {
219-
display: flex;
220-
align-items: flex-start;
221-
222-
width: 100%;
223-
height: auto;
224-
margin: 0;
225-
padding: 0;
226-
227-
cursor: pointer;
228-
white-space: nowrap;
229-
230-
color: var(--color-main-text);
231-
border: 0;
232-
border-radius: 0; // otherwise Safari will cut the border-radius area
233-
background-color: transparent;
234-
box-shadow: none;
187+
.action:has(:focus-visible) {
188+
outline: 2px solid currentColor;
189+
}
235190
236-
font-weight: normal;
237-
line-height: var(--default-clickable-area);
191+
.action-checkbox {
192+
&__icon {
193+
color: var(--color-primary-element);
194+
}
238195
239-
/* checkbox/radio fixes */
240-
&__checkbox {
196+
&__input {
197+
width: 20px;
198+
height: 20px;
199+
margin: auto;
241200
position: absolute;
242-
inset-inline-start: 0 !important;
243201
z-index: -1;
244-
opacity: 0;
245-
}
246-
247-
&__label {
248-
display: flex;
249-
align-items: center; // align checkbox to text
250-
251-
width: 100%;
252-
padding: 0 !important;
253-
padding-inline-end: $icon-margin !important;
254-
255-
&::before {
256-
margin-block: 0 !important;
257-
margin-inline: calc((var(--default-clickable-area) - 14px) / 2) !important;
258-
}
259-
}
260-
261-
&--disabled {
262-
&,
263-
.action-checkbox__label {
264-
cursor: pointer;
265-
}
202+
opacity: 0 !important;
266203
}
267204
}
268-
269205
</style>

0 commit comments

Comments
 (0)