Added theme-toggle component
This commit is contained in:
parent
849ef98877
commit
427726bd4b
@ -54,6 +54,7 @@
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit.
|
||||
</p>
|
||||
<sm-button variant="primary">This is a button</sm-button>
|
||||
<sm-carousel>
|
||||
<div class="card">
|
||||
<h3>Card</h3>
|
||||
|
||||
@ -96,7 +96,8 @@ ul {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
grid-template-columns: 12rem minmax(0, 1fr);
|
||||
grid-template-columns: 12rem 1fr;
|
||||
width: 100%;
|
||||
}
|
||||
.tr:nth-of-type(odd) {
|
||||
background-color: rgba(var(--text-color), 0.04);
|
||||
@ -320,29 +321,6 @@ ul {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.theme-switcher {
|
||||
position: relative;
|
||||
justify-self: flex-end;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.theme-switcher .icon {
|
||||
position: absolute;
|
||||
transition: transform 0.6s;
|
||||
}
|
||||
|
||||
.theme-switcher__checkbox {
|
||||
display: none;
|
||||
}
|
||||
.theme-switcher__checkbox:checked ~ .moon-icon {
|
||||
transform: scale(0) rotate(90deg);
|
||||
}
|
||||
.theme-switcher__checkbox:not(:checked) ~ .sun-icon {
|
||||
transform: scale(0) rotate(-90deg);
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.block-comment,
|
||||
.token.prolog,
|
||||
|
||||
2
components/css/main.min.css
vendored
2
components/css/main.min.css
vendored
File diff suppressed because one or more lines are too long
@ -89,7 +89,8 @@ ul{
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
grid-template-columns: 12rem minmax(0, 1fr);
|
||||
grid-template-columns: 12rem 1fr;
|
||||
width: 100%;
|
||||
&:nth-of-type(odd){
|
||||
background-color: rgba(var(--text-color), .04);
|
||||
}
|
||||
@ -268,28 +269,6 @@ ul{
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
.theme-switcher{
|
||||
position: relative;
|
||||
justify-self: flex-end;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
.icon{
|
||||
position: absolute;
|
||||
transition: transform 0.6s;
|
||||
}
|
||||
}
|
||||
.theme-switcher__checkbox{
|
||||
display: none;
|
||||
&:checked ~ .moon-icon{
|
||||
transform: scale(0) rotate(90deg);
|
||||
}
|
||||
&:not(:checked) ~ .sun-icon{
|
||||
transform: scale(0) rotate(-90deg);
|
||||
}
|
||||
}
|
||||
|
||||
//Syntax highlighting
|
||||
|
||||
.token.comment,
|
||||
|
||||
245
components/dist/select.js
vendored
245
components/dist/select.js
vendored
@ -7,16 +7,6 @@ smSelect.innerHTML = `
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.icon {
|
||||
fill: none;
|
||||
height: 0.8rem;
|
||||
width: 0.8rem;
|
||||
stroke: rgba(var(--text-color), 0.7);
|
||||
stroke-width: 6;
|
||||
overflow: visible;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
:host{
|
||||
display: -webkit-inline-box;
|
||||
display: -ms-inline-flexbox;
|
||||
@ -27,6 +17,10 @@ smSelect.innerHTML = `
|
||||
--max-height: auto;
|
||||
--min-width: 100%;
|
||||
}
|
||||
:host([disabled]) .select{
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.hide{
|
||||
display: none !important;
|
||||
}
|
||||
@ -37,12 +31,17 @@ smSelect.innerHTML = `
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.icon {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
fill: rgba(var(--text-color), 0.7);
|
||||
}
|
||||
.option-text{
|
||||
font-size: 0.9rem;
|
||||
overflow: hidden;
|
||||
@ -80,7 +79,7 @@ smSelect.innerHTML = `
|
||||
}
|
||||
.options{
|
||||
top: 100%;
|
||||
margin-top: 0.5rem;
|
||||
margin-top: 0.2rem;
|
||||
overflow: hidden auto;
|
||||
position: absolute;
|
||||
grid-area: options;
|
||||
@ -121,11 +120,9 @@ smSelect.innerHTML = `
|
||||
}
|
||||
</style>
|
||||
<div class="select" >
|
||||
<div class="selection" tabindex="0">
|
||||
<div class="selection">
|
||||
<div class="option-text"></div>
|
||||
<svg class="icon toggle" viewBox="0 0 64 64">
|
||||
<polyline points="63.65 15.99 32 47.66 0.35 15.99"/>
|
||||
</svg>
|
||||
<svg class="icon toggle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 13.172l4.95-4.95 1.414 1.414L12 16 5.636 9.636 7.05 8.222z"/></svg>
|
||||
</div>
|
||||
<div part="options" class="options hide">
|
||||
<slot></slot>
|
||||
@ -139,9 +136,45 @@ customElements.define('sm-select', class extends HTMLElement {
|
||||
}).append(smSelect.content.cloneNode(true))
|
||||
|
||||
this.reset = this.reset.bind(this)
|
||||
this.open = this.open.bind(this)
|
||||
this.collapse = this.collapse.bind(this)
|
||||
this.toggle = this.toggle.bind(this)
|
||||
this.handleSelectKeyDown = this.handleSelectKeyDown.bind(this)
|
||||
this.handleOptionsKeyDown = this.handleOptionsKeyDown.bind(this)
|
||||
this.handleOptionsKeyDown = this.handleOptionsKeyDown.bind(this)
|
||||
|
||||
this.availableOptions
|
||||
this.optionList = this.shadowRoot.querySelector('.options')
|
||||
this.chevron = this.shadowRoot.querySelector('.toggle')
|
||||
this.selection = this.shadowRoot.querySelector('.selection'),
|
||||
this.previousOption
|
||||
this.isOpen = false;
|
||||
this.slideDown = [{
|
||||
transform: `translateY(-0.5rem)`,
|
||||
opacity: 0
|
||||
},
|
||||
{
|
||||
transform: `translateY(0)`,
|
||||
opacity: 1
|
||||
}
|
||||
]
|
||||
this.slideUp = [{
|
||||
transform: `translateY(0)`,
|
||||
opacity: 1
|
||||
},
|
||||
{
|
||||
transform: `translateY(-0.5rem)`,
|
||||
opacity: 0
|
||||
}
|
||||
]
|
||||
this.animationOptions = {
|
||||
duration: 300,
|
||||
fill: "forwards",
|
||||
easing: 'ease'
|
||||
}
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return ['value']
|
||||
return ['value', 'disabled']
|
||||
}
|
||||
get value() {
|
||||
return this.getAttribute('value')
|
||||
@ -150,94 +183,89 @@ customElements.define('sm-select', class extends HTMLElement {
|
||||
this.setAttribute('value', val)
|
||||
}
|
||||
|
||||
reset(){
|
||||
reset() {
|
||||
|
||||
}
|
||||
|
||||
open() {
|
||||
this.optionList.classList.remove('hide')
|
||||
this.optionList.animate(this.slideDown, this.animationOptions)
|
||||
this.chevron.classList.add('rotate')
|
||||
this.isOpen = true
|
||||
}
|
||||
collapse() {
|
||||
this.chevron.classList.remove('rotate')
|
||||
this.optionList.animate(this.slideUp, this.animationOptions)
|
||||
.onfinish = () => {
|
||||
this.optionList.classList.add('hide')
|
||||
this.open = false
|
||||
.onfinish = () => {
|
||||
this.optionList.classList.add('hide')
|
||||
this.isOpen = false
|
||||
}
|
||||
}
|
||||
toggle() {
|
||||
if (!this.isOpen && !this.hasAttribute('disabled')) {
|
||||
this.open()
|
||||
} else {
|
||||
this.collapse()
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this.availableOptions
|
||||
this.optionList = this.shadowRoot.querySelector('.options')
|
||||
this.chevron = this.shadowRoot.querySelector('.toggle')
|
||||
let slot = this.shadowRoot.querySelector('.options slot'),
|
||||
selection = this.shadowRoot.querySelector('.selection'),
|
||||
previousOption
|
||||
this.open = false;
|
||||
this.slideDown = [{
|
||||
transform: `translateY(-0.5rem)`,
|
||||
opacity: 0
|
||||
},
|
||||
{
|
||||
transform: `translateY(0)`,
|
||||
opacity: 1
|
||||
}
|
||||
]
|
||||
this.slideUp = [{
|
||||
transform: `translateY(0)`,
|
||||
opacity: 1
|
||||
},
|
||||
{
|
||||
transform: `translateY(-0.5rem)`,
|
||||
opacity: 0
|
||||
}
|
||||
]
|
||||
this.animationOptions = {
|
||||
duration: 300,
|
||||
fill: "forwards",
|
||||
easing: 'ease'
|
||||
handleSelectKeyDown(e) {
|
||||
if (e.code === 'ArrowDown' || e.code === 'ArrowRight') {
|
||||
e.preventDefault()
|
||||
this.availableOptions[0].focus()
|
||||
}
|
||||
selection.addEventListener('click', e => {
|
||||
if (!this.open) {
|
||||
else if (e.code === 'Enter' || e.code === 'Space') {
|
||||
if (!this.isOpen) {
|
||||
this.optionList.classList.remove('hide')
|
||||
this.optionList.animate(this.slideDown, this.animationOptions)
|
||||
this.chevron.classList.add('rotate')
|
||||
this.open = true
|
||||
this.isOpen = true
|
||||
} else {
|
||||
this.collapse()
|
||||
}
|
||||
})
|
||||
selection.addEventListener('keydown', e => {
|
||||
if (e.code === 'ArrowDown' || e.code === 'ArrowRight') {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
handleOptionsKeyDown(e) {
|
||||
if (e.code === 'ArrowUp' || e.code === 'ArrowRight') {
|
||||
e.preventDefault()
|
||||
if (document.activeElement.previousElementSibling) {
|
||||
document.activeElement.previousElementSibling.focus()
|
||||
} else {
|
||||
this.availableOptions[this.availableOptions.length - 1].focus()
|
||||
}
|
||||
}
|
||||
else if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') {
|
||||
e.preventDefault()
|
||||
if (document.activeElement.nextElementSibling) {
|
||||
document.activeElement.nextElementSibling.focus()
|
||||
} else {
|
||||
this.availableOptions[0].focus()
|
||||
}
|
||||
if (e.code === 'Enter' || e.code === 'Space')
|
||||
if (!this.open) {
|
||||
this.optionList.classList.remove('hide')
|
||||
this.optionList.animate(this.slideDown, this.animationOptions)
|
||||
this.chevron.classList.add('rotate')
|
||||
this.open = true
|
||||
} else {
|
||||
this.collapse()
|
||||
}
|
||||
})
|
||||
this.optionList.addEventListener('keydown', e => {
|
||||
if (e.code === 'ArrowUp' || e.code === 'ArrowRight') {
|
||||
e.preventDefault()
|
||||
if (document.activeElement.previousElementSibling) {
|
||||
document.activeElement.previousElementSibling.focus()
|
||||
} else {
|
||||
this.availableOptions[this.availableOptions.length - 1].focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this.setAttribute('role', 'listbox')
|
||||
if (!this.hasAttribute('disabled')) {
|
||||
this.selection.setAttribute('tabindex', '0')
|
||||
}
|
||||
let slot = this.shadowRoot.querySelector('slot')
|
||||
slot.addEventListener('slotchange', e => {
|
||||
this.availableOptions = slot.assignedElements()
|
||||
if (this.availableOptions[0]) {
|
||||
let firstElement = this.availableOptions[0];
|
||||
this.previousOption = firstElement;
|
||||
firstElement.classList.add('check-selected')
|
||||
this.setAttribute('value', firstElement.getAttribute('value'))
|
||||
this.shadowRoot.querySelector('.option-text').textContent = firstElement.textContent
|
||||
this.availableOptions.forEach((element, index) => {
|
||||
element.setAttribute('tabindex', "0");
|
||||
})
|
||||
}
|
||||
if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') {
|
||||
e.preventDefault()
|
||||
if (document.activeElement.nextElementSibling) {
|
||||
document.activeElement.nextElementSibling.focus()
|
||||
} else{
|
||||
this.availableOptions[0].focus()
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
this.selection.addEventListener('click', this.toggle)
|
||||
this.selection.addEventListener('keydown', this.handleSelectKeyDown)
|
||||
this.optionList.addEventListener('keydown', this.handleOptionsKeyDown)
|
||||
this.addEventListener('optionSelected', e => {
|
||||
if (previousOption !== e.target) {
|
||||
if (this.previousOption !== e.target) {
|
||||
this.setAttribute('value', e.detail.value)
|
||||
this.shadowRoot.querySelector('.option-text').textContent = e.detail.text;
|
||||
this.dispatchEvent(new CustomEvent('change', {
|
||||
@ -247,36 +275,36 @@ customElements.define('sm-select', class extends HTMLElement {
|
||||
value: e.detail.value
|
||||
}
|
||||
}))
|
||||
if (previousOption) {
|
||||
previousOption.classList.remove('check-selected')
|
||||
if (this.previousOption) {
|
||||
this.previousOption.classList.remove('check-selected')
|
||||
}
|
||||
previousOption = e.target;
|
||||
this.previousOption = e.target;
|
||||
}
|
||||
if (!e.detail.switching)
|
||||
this.collapse()
|
||||
|
||||
e.target.classList.add('check-selected')
|
||||
})
|
||||
slot.addEventListener('slotchange', e => {
|
||||
this.availableOptions = slot.assignedElements()
|
||||
if (this.availableOptions[0]) {
|
||||
let firstElement = this.availableOptions[0];
|
||||
previousOption = firstElement;
|
||||
firstElement.classList.add('check-selected')
|
||||
this.setAttribute('value', firstElement.getAttribute('value'))
|
||||
this.shadowRoot.querySelector('.option-text').textContent = firstElement.textContent
|
||||
this.availableOptions.forEach((element, index) => {
|
||||
element.setAttribute('data-rank', index + 1);
|
||||
element.setAttribute('tabindex', "0");
|
||||
})
|
||||
}
|
||||
});
|
||||
document.addEventListener('mousedown', e => {
|
||||
if (!this.contains(e.target) && this.open) {
|
||||
if (this.isOpen && !this.contains(e.target)) {
|
||||
this.collapse()
|
||||
}
|
||||
})
|
||||
}
|
||||
attributeChangedCallback(name, oldVal, newVal) {
|
||||
if (name === "disabled") {
|
||||
if (this.hasAttribute('disabled')) {
|
||||
this.selection.removeAttribute('tabindex')
|
||||
}else {
|
||||
this.selection.setAttribute('tabindex', '0')
|
||||
}
|
||||
}
|
||||
}
|
||||
disconnectedCallback() {
|
||||
this.selection.removeEventListener('click', this.toggle)
|
||||
this.selection.removeEventListener('keydown', this.handleSelectKeyDown)
|
||||
this.optionList.removeEventListener('keydown', this.handleOptionsKeyDown)
|
||||
}
|
||||
})
|
||||
|
||||
// option
|
||||
@ -351,6 +379,8 @@ customElements.define('sm-option', class extends HTMLElement {
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(smOption.content.cloneNode(true))
|
||||
|
||||
this.sendDetails = this.sendDetails.bind(this)
|
||||
}
|
||||
|
||||
sendDetails(switching) {
|
||||
@ -367,15 +397,14 @@ customElements.define('sm-option', class extends HTMLElement {
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.setAttribute('role', 'option')
|
||||
let validKey = [
|
||||
'ArrowUp',
|
||||
'ArrowDown',
|
||||
'ArrowLeft',
|
||||
'ArrowRight'
|
||||
]
|
||||
this.addEventListener('click', e => {
|
||||
this.sendDetails()
|
||||
})
|
||||
this.addEventListener('click', this.sendDetails)
|
||||
this.addEventListener('keyup', e => {
|
||||
if (e.code === 'Enter' || e.code === 'Space') {
|
||||
e.preventDefault()
|
||||
|
||||
2
components/dist/select.min.js
vendored
2
components/dist/select.min.js
vendored
File diff suppressed because one or more lines are too long
178
components/dist/theme-toggle.js
vendored
Normal file
178
components/dist/theme-toggle.js
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
const themeToggle = document.createElement('template')
|
||||
themeToggle.innerHTML = `
|
||||
<style>
|
||||
*{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:host{
|
||||
}
|
||||
.theme-toggle {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: 1.4rem;
|
||||
height: 1.4rem;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.theme-toggle::after{
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
opacity: 0;
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
transition: transform 0.3s, opacity 0.3s;
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
background-color: rgba(var(--text-color), 0.12);
|
||||
}
|
||||
:host(:focus-within) .theme-toggle{
|
||||
outline: none;
|
||||
}
|
||||
:host(:focus-within) .theme-toggle::after{
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
.icon {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
fill: rgba(var(--text-color), 1);
|
||||
transition: transform 0.6s;
|
||||
}
|
||||
|
||||
.theme-switcher__checkbox {
|
||||
display: none;
|
||||
}
|
||||
:host([checked]) .moon-icon {
|
||||
transform: scale(0) rotate(90deg);
|
||||
}
|
||||
:host(:not([checked])) .sun-icon {
|
||||
transform: scale(0) rotate(-90deg);
|
||||
}
|
||||
</style>
|
||||
<label class="theme-toggle" title="Change theme" tabindex="0">
|
||||
<svg class="icon moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
|
||||
height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M10 6a8 8 0 0 0 11.955 6.956C21.474 18.03 17.2 22 12 22 6.477 22 2 17.523 2 12c0-5.2 3.97-9.474 9.044-9.955A7.963 7.963 0 0 0 10 6zm-6 6a8 8 0 0 0 8 8 8.006 8.006 0 0 0 6.957-4.045c-.316.03-.636.045-.957.045-5.523 0-10-4.477-10-10 0-.321.015-.64.045-.957A8.006 8.006 0 0 0 4 12zm14.164-9.709L19 2.5v1l-.836.209a2 2 0 0 0-1.455 1.455L16.5 6h-1l-.209-.836a2 2 0 0 0-1.455-1.455L13 3.5v-1l.836-.209A2 2 0 0 0 15.29.836L15.5 0h1l.209.836a2 2 0 0 0 1.455 1.455zm5 5L24 7.5v1l-.836.209a2 2 0 0 0-1.455 1.455L21.5 11h-1l-.209-.836a2 2 0 0 0-1.455-1.455L18 8.5v-1l.836-.209a2 2 0 0 0 1.455-1.455L20.5 5h1l.209.836a2 2 0 0 0 1.455 1.455z" />
|
||||
</svg>
|
||||
<svg class="icon sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
|
||||
height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z" />
|
||||
</svg>
|
||||
</label>
|
||||
`
|
||||
|
||||
class ThemeToggle extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.attachShadow({
|
||||
mode: 'open'
|
||||
}).append(themeToggle.content.cloneNode(true))
|
||||
|
||||
this.isChecked = false
|
||||
this.hasTheme = 'light'
|
||||
|
||||
this.toggleState = this.toggleState.bind(this)
|
||||
this.fireEvent = this.fireEvent.bind(this)
|
||||
this.handleThemeChange = this.handleThemeChange.bind(this)
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return ['checked'];
|
||||
}
|
||||
|
||||
daylight() {
|
||||
this.hasTheme = 'light'
|
||||
document.body.dataset.theme = 'light'
|
||||
}
|
||||
|
||||
nightlight() {
|
||||
this.hasTheme = 'dark'
|
||||
document.body.dataset.theme = 'dark'
|
||||
}
|
||||
|
||||
toggleState() {
|
||||
this.toggleAttribute('checked')
|
||||
this.fireEvent()
|
||||
}
|
||||
handleKeyDown(e) {
|
||||
if (e.code === 'Space') {
|
||||
this.toggleState()
|
||||
}
|
||||
}
|
||||
handleThemeChange(e) {
|
||||
if (e.detail.theme !== this.hasTheme) {
|
||||
if (e.detail.theme === 'dark') {
|
||||
this.setAttribute('checked', '')
|
||||
}
|
||||
else {
|
||||
this.removeAttribute('checked')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fireEvent() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('themechange', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
theme: this.hasTheme
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.setAttribute('role', 'switch')
|
||||
this.setAttribute('aria-label', 'theme toggle')
|
||||
if (localStorage.theme === "dark") {
|
||||
this.nightlight();
|
||||
this.setAttribute('checked', '')
|
||||
} else if (localStorage.theme === "light") {
|
||||
this.daylight();
|
||||
this.removeAttribute('checked')
|
||||
}
|
||||
else {
|
||||
if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) {
|
||||
this.nightlight();
|
||||
this.setAttribute('checked', '')
|
||||
} else {
|
||||
this.daylight();
|
||||
this.removeAttribute('checked')
|
||||
}
|
||||
}
|
||||
this.addEventListener("click", this.toggleState);
|
||||
this.addEventListener("keydown", this.handleKeyDown);
|
||||
document.addEventListener('themechange', this.handleThemeChange)
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener("click", this.toggleState);
|
||||
this.removeEventListener("keydown", this.handleKeyDown);
|
||||
document.removeEventListener('themechange', this.handleThemeChange)
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldVal, newVal) {
|
||||
if (name === 'checked') {
|
||||
if (this.hasAttribute('checked')) {
|
||||
this.nightlight();
|
||||
localStorage.setItem("theme", "dark");
|
||||
} else {
|
||||
this.daylight();
|
||||
localStorage.setItem("theme", "light");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('theme-toggle', ThemeToggle);
|
||||
1
components/dist/theme-toggle.min.js
vendored
Normal file
1
components/dist/theme-toggle.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const themeToggle=document.createElement("template");themeToggle.innerHTML='\n <style>\n *{\n padding: 0;\n margin: 0;\n box-sizing: border-box;\n }\n :host{\n }\n .theme-toggle {\n display: flex;\n position: relative;\n width: 1.4rem;\n height: 1.4rem;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n }\n .theme-toggle::after{\n content: \'\';\n position: absolute;\n width: 2.5rem;\n height: 2.5rem;\n top: 50%;\n left: 50%;\n opacity: 0;\n border-radius: 50%;\n pointer-events: none;\n transition: transform 0.3s, opacity 0.3s;\n transform: translate(-50%, -50%) scale(1.2);\n background-color: rgba(var(--text-color), 0.12);\n }\n :host(:focus-within) .theme-toggle{\n outline: none;\n }\n :host(:focus-within) .theme-toggle::after{\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n .icon {\n position: absolute;\n height: 100%;\n width: 100%;\n fill: rgba(var(--text-color), 1);\n transition: transform 0.6s;\n }\n \n .theme-switcher__checkbox {\n display: none;\n }\n :host([checked]) .moon-icon {\n transform: scale(0) rotate(90deg);\n }\n :host(:not([checked])) .sun-icon {\n transform: scale(0) rotate(-90deg);\n }\n </style>\n <label class="theme-toggle" title="Change theme" tabindex="0">\n <svg class="icon moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"\n height="24">\n <path fill="none" d="M0 0h24v24H0z" />\n <path\n d="M10 6a8 8 0 0 0 11.955 6.956C21.474 18.03 17.2 22 12 22 6.477 22 2 17.523 2 12c0-5.2 3.97-9.474 9.044-9.955A7.963 7.963 0 0 0 10 6zm-6 6a8 8 0 0 0 8 8 8.006 8.006 0 0 0 6.957-4.045c-.316.03-.636.045-.957.045-5.523 0-10-4.477-10-10 0-.321.015-.64.045-.957A8.006 8.006 0 0 0 4 12zm14.164-9.709L19 2.5v1l-.836.209a2 2 0 0 0-1.455 1.455L16.5 6h-1l-.209-.836a2 2 0 0 0-1.455-1.455L13 3.5v-1l.836-.209A2 2 0 0 0 15.29.836L15.5 0h1l.209.836a2 2 0 0 0 1.455 1.455zm5 5L24 7.5v1l-.836.209a2 2 0 0 0-1.455 1.455L21.5 11h-1l-.209-.836a2 2 0 0 0-1.455-1.455L18 8.5v-1l.836-.209a2 2 0 0 0 1.455-1.455L20.5 5h1l.209.836a2 2 0 0 0 1.455 1.455z" />\n </svg>\n <svg class="icon sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"\n height="24">\n <path fill="none" d="M0 0h24v24H0z" />\n <path\n d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z" />\n </svg>\n </label>\n';class ThemeToggle extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(themeToggle.content.cloneNode(!0)),this.isChecked=!1,this.hasTheme="light",this.toggleState=this.toggleState.bind(this),this.fireEvent=this.fireEvent.bind(this),this.handleThemeChange=this.handleThemeChange.bind(this)}static get observedAttributes(){return["checked"]}daylight(){this.hasTheme="light",document.body.dataset.theme="light"}nightlight(){this.hasTheme="dark",document.body.dataset.theme="dark"}toggleState(){this.toggleAttribute("checked"),this.fireEvent()}handleKeyDown(e){"Space"===e.code&&this.toggleState()}handleThemeChange(e){e.detail.theme!==this.hasTheme&&("dark"===e.detail.theme?this.setAttribute("checked",""):this.removeAttribute("checked"))}fireEvent(){this.dispatchEvent(new CustomEvent("themechange",{bubbles:!0,composed:!0,detail:{theme:this.hasTheme}}))}connectedCallback(){this.setAttribute("role","switch"),this.setAttribute("aria-label","theme toggle"),"dark"===localStorage.theme?(this.nightlight(),this.setAttribute("checked","")):"light"===localStorage.theme?(this.daylight(),this.removeAttribute("checked")):window.matchMedia("(prefers-color-scheme: dark)").matches?(this.nightlight(),this.setAttribute("checked","")):(this.daylight(),this.removeAttribute("checked")),this.addEventListener("click",this.toggleState),this.addEventListener("keydown",this.handleKeyDown),document.addEventListener("themechange",this.handleThemeChange)}disconnectedCallback(){this.removeEventListener("click",this.toggleState),this.removeEventListener("keydown",this.handleKeyDown),document.removeEventListener("themechange",this.handleThemeChange)}attributeChangedCallback(e,t,n){"checked"===e&&(this.hasAttribute("checked")?(this.nightlight(),localStorage.setItem("theme","dark")):(this.daylight(),localStorage.setItem("theme","light")))}}window.customElements.define("theme-toggle",ThemeToggle);
|
||||
@ -28,22 +28,7 @@
|
||||
</svg>
|
||||
</button>
|
||||
<h4>SM Components</h4>
|
||||
<label class="theme-switcher" title="Change theme">
|
||||
<input id="theme_switcher" class="theme-switcher__checkbox" type="checkbox"
|
||||
aria-label="Dark mode toggle">
|
||||
<svg class="icon moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
|
||||
height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M10 6a8 8 0 0 0 11.955 6.956C21.474 18.03 17.2 22 12 22 6.477 22 2 17.523 2 12c0-5.2 3.97-9.474 9.044-9.955A7.963 7.963 0 0 0 10 6zm-6 6a8 8 0 0 0 8 8 8.006 8.006 0 0 0 6.957-4.045c-.316.03-.636.045-.957.045-5.523 0-10-4.477-10-10 0-.321.015-.64.045-.957A8.006 8.006 0 0 0 4 12zm14.164-9.709L19 2.5v1l-.836.209a2 2 0 0 0-1.455 1.455L16.5 6h-1l-.209-.836a2 2 0 0 0-1.455-1.455L13 3.5v-1l.836-.209A2 2 0 0 0 15.29.836L15.5 0h1l.209.836a2 2 0 0 0 1.455 1.455zm5 5L24 7.5v1l-.836.209a2 2 0 0 0-1.455 1.455L21.5 11h-1l-.209-.836a2 2 0 0 0-1.455-1.455L18 8.5v-1l.836-.209a2 2 0 0 0 1.455-1.455L20.5 5h1l.209.836a2 2 0 0 0 1.455 1.455z" />
|
||||
</svg>
|
||||
<svg class="icon sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
|
||||
height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z" />
|
||||
</svg>
|
||||
</label>
|
||||
<theme-toggle></theme-toggle>
|
||||
</header>
|
||||
<hamburger-menu id="side_nav">
|
||||
<h4>Getting Started</h4>
|
||||
@ -282,7 +267,7 @@
|
||||
touch enabled devices swipe can be used.
|
||||
</p>
|
||||
<h2>Interactive demo</h2>
|
||||
<sm-carousel align-items="start" indicator>
|
||||
<sm-carousel indicator>
|
||||
<div class="card">
|
||||
<h3>Title</h3>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Provident, optio.</p>
|
||||
@ -525,10 +510,9 @@
|
||||
</p>
|
||||
<h2>Interactive demo</h2>
|
||||
<sm-form>
|
||||
<sm-input placeholder="Email" type="email" error-text="please enter correct email" required animate>
|
||||
</sm-input>
|
||||
<sm-input placeholder="Email" type="email" error-text="please enter correct email" required animate></sm-input>
|
||||
<sm-input placeholder="Password" type="password" required animate></sm-input>
|
||||
<sm-button variant="primary" disabled>Submit</sm-button>
|
||||
<sm-button variant="primary" disabled>Login</sm-button>
|
||||
</sm-form>
|
||||
<h2>Supported functions</h2>
|
||||
<section class="table">
|
||||
@ -806,6 +790,10 @@
|
||||
</p>
|
||||
<text-field value="Double click me!"></text-field>
|
||||
</section>
|
||||
<section id="theme_toggle_page" class="page hide-completely">
|
||||
<h1 class="page__title">Theme toggle</h1>
|
||||
<theme-toggle></theme-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<template id="nav_item_template">
|
||||
@ -839,6 +827,7 @@
|
||||
<script src="dist/tags-input.js"></script>
|
||||
<script src="dist/textarea.js"></script>
|
||||
<script src="dist/text-field.js"></script>
|
||||
<script src="dist/theme-toggle.js"></script>
|
||||
<script id="default_ui_library">
|
||||
const domRefs = {};
|
||||
|
||||
@ -921,42 +910,6 @@
|
||||
notify("We are back online.", "success");
|
||||
});
|
||||
|
||||
if (getRef("theme_switcher")) {
|
||||
if (localStorage.theme === "dark") {
|
||||
nightlight();
|
||||
getRef("theme_switcher").checked = true;
|
||||
} else if (localStorage.theme === "light") {
|
||||
daylight();
|
||||
getRef("theme_switcher").checked = false;
|
||||
}
|
||||
else {
|
||||
if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) {
|
||||
nightlight();
|
||||
getRef("theme_switcher").checked = true;
|
||||
} else {
|
||||
daylight();
|
||||
getRef("theme_switcher").checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
function daylight() {
|
||||
document.body.setAttribute("data-theme", "light");
|
||||
}
|
||||
|
||||
function nightlight() {
|
||||
document.body.setAttribute("data-theme", "dark");
|
||||
}
|
||||
getRef("theme_switcher").addEventListener("change", function (e) {
|
||||
if (this.checked) {
|
||||
nightlight();
|
||||
localStorage.setItem("theme", "dark");
|
||||
} else {
|
||||
daylight();
|
||||
localStorage.setItem("theme", "light");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// function required for popups or modals to appear
|
||||
class Stack {
|
||||
constructor() {
|
||||
@ -1158,7 +1111,6 @@
|
||||
renderComponentList()
|
||||
showPage(window.location.hash, { firstLoad: true })
|
||||
getRef('total_components_count').textContent = componentsList.length
|
||||
// PR.prettyPrint()
|
||||
getComponents()
|
||||
})
|
||||
function showPage(targetPage, options = {}) {
|
||||
@ -1281,6 +1233,10 @@
|
||||
name: 'Text field',
|
||||
pageId: 'text_field_page'
|
||||
},
|
||||
{
|
||||
name: 'theme toggle',
|
||||
pageId: 'theme_toggle_page'
|
||||
},
|
||||
]
|
||||
|
||||
function renderComponentList() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user