Simplify setup (#46)

This commit is contained in:
Ollie Taylor
2023-07-13 00:27:14 +01:00
committed by GitHub
parent 29ef65733e
commit d18b0c78d0
68 changed files with 1236 additions and 1470 deletions
-1
View File
@@ -1,2 +1 @@
export { default as PlayerStack } from './player-stack.svelte';
export { default as PlayerWidget } from './player-widget.svelte';
@@ -1,444 +0,0 @@
<script>
import {
Pause,
Play,
SpeakerWave,
} from '@inqling/svelte-icons/heroicon-20-solid';
import {
AudioPlayer,
episode_progress,
user_preferences,
} from 'svelte-podcast';
import { A11yIcon, Skip, Spinner, Timestamp } from './utility';
/** @type {string | undefined} */
export let src;
/** @type {import('svelte-podcast/audio/stores').EpisodeDetails} */
export let metadata = {};
export let hide_timestamps = false;
export let hide_playback_rate = false;
export let skip_back = 30;
export let skip_forward = 10;
export let playback_rate_values = [1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4];
</script>
<AudioPlayer {src} {metadata} let:Player let:action let:episode>
<div
{...$$restProps}
class="svpod--container svpod--reset"
data-loaded={episode.is_loaded ? 'true' : 'false'}
>
<div class="svpod--container--row svpod--row--controls">
<button
on:click={() => action.skip_back(10)}
class="svpod--reset svpod--toggle-pause"
type="button"
>
<Skip value={skip_back} type="backward" />
</button>
<!-- toggle play -->
{#if episode.is_loaded}
<button
on:click={() => action.toggle()}
class="svpod--reset svpod--toggle-pause"
type="button"
>
<A11yIcon
icon={episode.is_playing ? Pause : Play}
label={episode.is_playing ? 'Pause' : 'Play'}
/>
</button>
{:else}
<div class="svpod--reset svpod--toggle-pause">
<A11yIcon icon={Spinner} label="Waiting for audio..." />
</div>
{/if}
<button
on:click={() => action.skip_forward(10)}
class="svpod--reset svpod--toggle-pause"
type="button"
>
<Skip value={skip_forward} type="forward" />
</button>
</div>
<div class="svpod--artwork svpod--row--artwork">
<span class="svpod--aspect--square" />
<slot>
<div class="svpod--artwork--placeholder">
<span><SpeakerWave style="width:1em; height: 1em;" /></span>
</div>
</slot>
</div>
<div class="svpod--timeline svpod--row--timeline">
<Player.AudioProgress />
</div>
{#if !hide_timestamps || !hide_playback_rate}
<div class="svpod--container--row svpod--row--timestamps">
{#if !hide_timestamps}
<div class="svpod--timestamp">
<Timestamp
value={$episode_progress?.current_time || 0}
force_hours={episode.timestamp_hours}
/>
</div>
<div style="flex-grow: 1" />
<div class="svpod--timestamp">
<Timestamp value={episode.duration || 0} />
</div>
{/if}
{#if !hide_playback_rate}
<select
class="svpod--playback-rate"
value={$user_preferences.playback_rate}
on:change={(e) => {
const value = parseFloat(e.currentTarget.value);
user_preferences.set_playback_rate(value);
}}
>
{#each playback_rate_values as value}
<option {value}>
{#if Number.isInteger(value)}
{value}.0
{:else}
{value}
{/if}
</option>
{/each}
</select>
{/if}
</div>
{/if}
</div>
</AudioPlayer>
<style lang="postcss">
.svpod--container {
--fg: var(--svpod--content--base);
--bg: var(--svpod--surface--darker);
--padding: 2px;
--radius: 0.75em;
--inner-radius: calc(var(--radius) - var(--padding));
--svpod--timeline-track--shape--height: 3em;
--svpod--timeline-track--shape--radius: var(--inner-radius);
--svpod--timeline-track--shape--border: 2px;
--svpod--timeline-thumb--shape--height: var(
--svpod--timeline-track--shape--height
);
--svpod--timeline-thumb--shape--width: 3px;
--svpod--timeline-thumb--shape--radius: 1px;
--svpod--timeline-thumb--shape--border: 0px;
background-color: var(--bg);
color: var(--fg);
display: inline-flex;
flex-direction: column;
align-items: stretch;
font-size: 16px;
line-height: 1em;
width: auto;
max-width: 100%;
border-radius: var(--radius);
font-weight: 600;
padding: var(--padding);
gap: 0;
}
.svpod--container--row {
display: flex;
flex-direction: row;
align-items: stretch;
justify-content: flex-end;
width: 100%;
min-width: max-content;
border-radius: var(--inner-radius);
gap: var(--padding);
}
.svpod--container--row:last-child {
border-top: 1px solid var(--svpod--surface--base);
border-top-right-radius: 0;
border-top-left-radius: 0;
}
.svpod--row--artwork {
order: 0;
}
.svpod--row--timeline {
order: 1;
}
.svpod--row--controls {
order: 2;
justify-content: center;
}
.svpod--row--timestamps {
order: 3;
min-height: 32px;
gap: 0.5em;
}
div.svpod--timeline {
display: flex;
align-items: center;
justify-content: stretch;
flex-grow: 1;
flex-shrink: 1;
font-size: 1em;
background: none;
}
.svpod--toggle-pause {
align-items: center;
border: none;
display: flex;
font-size: 1em;
height: 3em;
justify-content: center;
width: 3em;
border-radius: var(--inner-radius);
}
button.svpod--toggle-pause {
cursor: pointer;
}
.svpod--toggle-pause :global(svg) {
width: 1.5em;
height: 1.5em;
}
div.svpod--timestamp {
background-color: var(--bg);
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 4px;
font-size: 1em;
line-height: 1em;
pointer-events: none;
border-radius: var(--inner-radius);
width: max-content;
flex-grow: 0;
text-align: center;
padding: 0 0.1em;
}
select.svpod--playback-rate {
background-color: var(--bg);
appearance: none;
padding: 0 0.75em;
color: var(--fg);
overflow: hidden;
letter-spacing: 0.5px;
font-size: 0.8em;
line-height: 1em;
text-transform: uppercase;
font-weight: 600;
text-align: center;
min-width: 48px;
outline-color: var(--svpod--accent--lighter);
cursor: pointer;
margin: 0;
border-radius: var(--inner-radius);
}
select.svpod--playback-rate,
select.svpod--playback-rate:hover,
select.svpod--playback-rate:focus {
border: none;
shadow: none;
}
select.svpod--playback-rate:focus {
--fg: var(--svpod--accent--lighter);
}
button.svpod--toggle-pause,
select.svpod--playback-rate {
background-color: var(--bg);
color: var(--fg);
}
button.svpod--toggle-pause:hover,
select.svpod--playback-rate:hover {
--bg: var(--svpod--surface--base);
--fg: var(--svpod--content--lighter);
background-color: var(--bg);
color: var(--fg);
}
div.svpod--artwork {
position: relative;
border-radius: var(--inner-radius);
overflow: hidden;
}
div.svpod--artwork > :global(div),
div.svpod--artwork > :global(img) {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.svpod--artwork--placeholder {
display: flex;
align-items: center;
justify-content: center;
color: var(--svpod--accent--lighter);
font-size: 2.5em;
}
.svpod--artwork--placeholder :global(svg) {
position: relative;
z-index: 1;
}
.svpod--artwork--placeholder::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
opacity: 0.5;
background: linear-gradient(
45deg,
var(--svpod--accent--base),
transparent
);
}
span.svpod--aspect--square {
display: block;
position: relative;
line-height: 0;
padding-bottom: 100%;
}
.svpod--timeline {
:global(input[type='range']) {
--track--shape--height: calc(
var(--svpod--timeline-track--shape--height)
);
--thumb-border-offset: calc(
var(--svpod--timeline-thumb--shape--border) * 2
);
--thumb--shape--height: calc(
var(--svpod--timeline-thumb--shape--height) -
var(--thumb-border-offset)
);
--svpod--timeline-track--bg: var(--svpod--surface--base);
--svpod--timeline-track--border: var(--bg);
--svpod--timeline-thumb--bg: var(--svpod--content--base);
--svpod--timeline-thumb--border: var(--bg);
font-size: 1em;
--borders: calc(var(--svpod--timeline-thumb--shape--border) * 2);
--no-shadow: 0px 0px 0px transparent;
height: calc(var(--thumb--shape--height) + var(--borders));
appearance: none;
margin: 0;
width: 100%;
background: transparent;
}
:global(input[type='range']:hover) {
--svpod--timeline-track--bg: var(--svpod--accent--darker);
--svpod--timeline-track--border: var(--svpod--accent--base);
--svpod--timeline-thumb--bg: var(--svpod--accent--lighter);
}
:global(input[type='range']:focus) {
outline: none;
}
:global(input[type='range']::-webkit-slider-runnable-track) {
width: 100%;
height: var(--track--shape--height);
cursor: pointer;
animate: 0.2s;
box-shadow: var(--no-shadow);
background: var(--svpod--timeline-track--bg);
border-radius: var(--svpod--timeline-track--shape--radius);
border: var(--svpod--timeline-track--shape--border) solid
var(--svpod--timeline-track--border);
}
:global(input[type='range']::-webkit-slider-thumb) {
--offset: calc(var(--svpod--timeline-track--shape--border) * -1);
box-shadow: var(--no-shadow);
border: var(--svpod--timeline-thumb--shape--border) solid
var(--svpod--timeline-thumb--border);
height: var(--thumb--shape--height);
width: var(--svpod--timeline-thumb--shape--width);
border-radius: var(--svpod--timeline-thumb--shape--radius);
background: var(--svpod--timeline-thumb--bg);
cursor: pointer;
-webkit-appearance: none;
margin-top: var(--offset);
}
:global(input[type='range']:focus::-webkit-slider-runnable-track) {
background: var(--svpod--timeline-track--bg);
}
:global(input[type='range']::-moz-range-track) {
width: 100%;
height: var(--track--shape--height);
cursor: pointer;
animate: 0.2s;
box-shadow: var(--no-shadow);
background: var(--svpod--timeline-track--bg);
border-radius: var(--svpod--timeline-track--shape--radius);
border: var(--svpod--timeline-track--shape--border) solid
var(--svpod--timeline-track--border);
}
:global(input[type='range']::-moz-range-thumb) {
box-shadow: var(--no-shadow);
border: var(--svpod--timeline-thumb--shape--border) solid
var(--svpod--timeline-thumb--border);
height: var(--thumb--shape--height);
width: var(--svpod--timeline-thumb--shape--width);
border-radius: var(--svpod--timeline-thumb--shape--radius);
background: var(--svpod--timeline-thumb--bg);
cursor: pointer;
}
:global(input[type='range']::-ms-track) {
width: 100%;
height: var(--track--shape--height);
cursor: pointer;
animate: 0.2s;
background: transparent;
border-color: transparent;
color: transparent;
}
:global(input[type='range']::-ms-fill-lower),
:global(input[type='range']::-ms-fill-upper) {
background: var(--svpod--timeline-track--bg);
border: var(--svpod--timeline-track--shape--border) solid
var(--svpod--timeline-track--border);
border-radius: calc(var(--svpod--timeline-track--shape--radius) * 2);
box-shadow: var(--no-shadow);
}
:global(input[type='range']::-ms-thumb) {
margin-top: 1px;
box-shadow: var(--no-shadow);
border: var(--svpod--timeline-thumb--shape--border) solid
var(--svpod--timeline-thumb--border);
height: var(--thumb--shape--height);
width: var(--svpod--timeline-thumb--shape--width);
border-radius: var(--svpod--timeline-thumb--shape--radius);
background: var(--svpod--timeline-thumb--bg);
cursor: pointer;
}
:global(input[type='range']:focus::-ms-fill-lower),
:global(input[type='range']:focus::-ms-fill-upper) {
background: var(--svpod--timeline-track--bg);
}
}
</style>
@@ -6,7 +6,7 @@
/** @type {string | undefined} */
export let src;
/** @type {import('svelte-podcast/audio/stores').EpisodeDetails} */
/** @type {import('svelte-podcast/audio').AudioMetadata} */
export let metadata = {};
export let hide_duration = false;
@@ -21,11 +21,105 @@
export let playback_rate_values = [1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4];
</script>
<AudioPlayer {src} {metadata} let:Player let:action let:episode>
<svelte:head>
<style>
:root {
--svpod--font: ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
--svpod--surface--darker: rgb(0, 0, 0);
--svpod--surface--base: rgb(40, 40, 40);
--svpod--surface--lighter: rgb(60, 60, 60);
--svpod--content--darker: rgb(150, 150, 150);
--svpod--content--base: rgb(200, 200, 200);
--svpod--content--lighter: rgb(255, 255, 255);
--svpod--accent--darker: rgb(75, 15, 0);
--svpod--accent--base: rgb(180, 40, 0);
--svpod--accent--lighter: rgb(255, 55, 25);
--bg: var(--svpod--surface--darker);
--fg: var(--svpod--content--darker);
--svpod--radius--rounded: 9999px;
/* element : timeline */
--svpod--timeline-track--shape--height: 20px;
--svpod--timeline-track--shape--radius: 0;
--svpod--timeline-track--shape--border: 0;
--svpod--timeline-thumb--shape--width: 8px;
--svpod--timeline-thumb--shape--height: 32px;
--svpod--timeline-thumb--shape--radius: 6px;
--svpod--timeline-thumb--shape--border: 2px;
}
.svpod--reset {
text-transform: none;
font-style: normal;
text-indent: 0;
text-shadow: none;
text-align: left;
margin: 0;
padding: 0;
}
.svpod--a11y-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.svpod--button,
.svpod--icon-button {
--fg: var(--svpod--content--base);
--bg: var(--svpod--surface--base);
position: relative;
line-height: 1em;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font-size: 16px;
border: 0px solid transparent;
border-radius: var(--svpod--radius--rounded);
background-color: var(--bg);
color: var(--fg);
cursor: pointer;
}
.svpod--button:hover,
.svpod--icon-button:hover {
--fg: var(--svpod--content--lighter);
--bg: var(--svpod--surface--lighter);
}
.svpod--button {
padding: 4px calc(1em);
}
.svpod--icon-button {
padding: 8px;
min-width: 48px;
min-height: 48px;
}
</style>
</svelte:head>
<AudioPlayer {src} {metadata} let:Player let:action let:attributes>
<div
{...$$restProps}
class="svpod--container svpod--reset"
data-loaded={episode.is_loaded ? 'true' : 'false'}
data-loaded={attributes.is_loaded ? 'true' : 'false'}
>
{#if !hide_skip_back}
<button
@@ -38,15 +132,15 @@
{/if}
<!-- toggle play -->
{#if episode.is_loaded}
{#if attributes.is_loaded}
<button
on:click={() => action.toggle()}
class="svpod--reset svpod--toggle-pause"
type="button"
>
<A11yIcon
icon={episode.is_playing ? Pause : Play}
label={episode.is_playing ? 'Pause' : 'Play'}
icon={attributes.is_paused ? Play : Pause}
label={attributes.is_paused ? 'Play' : 'Pause'}
/>
</button>
{:else}
@@ -68,8 +162,8 @@
{#if !hide_current_time}
<div class="svpod--timestamp">
<Timestamp
value={episode.current_time}
force_hours={episode.timestamp_hours}
value={attributes.current_time}
force_hours={attributes.duration >= 3600}
/>
</div>
{/if}
@@ -80,7 +174,7 @@
{#if !hide_duration}
<div class="svpod--timestamp">
<Timestamp value={episode.duration} />
<Timestamp value={attributes.duration} />
</div>
{/if}
@@ -153,8 +153,4 @@
transform: rotate(360deg);
}
}
svg {
/* animation: Spin 1s cubic-bezier(0.5, 0.1, 0.1, 0.8) infinite; */
}
</style>
@@ -1,5 +1,5 @@
<script>
import { seconds_to_timestamp } from 'svelte-podcast';
import { seconds_to_timestamp } from 'svelte-podcast/utility';
/** @type {number} */
export let value;