@outbook/webcomponents-player 1.3.0
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/LICENSE +202 -0
- package/README.md +126 -0
- package/_i18n/af.json +12 -0
- package/_i18n/ar.json +12 -0
- package/_i18n/bg.json +12 -0
- package/_i18n/bn.json +12 -0
- package/_i18n/ca.json +12 -0
- package/_i18n/cs.json +12 -0
- package/_i18n/da.json +12 -0
- package/_i18n/de.json +12 -0
- package/_i18n/el.json +12 -0
- package/_i18n/en.json +12 -0
- package/_i18n/es.json +12 -0
- package/_i18n/eu.json +12 -0
- package/_i18n/fa.json +12 -0
- package/_i18n/fi.json +12 -0
- package/_i18n/fr.json +12 -0
- package/_i18n/gl.json +12 -0
- package/_i18n/ha.json +12 -0
- package/_i18n/hi.json +12 -0
- package/_i18n/hu.json +12 -0
- package/_i18n/i18n.js +95 -0
- package/_i18n/id.json +12 -0
- package/_i18n/it.json +12 -0
- package/_i18n/ja.json +12 -0
- package/_i18n/km.json +12 -0
- package/_i18n/ko.json +12 -0
- package/_i18n/lo.json +12 -0
- package/_i18n/ms.json +12 -0
- package/_i18n/nl.json +12 -0
- package/_i18n/no.json +12 -0
- package/_i18n/pl.json +12 -0
- package/_i18n/pt.json +12 -0
- package/_i18n/ro.json +12 -0
- package/_i18n/ru.json +12 -0
- package/_i18n/sk.json +12 -0
- package/_i18n/sv.json +12 -0
- package/_i18n/sw.json +12 -0
- package/_i18n/th.json +12 -0
- package/_i18n/tl.json +12 -0
- package/_i18n/tr.json +12 -0
- package/_i18n/uk.json +12 -0
- package/_i18n/vi.json +12 -0
- package/_i18n/zh.json +12 -0
- package/_i18n/zu.json +12 -0
- package/_lib/background.js +28 -0
- package/_lib/controls/index.js +91 -0
- package/_lib/cover-fallback/index.js +48 -0
- package/_lib/get-cover.js +4 -0
- package/_lib/hooks.js +23 -0
- package/_lib/order-by-luminance.js +9 -0
- package/_lib/playlist/index.js +105 -0
- package/_lib/playlist/modules/item-duration.js +17 -0
- package/_lib/playlist/modules/show-cover.js +14 -0
- package/_lib/rgb-to-hex.js +13 -0
- package/_lib/setup-media-session.js +29 -0
- package/_lib/timeline/_lib/handle-timeline.js +42 -0
- package/_lib/timeline/index.js +121 -0
- package/_lib/track-info/index.js +36 -0
- package/_lib/track-text/index.js +20 -0
- package/_style/_lib/_variables.scss +5 -0
- package/_style/_modules/_controls.scss +50 -0
- package/_style/_modules/_cover-fallback.scss +52 -0
- package/_style/_modules/_playlist.scss +119 -0
- package/_style/_modules/_timeline.scss +125 -0
- package/_style/_modules/_track-info.scss +54 -0
- package/_style/player.style.js +4 -0
- package/package.json +48 -0
- package/player.js +324 -0
package/_i18n/i18n.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import defaultLiterals from './en.json';
|
|
2
|
+
import af from './af.json';
|
|
3
|
+
import ar from './ar.json';
|
|
4
|
+
import bg from './bg.json';
|
|
5
|
+
import ca from './ca.json';
|
|
6
|
+
import cs from './cs.json';
|
|
7
|
+
import da from './da.json';
|
|
8
|
+
import de from './de.json';
|
|
9
|
+
import el from './el.json';
|
|
10
|
+
import es from './es.json';
|
|
11
|
+
import eu from './eu.json';
|
|
12
|
+
import fa from './fa.json';
|
|
13
|
+
import fi from './fi.json';
|
|
14
|
+
import fr from './fr.json';
|
|
15
|
+
import gl from './gl.json';
|
|
16
|
+
import ha from './ha.json';
|
|
17
|
+
import hi from './hi.json';
|
|
18
|
+
import hu from './hu.json';
|
|
19
|
+
import id from './id.json';
|
|
20
|
+
import it from './it.json';
|
|
21
|
+
import ja from './ja.json';
|
|
22
|
+
import km from './km.json';
|
|
23
|
+
import ko from './ko.json';
|
|
24
|
+
import lo from './lo.json';
|
|
25
|
+
import ms from './ms.json';
|
|
26
|
+
import nl from './nl.json';
|
|
27
|
+
import no from './no.json';
|
|
28
|
+
import pl from './pl.json';
|
|
29
|
+
import pt from './pt.json';
|
|
30
|
+
import ro from './ro.json';
|
|
31
|
+
import ru from './ru.json';
|
|
32
|
+
import sk from './sk.json';
|
|
33
|
+
import sv from './sv.json';
|
|
34
|
+
import sw from './sw.json';
|
|
35
|
+
import th from './th.json';
|
|
36
|
+
import tl from './tl.json';
|
|
37
|
+
import tr from './tr.json';
|
|
38
|
+
import uk from './uk.json';
|
|
39
|
+
import vi from './vi.json';
|
|
40
|
+
import zh from './zh.json';
|
|
41
|
+
import zu from './zu.json';
|
|
42
|
+
|
|
43
|
+
const literals = {
|
|
44
|
+
af,
|
|
45
|
+
ar,
|
|
46
|
+
bg,
|
|
47
|
+
ca,
|
|
48
|
+
cs,
|
|
49
|
+
da,
|
|
50
|
+
de,
|
|
51
|
+
el,
|
|
52
|
+
en: defaultLiterals,
|
|
53
|
+
es,
|
|
54
|
+
eu,
|
|
55
|
+
fa,
|
|
56
|
+
fi,
|
|
57
|
+
fr,
|
|
58
|
+
gl,
|
|
59
|
+
ha,
|
|
60
|
+
hi,
|
|
61
|
+
hu,
|
|
62
|
+
id,
|
|
63
|
+
it,
|
|
64
|
+
ja,
|
|
65
|
+
km,
|
|
66
|
+
ko,
|
|
67
|
+
lo,
|
|
68
|
+
ms,
|
|
69
|
+
nl,
|
|
70
|
+
no,
|
|
71
|
+
pl,
|
|
72
|
+
pt,
|
|
73
|
+
ro,
|
|
74
|
+
ru,
|
|
75
|
+
sk,
|
|
76
|
+
sv,
|
|
77
|
+
sw,
|
|
78
|
+
th,
|
|
79
|
+
tl,
|
|
80
|
+
tr,
|
|
81
|
+
uk,
|
|
82
|
+
vi,
|
|
83
|
+
zh,
|
|
84
|
+
zu
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {string} lang - ISO language code (e.g. "fr", "de").
|
|
89
|
+
* @returns {Promise<Object>} Resolved with the language object.
|
|
90
|
+
*/
|
|
91
|
+
export async function loadLiterals(lang) {
|
|
92
|
+
const key = String(lang).trim().toLowerCase();
|
|
93
|
+
const loadedLiterals = literals[key] || defaultLiterals;
|
|
94
|
+
return Promise.resolve(loadedLiterals);
|
|
95
|
+
}
|
package/_i18n/id.json
ADDED
package/_i18n/it.json
ADDED
package/_i18n/ja.json
ADDED
package/_i18n/km.json
ADDED
package/_i18n/ko.json
ADDED
package/_i18n/lo.json
ADDED
package/_i18n/ms.json
ADDED
package/_i18n/nl.json
ADDED
package/_i18n/no.json
ADDED
package/_i18n/pl.json
ADDED
package/_i18n/pt.json
ADDED
package/_i18n/ro.json
ADDED
package/_i18n/ru.json
ADDED
package/_i18n/sk.json
ADDED
package/_i18n/sv.json
ADDED
package/_i18n/sw.json
ADDED
package/_i18n/th.json
ADDED
package/_i18n/tl.json
ADDED
package/_i18n/tr.json
ADDED
package/_i18n/uk.json
ADDED
package/_i18n/vi.json
ADDED
package/_i18n/zh.json
ADDED
package/_i18n/zu.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { prominent } from 'color.js';
|
|
2
|
+
import { rgbToHex } from './rgb-to-hex.js';
|
|
3
|
+
import { orderByLuminance } from './order-by-luminance.js';
|
|
4
|
+
|
|
5
|
+
export const emptyBackground = '';
|
|
6
|
+
|
|
7
|
+
export function getBackgroundColor(coverUrl) {
|
|
8
|
+
return prominent(coverUrl, {
|
|
9
|
+
amount: 5,
|
|
10
|
+
format: 'rgb',
|
|
11
|
+
group: 40,
|
|
12
|
+
sample: 10
|
|
13
|
+
}).then(colors => {
|
|
14
|
+
const colorPropsRgb = colors.map(item => ({
|
|
15
|
+
r: item[0],
|
|
16
|
+
g: item[1],
|
|
17
|
+
b: item[2]
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const orderedColorsRgb = orderByLuminance(colorPropsRgb);
|
|
21
|
+
|
|
22
|
+
return orderedColorsRgb
|
|
23
|
+
.map((item, index) => {
|
|
24
|
+
return `--player--main-color-${index}: ${rgbToHex(item)};`;
|
|
25
|
+
})
|
|
26
|
+
.join(''); // Return the string!
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { virtual } from 'haunted';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { TypeIcon } from '@outbook/webcomponents-type-icon/shadow';
|
|
4
|
+
import { skip_next, skip_previous, play_arrow, pause, stop } from '@outbook/icons';
|
|
5
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
6
|
+
import { isEventClick, isKeyEnterOrKeySpace } from 'a11y-key-conjurer';
|
|
7
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
8
|
+
|
|
9
|
+
export const Controls = virtual(({ props, handleAudio, literals = {} }) => {
|
|
10
|
+
const { isPlaying = false, playlist, indexSelected } = props;
|
|
11
|
+
const controlsDisabled = !Number.isInteger(indexSelected);
|
|
12
|
+
const controlItems = [
|
|
13
|
+
{
|
|
14
|
+
label: literals.previousTrack,
|
|
15
|
+
icon: 'skip_previous',
|
|
16
|
+
isDisabled: controlsDisabled || indexSelected === 0,
|
|
17
|
+
action: handleAudio.previous,
|
|
18
|
+
id: 'previous'
|
|
19
|
+
},
|
|
20
|
+
isPlaying && !controlsDisabled
|
|
21
|
+
? {
|
|
22
|
+
label: literals.pause,
|
|
23
|
+
icon: 'pause',
|
|
24
|
+
action: handleAudio.pause,
|
|
25
|
+
isDisabled: false,
|
|
26
|
+
id: 'pause'
|
|
27
|
+
}
|
|
28
|
+
: {
|
|
29
|
+
label: literals.play,
|
|
30
|
+
icon: 'play_arrow',
|
|
31
|
+
action: handleAudio.play,
|
|
32
|
+
isDisabled: controlsDisabled,
|
|
33
|
+
id: 'play'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: literals.stop,
|
|
37
|
+
icon: 'stop',
|
|
38
|
+
isDisabled: controlsDisabled,
|
|
39
|
+
action: handleAudio.stop,
|
|
40
|
+
id: 'stop'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: literals.nextTrack,
|
|
44
|
+
icon: 'skip_next',
|
|
45
|
+
isDisabled: controlsDisabled || playlist.length - 1 === indexSelected,
|
|
46
|
+
action: handleAudio.next,
|
|
47
|
+
id: 'next'
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
function handleControl(action) {
|
|
52
|
+
return ev => {
|
|
53
|
+
if (isEventClick(ev) || isKeyEnterOrKeySpace(ev)) {
|
|
54
|
+
action();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return html`
|
|
60
|
+
<div class="myth-controls">
|
|
61
|
+
<ul class="myth-controls__items">
|
|
62
|
+
${controlItems.map(item => {
|
|
63
|
+
const buttonClasses = classMap({
|
|
64
|
+
'myth-controls__button': true,
|
|
65
|
+
'is-disabled': item.isDisabled
|
|
66
|
+
});
|
|
67
|
+
return html`
|
|
68
|
+
<li class="myth-controls__item">
|
|
69
|
+
<button
|
|
70
|
+
class="${buttonClasses}"
|
|
71
|
+
aria-label="${item.label}"
|
|
72
|
+
disabled="${ifDefined(
|
|
73
|
+
item.isDisabled ? 'disabled' : undefined
|
|
74
|
+
)}"
|
|
75
|
+
data-test-id="player-button-${item.id}"
|
|
76
|
+
@click="${handleControl(item.action)}"
|
|
77
|
+
@keydown="${handleControl(item.action)}"
|
|
78
|
+
>
|
|
79
|
+
${TypeIcon({
|
|
80
|
+
icon: item.icon,
|
|
81
|
+
icons: { skip_next, skip_previous, play_arrow, pause, stop },
|
|
82
|
+
extraClasses: 'myth-controls__icon'
|
|
83
|
+
})}
|
|
84
|
+
</button>
|
|
85
|
+
</li>
|
|
86
|
+
`;
|
|
87
|
+
})}
|
|
88
|
+
</ul>
|
|
89
|
+
</div>
|
|
90
|
+
`;
|
|
91
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
3
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
4
|
+
import { TypeIcon } from '@outbook/webcomponents-type-icon/shadow';
|
|
5
|
+
import { audiotrack } from '@outbook/icons';
|
|
6
|
+
import { component } from 'haunted';
|
|
7
|
+
|
|
8
|
+
function CoverFallbackComponent(element) {
|
|
9
|
+
const { props, icon = 'audiotrack' } = element;
|
|
10
|
+
const { isInFileItem } = props;
|
|
11
|
+
const mainClasses = classMap({
|
|
12
|
+
'cover-fallback': true,
|
|
13
|
+
'cover-fallback--in-file-item': isInFileItem
|
|
14
|
+
});
|
|
15
|
+
return html`
|
|
16
|
+
<div class="${mainClasses}">
|
|
17
|
+
<div class="cover-fallback__icon">
|
|
18
|
+
${TypeIcon({
|
|
19
|
+
icon,
|
|
20
|
+
icons: { audiotrack },
|
|
21
|
+
extraClasses: 'cover-fallback__icon-inner'
|
|
22
|
+
})}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!customElements.get('mythical-cover-fallback')) {
|
|
29
|
+
customElements.define(
|
|
30
|
+
'mythical-cover-fallback',
|
|
31
|
+
component(CoverFallbackComponent, {
|
|
32
|
+
observedAttributes: ['icon'],
|
|
33
|
+
useShadowDOM: false
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function CoverFallback(props) {
|
|
39
|
+
const { extraClasses, icon = null } = props;
|
|
40
|
+
|
|
41
|
+
return html`
|
|
42
|
+
<mythical-cover-fallback
|
|
43
|
+
class="${ifDefined(extraClasses || undefined)}"
|
|
44
|
+
icon="${ifDefined(icon || undefined)}"
|
|
45
|
+
.props="${props}"
|
|
46
|
+
></mythical-cover-fallback>
|
|
47
|
+
`;
|
|
48
|
+
}
|
package/_lib/hooks.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// https://en.wikipedia.org/wiki/Luma_(video)
|
|
2
|
+
const calculateLuminance = p => {
|
|
3
|
+
return 0.2126 * p.r + 0.7152 * p.g + 0.0722 * p.b;
|
|
4
|
+
};
|
|
5
|
+
export const orderByLuminance = rgbValues => {
|
|
6
|
+
return rgbValues.sort((p1, p2) => {
|
|
7
|
+
return calculateLuminance(p2) - calculateLuminance(p1);
|
|
8
|
+
});
|
|
9
|
+
};
|