Skip to content

Instantly share code, notes, and snippets.

@leandrohsilveira
Last active January 18, 2023 03:33
Show Gist options
  • Save leandrohsilveira/1d36bc71ea6aebce59ac57b1e1c9effd to your computer and use it in GitHub Desktop.
Save leandrohsilveira/1d36bc71ea6aebce59ac57b1e1c9effd to your computer and use it in GitHub Desktop.
Using RXJS Observables in Svelte with autosubscribe feature
<script lang="ts">
import { map } from 'rxjs';
import { count$, evenCount$, oddCount$ } from './counter';
import { toReadable, toWritable } from './util';
let count = toWritable(count$);
let lastEven = toReadable(evenCount$.pipe(map(String)), 'none')
let lastOdd = toReadable(oddCount$.pipe(map(String)), 'none')
</script>
<div class="counter">
<button on:click={() => ($count -= 1)} aria-label="Decrease the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5" />
</svg>
</button>
<div class="counter-viewport">
<div class="counter-digits">
<strong>{Math.floor($count)}</strong>
</div>
</div>
<button on:click={() => ($count += 1)} aria-label="Increase the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
</svg>
</button>
</div>
Last even: {$lastEven}
Last odd: {$lastOdd}
<style>
.counter {
display: flex;
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin: 1rem 0;
}
.counter button {
width: 2em;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border: 0;
background-color: transparent;
touch-action: manipulation;
color: var(--text-color);
font-size: 2rem;
}
.counter button:hover {
background-color: var(--secondary-color);
}
svg {
width: 25%;
height: 25%;
}
path {
vector-effect: non-scaling-stroke;
stroke-width: 2px;
stroke: var(--text-color);
}
.counter-viewport {
width: 8em;
height: 4em;
overflow: hidden;
text-align: center;
position: relative;
}
.counter-viewport strong {
position: absolute;
display: flex;
width: 100%;
height: 100%;
font-weight: 400;
color: var(--accent-color);
font-size: 4rem;
align-items: center;
justify-content: center;
}
.counter-digits {
position: absolute;
width: 100%;
height: 100%;
}
</style>
import { BehaviorSubject, filter } from 'rxjs';
export const count$ = new BehaviorSubject(0);
export const evenCount$ = count$.pipe(filter((value) => value % 2 === 0));
export const oddCount$ = count$.pipe(filter((value) => value % 2 !== 0));
import { Observable, type Subject } from 'rxjs';
import { get, readable, type Readable, type Writable } from 'svelte/store';
export function toReadable<T>(observable: Observable<T>, initialValue?: T): Readable<T> {
return readable<T>(initialValue, (next) => {
const subscription = observable.subscribe({ next });
return () => subscription.unsubscribe();
});
}
export function toWritable<T>(subject: Subject<T>, initialValue?: T): Writable<T> {
const readable = toReadable(subject, initialValue);
return {
...readable,
set(value) {
subject.next(value);
},
update(updater) {
subject.next(updater(get(readable)));
}
};
}
export function fromReadable<T>(readable: Readable<T>): Observable<T> {
return new Observable((observer) => readable.subscribe((value) => observer.next(value)));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment