Skip to content

Commit 74a6121

Browse files
bjarnefiOvergaard
andauthored
feat: Indeterminate state for boolean input and checkbox (#856)
* Add indeterminate state for boolean input and checkbox * Remove output * Remove checked attribute * Remove checked attribute * Remove iconMinus and use iconSubtract instead * chore: run prettier --------- Co-authored-by: Jacob Overgaard <[email protected]>
1 parent c3f5aee commit 74a6121

File tree

3 files changed

+65
-11
lines changed

3 files changed

+65
-11
lines changed

packages/uui-boolean-input/lib/uui-boolean-input.element.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ export abstract class UUIBooleanInputElement extends UUIFormControlMixin(
7676
this.requestUpdate('checked', oldValue);
7777
}
7878

79+
/**
80+
* Indeterminate state for the input.
81+
* @type {boolean}
82+
* @attr
83+
* @default false
84+
*/
85+
@property({ type: Boolean, reflect: true })
86+
indeterminate = false;
87+
7988
/**
8089
* Disables the input.
8190
* @type {boolean}
@@ -167,6 +176,7 @@ export abstract class UUIBooleanInputElement extends UUIFormControlMixin(
167176
e.stopPropagation();
168177
this.pristine = false;
169178
this.checked = this._input.checked;
179+
this.indeterminate = this._input.indeterminate;
170180
this.dispatchEvent(new UUIBooleanInputEvent(UUIBooleanInputEvent.CHANGE));
171181
}
172182

@@ -187,6 +197,7 @@ export abstract class UUIBooleanInputElement extends UUIFormControlMixin(
187197
@change="${this._onInputChange}"
188198
.disabled=${this.disabled || this.readonly}
189199
.checked=${this.checked}
200+
.indeterminate=${this.indeterminate}
190201
aria-checked="${this.checked ? 'true' : 'false'}"
191202
aria-label=${this.label}
192203
role="${this.inputRole}" />

packages/uui-checkbox/lib/uui-checkbox.element.ts

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import {
44
} from '@umbraco-ui/uui-base/lib/animations';
55
import { defineElement } from '@umbraco-ui/uui-base/lib/registration';
66
import { UUIBooleanInputElement } from '@umbraco-ui/uui-boolean-input/lib';
7-
import { iconCheck } from '@umbraco-ui/uui-icon-registry-essential/lib/svgs';
7+
import {
8+
iconCheck,
9+
iconSubtract,
10+
} from '@umbraco-ui/uui-icon-registry-essential/lib/svgs';
811
import { css, html } from 'lit';
912

1013
/**
@@ -25,7 +28,9 @@ export class UUICheckboxElement extends UUIBooleanInputElement {
2528
renderCheckbox() {
2629
return html`
2730
<div id="ticker">
28-
<div id="icon-check">${iconCheck}</div>
31+
<div id="icon-check">
32+
${this.indeterminate ? iconSubtract : iconCheck}
33+
</div>
2934
</div>
3035
`;
3136
}
@@ -83,15 +88,18 @@ export class UUICheckboxElement extends UUIBooleanInputElement {
8388
var(--uui-color-surface-emphasis)
8489
);
8590
}
86-
input:checked:not([disabled]) + #ticker {
91+
input:checked:not([disabled]) + #ticker,
92+
input:indeterminate:not([disabled]) + #ticker {
8793
border-color: var(--uui-color-selected);
8894
}
8995
90-
label:hover input:checked:not([disabled]) + #ticker {
96+
label:hover input:checked:not([disabled]) + #ticker,
97+
label:hover input:indeterminate:not([disabled]) + #ticker {
9198
border-color: var(--uui-color-selected-emphasis);
9299
}
93100
94-
label:focus input:checked + #ticker {
101+
label:focus input:checked + #ticker,
102+
label:focus input:indeterminate + #ticker {
95103
border-color: var(--uui-color-selected-emphasis);
96104
}
97105
@@ -126,18 +134,22 @@ export class UUICheckboxElement extends UUIBooleanInputElement {
126134
transform: scale(0);
127135
opacity: 0;
128136
}
129-
label:hover input:checked:not([disabled]) + #ticker::before {
137+
label:hover input:checked:not([disabled]) + #ticker::before,
138+
label:hover input:indeterminate:not([disabled]) + #ticker::before {
130139
background-color: var(--uui-color-selected-emphasis);
131140
}
132141
133-
input:checked + #ticker::before {
142+
input:checked + #ticker::before,
143+
input:indeterminate + #ticker::before {
134144
transform: scale(1);
135145
opacity: 1;
136146
}
137-
input:checked + #ticker #icon-check {
147+
input:checked + #ticker #icon-check,
148+
input:indeterminate + #ticker #icon-check {
138149
opacity: 1;
139150
}
140-
label:focus input:checked + #ticker {
151+
label:focus input:checked + #ticker,
152+
label:focus input:indeterminate + #ticker {
141153
background-color: var(--uui-color-selected-emphasis);
142154
}
143155
@@ -154,15 +166,27 @@ export class UUICheckboxElement extends UUIBooleanInputElement {
154166
transform: scale(0.9);
155167
}
156168
169+
:host(:not([disabled], [readonly]))
170+
label:active
171+
input:indeterminate
172+
+ #ticker::before {
173+
/** Stretch when mouse down */
174+
transform: scale(0.9);
175+
}
176+
157177
:host(:not([pristine]):invalid) #ticker,
158178
:host(:not([pristine]):invalid) label:hover #ticker,
159179
:host(:not([pristine]):invalid) label:hover input:checked:not([disabled]) + #ticker,
180+
:host(:not([pristine]):invalid) label:hover input:indeterminate:not([disabled]) + #ticker,
160181
:host(:not([pristine]):invalid) label:focus input:checked + #ticker,
182+
:host(:not([pristine]):invalid) label:focus input:indeterminate + #ticker,
161183
/* polyfill support */
162184
:host(:not([pristine])[internals-invalid]) #ticker,
163185
:host(:not([pristine])[internals-invalid]) label:hover #ticker,
164186
:host(:not([pristine])[internals-invalid]) label:hover input:checked:not([disabled]) + #ticker,
165-
:host(:not([pristine])[internals-invalid]) label:focus input:checked + #ticker {
187+
:host(:not([pristine])[internals-invalid]) label:hover input:indeterminate:not([disabled]) + #ticker,
188+
:host(:not([pristine])[internals-invalid]) label:focus input:checked + #ticker,
189+
:host(:not([pristine])[internals-invalid]) label:focus input:indeterminate + #ticker {
166190
border: 1px solid var(--uui-color-danger-standalone);
167191
}
168192
@@ -172,6 +196,9 @@ export class UUICheckboxElement extends UUIBooleanInputElement {
172196
:host([disabled]) input:checked + #ticker {
173197
background-color: var(--uui-color-disabled);
174198
}
199+
:host([disabled]) input:indeterminate + #ticker {
200+
background-color: var(--uui-color-disabled);
201+
}
175202
:host([disabled]) #ticker::before {
176203
background-color: var(--uui-color-disabled);
177204
}
@@ -181,7 +208,8 @@ export class UUICheckboxElement extends UUIBooleanInputElement {
181208
:host([disabled]) label:active #ticker {
182209
animation: ${UUIHorizontalShakeAnimationValue};
183210
}
184-
:host([disabled]) input:checked + #ticker #icon-check {
211+
:host([disabled]) input:checked + #ticker #icon-check,
212+
:host([disabled]) input:indeterminate + #ticker #icon-check {
185213
color: var(--uui-color-disabled-contrast);
186214
}
187215
`,

packages/uui-checkbox/lib/uui-checkbox.story.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,18 @@ Readonly.parameters = {
175175
},
176176
},
177177
};
178+
179+
export const Indeterminate: Story = props => html`
180+
<uui-checkbox
181+
?indeterminate=${props.indeterminate}
182+
.label=${'Indeterminate'}></uui-checkbox>
183+
`;
184+
Indeterminate.args = { indeterminate: true };
185+
Indeterminate.parameters = {
186+
controls: { include: ['indeterminate'] },
187+
docs: {
188+
source: {
189+
code: `<uui-checkbox label="Indeterminate" indeterminate></uui-checkbox>`,
190+
},
191+
},
192+
};

0 commit comments

Comments
 (0)