Commit 6461c864 authored by Kent Tamura's avatar Kent Tamura Committed by Commit Bot

std-switch: Implement a way to toggle a platform-agnostic theme and a platform-matching theme

This CL implements the option F in [1].
Add Material-like theme as a platform-matching theme.

[1] https://github.com/tkent-google/std-switch/issues/6#issuecomment-499842423

Bug: 972476
Change-Id: I643d36b0a4750191d45e7e6cfe2ae320826e3f48
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1832128
Commit-Queue: Kent Tamura <tkent@chromium.org>
Reviewed-by: default avatarFergal Daly <fergal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#701952}
parent 19768c29
......@@ -9,10 +9,37 @@ import * as style from './style.mjs';
import {SwitchTrack} from './track.mjs';
const generateStyleSheet = style.styleSheetFactory();
const generateMaterialStyleSheet = style.materialStyleSheetFactory();
// https://github.com/tkent-google/std-switch/issues/2
const STATE_ATTR = 'on';
function parentOrHostElement(element) {
const parent = element.parentNode;
if (!parent) {
return null;
}
if (parent.nodeType === Node.ELEMENT_NODE) {
return parent;
}
if (parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
return parent.host;
}
return null;
}
function shouldUsePlatformTheme(element) {
for (; element; element = parentOrHostElement(element)) {
const themeValue = element.getAttribute('theme');
if (themeValue === 'match-platform') {
return true;
} else if (themeValue === 'platform-agnostic') {
return false;
}
}
return false;
}
export class StdSwitchElement extends HTMLElement {
// TODO(tkent): The following should be |static fooBar = value;|
// after enabling babel-eslint.
......@@ -27,6 +54,7 @@ export class StdSwitchElement extends HTMLElement {
#track;
#containerElement;
#inUserAction = false;
#shadowRoot;
constructor() {
super();
......@@ -62,6 +90,11 @@ export class StdSwitchElement extends HTMLElement {
}
connectedCallback() {
// The element might have been disconnected when the callback is invoked.
if (!this.isConnected) {
return;
}
// TODO(tkent): We should not add tabindex attribute.
// https://github.com/w3c/webcomponents/issues/762
if (!this.hasAttribute('tabindex')) {
......@@ -76,6 +109,15 @@ export class StdSwitchElement extends HTMLElement {
this.setAttribute('role', 'switch');
}
}
if (shouldUsePlatformTheme(this)) {
// TODO(tkent): Should we apply Cocoa-like on macOS and Fluent-like
// on Windows?
this.#shadowRoot.adoptedStyleSheets =
[generateStyleSheet(), generateMaterialStyleSheet()];
} else {
this.#shadowRoot.adoptedStyleSheets = [generateStyleSheet()];
}
}
formResetCallback() {
......@@ -100,7 +142,7 @@ export class StdSwitchElement extends HTMLElement {
thumbElement.id = 'thumb';
thumbElement.part.add('thumb');
root.adoptedStyleSheets = [generateStyleSheet()];
this.#shadowRoot = root;
};
#onClick = () => {
......
......@@ -11,6 +11,10 @@ const THUMB_WIDTH = '22px';
const THUMB_MARGIN_START = '2px';
const THUMB_MARGIN_END = '2px';
const MATERIAL_THUMB_SIZE = '20px';
const RIPPLE_COLOR = 'rgba(100,100,100,0.3)';
const RIPPLE_MAX_SIZE = '48px';
// Returns a function returning a CSSStyleSheet().
// TODO(tkent): Share this stylesheet factory feature with elements/toast/.
export function styleSheetFactory() {
......@@ -151,6 +155,122 @@ export function styleSheetFactory() {
};
}
export function materialStyleSheetFactory() {
let styleSheet;
return () => {
if (!styleSheet) {
styleSheet = new CSSStyleSheet();
styleSheet.replaceSync(`
:host {
block-size: 20px;
inline-size: 36px;
}
#track,
:host(:active) #track {
background: rgba(0,0,0,0.4);
block-size: 14px;
border: none;
box-shadow: none;
}
:host([on]) #track,
:host([on]:active) #track,
:host([on]:focus) #track {
border: none;
box-shadow: none;
}
#trackFill,
:host([on]:active) #trackFill {
background: rgba(63,81,181,0.5);
}
#thumb,
:host(:focus) #thumb {
block-size: ${MATERIAL_THUMB_SIZE};
border-radius: calc(${MATERIAL_THUMB_SIZE} / 2);
border: none;
box-shadow: 0 1px 5px 0 rgba(0,0,0,0.6);
inline-size: ${MATERIAL_THUMB_SIZE};
margin-inline-start: -100%;
}
:host([on]) #thumb,
:host([on]:focus) #thumb,
:host([on]:not(:disabled):hover) #thumb {
background: rgb(63,81,181);
border: none;
margin-inline-start: calc(0px - 20px);
}
:host(:not(:disabled):hover) #thumb {
inline-size: ${MATERIAL_THUMB_SIZE};
}
/*
* Ripple effect
*
* Translucent circle is painted on the thumb if the element is :active or
* :focus-visible. It has
* - Size transition when it appears
* - Opacity transition when it disappears
* part(thumb)::before represents the former, and part(thumb)::after represents
* the latter.
*/
#thumb::before {
background: ${RIPPLE_COLOR};
block-size: 0px;
border-radius: 0px;
content: "";
display: inline-block;
inline-size: 0px;
left: calc(${MATERIAL_THUMB_SIZE} / 2);
position: relative;
top: calc(${MATERIAL_THUMB_SIZE} / 2);
transition: none;
}
:host(:active) #thumb::before,
:host(:focus-visible) #thumb::before {
block-size: ${RIPPLE_MAX_SIZE};
border-radius: calc(${RIPPLE_MAX_SIZE} / 2);
inline-size: ${RIPPLE_MAX_SIZE};
left: calc((${MATERIAL_THUMB_SIZE} - ${RIPPLE_MAX_SIZE}) / 2);
top: calc((${MATERIAL_THUMB_SIZE} - ${RIPPLE_MAX_SIZE}) / 2);
transition: all linear 0.1s;
}
#thumb::after {
background: ${RIPPLE_COLOR};
block-size: ${RIPPLE_MAX_SIZE};
border-radius: calc(${RIPPLE_MAX_SIZE} / 2);
content: "";
display: inline-block;
inline-size: ${RIPPLE_MAX_SIZE};
left: calc((${MATERIAL_THUMB_SIZE} - ${RIPPLE_MAX_SIZE}) / 2);
opacity: 0;
position: relative;
/* Why 18px? */
top: calc((${MATERIAL_THUMB_SIZE} - ${RIPPLE_MAX_SIZE}) / 2 - 18px);
transition: opacity linear 0.3s;
}
:host(:active) #thumb::after,
:host(:focus-visible) #thumb::after {
block-size: 0px;
content: "";
inline-size: 0px;
opacity: 1;
transition: none;
}
`);
}
return styleSheet;
};
}
/**
* Add '$part-transitioning' part to the element if the element already has
* a part name.
......
......@@ -5843,6 +5843,7 @@ crbug.com/959693 external/wpt/trusted-types/WorkerGlobalScope-importScripts.http
# Allow failure until its appearance gets stable.
crbug.com/972476 std-switch/switch-appearance.html [ Failure ]
crbug.com/972476 std-switch/switch-appearance-customization.html [ Failure ]
crbug.com/972476 std-switch/switch-appearance-theme.html [ Failure ]
# Sheriff 2019-05-20
crbug.com/965389 [ Mac ] media/track/track-cue-rendering-position-auto.html [ Pass Failure ]
......
<!DOCTYPE html>
<body>
<script type="module">
import 'std:elements/switch';
document.body.insertAdjacentHTML('beforeend', `
<h2>Attribute on the element</h2>
<std-switch theme="platform-agnostic"></std-switch>
<std-switch theme="match-platform"></std-switch>`);
document.body.insertAdjacentHTML('beforeend', `
<h2>Attribute on an ancestor</h2>
<span theme="platform-agnostic"><std-switch></std-switch></span>
<i theme="match-platform"><b><std-switch></std-switch></b></i>`);
document.body.insertAdjacentHTML('beforeend', `
<h2>Nested attributes</h2>
<s theme="match-platform"><span theme="platform-agnostic"><code theme="foo"
><std-switch></std-switch></code></span></s>
<kbd theme="platform-agnostic"><i theme="match-platform"><b><std-switch></std-switch></b></i></kbd>`);
document.body.insertAdjacentHTML('beforeend', `
<h2>Attribute over shadow-boundary</h2>
<span theme="platform-agnostic"><span id="host1"></span></span>
<i theme="match-platform"><b><span id="host2"></span></b></i>`);
let root1 = document.querySelector('#host1').attachShadow({mode:'open'});
root1.innerHTML = '<std-switch></std-switch>';
let root2 = document.querySelector('#host2').attachShadow({mode:'closed'});
root2.innerHTML = '<std-switch></std-switch>';
document.body.insertAdjacentHTML('beforeend', `
<h2>NO support for updating attribute values for now</h2>
<std-switch id="update1" theme="match-platform"></std-switch>
<std-switch id="update2" theme="platform-agnostic"></std-switch>`);
document.querySelector('#update1').setAttribute('theme', 'platform-agnostic');
document.querySelector('#update2').setAttribute('theme', 'match-platform');
</script>
</body>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment