232 lines
6.1 KiB
JavaScript
232 lines
6.1 KiB
JavaScript
const smSwitch = document.createElement('template')
|
|
smSwitch.innerHTML = `
|
|
<style>
|
|
*{
|
|
-webkit-box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
:host{
|
|
display: -webkit-inline-box;
|
|
display: -ms-inline-flexbox;
|
|
display: inline-flex;
|
|
--accent-color: #4d2588;
|
|
--text-color: 17, 17, 17;
|
|
--background-color: 255, 255, 255;
|
|
}
|
|
label{
|
|
display: -webkit-box;
|
|
display: -ms-flexbox;
|
|
display: flex;
|
|
-webkit-box-align: center;
|
|
-ms-flex-align: center;
|
|
align-items: center;
|
|
width: 100%;
|
|
outline: none;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
:host(:not([disabled])) label:focus-visible{
|
|
-webkit-box-shadow: 0 0 0 0.1rem var(--accent-color);
|
|
box-shadow: 0 0 0 0.1rem var(--accent-color);
|
|
}
|
|
:host([disabled]) {
|
|
cursor: not-allowed;
|
|
opacity: 0.6;
|
|
pointer-events: none;
|
|
}
|
|
.switch {
|
|
position: relative;
|
|
display: -webkit-box;
|
|
display: -ms-flexbox;
|
|
display: flex;
|
|
-webkit-box-align: center;
|
|
-ms-flex-align: center;
|
|
align-items: center;
|
|
width: 2.4rem;
|
|
flex-shrink: 0;
|
|
margin-left: auto;
|
|
padding: 0.2rem;
|
|
cursor: pointer;
|
|
border-radius: 2rem;
|
|
}
|
|
|
|
input {
|
|
display: none;
|
|
}
|
|
|
|
.track {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1.4rem;
|
|
-webkit-transition: background 0.3s;
|
|
-o-transition: background 0.3s;
|
|
transition: background 0.3s;
|
|
background: rgba(var(--text-color), 0.4);
|
|
-webkit-box-shadow: 0 0.1rem 0.3rem #00000040 inset;
|
|
box-shadow: 0 0.1rem 0.3rem #00000040 inset;
|
|
border-radius: 1rem;
|
|
}
|
|
|
|
.switch:active .button::after,
|
|
.switch:focus .button::after{
|
|
opacity: 1
|
|
}
|
|
.switch:focus-visible .button::after{
|
|
opacity: 1
|
|
}
|
|
|
|
.button::after{
|
|
content: '';
|
|
display: -webkit-box;
|
|
display: -ms-flexbox;
|
|
display: flex;
|
|
position: absolute;
|
|
height: 2.6rem;
|
|
width: 2.6rem;
|
|
background: rgba(var(--text-color), 0.2);
|
|
border-radius: 2rem;
|
|
opacity: 0;
|
|
-webkit-transition: opacity 0.3s;
|
|
-o-transition: opacity 0.3s;
|
|
transition: opacity 0.3s;
|
|
}
|
|
|
|
.button {
|
|
position: relative;
|
|
display: -webkit-inline-box;
|
|
display: -ms-inline-flexbox;
|
|
display: inline-flex;
|
|
height: 1rem;
|
|
width: 1rem;
|
|
-webkit-box-pack: center;
|
|
-ms-flex-pack: center;
|
|
justify-content: center;
|
|
-webkit-box-align: center;
|
|
-ms-flex-align: center;
|
|
align-items: center;
|
|
border-radius: 1rem;
|
|
-webkit-box-shadow: 0 0.1rem 0.4rem #00000060;
|
|
box-shadow: 0 0.1rem 0.4rem #00000060;
|
|
-webkit-transition: -webkit-transform 0.3s;
|
|
transition: -webkit-transform 0.3s;
|
|
-o-transition: transform 0.3s;
|
|
transition: transform 0.3s;
|
|
transition: transform 0.3s, -webkit-transform 0.3s;
|
|
border: solid 0.3rem white;
|
|
}
|
|
|
|
input:checked ~ .button {
|
|
-webkit-transform: translateX(100%);
|
|
-ms-transform: translateX(100%);
|
|
transform: translateX(100%);
|
|
}
|
|
|
|
input:checked ~ .track {
|
|
background: var(--accent-color);
|
|
}
|
|
</style>
|
|
<label tabindex="0">
|
|
<slot name="left"></slot>
|
|
<div part="switch" class="switch">
|
|
<input type="checkbox">
|
|
<div class="track"></div>
|
|
<div class="button"></div>
|
|
</div>
|
|
<slot name="right"></slot>
|
|
</label>`
|
|
|
|
customElements.define('sm-switch', class extends HTMLElement {
|
|
constructor() {
|
|
super()
|
|
this.attachShadow({
|
|
mode: 'open'
|
|
}).append(smSwitch.content.cloneNode(true))
|
|
this.switch = this.shadowRoot.querySelector('.switch');
|
|
this.input = this.shadowRoot.querySelector('input')
|
|
this.isChecked = false
|
|
this.isDisabled = false
|
|
|
|
this.dispatch = this.dispatch.bind(this)
|
|
}
|
|
|
|
static get observedAttributes() {
|
|
return ['disabled', 'checked']
|
|
}
|
|
|
|
get disabled() {
|
|
return this.isDisabled
|
|
}
|
|
|
|
set disabled(val) {
|
|
if (val) {
|
|
this.setAttribute('disabled', '')
|
|
} else {
|
|
this.removeAttribute('disabled')
|
|
}
|
|
}
|
|
|
|
get checked() {
|
|
return this.isChecked
|
|
}
|
|
|
|
set checked(value) {
|
|
if (value) {
|
|
this.setAttribute('checked', '')
|
|
} else {
|
|
this.removeAttribute('checked')
|
|
}
|
|
}
|
|
|
|
dispatch(){
|
|
this.dispatchEvent(new CustomEvent('change', {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: {
|
|
value: this.isChecked
|
|
}
|
|
}))
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.addEventListener('keydown', e => {
|
|
if (e.code === "Space" && !this.isDisabled) {
|
|
e.preventDefault()
|
|
this.input.click()
|
|
}
|
|
})
|
|
this.input.addEventListener('click', e => {
|
|
if (this.input.checked)
|
|
this.checked = true
|
|
else
|
|
this.checked = false
|
|
this.dispatch()
|
|
})
|
|
}
|
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
if (oldValue !== newValue) {
|
|
if (name === 'disabled') {
|
|
if (this.hasAttribute('disabled')) {
|
|
this.disabled = true
|
|
}
|
|
else {
|
|
this.disabled = false
|
|
}
|
|
}
|
|
else if (name === 'checked') {
|
|
if (this.hasAttribute('checked')) {
|
|
this.isChecked = true
|
|
this.input.checked = true
|
|
}
|
|
else {
|
|
this.isChecked = false
|
|
this.input.checked = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}) |