Fixed observable completion semantics

This commit is contained in:
squidfunk
2021-11-28 14:54:14 +01:00
parent abe475e151
commit c30c3d196e
55 changed files with 493 additions and 1347 deletions

View File

@@ -23,7 +23,6 @@
import {
Observable,
Subject,
animationFrameScheduler,
bufferCount,
combineLatest,
combineLatestWith,
@@ -32,18 +31,15 @@ import {
distinctUntilKeyChanged,
filter,
map,
observeOn,
of,
shareReplay,
startWith,
switchMap
switchMap,
takeLast,
takeUntil
} from "rxjs"
import { feature } from "~/_"
import {
resetHeaderState,
setHeaderState
} from "~/actions"
import {
Viewport,
watchElementSize,
@@ -184,24 +180,28 @@ export function watchHeader(
export function mountHeader(
el: HTMLElement, { header$, main$ }: MountOptions
): Observable<Component<Header>> {
const internal$ = new Subject<Main>()
internal$
.pipe(
distinctUntilKeyChanged("active"),
combineLatestWith(header$),
observeOn(animationFrameScheduler)
)
.subscribe(([{ active }, { hidden }]) => {
if (active)
setHeaderState(el, hidden ? "hidden" : "shadow")
else
resetHeaderState(el)
})
return defer(() => {
const push$ = new Subject<Main>()
push$
.pipe(
distinctUntilKeyChanged("active"),
combineLatestWith(header$)
)
.subscribe(([{ active }, { hidden }]) => {
if (active)
el.setAttribute("data-md-state", hidden ? "hidden" : "shadow")
else
el.removeAttribute("data-md-state")
})
/* Connect to long-living subject and return component */
main$.subscribe(main => internal$.next(main))
return header$
.pipe(
map(state => ({ ref: el, ...state }))
)
/* Link to main area */
main$.subscribe(push$)
/* Create and return component */
return header$
.pipe(
takeUntil(push$.pipe(takeLast(1))),
map(state => ({ ref: el, ...state }))
)
})
}

View File

@@ -21,21 +21,16 @@
*/
import {
NEVER,
EMPTY,
Observable,
Subject,
animationFrameScheduler,
defer,
distinctUntilKeyChanged,
finalize,
map,
observeOn,
tap
} from "rxjs"
import {
resetHeaderTitleState,
setHeaderTitleState
} from "~/actions"
import {
Viewport,
getElementSize,
@@ -118,28 +113,26 @@ export function watchHeaderTitle(
export function mountHeaderTitle(
el: HTMLElement, options: MountOptions
): Observable<Component<HeaderTitle>> {
const internal$ = new Subject<HeaderTitle>()
internal$
.pipe(
observeOn(animationFrameScheduler)
)
.subscribe(({ active }) => {
if (active)
setHeaderTitleState(el, "active")
else
resetHeaderTitleState(el)
})
return defer(() => {
const push$ = new Subject<HeaderTitle>()
push$.subscribe(({ active }) => {
if (active)
el.setAttribute("data-md-state", "active")
else
el.removeAttribute("data-md-state")
})
/* Obtain headline, if any */
const headline = getOptionalElement<HTMLHeadingElement>("article h1")
if (typeof headline === "undefined")
return NEVER
/* Obtain headline, if any */
const heading = getOptionalElement<HTMLHeadingElement>("article h1")
if (typeof heading === "undefined")
return EMPTY
/* Create and return component */
return watchHeaderTitle(headline, options)
.pipe(
tap(state => internal$.next(state)),
finalize(() => internal$.complete()),
map(state => ({ ref: el, ...state }))
)
/* Create and return component */
return watchHeaderTitle(heading, options)
.pipe(
tap(state => push$.next(state)),
finalize(() => push$.complete()),
map(state => ({ ref: el, ...state }))
)
})
}