@outbook/webcomponents-player 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_lib/controls/index.js +14 -9
- package/_lib/playlist/index.js +10 -10
- package/_lib/playlist/modules/item-duration.js +3 -4
- package/_lib/playlist/modules/show-cover.js +1 -1
- package/_lib/track-info/index.js +22 -26
- package/_lib/track-text/index.js +5 -6
- package/_style/_modules/_controls.scss +8 -8
- package/_style/_modules/_playlist.scss +22 -18
- package/_style/_modules/_track-info.scss +6 -6
- package/_style/player.style.js +1 -1
- package/package.json +3 -2
- package/player.js +7 -11
- package/_lib/hooks.js +0 -23
- package/_lib/timeline/_lib/handle-timeline.js +0 -42
- package/_lib/timeline/index.js +0 -121
package/_lib/controls/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { virtual } from 'haunted';
|
|
2
1
|
import { html } from 'lit';
|
|
3
2
|
import { TypeIcon } from '@outbook/webcomponents-type-icon/shadow';
|
|
4
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
skip_next,
|
|
5
|
+
skip_previous,
|
|
6
|
+
play_arrow,
|
|
7
|
+
pause,
|
|
8
|
+
stop
|
|
9
|
+
} from '@outbook/icons';
|
|
5
10
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
6
11
|
import { isEventClick, isKeyEnterOrKeySpace } from 'a11y-key-conjurer';
|
|
7
12
|
import { classMap } from 'lit/directives/class-map.js';
|
|
8
13
|
|
|
9
|
-
export
|
|
14
|
+
export function Controls({ props, handleAudio, literals = {} }) {
|
|
10
15
|
const { isPlaying = false, playlist, indexSelected } = props;
|
|
11
16
|
const controlsDisabled = !Number.isInteger(indexSelected);
|
|
12
17
|
const controlItems = [
|
|
@@ -57,15 +62,15 @@ export const Controls = virtual(({ props, handleAudio, literals = {} }) => {
|
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
return html`
|
|
60
|
-
<div class="
|
|
61
|
-
<ul class="
|
|
65
|
+
<div class="player-controls">
|
|
66
|
+
<ul class="player-controls__items">
|
|
62
67
|
${controlItems.map(item => {
|
|
63
68
|
const buttonClasses = classMap({
|
|
64
|
-
'
|
|
69
|
+
'player-controls__button': true,
|
|
65
70
|
'is-disabled': item.isDisabled
|
|
66
71
|
});
|
|
67
72
|
return html`
|
|
68
|
-
<li class="
|
|
73
|
+
<li class="player-controls__item">
|
|
69
74
|
<button
|
|
70
75
|
class="${buttonClasses}"
|
|
71
76
|
aria-label="${item.label}"
|
|
@@ -79,7 +84,7 @@ export const Controls = virtual(({ props, handleAudio, literals = {} }) => {
|
|
|
79
84
|
${TypeIcon({
|
|
80
85
|
icon: item.icon,
|
|
81
86
|
icons: { skip_next, skip_previous, play_arrow, pause, stop },
|
|
82
|
-
extraClasses: '
|
|
87
|
+
extraClasses: 'player-controls__icon'
|
|
83
88
|
})}
|
|
84
89
|
</button>
|
|
85
90
|
</li>
|
|
@@ -88,4 +93,4 @@ export const Controls = virtual(({ props, handleAudio, literals = {} }) => {
|
|
|
88
93
|
</ul>
|
|
89
94
|
</div>
|
|
90
95
|
`;
|
|
91
|
-
}
|
|
96
|
+
}
|
package/_lib/playlist/index.js
CHANGED
|
@@ -32,18 +32,18 @@ export const Playlist = virtual(
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
return html`
|
|
35
|
-
<div class="
|
|
36
|
-
<ul class="
|
|
35
|
+
<div class="player-playlist" id="${listId}">
|
|
36
|
+
<ul class="player-playlist__items">
|
|
37
37
|
${_playlist.map((item, index) => {
|
|
38
38
|
const isPlayingItem = indexSelected === index;
|
|
39
39
|
const itemClasses = classMap({
|
|
40
|
-
'
|
|
40
|
+
'player-playlist__item': true,
|
|
41
41
|
'is-playing': isPlayingItem
|
|
42
42
|
});
|
|
43
43
|
return html`
|
|
44
44
|
<li class="${itemClasses}" data-playlist-item="${index}">
|
|
45
45
|
<div
|
|
46
|
-
class="
|
|
46
|
+
class="player-playlist__item-button"
|
|
47
47
|
tabindex="0"
|
|
48
48
|
role="button"
|
|
49
49
|
@click="${handleButton(index)}"
|
|
@@ -53,13 +53,13 @@ export const Playlist = virtual(
|
|
|
53
53
|
${showIconIsPlaying && isPlayingItem
|
|
54
54
|
? html`
|
|
55
55
|
<div
|
|
56
|
-
class="
|
|
56
|
+
class="player-playlist__item-block player-playlist__item-playing-icon"
|
|
57
57
|
>
|
|
58
58
|
${TypeIcon({
|
|
59
59
|
icon: isPlaying ? 'play_arrow' : 'pause',
|
|
60
60
|
icons: { play_arrow, pause },
|
|
61
61
|
extraClasses:
|
|
62
|
-
'
|
|
62
|
+
'player-playlist__item-playing-icon-inner'
|
|
63
63
|
})}
|
|
64
64
|
</div>
|
|
65
65
|
`
|
|
@@ -67,13 +67,13 @@ export const Playlist = virtual(
|
|
|
67
67
|
${showTrackNumber
|
|
68
68
|
? html`
|
|
69
69
|
<div
|
|
70
|
-
class="
|
|
70
|
+
class="player-playlist__item-block track-text__item size-small"
|
|
71
71
|
>
|
|
72
72
|
${item.trackNumber || index + 1}
|
|
73
73
|
</div>
|
|
74
74
|
`
|
|
75
75
|
: nothing}
|
|
76
|
-
<div class="
|
|
76
|
+
<div class="player-playlist__item-block">
|
|
77
77
|
${TrackText({
|
|
78
78
|
title: item.title,
|
|
79
79
|
artist: item.artist,
|
|
@@ -82,11 +82,11 @@ export const Playlist = virtual(
|
|
|
82
82
|
</div>
|
|
83
83
|
${item.duration || showFileExtension
|
|
84
84
|
? html`
|
|
85
|
-
<div class="
|
|
85
|
+
<div class="player-playlist__item-group is-last-right">
|
|
86
86
|
${ItemDuration({ duration: item.duration })}
|
|
87
87
|
${showFileExtension
|
|
88
88
|
? html`
|
|
89
|
-
<div class="
|
|
89
|
+
<div class="player-playlist__item-block">
|
|
90
90
|
${BadgeFileExtension({ text: item.format })}
|
|
91
91
|
</div>
|
|
92
92
|
`
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { virtual } from 'haunted';
|
|
2
1
|
import { html } from 'lit';
|
|
3
2
|
import {
|
|
4
3
|
readable as formatTime,
|
|
5
4
|
iso as formatTimeDuration
|
|
6
5
|
} from 'mystic-format-time';
|
|
7
6
|
|
|
8
|
-
export
|
|
7
|
+
export function ItemDuration({ duration }) {
|
|
9
8
|
return html`
|
|
10
9
|
<time
|
|
11
|
-
class="
|
|
10
|
+
class="player-playlist__item-block track-text__item size-small"
|
|
12
11
|
datetime="${formatTimeDuration(duration)}"
|
|
13
12
|
>
|
|
14
13
|
${formatTime(duration)}
|
|
15
14
|
</time>
|
|
16
15
|
`;
|
|
17
|
-
}
|
|
16
|
+
}
|
|
@@ -6,7 +6,7 @@ export const ShowCover = ({ cover }) => {
|
|
|
6
6
|
return html`
|
|
7
7
|
${PlayerArtwork({
|
|
8
8
|
src1x: cover ? getCover(cover) : undefined,
|
|
9
|
-
extraClasses: '
|
|
9
|
+
extraClasses: 'player-playlist__item-block player-playlist__item-image'
|
|
10
10
|
})}
|
|
11
11
|
`;
|
|
12
12
|
};
|
package/_lib/track-info/index.js
CHANGED
|
@@ -1,33 +1,29 @@
|
|
|
1
|
-
import { virtual } from 'haunted';
|
|
2
1
|
import { html } from 'lit';
|
|
3
2
|
import { TrackText } from '../track-text/index.js';
|
|
4
3
|
import { getCover } from '../get-cover.js';
|
|
5
4
|
import { PlayerArtwork } from '@outbook/webcomponents-player-artwork';
|
|
6
5
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
})
|
|
27
|
-
: TrackText({ title: literals.noPlaying, hasTrackInfo })}
|
|
28
|
-
</div>
|
|
6
|
+
export function TrackInfo({ track = {}, indexSelected, literals = {} }) {
|
|
7
|
+
const hasTrackInfo = Number.isInteger(indexSelected);
|
|
8
|
+
const cover = hasTrackInfo && track.cover ? getCover(track.cover) : undefined;
|
|
9
|
+
return html`
|
|
10
|
+
<div class="track-info">
|
|
11
|
+
<div class="track-info__inner">
|
|
12
|
+
${PlayerArtwork({
|
|
13
|
+
src1x: cover,
|
|
14
|
+
extraClasses: 'track-info__image'
|
|
15
|
+
})}
|
|
16
|
+
<div class="track-info__data">
|
|
17
|
+
${hasTrackInfo
|
|
18
|
+
? TrackText({
|
|
19
|
+
title: track.title,
|
|
20
|
+
artist: track.artist,
|
|
21
|
+
album: track.album,
|
|
22
|
+
hasTrackInfo
|
|
23
|
+
})
|
|
24
|
+
: TrackText({ title: literals.noPlaying, hasTrackInfo })}
|
|
29
25
|
</div>
|
|
30
26
|
</div>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
</div>
|
|
28
|
+
`;
|
|
29
|
+
}
|
package/_lib/track-text/index.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import { virtual } from 'haunted';
|
|
2
1
|
import { html, nothing } from 'lit';
|
|
3
2
|
|
|
4
|
-
export
|
|
3
|
+
export function TrackText({ artist, album, title, hasTrackInfo }) {
|
|
5
4
|
return html`
|
|
6
|
-
<div class="
|
|
5
|
+
<div class="track-text">
|
|
7
6
|
<div
|
|
8
|
-
class="
|
|
7
|
+
class="track-text__item"
|
|
9
8
|
data-test-id="player-data-${hasTrackInfo ? 'title' : 'stopped'}"
|
|
10
9
|
>
|
|
11
10
|
${title}
|
|
12
11
|
</div>
|
|
13
|
-
<div class="
|
|
12
|
+
<div class="track-text__item size-small">
|
|
14
13
|
${artist ? html`<span>${artist}</span>` : nothing}
|
|
15
14
|
${artist && album ? html`<span> / </span>` : nothing}
|
|
16
15
|
${album ? html`<span>${album}</span>` : nothing}
|
|
17
16
|
</div>
|
|
18
17
|
</div>
|
|
19
18
|
`;
|
|
20
|
-
}
|
|
19
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
@use '../_lib/variables';
|
|
4
4
|
|
|
5
5
|
@mixin style() {
|
|
6
|
-
.
|
|
6
|
+
.player-controls {
|
|
7
7
|
--_controls-color: light-dark(
|
|
8
8
|
var(--_accent-950),
|
|
9
9
|
var(--_accent-100)
|
|
@@ -12,27 +12,27 @@
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
@container player (width >= #{variables.$player-widht-m}) {
|
|
15
|
-
.
|
|
15
|
+
.player-controls {
|
|
16
16
|
--_controls-size: #{measures.$baseline * 7};
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
@container player (width >= #{variables.$player-widht-l}) {
|
|
21
|
-
.
|
|
21
|
+
.player-controls {
|
|
22
22
|
--_controls-size: #{measures.$baseline * 8};
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
.
|
|
27
|
-
.
|
|
26
|
+
.player-controls,
|
|
27
|
+
.player-controls__items {
|
|
28
28
|
width: auto;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
.
|
|
31
|
+
.player-controls__items {
|
|
32
32
|
display: flex;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
.
|
|
35
|
+
.player-controls__button {
|
|
36
36
|
background: transparent;
|
|
37
37
|
border: 0;
|
|
38
38
|
color: var(--_controls-color);
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
opacity: 0.4;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
.
|
|
46
|
+
.player-controls__icon {
|
|
47
47
|
width: var(--_controls-size);
|
|
48
48
|
height: var(--_controls-size);
|
|
49
49
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
@use '../_lib/variables';
|
|
5
5
|
|
|
6
6
|
@mixin style() {
|
|
7
|
-
.
|
|
7
|
+
.player-playlist {
|
|
8
8
|
--_playlist-font-weight: normal;
|
|
9
9
|
--_playlist-font-size: #{measures.$baseline * 1.5};
|
|
10
10
|
--_playlist-font-size-small: #{measures.$baseline * 1.25};
|
|
@@ -19,16 +19,22 @@
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
@container player (width >= #{variables.$player-widht-m}) {
|
|
23
|
+
.player-playlist {
|
|
24
|
+
--_playlist-font-size: #{measures.$baseline * 1.75};
|
|
25
|
+
--_playlist-font-size-small: #{measures.$baseline * 1.5};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
22
28
|
@container player (width >= #{variables.$player-widht-l}) {
|
|
23
|
-
.
|
|
29
|
+
.player-playlist {
|
|
24
30
|
--_playlist-font-size: #{measures.$baseline * 2};
|
|
25
|
-
--_playlist-font-size-small: #{measures.$baseline * 1.
|
|
31
|
+
--_playlist-font-size-small: #{measures.$baseline * 1.75};
|
|
26
32
|
--_playlist-item-image-size: #{measures.$baseline * 8};
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
.
|
|
31
|
-
.
|
|
36
|
+
.player-playlist {
|
|
37
|
+
.track-text__item {
|
|
32
38
|
font-weight: var(--_playlist-font-weight);
|
|
33
39
|
font-size: var(--_playlist-font-size);
|
|
34
40
|
line-height: 150%;
|
|
@@ -38,12 +44,12 @@
|
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
.
|
|
47
|
+
.player-playlist__items {
|
|
42
48
|
padding: 0;
|
|
43
49
|
position: relative;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
.
|
|
52
|
+
.player-playlist__item {
|
|
47
53
|
position: relative;
|
|
48
54
|
border-bottom: 1px solid var(--_separator-color);
|
|
49
55
|
&.is-playing {
|
|
@@ -63,7 +69,7 @@
|
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
.
|
|
72
|
+
.player-playlist__item-button {
|
|
67
73
|
display: flex;
|
|
68
74
|
padding: #{measures.$baseline};
|
|
69
75
|
position: relative;
|
|
@@ -73,9 +79,12 @@
|
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
.
|
|
77
|
-
.
|
|
82
|
+
.player-playlist__item-group,
|
|
83
|
+
.player-playlist__item-block {
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
78
86
|
margin-left: #{measures.$baseline * 2};
|
|
87
|
+
height: var(--_playlist-item-image-size);
|
|
79
88
|
&:first-child {
|
|
80
89
|
margin-left: 0;
|
|
81
90
|
}
|
|
@@ -85,25 +94,20 @@
|
|
|
85
94
|
}
|
|
86
95
|
}
|
|
87
96
|
|
|
88
|
-
.
|
|
97
|
+
.player-playlist__item-image {
|
|
89
98
|
width: var(--_playlist-item-image-size);
|
|
90
99
|
height: var(--_playlist-item-image-size);
|
|
91
100
|
flex-shrink: 0;
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
.
|
|
95
|
-
display: flex;
|
|
96
|
-
align-items: center;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
.myth-playlist__item-playing-icon {
|
|
103
|
+
.player-playlist__item-playing-icon {
|
|
100
104
|
width: #{measures.$baseline * 2};
|
|
101
105
|
height: #{measures.$baseline * 2};
|
|
102
106
|
display: flex;
|
|
103
107
|
align-items: center;
|
|
104
108
|
}
|
|
105
109
|
|
|
106
|
-
.
|
|
110
|
+
.player-playlist__item-playing-icon-inner {
|
|
107
111
|
width: 100%;
|
|
108
112
|
aspect-ratio: 1 / 1;
|
|
109
113
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
@mixin style() {
|
|
5
5
|
$height-xs: measures.$baseline * 8;
|
|
6
6
|
|
|
7
|
-
.
|
|
7
|
+
.track-info {
|
|
8
8
|
--track-info--flex-direction: row;
|
|
9
9
|
--track-info--image-width: #{measures.$baseline * 8};
|
|
10
10
|
--track-info--image-separation-horizontal: #{measures.$baseline * 2};
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
--track-info--height: #{$height-xs};
|
|
13
13
|
}
|
|
14
14
|
@container player (width >= #{variables.$player-widht-m}) {
|
|
15
|
-
.
|
|
15
|
+
.track-info {
|
|
16
16
|
--track-info--flex-direction: column;
|
|
17
17
|
--track-info--image-width: 100%;
|
|
18
18
|
--track-info--image-separation-horizontal: 0;
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
.
|
|
25
|
+
.track-info__inner {
|
|
26
26
|
display: flex;
|
|
27
27
|
flex-direction: var(--track-info--flex-direction);
|
|
28
28
|
height: var(--track-info--height);
|
|
29
|
-
.
|
|
29
|
+
.track-text__item {
|
|
30
30
|
font-size: #{measures.$baseline * 2};
|
|
31
31
|
line-height: 150%;
|
|
32
32
|
&.size-small {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
.
|
|
38
|
+
.track-info__image {
|
|
39
39
|
width: var(--track-info--image-width);
|
|
40
40
|
flex-shrink: 0;
|
|
41
41
|
display: flex;
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
.
|
|
50
|
+
.track-info__data {
|
|
51
51
|
display: flex;
|
|
52
52
|
align-items: center;
|
|
53
53
|
}
|
package/_style/player.style.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
import { css } from 'lit';
|
|
3
3
|
|
|
4
|
-
export default css`.skeleton-loader{--pulse-bg-start: blue;--pulse-bg-end: red}@keyframes colorPulse{from{background-color:var(--pulse-bg-start)}to{background-color:var(--pulse-bg-end)}}.skeleton-loader{background-color:var(--pulse-bg-start);animation:colorPulse 3.2s ease infinite alternate-reverse;opacity:.3}:host{display:block;color-scheme:inherit;--_accent-0: var(--outbook-player--color-accent-0, oklch(100% 0 0deg));--_accent-50: var(--outbook-player--color-accent-50, oklch(98.5% 0 0deg));--_accent-100: var(--outbook-player--color-accent-100, oklch(97% 0 0deg));--_accent-200: var(--outbook-player--color-accent-200, oklch(92.2% 0 0deg));--_accent-300: var(--outbook-player--color-accent-300, oklch(87% 0 0deg));--_accent-400: var(--outbook-player--color-accent-400, oklch(70.8% 0 0deg));--_accent-500: var(--outbook-player--color-accent-500, oklch(55.6% 0 0deg));--_accent-600: var(--outbook-player--color-accent-600, oklch(43.9% 0 0deg));--_accent-700: var(--outbook-player--color-accent-700, oklch(37.1% 0 0deg));--_accent-800: var(--outbook-player--color-accent-800, oklch(26.9% 0 0deg));--_accent-900: var(--outbook-player--color-accent-900, oklch(20.5% 0 0deg));--_accent-950: var(--outbook-player--color-accent-950, oklch(14.5% 0 0deg));--_accent-1000: var(--outbook-player--color-accent-1000, oklch(0% 0 0deg));--pulse-bg-start: light-dark( var(--_accent-100), var(--_accent-600) );--pulse-bg-end: light-dark( var(--_accent-600), var(--_accent-500) );--outbook-badge-file-extension--border-color: light-dark( oklch(37.1% 0 0deg), oklch(87% 0 0deg) );--outbook-badge-file-extension--text-color: light-dark( oklch(14.5% 0 0deg), oklch(100% 0 0deg) );--outbook-scrollable--indicator-color: light-dark( var(--_accent-900), var(--_accent-200) );--outbook-player-timeline--background: light-dark( var(--_accent-200), var(--_accent-800) );--outbook-player-timeline--background-completed: light-dark( var(--_accent-800), var(--_accent-200) );--outbook-player-timeline--chapter--mark-color: light-dark( oklch(14.5% 0 0deg), oklch(98.5% 0 0deg) );--outbook-player-timeline--chapter-played-mark-color: light-dark( oklch(98.5% 0 0deg), oklch(14.5% 0 0deg) );--_text-color: light-dark(oklch(20.5% 0 0deg), oklch(100% 0 0deg));--_background-color: light-dark(oklch(98.5% 0 0deg), oklch(20.5% 0 0deg));--_separator-color: light-dark(oklch(92.2% 0 0deg), oklch(43.9% 0 0deg));--_link-color: light-dark(var(--_accent-700), var(--_accent-200));--_link-color-hover: light-dark(var(--_accent-500), var(--_accent-50))}*{box-sizing:border-box;padding:0;margin:0}ul,ol,li{list-style:none}a,button,[role=button],[role=link]{cursor:pointer}a[disabled],button[disabled],[role=button][disabled],[role=link][disabled]{cursor:default}.
|
|
4
|
+
export default css`.skeleton-loader{--pulse-bg-start: blue;--pulse-bg-end: red}@keyframes colorPulse{from{background-color:var(--pulse-bg-start)}to{background-color:var(--pulse-bg-end)}}.skeleton-loader{background-color:var(--pulse-bg-start);animation:colorPulse 3.2s ease infinite alternate-reverse;opacity:.3}:host{display:block;color-scheme:inherit;--_accent-0: var(--outbook-player--color-accent-0, oklch(100% 0 0deg));--_accent-50: var(--outbook-player--color-accent-50, oklch(98.5% 0 0deg));--_accent-100: var(--outbook-player--color-accent-100, oklch(97% 0 0deg));--_accent-200: var(--outbook-player--color-accent-200, oklch(92.2% 0 0deg));--_accent-300: var(--outbook-player--color-accent-300, oklch(87% 0 0deg));--_accent-400: var(--outbook-player--color-accent-400, oklch(70.8% 0 0deg));--_accent-500: var(--outbook-player--color-accent-500, oklch(55.6% 0 0deg));--_accent-600: var(--outbook-player--color-accent-600, oklch(43.9% 0 0deg));--_accent-700: var(--outbook-player--color-accent-700, oklch(37.1% 0 0deg));--_accent-800: var(--outbook-player--color-accent-800, oklch(26.9% 0 0deg));--_accent-900: var(--outbook-player--color-accent-900, oklch(20.5% 0 0deg));--_accent-950: var(--outbook-player--color-accent-950, oklch(14.5% 0 0deg));--_accent-1000: var(--outbook-player--color-accent-1000, oklch(0% 0 0deg));--pulse-bg-start: light-dark( var(--_accent-100), var(--_accent-600) );--pulse-bg-end: light-dark( var(--_accent-600), var(--_accent-500) );--outbook-badge-file-extension--border-color: light-dark( oklch(37.1% 0 0deg), oklch(87% 0 0deg) );--outbook-badge-file-extension--text-color: light-dark( oklch(14.5% 0 0deg), oklch(100% 0 0deg) );--outbook-scrollable--indicator-color: light-dark( var(--_accent-900), var(--_accent-200) );--outbook-player-timeline--background: light-dark( var(--_accent-200), var(--_accent-800) );--outbook-player-timeline--background-completed: light-dark( var(--_accent-800), var(--_accent-200) );--outbook-player-timeline--chapter--mark-color: light-dark( oklch(14.5% 0 0deg), oklch(98.5% 0 0deg) );--outbook-player-timeline--chapter-played-mark-color: light-dark( oklch(98.5% 0 0deg), oklch(14.5% 0 0deg) );--_text-color: light-dark(oklch(20.5% 0 0deg), oklch(100% 0 0deg));--_background-color: light-dark(oklch(98.5% 0 0deg), oklch(20.5% 0 0deg));--_separator-color: light-dark(oklch(92.2% 0 0deg), oklch(43.9% 0 0deg));--_link-color: light-dark(var(--_accent-700), var(--_accent-200));--_link-color-hover: light-dark(var(--_accent-500), var(--_accent-50))}*{box-sizing:border-box;padding:0;margin:0}ul,ol,li{list-style:none}a,button,[role=button],[role=link]{cursor:pointer}a[disabled],button[disabled],[role=button][disabled],[role=link][disabled]{cursor:default}.player{container-name:player;container-type:inline-size;height:var(--_main-height, 32rem);background-color:var(--_background-color);position:relative}.player::after,.player::before{content:"";height:100%;width:100%;position:absolute;left:0;top:0}.player.has-gradient::after{opacity:.65;background-color:var(--_background-color)}.player::before{opacity:.45;background:linear-gradient(145deg, var(--_accent-400) 0%, var(--_accent-500) 25%, var(--_accent-600) 50%, var(--_accent-700) 75%, var(--_accent-800) 100%)}.player.has-gradient::before{opacity:1;background:linear-gradient(145deg, var(--player--main-color-0) 0%, var(--player--main-color-1) 25%, var(--player--main-color-2) 50%, var(--player--main-color-3) 75%, var(--player--main-color-4) 100%)}@container player (width < 768px){.player__inner{--player--flex-direction: column;--_secondary-block-width: unset;--_secondary-block-min-width: unset}}@container player (width >= 768px){.player__inner{--player--flex-direction: row;--_secondary-block-width: 25%;--_secondary-block-min-width: 20rem}}.player__inner{display:flex;flex-direction:column;height:100%;color:var(--_text-color);position:relative;z-index:2}.player__body{display:flex;flex-direction:var(--player--flex-direction);flex:1 0 0;overflow:hidden}.player__playlist{overflow:hidden;padding:0 0 0 0.5rem;display:flex;flex:1 0 0}.player__track-info{width:var(--_secondary-block-width);min-width:var(--_secondary-block-min-width);padding:0.5rem}.player__controls{display:flex;justify-content:center;margin-top:-1rem}.player__controls,.player__timeline{position:relative}.player__timeline{padding:0 0.5rem}.player-controls{--_controls-color: light-dark( var(--_accent-950), var(--_accent-100) );--_controls-size: 3rem}@container player (width >= 768px){.player-controls{--_controls-size: 3.5rem}}@container player (width >= 1024px){.player-controls{--_controls-size: 4rem}}.player-controls,.player-controls__items{width:auto}.player-controls__items{display:flex}.player-controls__button{background:rgba(0,0,0,0);border:0;color:var(--_controls-color)}.player-controls__button:focus-visible{outline:var(--outbook-outline--color, light-dark(oklch(48.8% 0.243 264.376deg), oklch(88.2% 0.059 254.128deg))) solid 2px;outline-offset:-4px}.player-controls__button.is-disabled{opacity:.4}.player-controls__icon{width:var(--_controls-size);height:var(--_controls-size)}.player-playlist{--_playlist-font-weight: normal;--_playlist-font-size: 0.75rem;--_playlist-font-size-small: 0.625rem;--_playlist-item-background-color: transparent;--_playlist-item-image-size: 3rem}.player-playlist .is-playing{--_playlist-font-weight: 600;--_playlist-item-background-color: var(--_accent-200)}.ambient-dark .player-playlist .is-playing{--_playlist-item-background-color: var(--_accent-700)}@container player (width >= 768px){.player-playlist{--_playlist-font-size: 0.875rem;--_playlist-font-size-small: 0.75rem}}@container player (width >= 1024px){.player-playlist{--_playlist-font-size: 1rem;--_playlist-font-size-small: 0.875rem;--_playlist-item-image-size: 4rem}}.player-playlist .track-text__item{font-weight:var(--_playlist-font-weight);font-size:var(--_playlist-font-size);line-height:150%}.player-playlist .track-text__item.size-small{font-size:var(--_playlist-font-size-small)}.player-playlist__items{padding:0;position:relative}.player-playlist__item{position:relative;border-bottom:1px solid var(--_separator-color)}.player-playlist__item.is-playing:before{content:"";position:absolute;top:0;left:0;width:100%;height:100%;opacity:.45}@keyframes colorPulse{from{background-color:var(--pulse-bg-start)}to{background-color:var(--pulse-bg-end)}}.player-playlist__item.is-playing:before{background-color:var(--pulse-bg-start);animation:colorPulse 3.2s ease infinite alternate-reverse;opacity:.3}.player-playlist__item:last-child{border-bottom:0}.player-playlist__item-button{display:flex;padding:0.5rem;position:relative;align-items:center}.player-playlist__item-button:focus-visible{outline:var(--outbook-outline--color, light-dark(oklch(48.8% 0.243 264.376deg), oklch(88.2% 0.059 254.128deg))) solid 2px;outline-offset:-4px}.player-playlist__item-group,.player-playlist__item-block{display:flex;align-items:center;margin-left:1rem;height:var(--_playlist-item-image-size)}.player-playlist__item-group:first-child,.player-playlist__item-block:first-child{margin-left:0}.player-playlist__item-group.is-last-right,.player-playlist__item-block.is-last-right{margin-left:auto;padding-left:0.5rem}.player-playlist__item-image{width:var(--_playlist-item-image-size);height:var(--_playlist-item-image-size);flex-shrink:0}.player-playlist__item-playing-icon{width:1rem;height:1rem;display:flex;align-items:center}.player-playlist__item-playing-icon-inner{width:100%;aspect-ratio:1/1}.track-info{--track-info--flex-direction: row;--track-info--image-width: 4rem;--track-info--image-separation-horizontal: 1rem;--track-info--image-separation-vertical: 0;--track-info--height: 4rem}@container player (width >= 768px){.track-info{--track-info--flex-direction: column;--track-info--image-width: 100%;--track-info--image-separation-horizontal: 0;--track-info--image-separation-vertical: 1rem;--track-info--height: auto}}.track-info__inner{display:flex;flex-direction:var(--track-info--flex-direction);height:var(--track-info--height)}.track-info__inner .track-text__item{font-size:1rem;line-height:150%}.track-info__inner .track-text__item.size-small{font-size:0.75rem}.track-info__image{width:var(--track-info--image-width);flex-shrink:0;display:flex;align-items:center;margin-right:var(--track-info--image-separation-horizontal);margin-bottom:var(--track-info--image-separation-vertical)}.track-info__image>*{width:100%}.track-info__data{display:flex;align-items:center}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outbook/webcomponents-player",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"main": "player.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -33,8 +33,9 @@
|
|
|
33
33
|
"mystic-format-time": "1.0.2",
|
|
34
34
|
"@outbook/webcomponents-scrollable": ">=1.0.0",
|
|
35
35
|
"@outbook/webcomponents-type-icon": ">=1.1.2",
|
|
36
|
-
"@outbook/webcomponents-badge-file-extension": ">=1.2.
|
|
36
|
+
"@outbook/webcomponents-badge-file-extension": ">=1.2.1",
|
|
37
37
|
"@outbook/webcomponents-player-artwork": ">=1.0.0",
|
|
38
|
+
"@outbook/webcomponents-player-timeline": ">=1.0.1",
|
|
38
39
|
"@outbook/icons": ">=1.3.0"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
package/player.js
CHANGED
|
@@ -233,17 +233,16 @@ function ComponentPlayer(element) {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
const mainClasses = classMap({
|
|
236
|
-
|
|
237
|
-
'root-container': true,
|
|
236
|
+
player: true,
|
|
238
237
|
'has-gradient': hasProminentColors
|
|
239
238
|
});
|
|
240
239
|
|
|
241
240
|
return literals
|
|
242
241
|
? html`
|
|
243
242
|
<div class="${mainClasses}" style="${prominentColors}">
|
|
244
|
-
<div class="
|
|
245
|
-
<div class="
|
|
246
|
-
<div class="
|
|
243
|
+
<div class="player__inner">
|
|
244
|
+
<div class="player__body">
|
|
245
|
+
<div class="player__playlist">
|
|
247
246
|
${Scrollable({
|
|
248
247
|
extraClasses: 'scrollable--default',
|
|
249
248
|
eventScrollToPlayingItem: true,
|
|
@@ -260,7 +259,7 @@ function ComponentPlayer(element) {
|
|
|
260
259
|
}
|
|
261
260
|
})}
|
|
262
261
|
</div>
|
|
263
|
-
<div class="
|
|
262
|
+
<div class="player__track-info">
|
|
264
263
|
${TrackInfo({
|
|
265
264
|
indexSelected,
|
|
266
265
|
track: playlist[indexSelected],
|
|
@@ -268,16 +267,13 @@ function ComponentPlayer(element) {
|
|
|
268
267
|
})}
|
|
269
268
|
</div>
|
|
270
269
|
</div>
|
|
271
|
-
<div
|
|
272
|
-
class="mythical-player__timeline"
|
|
273
|
-
@reposition="${handleReposition}"
|
|
274
|
-
>
|
|
270
|
+
<div class="player__timeline" @reposition="${handleReposition}">
|
|
275
271
|
${PlayerTimeline({
|
|
276
272
|
duration: totalTime,
|
|
277
273
|
currentTime
|
|
278
274
|
})}
|
|
279
275
|
</div>
|
|
280
|
-
<div class="
|
|
276
|
+
<div class="player__controls">
|
|
281
277
|
${Controls({
|
|
282
278
|
props: { isPlaying, playlist, indexSelected },
|
|
283
279
|
handleAudio,
|
package/_lib/hooks.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'haunted';
|
|
2
|
-
|
|
3
|
-
export function useDocumentEvent(eventName, handler, condition) {
|
|
4
|
-
useEffect(() => {
|
|
5
|
-
if (condition) {
|
|
6
|
-
document.addEventListener(eventName, handler);
|
|
7
|
-
return () => {
|
|
8
|
-
document.removeEventListener(eventName, handler);
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
}, [eventName, handler, condition]);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function useWindowEvent(eventName, handler, condition = true) {
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
if (condition) {
|
|
17
|
-
window.addEventListener(eventName, handler);
|
|
18
|
-
return () => {
|
|
19
|
-
window.removeEventListener(eventName, handler);
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
}, [eventName, handler, condition]);
|
|
23
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isEventClick,
|
|
3
|
-
isKeyArrowLeft,
|
|
4
|
-
isKeyArrowRight
|
|
5
|
-
} from 'a11y-key-conjurer';
|
|
6
|
-
|
|
7
|
-
const SKIP_TIME = 10;
|
|
8
|
-
|
|
9
|
-
function handleClick(ev, getMediaElement) {
|
|
10
|
-
const audioElement = getMediaElement();
|
|
11
|
-
const target = ev.currentTarget;
|
|
12
|
-
const rect = target.getBoundingClientRect();
|
|
13
|
-
const x = ev.clientX - rect.left;
|
|
14
|
-
const percent = (100 * x) / target.offsetWidth;
|
|
15
|
-
audioElement.currentTime = (audioElement.duration / 100) * percent;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function handleArrows({ multiplier }, getMediaElement) {
|
|
19
|
-
const audioElement = getMediaElement();
|
|
20
|
-
const newTime = audioElement.currentTime + SKIP_TIME * multiplier;
|
|
21
|
-
const duration = audioElement.duration;
|
|
22
|
-
audioElement.currentTime =
|
|
23
|
-
newTime > 0 ? (newTime < duration ? newTime : duration) : 0;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function handleArrowLeft(ev, getMediaElement) {
|
|
27
|
-
handleArrows({ ev, multiplier: -1 }, getMediaElement);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function handleArrowRight(ev, getMediaElement) {
|
|
31
|
-
handleArrows({ ev, multiplier: 1 }, getMediaElement);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function handleTimeline(ev, getMediaElement) {
|
|
35
|
-
if (isEventClick(ev)) {
|
|
36
|
-
handleClick(ev, getMediaElement);
|
|
37
|
-
} else if (isKeyArrowLeft(ev)) {
|
|
38
|
-
handleArrowLeft(ev, getMediaElement);
|
|
39
|
-
} else if (isKeyArrowRight(ev)) {
|
|
40
|
-
handleArrowRight(ev, getMediaElement);
|
|
41
|
-
}
|
|
42
|
-
}
|
package/_lib/timeline/index.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { html, nothing } from 'lit';
|
|
2
|
-
import { component, useState } from 'haunted';
|
|
3
|
-
import { classMap } from 'lit/directives/class-map.js';
|
|
4
|
-
import { handleTimeline } from './_lib/handle-timeline.js';
|
|
5
|
-
import { readable as formatTime } from 'mystic-format-time';
|
|
6
|
-
|
|
7
|
-
function getChapter({ targetTime, chapters }) {
|
|
8
|
-
const selected = chapters.find(
|
|
9
|
-
chapter =>
|
|
10
|
-
targetTime >= chapter.start / 1000 && targetTime < chapter.end / 1000
|
|
11
|
-
);
|
|
12
|
-
return selected?.tags?.title || null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function TimelineComponent(element) {
|
|
16
|
-
const { props } = element;
|
|
17
|
-
const {
|
|
18
|
-
totalTime,
|
|
19
|
-
currentTime = 0, // keep currentTime here for the INITIAL render, but we won't rely on it for updates
|
|
20
|
-
currentItem = {},
|
|
21
|
-
getMediaElement,
|
|
22
|
-
id
|
|
23
|
-
} = props;
|
|
24
|
-
|
|
25
|
-
const [timePosition, setTimePosition] = useState(null);
|
|
26
|
-
const chapters = currentItem?.metadata?.chapters || [];
|
|
27
|
-
|
|
28
|
-
function handleTimelineIn(ev) {
|
|
29
|
-
const target = ev.currentTarget;
|
|
30
|
-
const leftPosition = ev.layerX;
|
|
31
|
-
const targetWidth = target.offsetWidth;
|
|
32
|
-
const targetTime = (leftPosition * totalTime) / targetWidth;
|
|
33
|
-
setTimePosition({
|
|
34
|
-
visual: formatTime(targetTime),
|
|
35
|
-
chapter:
|
|
36
|
-
chapters.length > 0 ? getChapter({ targetTime, chapters }) : null,
|
|
37
|
-
left: leftPosition
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function handleTimelineOut() {
|
|
42
|
-
setTimePosition(null);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function fnHandleTimeline(ev) {
|
|
46
|
-
handleTimeline(ev, getMediaElement);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return html`
|
|
50
|
-
<div class="timeline">
|
|
51
|
-
<div
|
|
52
|
-
class="timeline__track"
|
|
53
|
-
@click="${fnHandleTimeline}"
|
|
54
|
-
@keydown="${fnHandleTimeline}"
|
|
55
|
-
@mousemove="${handleTimelineIn}"
|
|
56
|
-
@mouseout="${handleTimelineOut}"
|
|
57
|
-
role="progressbar"
|
|
58
|
-
tabindex="0"
|
|
59
|
-
>
|
|
60
|
-
<div
|
|
61
|
-
id="bar-${id}"
|
|
62
|
-
class="timeline__completed"
|
|
63
|
-
style="width: 0;"
|
|
64
|
-
></div>
|
|
65
|
-
</div>
|
|
66
|
-
<div class="timeline__numbers">
|
|
67
|
-
<div id="txt-${id}">00:00</div>
|
|
68
|
-
<div>${formatTime(totalTime)}</div>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
${chapters.map(chapter => {
|
|
72
|
-
const limitClasses = {
|
|
73
|
-
'timeline__chapter-limit': true,
|
|
74
|
-
'timeline__chapter-limit--played': currentTime > chapter.start / 1000
|
|
75
|
-
};
|
|
76
|
-
const position = (100 / totalTime) * (chapter.start / 1000);
|
|
77
|
-
return html`
|
|
78
|
-
<div
|
|
79
|
-
class="${classMap(limitClasses)}"
|
|
80
|
-
style="--timeline--chapter-limit-position: ${position}%;"
|
|
81
|
-
></div>
|
|
82
|
-
`;
|
|
83
|
-
})}
|
|
84
|
-
${timePosition === null
|
|
85
|
-
? nothing
|
|
86
|
-
: html`
|
|
87
|
-
<div
|
|
88
|
-
class="timeline__tooltip"
|
|
89
|
-
style="--timeline-tooltip-position: ${timePosition.left}px;"
|
|
90
|
-
>
|
|
91
|
-
<div class="timeline__tooltip-inner">
|
|
92
|
-
${timePosition.chapter
|
|
93
|
-
? html`<span class="timeline__tooltip-chapter-title"
|
|
94
|
-
>${timePosition.chapter}</span
|
|
95
|
-
>`
|
|
96
|
-
: nothing}
|
|
97
|
-
<span class="timeline__tooltip-time"
|
|
98
|
-
>${timePosition.visual}</span
|
|
99
|
-
>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
`}
|
|
103
|
-
</div>
|
|
104
|
-
`;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (!customElements.get('mythical-player-timeline')) {
|
|
108
|
-
customElements.define(
|
|
109
|
-
'mythical-player-timeline',
|
|
110
|
-
component(TimelineComponent, {
|
|
111
|
-
observedAttributes: [],
|
|
112
|
-
useShadowDOM: false
|
|
113
|
-
})
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function Timeline(props) {
|
|
118
|
-
return html`<mythical-player-timeline
|
|
119
|
-
.props="${props}"
|
|
120
|
-
></mythical-player-timeline>`;
|
|
121
|
-
}
|