Skip to content

Commit

Permalink
perf(checkbox): refactor architecture for more rendering perf and DOM…
Browse files Browse the repository at this point in the history
… element count
  • Loading branch information
Westbrook committed Nov 30, 2023
1 parent 5be8198 commit 7c2277f
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 112 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
"alex": "^10.0.0",
"cem-plugin-module-file-extensions": "^0.0.5",
"chalk": "^5.0.1",
"chromedriver": "119",
"chromedriver": "^119.0.1",
"common-tags": "^1.8.2",
"cssnano": "^6.0.1",
"custom-elements-manifest": "^2.0.0",
Expand Down
4 changes: 4 additions & 0 deletions packages/checkbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
"development": "./src/CheckboxBase.dev.js",
"default": "./src/CheckboxBase.js"
},
"./src/CheckboxMixin.js": {
"development": "./src/CheckboxMixin.dev.js",
"default": "./src/CheckboxMixin.js"
},
"./src/checkbox.css.js": "./src/checkbox.css.js",
"./src/index.js": {
"development": "./src/index.dev.js",
Expand Down
177 changes: 121 additions & 56 deletions packages/checkbox/src/Checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import {
html,
PropertyValues,
SizedMixin,
SpectrumElement,
TemplateResult,
} from '@spectrum-web-components/base';
import { property } from '@spectrum-web-components/base/src/decorators.js';
import { CheckboxBase } from './CheckboxBase.js';
import { CheckboxMixin } from './CheckboxMixin.js';
import checkboxStyles from './checkbox.css.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-checkmark75.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-checkmark100.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-checkmark200.js';
Expand All @@ -28,72 +30,98 @@ import '@spectrum-web-components/icons-ui/icons/sp-icon-dash75.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-dash100.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-dash200.js';
import '@spectrum-web-components/icons-ui/icons/sp-icon-dash300.js';
import checkboxStyles from './checkbox.css.js';
import checkmarkSmallStyles from '@spectrum-web-components/icon/src/spectrum-icon-checkmark.css.js';
import dashSmallStyles from '@spectrum-web-components/icon/src/spectrum-icon-dash.css.js';

const checkmarkIcon = {
s: html`
<sp-icon-checkmark75
id="checkmark"
class="spectrum-Icon spectrum-UIIcon-Checkmark75"
></sp-icon-checkmark75>
`,
m: html`
<sp-icon-checkmark100
id="checkmark"
class="spectrum-Icon spectrum-UIIcon-Checkmark100"
></sp-icon-checkmark100>
`,
l: html`
<sp-icon-checkmark200
id="checkmark"
class="spectrum-Icon spectrum-UIIcon-Checkmark200"
></sp-icon-checkmark200>
`,
xl: html`
<sp-icon-checkmark300
id="checkmark"
class="spectrum-Icon spectrum-UIIcon-Checkmark300"
></sp-icon-checkmark300>
`,
s: () => {
return html`
<sp-icon-checkmark75
id="checkmark"
class="spectrum-Icon spectrum-UIIcon-Checkmark75"
></sp-icon-checkmark75>
`;
},
m: () => {
return html`
<sp-icon-checkmark100
id="checkmark"
class="spectrum-Icon spectrum-UIIcon-Checkmark100"
></sp-icon-checkmark100>
`;
},
l: () => {
return html`
<sp-icon-checkmark200
id="checkmark"
class="spectrum-Icon spectrum-UIIcon-Checkmark200"
></sp-icon-checkmark200>
`;
},
xl: () => {
return html`
<sp-icon-checkmark300
id="checkmark"
class="spectrum-Icon spectrum-UIIcon-Checkmark300"
></sp-icon-checkmark300>
`;
},
};

const dashIcon = {
s: html`
<sp-icon-dash75
id="partialCheckmark"
class="spectrum-Icon spectrum-UIIcon-Dash75"
></sp-icon-dash75>
`,
m: html`
<sp-icon-dash100
id="partialCheckmark"
class="spectrum-Icon spectrum-UIIcon-Dash100"
></sp-icon-dash100>
`,
l: html`
<sp-icon-dash200
id="partialCheckmark"
class="spectrum-Icon spectrum-UIIcon-Dash200"
></sp-icon-dash200>
`,
xl: html`
<sp-icon-dash300
id="partialCheckmark"
class="spectrum-Icon spectrum-UIIcon-Dash300"
></sp-icon-dash300>
`,
s: () => {
return html`
<sp-icon-dash75
id="partialCheckmark"
class="spectrum-Icon spectrum-UIIcon-Dash75"
></sp-icon-dash75>
`;
},
m: () => {
return html`
<sp-icon-dash100
id="partialCheckmark"
class="spectrum-Icon spectrum-UIIcon-Dash100"
></sp-icon-dash100>
`;
},
l: () => {
return html`
<sp-icon-dash200
id="partialCheckmark"
class="spectrum-Icon spectrum-UIIcon-Dash200"
></sp-icon-dash200>
`;
},
xl: () => {
return html`
<sp-icon-dash300
id="partialCheckmark"
class="spectrum-Icon spectrum-UIIcon-Dash300"
></sp-icon-dash300>
`;
},
};

/**
* @element sp-checkbox
* @slot - content to display as the label for the Checkbox
* @fires change - Announces a change in the `checked` property of a Checkbox
*/
export class Checkbox extends SizedMixin(CheckboxBase, {
export class Checkbox extends SizedMixin(CheckboxMixin(SpectrumElement), {
noDefaultSize: true,
}) {
static override shadowRootOptions = {
...SpectrumElement.shadowRootOptions,
delegatesFocus: true,
};

/**
* Disable this control. It will not receive focus or events
*/
@property({ type: Boolean, reflect: true })
public disabled = false;

@property({ type: Boolean, reflect: true })
public indeterminate = false;

Expand All @@ -103,10 +131,30 @@ export class Checkbox extends SizedMixin(CheckboxBase, {
@property({ type: Boolean, reflect: true })
public emphasized = false;

@property({ reflect: true, type: Number, attribute: 'tabindex' })
public override tabIndex = 0;

public override connectedCallback(): void {
super.connectedCallback();
if (this.hasAttribute('autofocus')) {
this.updateComplete.then(() => {
this.focus();
});
}
}

public static override get styles(): CSSResultArray {
return [checkboxStyles, checkmarkSmallStyles, dashSmallStyles];
}

public override click(): void {
if (this.disabled) {
return;
}

this.inputElement?.click();
}

public override handleChange(): void {
this.indeterminate = false;
super.handleChange();
Expand All @@ -116,24 +164,41 @@ export class Checkbox extends SizedMixin(CheckboxBase, {
return html`
${super.render()}
<span id="box">
${checkmarkIcon[this.size as DefaultElementSize]}
${dashIcon[this.size as DefaultElementSize]}
${this.checked
? checkmarkIcon[this.size as DefaultElementSize]()
: html``}
${this.indeterminate
? dashIcon[this.size as DefaultElementSize]()
: html``}
</span>
<label id="label" for="input"><slot></slot></label>
`;
}

protected override updated(changes: PropertyValues): void {
super.updated(changes);
if (
changes.has('disabled') &&
(typeof changes.get('disabled') !== 'undefined' || this.disabled)
) {
if (this.disabled) {
this.inputElement.tabIndex = this.tabIndex;
this.tabIndex = -1;
} else {
this.tabIndex = this.inputElement.tabIndex;
this.inputElement.removeAttribute('tabindex');
}
this.inputElement.disabled = this.disabled;
}
if (changes.has('indeterminate')) {
this.inputElement.indeterminate = this.indeterminate;
}
if (changes.has('invalid')) {
if (this.invalid) {
this.inputElement.setAttribute('aria-invalid', 'true');
} else {
this.inputElement.removeAttribute('aria-invalid');
}
}
if (changes.has('indeterminate')) {
this.inputElement.indeterminate = this.indeterminate;
}
}
}
54 changes: 2 additions & 52 deletions packages/checkbox/src/CheckboxBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,61 +9,11 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/

import { html, TemplateResult } from '@spectrum-web-components/base';
import {
property,
query,
} from '@spectrum-web-components/base/src/decorators.js';
import { ifDefined } from '@spectrum-web-components/base/src/directives.js';
import { Focusable } from '@spectrum-web-components/shared/src/focusable.js';
import { CheckboxMixin } from './CheckboxMixin.js';

export class CheckboxBase extends Focusable {
@property({ type: Boolean, reflect: true })
public checked = false;

@property({ type: Boolean, reflect: true })
public readonly = false;

@property({ type: String, reflect: true })
public name: string | undefined;

@query('#input')
protected inputElement!: HTMLInputElement;

export class CheckboxBase extends CheckboxMixin(Focusable) {
public override get focusElement(): HTMLElement {
return this.inputElement;
}

public handleChange(): void {
if (this.readonly) {
this.inputElement.checked = this.checked;
return;
}
this.checked = this.inputElement.checked;

const changeEvent = new CustomEvent('change', {
bubbles: true,
cancelable: true,
composed: true,
});
const applyDefault = this.dispatchEvent(changeEvent);

if (!applyDefault) {
this.checked = !this.inputElement.checked;
this.inputElement.checked = this.checked;
}
}

protected override render(): TemplateResult {
return html`
<input
name=${ifDefined(this.name || undefined)}
id="input"
type="checkbox"
.checked=${this.checked}
@change=${this.handleChange}
/>
`;
}
}
Loading

0 comments on commit 7c2277f

Please sign in to comment.