Skip to content

Commit 42a1326

Browse files
authored
feat(ui5-checkbox): add indeterminate state (#3309)
Added new property attribute which determines whether the component is displayed as partially checked. Fixes: #3217
1 parent 4ac6d23 commit 42a1326

File tree

6 files changed

+135
-7
lines changed

6 files changed

+135
-7
lines changed

packages/main/src/CheckBox.hbs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div
22
class="ui5-checkbox-root {{classes.main}}"
33
role="checkbox"
4-
aria-checked="{{checked}}"
4+
aria-checked="{{ariaChecked}}"
55
aria-readonly="{{ariaReadonly}}"
66
aria-disabled="{{ariaDisabled}}"
77
aria-label="{{ariaLabelText}}"
@@ -14,7 +14,7 @@
1414
dir="{{effectiveDir}}"
1515
>
1616
<div id="{{_id}}-CbBg" class="ui5-checkbox-inner">
17-
{{#if checked}}
17+
{{#if isCompletelyChecked}}
1818
<ui5-icon name="accept" class="ui5-checkbox-icon"></ui5-icon>
1919
{{/if}}
2020

packages/main/src/CheckBox.js

+36-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,27 @@ const metadata = {
5656
type: Boolean,
5757
},
5858

59+
/**
60+
* Defines whether the component is displayed as partially checked.
61+
* <br><br>
62+
* <b>Note:</b> The indeterminate state can be set only programatically and can’t be achieved by user
63+
* interaction and the resulting visual state depends on the values of the <code>indeterminate</code>
64+
* and <code>checked</code> properties:
65+
* <ul>
66+
* <li> If the component is checked and indeterminate, it will be displayed as partially checked
67+
* <li> If the component is checked and it is not indeterminate, it will be displayed as checked
68+
* <li> If the component is not checked, it will be displayed as not checked regardless value of the indeterminate attribute
69+
* </ul>
70+
*
71+
* @type {boolean}
72+
* @defaultvalue false
73+
* @public
74+
* @since 1.0.0-rc.15
75+
*/
76+
indeterminate: {
77+
type: Boolean,
78+
},
79+
5980
/**
6081
* Defines if the component is checked.
6182
* <br><br>
@@ -307,7 +328,13 @@ class CheckBox extends UI5Element {
307328

308329
toggle() {
309330
if (this.canToggle()) {
310-
this.checked = !this.checked;
331+
if (this.indeterminate) {
332+
this.indeterminate = false;
333+
this.checked = true;
334+
} else {
335+
this.checked = !this.checked;
336+
}
337+
311338
this.fireEvent("change");
312339
// Angular two way data binding
313340
this.fireEvent("value-changed");
@@ -349,6 +376,10 @@ class CheckBox extends UI5Element {
349376
return getEffectiveAriaLabelText(this);
350377
}
351378

379+
get ariaChecked() {
380+
return this.indeterminate && this.checked ? "mixed" : this.checked;
381+
}
382+
352383
get ariaLabelledBy() {
353384
if (!this.ariaLabelText) {
354385
return this.text ? `${this._id}-label` : undefined;
@@ -374,6 +405,10 @@ class CheckBox extends UI5Element {
374405
return this.disabled ? undefined : tabindex || "0";
375406
}
376407

408+
get isCompletelyChecked() {
409+
return this.checked && !this.indeterminate;
410+
}
411+
377412
static get dependencies() {
378413
return [
379414
Label,

packages/main/src/themes/CheckBox.css

+9-4
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
color: var(--sapField_SuccessColor);
7575
}
7676

77-
:host([value-state="Warning"]) .ui5-checkbox-icon {
77+
:host([value-state="Warning"]) .ui5-checkbox-icon,
78+
:host([value-state="Warning"][indeterminate]) .ui5-checkbox-inner::after {
7879
color: var(--_ui5_checkbox_checkmark_warning_color);
7980
}
8081

@@ -142,9 +143,11 @@ https://github.com/philipwalton/flexbugs/issues/231
142143
pointer-events: none;
143144
}
144145

145-
.ui5-checkbox-icon {
146-
color: currentColor;
147-
cursor: default;
146+
:host([indeterminate][checked]) .ui5-checkbox-inner::after {
147+
content: "";
148+
background-color: currentColor;
149+
width: var(--_ui5_checkbox_partially_icon_size);
150+
height: var(--_ui5_checkbox_partially_icon_size);
148151
}
149152

150153
.ui5-checkbox-inner input {
@@ -170,6 +173,8 @@ https://github.com/philipwalton/flexbugs/issues/231
170173
.ui5-checkbox-icon {
171174
width: var(--_ui5_checkbox_icon_size);
172175
height: var(--_ui5_checkbox_icon_size);
176+
color: currentColor;
177+
cursor: default;
173178
}
174179

175180
/* RTL */

packages/main/src/themes/base/sizes-parameters.css

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
--_ui5_checkbox_root_side_padding: .6875rem;
1111
--_ui5_checkbox_icon_size: 1rem;
12+
--_ui5_checkbox_partially_icon_size: .75rem;
1213
--_ui5_custom_list_item_height: 3rem;
1314
--_ui5_custom_list_item_rb_min_width: 2.75rem;
1415
--_ui5_day_picker_item_width: 2.25rem;
@@ -116,6 +117,7 @@
116117
--_ui5_checkbox_focus_position: var(--_ui5_checkbox_compact_focus_position);
117118
--_ui5_checkbox_inner_width_height: var(--_ui5_checkbox_compact_inner_size);
118119
--_ui5_checkbox_icon_size: .75rem;
120+
--_ui5_checkbox_partially_icon_size: .5rem;
119121

120122
/* ColorPalette */
121123
--_ui5_color-palette-item-height: 1.25rem;

packages/main/test/pages/CheckBox.html

+6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@
3535
<br>
3636
<ui5-title>ACC test - aria-label</ui5-title>
3737
<ui5-checkbox id="accCb" aria-label="Hello world"></ui5-checkbox>
38+
<br />
39+
<ui5-checkbox value-state="Warning" text="Long long long text" indeterminate checked></ui5-checkbox>
40+
<ui5-checkbox value-state="Error" text="Long long long text" indeterminate checked></ui5-checkbox>
41+
<ui5-checkbox value-state="None" text="Long long long text" indeterminate checked></ui5-checkbox>
42+
<ui5-checkbox value-state="Success" text="Long long long text" indeterminate checked></ui5-checkbox>
43+
<ui5-checkbox value-state="Information" text="Long long long text" indeterminate checked></ui5-checkbox>
3844

3945
<script>
4046
var hcb = false;

packages/main/test/samples/CheckBox.sample.html

+80
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,44 @@ <h3>Basic CheckBox</h3>
2626
<section>
2727
<h3>CheckBox states</h3>
2828
<div class="snippet">
29+
<ui5-checkbox text="Success" value-state="Success"></ui5-checkbox>
2930
<ui5-checkbox text="Error" value-state="Error"></ui5-checkbox>
3031
<ui5-checkbox text="Warning" value-state="Warning"></ui5-checkbox>
32+
<ui5-checkbox text="Information" value-state="Information"></ui5-checkbox>
3133
<ui5-checkbox text="Disabled" disabled checked></ui5-checkbox>
3234
<ui5-checkbox text="Readonly" readonly checked></ui5-checkbox>
35+
<ui5-checkbox text="Success disabled" disabled value-state="Success" checked></ui5-checkbox>
3336
<ui5-checkbox text="Error disabled" disabled value-state="Error" checked></ui5-checkbox>
3437
<ui5-checkbox text="Warning disabled " disabled value-state="Warning" checked></ui5-checkbox>
38+
<ui5-checkbox text="Information disabled " disabled value-state="Information" checked></ui5-checkbox>
39+
<ui5-checkbox text="Success readonly" readonly value-state="Success" checked></ui5-checkbox>
3540
<ui5-checkbox text="Error readonly" readonly value-state="Error" checked></ui5-checkbox>
3641
<ui5-checkbox text="Warning readonly" readonly value-state="Warning" checked></ui5-checkbox>
42+
<ui5-checkbox text="Information readonly" readonly value-state="Information" checked></ui5-checkbox>
43+
<ui5-checkbox text="Success indeterminate" value-state="Success" indeterminate checked></ui5-checkbox>
44+
<ui5-checkbox text="Error indeterminate" value-state="Error" indeterminate checked></ui5-checkbox>
45+
<ui5-checkbox text="Warning indeterminate" value-state="Warning" indeterminate checked></ui5-checkbox>
46+
<ui5-checkbox text="Information indeterminate" value-state="Information" indeterminate checked></ui5-checkbox>
3747
</div>
3848
<pre class="prettyprint lang-html"><xmp>
49+
<ui5-checkbox text="Success" value-state="Success"></ui5-checkbox>
3950
<ui5-checkbox text="Error" value-state="Error"></ui5-checkbox>
4051
<ui5-checkbox text="Warning" value-state="Warning"></ui5-checkbox>
52+
<ui5-checkbox text="Information" value-state="Information"></ui5-checkbox>
4153
<ui5-checkbox text="Disabled" disabled checked></ui5-checkbox>
4254
<ui5-checkbox text="Readonly" readonly checked></ui5-checkbox>
55+
<ui5-checkbox text="Success disabled" disabled value-state="Success" checked></ui5-checkbox>
4356
<ui5-checkbox text="Error disabled" disabled value-state="Error" checked></ui5-checkbox>
4457
<ui5-checkbox text="Warning disabled " disabled value-state="Warning" checked></ui5-checkbox>
58+
<ui5-checkbox text="Information disabled " disabled value-state="Information" checked></ui5-checkbox>
59+
<ui5-checkbox text="Success readonly" readonly value-state="Success" checked></ui5-checkbox>
4560
<ui5-checkbox text="Error readonly" readonly value-state="Error" checked></ui5-checkbox>
4661
<ui5-checkbox text="Warning readonly" readonly value-state="Warning" checked></ui5-checkbox>
62+
<ui5-checkbox text="Information readonly" readonly value-state="Information" checked></ui5-checkbox>
63+
<ui5-checkbox text="Success indeterminate" value-state="Success" indeterminate checked></ui5-checkbox>
64+
<ui5-checkbox text="Error indeterminate" value-state="Error" indeterminate checked></ui5-checkbox>
65+
<ui5-checkbox text="Warning indeterminate" value-state="Warning" indeterminate checked></ui5-checkbox>
66+
<ui5-checkbox text="Information indeterminate" value-state="Information" indeterminate checked></ui5-checkbox>
4767
</xmp></pre>
4868
</section>
4969

@@ -59,4 +79,64 @@ <h3>CheckBox with Text Wrapping</h3>
5979
</xmp></pre>
6080
</section>
6181

82+
<section>
83+
<h3>CheckBox with indeterminate</h3>
84+
<div class="snippet">
85+
<ui5-checkbox id="result-cb" text="Select / deselect all" indeterminate checked></ui5-checkbox>
86+
<hr />
87+
<div style="display: flex; flex-direction: column; align-items: flex-start;">
88+
<ui5-checkbox id="cb1" text="English" checked></ui5-checkbox>
89+
<ui5-checkbox id="cb2" text="German"></ui5-checkbox>
90+
<ui5-checkbox id="cb3" text="French"></ui5-checkbox>
91+
</div>
92+
<script>
93+
var resultCb = document.querySelector("#result-cb");
94+
var langCbs = Array.from(document.querySelectorAll("[id^=cb]"));
95+
96+
langCbs.forEach(cbEl => {
97+
cbEl.addEventListener("ui5-change", event => {
98+
const hasChecked = langCbs.some(cb => cb.checked);
99+
const hasUnchecked = langCbs.some(cb => !cb.checked);
100+
101+
resultCb.indeterminate = hasChecked && hasUnchecked;
102+
resultCb.checked = hasChecked;
103+
});
104+
});
105+
106+
resultCb.addEventListener("ui5-change", event => {
107+
langCbs.forEach(cb => cb.checked = event.target.checked);
108+
});
109+
</script>
110+
</div>
111+
<pre class="prettyprint lang-html"><xmp>
112+
<ui5-checkbox id="result-cb" text="Select / deselect all" indeterminate checked></ui5-checkbox>
113+
<hr />
114+
<div style="display: flex; flex-direction: column; align-items: flex-start;">
115+
<ui5-checkbox id="cb1" text="English" checked></ui5-checkbox>
116+
<ui5-checkbox id="cb2" text="German"></ui5-checkbox>
117+
<ui5-checkbox id="cb3" text="French"></ui5-checkbox>
118+
</div>
119+
<script>
120+
var resultCb = document.querySelector("#result-cb");
121+
var langCbs = Array.from(document.querySelectorAll("[id^=cb]"));
122+
123+
langCbs.forEach(function (cbEl) {
124+
cbEl.addEventListener("ui5-change", function (event) {
125+
const hasChecked = langCbs.some(function (cb) { return cb.checked });
126+
const hasUnchecked = langCbs.some(function (cb) { return !cb.checked });
127+
128+
resultCb.indeterminate = hasChecked && hasUnchecked;
129+
resultCb.checked = hasChecked;
130+
});
131+
});
132+
133+
resultCb.addEventListener("ui5-change", function (event) {
134+
langCbs.forEach(function (cb) {
135+
cb.checked = event.target.checked;
136+
});
137+
});
138+
</script>
139+
</xmp></pre>
140+
</section>
141+
62142
<!-- JSDoc marker -->

0 commit comments

Comments
 (0)