Added animation to content tabs

This commit is contained in:
squidfunk 2021-12-18 00:36:45 +01:00
parent dffcba47fb
commit 4b2a97a51d
7 changed files with 74 additions and 26 deletions

View File

@ -34,7 +34,7 @@
{% endif %}
{% endblock %}
{% block styles %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.8e8281cd.min.css' | url }}">
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.ffa0dd14.min.css' | url }}">
{% if config.theme.palette %}
{% set palette = config.theme.palette %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.e6a45f82.min.css' | url }}">
@ -213,7 +213,7 @@
</script>
{% endblock %}
{% block scripts %}
<script src="{{ 'assets/javascripts/bundle.649a939e.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.1cbe63b4.min.js' | url }}"></script>
{% for path in config["extra_javascript"] %}
<script src="{{ path | url }}"></script>
{% endfor %}

View File

@ -29,12 +29,14 @@ import {
map,
mapTo,
merge,
startWith,
tap
} from "rxjs"
import {
getElement,
getElementOffset,
getElementSize,
getElements
} from "~/browser"
@ -65,14 +67,18 @@ export interface ContentTabs {
export function watchContentTabs(
el: HTMLElement
): Observable<ContentTabs> {
return merge(...getElements(":scope > input", el)
.map(input => fromEvent(input, "change")
const inputs = getElements(":scope > input", el)
return merge(...inputs.map(input => fromEvent(input, "change")
.pipe(
mapTo<ContentTabs>({
active: getElement(`label[for=${input.id}]`)
})
)
)
))
.pipe(
startWith({
active: getElement(`label[for=${inputs[0].id}]`)
} as ContentTabs)
)
}
@ -94,12 +100,29 @@ export function mountContentTabs(
const container = getElement(".tabbed-labels", el)
return defer(() => {
const push$ = new Subject<ContentTabs>()
push$.subscribe(({ active }) => {
const { x } = getElementOffset(active)
push$.subscribe({
/* Handle emission */
next({ active }) {
const offset = getElementOffset(active)
const { width } = getElementSize(active)
/* Set tab indicator offset and width */
el.style.setProperty("--md-indicator-x", `${offset.x}px`)
el.style.setProperty("--md-indicator-width", `${width}px`)
/* Smoothly scroll container */
container.scrollTo({
behavior: "smooth",
left: x
left: offset.x
})
},
/* Handle complete */
complete() {
el.style.removeProperty("--md-indicator-x")
el.style.removeProperty("--md-indicator-width")
}
})
/* Create and return component */

View File

@ -87,6 +87,27 @@
display: contents;
}
// [js]: Show animated tab indicator
.js & {
position: relative;
// Tab indicator
&::after {
position: absolute;
bottom: 0;
display: block;
width: var(--md-indicator-width);
height: 2px;
background: var(--md-accent-fg-color);
transform: translateX(var(--md-indicator-x));
transition:
width 250ms,
transform 250ms;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
content: "";
}
}
// Webkit scrollbar
&::-webkit-scrollbar {
display: none; // Chrome, Safari
@ -207,8 +228,12 @@
// [screen]: Show active state
@media screen {
color: var(--md-accent-fg-color);
// [no-js]: Show border (indicator is animated with JavaScript)
.no-js & {
border-color: var(--md-accent-fg-color);
}
}
}
// Tab label on keyboard focus placeholder