initial commit
|
@ -0,0 +1,53 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
'env': {
|
||||
'browser': true,
|
||||
'node': true,
|
||||
'es2019': true
|
||||
},
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
'plugin:lit/recommended',
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
'rules': {
|
||||
'no-var': 'error',
|
||||
'semi': [
|
||||
'error',
|
||||
'never',
|
||||
],
|
||||
'quotes': [
|
||||
'error',
|
||||
'single',
|
||||
],
|
||||
'object-curly-spacing': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'array-bracket-spacing': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
'space-in-parens': [
|
||||
'warn',
|
||||
'never',
|
||||
],
|
||||
'array-bracket-newline': [
|
||||
'warn',
|
||||
'consistent',
|
||||
],
|
||||
'object-curly-newline': [
|
||||
'warn',
|
||||
{
|
||||
'consistent': true,
|
||||
},
|
||||
],
|
||||
'space-before-blocks': [
|
||||
'warn',
|
||||
'always',
|
||||
],
|
||||
},
|
||||
};
|
|
@ -0,0 +1,125 @@
|
|||
# ---> Node
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# sqliteDb files
|
||||
**/db/*.db
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# MM.Media
|
||||
public/fonts/*.woff*
|
||||
public/images
|
||||
public/media
|
|
@ -0,0 +1,19 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta base="/" />
|
||||
<title>Meredith Monk</title>
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-title" content="Meredith Monk Listening Rooms" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
|
||||
<script type="module" src="/src/App.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<mm-app></mm-app>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "mm.spa",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lit-labs/task": "^3.0.2",
|
||||
"@thepassle/app-tools": "^0.9.8",
|
||||
"@web/rollup-plugin-polyfills-loader": "^2.0.1",
|
||||
"lit": "^2.7.6",
|
||||
"urlpattern-polyfill": "^9.0.0",
|
||||
"wavesurfer.js": "^7.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-plugin-lit": "^1.9.1",
|
||||
"sass": "^1.67.0",
|
||||
"vite": "^4.4.5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
[
|
||||
{
|
||||
"title": "Totally Wired with Kimberly Haas",
|
||||
"details": "1980s",
|
||||
"media": "01_Meredith Monk_Totally Wired with Kimberly Haas_1980s"
|
||||
},
|
||||
{
|
||||
"title": "Speaking of Music",
|
||||
"details": "1984, Other Minds radio program Part 1 of 2",
|
||||
"media": "02_Speaking of Music, Meredith Monk. 1984 Other Minds radio program_Part 1 of 2"
|
||||
},
|
||||
{
|
||||
"title": "Speaking of Music",
|
||||
"details": "1984, Other Minds radio program Part 2 of 2",
|
||||
"media": "03_Speaking of Music_ Meredith Monk, 1984. Other Minds radio program_Part 2 of 2"
|
||||
},
|
||||
{
|
||||
"title": "Interview with Irene Ferchl Bayn Rundfunk Vielkurjieunal",
|
||||
"details": "1986",
|
||||
"media": "04 Meredith Monk Interview with Irene Ferchl_Bayn Rundfunk Vielkurjieunal_1986"
|
||||
},
|
||||
{
|
||||
"title": "Terry Gross Interview",
|
||||
"details": "1988",
|
||||
"media": "05 Terry Gross interview_1987"
|
||||
},
|
||||
{
|
||||
"title": "WNYC Interview with David Garland",
|
||||
"details": "2008, Sacred Sundays",
|
||||
"media": "06 WNYC Interview w_ David Garland_SacredSundays_2008"
|
||||
},
|
||||
{
|
||||
"title": "Mark Frosty McNeil and Meredith Monk in Conversation",
|
||||
"details": "2019, Red Bull Radio, Fireside Chat",
|
||||
"media": "07_Red Bull Radio_Fireside Chat_Mark frosty McNeil and MM in conversation_4.11.19"
|
||||
},
|
||||
{
|
||||
"title": "Late Junction",
|
||||
"details": "2019, BBC",
|
||||
"media": "08_Late Junction_2019 - Meredith Monk v2 BBC"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
[
|
||||
{
|
||||
"title": "Meredith Monk: Inner Voice",
|
||||
"detail": "2009, 82:00",
|
||||
"media": "MeredithMonk_Inner Voice.mp4"
|
||||
},
|
||||
{
|
||||
"title": "Meredith Monk: A Documentary",
|
||||
"detail": "by Sidsel Mundal, Norwegian Television Documentary, 1994",
|
||||
"media": "1994 documentary by Sidsel Mundal_for Norwegian Television.m4v"
|
||||
},
|
||||
{
|
||||
"title": "Meredith Monk: A Documentary",
|
||||
"detail": "by Mariusz Grzegorzek, Polish Television Documentary, 1995",
|
||||
"media": ""
|
||||
},
|
||||
{
|
||||
"title": "Souls messenger",
|
||||
"detail": "Poland, 2011",
|
||||
"media": ""
|
||||
},
|
||||
{
|
||||
"title": "Meredith Monk",
|
||||
"detail": "by Ingo Berhman, ECM Documentary - ECM50 | 1981, 2019",
|
||||
"media": ""
|
||||
},
|
||||
{
|
||||
"title": "4 American Composers",
|
||||
"detail": "Peter Greenaway Documentary",
|
||||
"media": ""
|
||||
}
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,128 @@
|
|||
[
|
||||
{
|
||||
"title": "On Behalf of Nature - Album",
|
||||
"details": "2016",
|
||||
"media": "on_behalf_of_nature",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Piano Songs - Album",
|
||||
"details": "2014",
|
||||
"media": "piano_songs",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Songs of Ascension",
|
||||
"details": "2011",
|
||||
"media": "songs_of_ascension",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "impermanance",
|
||||
"details": "2008",
|
||||
"media": "impermanance",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "mercy",
|
||||
"details": "2003",
|
||||
"media": "mercy",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Volcano Songs",
|
||||
"details": "1997",
|
||||
"media": "volcano_songs",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "ATLAS: an opera in three parts",
|
||||
"details": "2-CD album, 1993",
|
||||
"media": "atlas",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Facing North",
|
||||
"details": "1992",
|
||||
"media": "facing_north",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Book of Days",
|
||||
"details": "1990",
|
||||
"media": "book_of_days",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Do You Be",
|
||||
"details": "1987",
|
||||
"media": "do_you_be",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Turtle Dreams - Album",
|
||||
"details": "1983",
|
||||
"media": "turtule_dreams",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Dolmen Music - Album",
|
||||
"details": "1981",
|
||||
"media": "dolmen_music",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Songs from the Hill/Tablet - Album",
|
||||
"details": "1977",
|
||||
"media": "songs_from_the_hill",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Our Lady of Late - Album",
|
||||
"details": "1973",
|
||||
"media": "our_lady_of_late",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "KEY: an album of invisible theater - Album",
|
||||
"details": "1971",
|
||||
"media": "key",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "MEMORY GAME",
|
||||
"details": "2020",
|
||||
"media": "memory_game",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "MONK MIX: Remixes and Interpretations of Music by Meredith Monk",
|
||||
"details": "2-CD Album, 2012",
|
||||
"media": "monk_mix",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Beginnings",
|
||||
"details": "Album, 2009",
|
||||
"media": "beginnings",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Radio Songs",
|
||||
"details": "LP/Album, 2014",
|
||||
"media": "radio_songs",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Monk and the Abbiss",
|
||||
"details": "1996",
|
||||
"media": "monk_and_the_abbiss",
|
||||
"album": true
|
||||
},
|
||||
{
|
||||
"title": "Biography (from Education of the Girlchild)",
|
||||
"details": "recording, 1978",
|
||||
"media": "biography",
|
||||
"album": true
|
||||
}
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,54 @@
|
|||
@font-face {
|
||||
font-family: 'Theinhardt';
|
||||
src: url('Theinhardt-Light.woff2') format('woff2'),
|
||||
url('Theinhardt-Light.woff') format('woff');
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Theinhardt';
|
||||
src: url('Theinhardt-LightIta.woff2') format('woff2'),
|
||||
url('Theinhardt-LightIta.woff') format('woff');
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Theinhardt';
|
||||
src: url('Theinhardt-Medium.woff2') format('woff2'),
|
||||
url('Theinhardt-Medium.woff') format('woff');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Theinhardt';
|
||||
src: url('Theinhardt-RegularIta.woff2') format('woff2'),
|
||||
url('Theinhardt-RegularIta.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Theinhardt';
|
||||
src: url('Theinhardt-Regular.woff2') format('woff2'),
|
||||
url('Theinhardt-Regular.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Theinhardt';
|
||||
src: url('Theinhardt-MediumIta.woff2') format('woff2'),
|
||||
url('Theinhardt-MediumIta.woff') format('woff');
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,34 @@
|
|||
import { LitElement } from 'lit'
|
||||
import Router from './api/Router.js'
|
||||
|
||||
// global reset
|
||||
import './assets/styles/main.scss'
|
||||
|
||||
export class App extends LitElement {
|
||||
static properties = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
Router.addEventListener('route-changed', () => {
|
||||
if ('startViewTransition' in document) {
|
||||
return document.startViewTransition(() => {
|
||||
this.requestUpdate()
|
||||
})
|
||||
}
|
||||
else {
|
||||
this.requestUpdate()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return Router.render()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define('mm-app', App)
|
|
@ -0,0 +1,105 @@
|
|||
// // Conditional ESM module loading (Node.js and browser)
|
||||
// // @ts-ignore: Property 'UrlPattern' does not exist
|
||||
if (!globalThis.URLPattern) {
|
||||
await import('urlpattern-polyfill')
|
||||
}
|
||||
|
||||
import { Router } from '@thepassle/app-tools/router.js'
|
||||
import { lazy } from '@thepassle/app-tools/router/plugins/lazy.js'
|
||||
import { html } from 'lit'
|
||||
|
||||
// home view
|
||||
import '../views/home.js'
|
||||
|
||||
const baseURL = import.meta.env.BASE_URL
|
||||
|
||||
export default new Router({
|
||||
fallback: '/404',
|
||||
routes: [
|
||||
{
|
||||
path: resolveRouterPath(),
|
||||
title: 'home',
|
||||
short: 'home',
|
||||
icon: 'home',
|
||||
render: () => html`<mm-home></mm-home>`
|
||||
},
|
||||
{
|
||||
path: resolveRouterPath('documentaries_interviews'),
|
||||
title: 'Video Documentaries & Interviews',
|
||||
short: 'docs & interviews',
|
||||
icon: 'film',
|
||||
plugins: [
|
||||
lazy(() => import('../views/videos.js'))
|
||||
],
|
||||
render: () => html`<mm-videos></mm-videos>`
|
||||
},
|
||||
{
|
||||
path: resolveRouterPath('audio_interviews'),
|
||||
title: 'Audio Interviews',
|
||||
short: 'interviews',
|
||||
icon: 'headphones',
|
||||
plugins: [
|
||||
lazy(() => import('../views/audio.js'))
|
||||
],
|
||||
render: () => html`<mm-audio></mm-audio>`
|
||||
},
|
||||
{
|
||||
path: resolveRouterPath('recordings_live'),
|
||||
title: 'Recordings of Live Works',
|
||||
short: 'live works',
|
||||
icon: 'film',
|
||||
plugins: [
|
||||
lazy(() => import('../views/videos.js'))
|
||||
],
|
||||
render: () => html`<mm-videos></mm-videos>`
|
||||
},
|
||||
{
|
||||
path: resolveRouterPath('music_sound'),
|
||||
title: 'Music & Sound',
|
||||
short: 'music & sound',
|
||||
icon: 'headphones',
|
||||
plugins: [
|
||||
lazy(() => import('../views/audio.js'))
|
||||
],
|
||||
render: () => html`<mm-audio></mm-audio>`
|
||||
},
|
||||
{
|
||||
path: resolveRouterPath('films'),
|
||||
title: 'Films',
|
||||
short: 'films',
|
||||
icon: 'film',
|
||||
plugins: [
|
||||
lazy(() => import('../views/videos.js'))
|
||||
],
|
||||
render: () => html`<mm-videos></mm-videos>`
|
||||
},
|
||||
{
|
||||
path: resolveRouterPath('images'),
|
||||
title: 'Images',
|
||||
short: 'images',
|
||||
icon: 'camera',
|
||||
plugins: [
|
||||
lazy(() => import('../views/images.js'))
|
||||
],
|
||||
render: () => html`<mm-images></mm-images>`
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
title: 'Not found',
|
||||
hide: true,
|
||||
plugins: [
|
||||
lazy(() => import('../views/fourohfour.js'))
|
||||
],
|
||||
render: () => html`<mm-404></mm-404>`
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export function resolveRouterPath(unresolvedPath) {
|
||||
let resolvedPath = baseURL
|
||||
if(unresolvedPath) {
|
||||
resolvedPath = resolvedPath + unresolvedPath
|
||||
}
|
||||
|
||||
return resolvedPath
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
function pad(d) {
|
||||
return (d < 10) ? '0' + d.toString() : d.toString();
|
||||
}
|
||||
|
||||
export const formatSeconds = (seconds) => {
|
||||
let hrs = 0, min = 0, sec = 0
|
||||
|
||||
if (seconds) {
|
||||
const fraction = seconds / 60
|
||||
min = fraction
|
||||
|
||||
if (min > 60) {
|
||||
hrs = min / 60
|
||||
min = 60 * (hrs % 1)
|
||||
}
|
||||
sec = parseInt(60 * (fraction % 1))
|
||||
}
|
||||
|
||||
hrs = parseInt(hrs)
|
||||
min = pad(parseInt(min))
|
||||
sec = pad(sec)
|
||||
|
||||
return `${hrs}:${min}:${sec}`
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<!-- Shape: svg-g -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-08e30ed9-75c0-8079-8003-292ade8ea3ef" version="1.1" viewBox="219.5 605.5 79 78" height="78">
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-292ade8ea3ef" style="fill:currentcolor" clip-path="url(#rumext-id-1405-clip2_9_654)" rx="0" ry="0">
|
||||
<defs>
|
||||
<clipPath id="rumext-id-1405-clip2_9_654" class="svg-def" transform="matrix(3.905449, 0.000000, 0.000000, 3.860753, 165.254782, 119.487755)">
|
||||
<rect width="24" height="24" fill="white" transform="translate(12 124)">
|
||||
</rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-292ade8ee7b4">
|
||||
<g class="fills" id="fills-08e30ed9-75c0-8079-8003-292ade8ee7b4">
|
||||
<path rx="0" ry="0" style="fill:currentcolor;" d="M288.702,621.158L288.702,621.158ZC293.167,626.679,295.960,633.331,296.761,640.350C297.562,647.372,296.339,654.472,293.232,660.831C290.125,667.193,285.262,672.556,279.203,676.301C273.144,680.046,266.138,682.023,258.992,681.999L258.978,681.999C257.537,682.007,256.098,681.930,254.666,681.779C249.717,681.250,244.924,679.753,240.565,677.378C236.206,675.004,232.367,671.796,229.271,667.942L229.270,667.942C222.994,660.151,220.107,650.214,221.242,640.319C222.377,630.424,227.443,621.378,235.324,615.174C243.205,608.969,253.257,606.116,263.267,607.240C273.277,608.359,282.426,613.367,288.702,621.158ZZM263.114,609.236L263.114,609.236ZL263.114,609.236ZC261.731,609.089,260.354,609.024,258.978,609.031C252.051,609.039,245.274,611.023,239.463,614.753C233.650,618.479,229.051,623.787,226.220,630.041C223.390,636.292,222.448,643.218,223.509,649.986C224.570,656.758,227.588,663.078,232.199,668.190C236.811,673.301,242.819,676.988,249.500,678.803C256.182,680.613,263.251,680.482,269.857,678.413C276.463,676.347,282.323,672.436,286.733,667.151C291.141,661.870,293.911,655.437,294.708,648.631C295.254,643.994,294.870,639.296,293.577,634.806C292.285,630.316,290.109,626.119,287.175,622.459C284.240,618.799,280.605,615.749,276.476,613.479C272.347,611.213,267.806,609.769,263.114,609.236ZZ">
|
||||
</path>
|
||||
</g>
|
||||
<g id="strokes-08e30ed9-75c0-8079-8003-292ade8ee7b4" class="strokes">
|
||||
<g class="stroke-shape">
|
||||
<path rx="0" ry="0" style="stroke-width:3;stroke:currentcolor;" d="M288.702,621.158L288.702,621.158ZC293.167,626.679,295.960,633.331,296.761,640.350C297.562,647.372,296.339,654.472,293.232,660.831C290.125,667.193,285.262,672.556,279.203,676.301C273.144,680.046,266.138,682.023,258.992,681.999L258.978,681.999C257.537,682.007,256.098,681.930,254.666,681.779C249.717,681.250,244.924,679.753,240.565,677.378C236.206,675.004,232.367,671.796,229.271,667.942L229.270,667.942C222.994,660.151,220.107,650.214,221.242,640.319C222.377,630.424,227.443,621.378,235.324,615.174C243.205,608.969,253.257,606.116,263.267,607.240C273.277,608.359,282.426,613.367,288.702,621.158ZZM263.114,609.236L263.114,609.236ZL263.114,609.236ZC261.731,609.089,260.354,609.024,258.978,609.031C252.051,609.039,245.274,611.023,239.463,614.753C233.650,618.479,229.051,623.787,226.220,630.041C223.390,636.292,222.448,643.218,223.509,649.986C224.570,656.758,227.588,663.078,232.199,668.190C236.811,673.301,242.819,676.988,249.500,678.803C256.182,680.613,263.251,680.482,269.857,678.413C276.463,676.347,282.323,672.436,286.733,667.151C291.141,661.870,293.911,655.437,294.708,648.631C295.254,643.994,294.870,639.296,293.577,634.806C292.285,630.316,290.109,626.119,287.175,622.459C284.240,618.799,280.605,615.749,276.476,613.479C272.347,611.213,267.806,609.769,263.114,609.236ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-292ade8f32ac">
|
||||
<g class="fills" id="fills-08e30ed9-75c0-8079-8003-292ade8f32ac">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M253.561,636.524C255.167,635.466,257.054,634.898,258.986,634.898C261.575,634.898,264.058,635.914,265.890,637.724C267.720,639.535,268.749,641.990,268.749,644.550C268.749,646.457,268.177,648.326,267.104,649.913C266.031,651.500,264.506,652.735,262.722,653.469C260.938,654.198,258.975,654.387,257.081,654.017C255.187,653.646,253.447,652.723,252.082,651.376C250.716,650.025,249.786,648.307,249.409,646.434C249.033,644.562,249.226,642.620,249.965,640.855C250.704,639.091,251.956,637.585,253.561,636.524ZZM254.646,650.971C255.931,651.820,257.441,652.272,258.986,652.272C261.057,652.272,263.044,651.457,264.509,650.009C265.974,648.561,266.796,646.596,266.796,644.550C266.796,643.021,266.338,641.531,265.480,640.261C264.622,638.991,263.402,638.002,261.975,637.415C260.547,636.833,258.977,636.678,257.462,636.975C255.947,637.277,254.555,638.010,253.462,639.091C252.370,640.168,251.626,641.547,251.325,643.044C251.024,644.542,251.178,646.094,251.769,647.504C252.360,648.917,253.362,650.121,254.646,650.971ZZ">
|
||||
</path>
|
||||
</g>
|
||||
<g id="strokes-08e30ed9-75c0-8079-8003-292ade8f32ac" class="strokes">
|
||||
<g class="stroke-shape">
|
||||
<path rx="0" ry="0" style="stroke-width:3;stroke:currentcolor" d="M253.561,636.524C255.167,635.466,257.054,634.898,258.986,634.898C261.575,634.898,264.058,635.914,265.890,637.724C267.720,639.535,268.749,641.990,268.749,644.550C268.749,646.457,268.177,648.326,267.104,649.913C266.031,651.500,264.506,652.735,262.722,653.469C260.938,654.198,258.975,654.387,257.081,654.017C255.187,653.646,253.447,652.723,252.082,651.376C250.716,650.025,249.786,648.307,249.409,646.434C249.033,644.562,249.226,642.620,249.965,640.855C250.704,639.091,251.956,637.585,253.561,636.524ZZM254.646,650.971C255.931,651.820,257.441,652.272,258.986,652.272C261.057,652.272,263.044,651.457,264.509,650.009C265.974,648.561,266.796,646.596,266.796,644.550C266.796,643.021,266.338,641.531,265.480,640.261C264.622,638.991,263.402,638.002,261.975,637.415C260.547,636.833,258.977,636.678,257.462,636.975C255.947,637.277,254.555,638.010,253.462,639.091C252.370,640.168,251.626,641.547,251.325,643.044C251.024,644.542,251.178,646.094,251.769,647.504C252.360,648.917,253.362,650.121,254.646,650.971ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.0 KiB |
|
@ -0,0 +1,9 @@
|
|||
<!-- Shape: svg-path -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="20" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-3275de8a-06b0-809d-8003-27fb8ec0beab" version="1.1" viewBox="245 423 20 34.808" height="34.808">
|
||||
<g id="shape-3275de8a-06b0-809d-8003-27fb8ec0beab">
|
||||
<g class="fills" id="fills-3275de8a-06b0-809d-8003-27fb8ec0beab">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M245.000,440.404C245.000,441.073,245.256,441.643,245.787,442.154L261.126,457.159C261.539,457.592,262.089,457.808,262.719,457.808C263.997,457.808,265.000,456.825,265.000,455.527C265.000,454.898,264.744,454.327,264.312,453.895L250.487,440.404L264.312,426.914C264.744,426.461,265.000,425.891,265.000,425.262C265.000,423.983,263.997,423.000,262.719,423.000C262.089,423.000,261.539,423.216,261.126,423.649L245.787,438.654C245.256,439.165,245.020,439.735,245.000,440.404ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 969 B |
|
@ -0,0 +1,11 @@
|
|||
<!-- Shape: Mnochrome=camera.fill -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="28" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-08e30ed9-75c0-8079-8003-293d62258e73" version="1.1" viewBox="2029 1889 28 27.034" height="27.034">
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-293d62258e73" width="29" height="28" rx="0" ry="0">
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-293d62258e76">
|
||||
<g class="fills" id="fills-08e30ed9-75c0-8079-8003-293d62258e76">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M2032.553,1912.508L2053.236,1912.508C2055.589,1912.508,2056.789,1911.331,2056.789,1909.000L2056.789,1897.154C2056.789,1894.823,2055.589,1893.658,2053.236,1893.658L2050.000,1893.658C2049.106,1893.658,2048.835,1893.477,2048.325,1892.911L2047.420,1891.893C2046.855,1891.270,2046.277,1890.931,2045.112,1890.931L2040.598,1890.931C2039.432,1890.931,2038.855,1891.270,2038.289,1891.893L2037.384,1892.911C2036.875,1893.466,2036.592,1893.658,2035.710,1893.658L2032.553,1893.658C2030.188,1893.658,2029.000,1894.823,2029.000,1897.154L2029.000,1909.000C2029.000,1911.331,2030.188,1912.508,2032.553,1912.508ZZM2042.894,1909.170C2039.466,1909.170,2036.717,1906.432,2036.717,1902.981C2036.717,1899.542,2039.466,1896.803,2042.894,1896.803C2046.323,1896.803,2049.061,1899.542,2049.061,1902.981C2049.061,1906.432,2046.311,1909.170,2042.894,1909.170ZZM2042.894,1907.450C2045.361,1907.450,2047.352,1905.470,2047.352,1902.981C2047.352,1900.503,2045.361,1898.512,2042.894,1898.512C2040.428,1898.512,2038.425,1900.503,2038.425,1902.981C2038.425,1905.470,2040.439,1907.450,2042.894,1907.450ZZM2049.819,1898.817C2049.819,1898.048,2050.498,1897.369,2051.290,1897.369C2052.071,1897.369,2052.738,1898.048,2052.738,1898.817C2052.738,1899.621,2052.071,1900.266,2051.290,1900.277C2050.498,1900.277,2049.819,1899.632,2049.819,1898.817ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,19 @@
|
|||
<!-- Shape: Property 1=video.fill -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-08e30ed9-75c0-8079-8003-293d62258e64" version="1.1" viewBox="1797 1888 29 28" height="28">
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-293d62258e64" style="fill:currentcolor" rx="0" ry="0">
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-293d62258e67" style="fill:currentcolor" clip-path="url(#rumext-id-388-clip0_2201_1398)" rx="0" ry="0">
|
||||
<defs>
|
||||
<clipPath id="rumext-id-388-clip0_2201_1398" class="svg-def" transform="matrix(1.000000, 0.000000, 0.000000, 1.000000, 1797.000000, 1888.000000)">
|
||||
<rect width="28.7695" height="19.043" fill="white" transform="translate(0 4)">
|
||||
</rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-293d62258e69">
|
||||
<g class="fills" id="fills-08e30ed9-75c0-8079-8003-293d62258e69">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1800.762,1911.031L1813.348,1911.031C1815.703,1911.031,1817.109,1909.672,1817.109,1907.316L1817.109,1895.703C1817.109,1893.359,1815.820,1892.000,1813.465,1892.000L1800.762,1892.000C1798.512,1892.000,1797.000,1893.359,1797.000,1895.703L1797.000,1907.316C1797.000,1909.672,1798.406,1911.031,1800.762,1911.031ZZM1818.703,1904.973L1822.969,1908.664C1823.367,1909.016,1823.836,1909.238,1824.258,1909.238C1825.172,1909.238,1825.769,1908.570,1825.769,1907.609L1825.769,1895.422C1825.769,1894.461,1825.172,1893.793,1824.258,1893.793C1823.836,1893.793,1823.367,1894.016,1822.969,1894.367L1818.703,1898.047L1818.703,1904.973ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,17 @@
|
|||
<!-- Shape: Property 1=arrow.down.backward.and.arrow.up.forward.square -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="34" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-b4e55d81-e415-8079-8003-280c9aed5d3f" version="1.1" viewBox="2164 1264 34 34" height="34">
|
||||
<g id="shape-b4e55d81-e415-8079-8003-280c9aed5d3f" width="28" height="28" rx="0" ry="0">
|
||||
<g id="shape-b4e55d81-e415-8079-8003-280c9aedfba7">
|
||||
<g class="fills" id="fills-b4e55d81-e415-8079-8003-280c9aedfba7">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M2172.111,1293.869L2189.372,1293.869C2192.360,1293.869,2193.840,1292.389,2193.840,1289.457L2193.840,1272.083C2193.840,1269.151,2192.360,1267.671,2189.372,1267.671L2172.111,1267.671C2169.137,1267.671,2167.643,1269.137,2167.643,1272.083L2167.643,1289.457C2167.643,1292.403,2169.137,1293.869,2172.111,1293.869ZZM2172.140,1291.578C2170.717,1291.578,2169.934,1290.823,2169.934,1289.343L2169.934,1272.196C2169.934,1270.717,2170.717,1269.962,2172.140,1269.962L2189.343,1269.962C2190.752,1269.962,2191.549,1270.717,2191.549,1272.196L2191.549,1289.343C2191.549,1290.823,2190.752,1291.578,2189.343,1291.578L2172.140,1291.578ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="shape-b4e55d81-e415-8079-8003-280c9aee5cac">
|
||||
<g class="fills" id="fills-b4e55d81-e415-8079-8003-280c9aee5cac">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M2186.896,1280.734C2187.451,1280.734,2187.906,1280.293,2187.906,1279.738L2187.906,1274.786C2187.906,1273.932,2187.465,1273.605,2186.725,1273.605L2181.802,1273.605C2181.247,1273.605,2180.791,1274.061,2180.791,1274.616C2180.791,1275.170,2181.247,1275.612,2181.802,1275.612L2182.499,1275.612L2184.676,1275.469L2181.460,1278.529C2181.047,1278.927,2181.033,1279.624,2181.446,1280.037C2181.873,1280.464,2182.542,1280.478,2182.968,1280.023L2186.042,1276.793L2185.900,1279.041L2185.900,1279.738C2185.900,1280.293,2186.341,1280.734,2186.896,1280.734ZZM2179.710,1287.920C2180.265,1287.920,2180.706,1287.465,2180.706,1286.910C2180.706,1286.355,2180.265,1285.914,2179.710,1285.914L2179.013,1285.914L2176.835,1286.056L2180.037,1282.997C2180.450,1282.598,2180.464,1281.901,2180.051,1281.489C2179.639,1281.062,2178.956,1281.047,2178.543,1281.503L2175.469,1284.733L2175.612,1282.485L2175.612,1281.787C2175.612,1281.232,2175.156,1280.791,2174.601,1280.791C2174.046,1280.791,2173.605,1281.232,2173.605,1281.787L2173.605,1286.739C2173.605,1287.593,2174.032,1287.920,2174.772,1287.920L2179.710,1287.920ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,19 @@
|
|||
<!-- Shape: Monochrome=headphones -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-08e30ed9-75c0-8079-8003-293d62258e6c" version="1.1" viewBox="1460 1886 28 28">
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-293d62258e6c" style="fill:currentcolor" width="28" height="28" rx="0" ry="0">
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-293d62258e6e" style="fill:currentcolor" clip-path="url(#rumext-id-555-clip0_2124_24753)" rx="0" ry="0">
|
||||
<defs>
|
||||
<clipPath id="rumext-id-555-clip0_2124_24753" class="svg-def" transform="matrix(1.000000, 0.000000, 0.000000, 1.000000, 1460.000000, 1886.000000)">
|
||||
<rect width="23.8594" height="24.8555" fill="white" transform="translate(2 2)">
|
||||
</rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-293d62258e70">
|
||||
<g class="fills" id="fills-08e30ed9-75c0-8079-8003-293d62258e70">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1462.000,1900.832C1462.000,1904.359,1463.020,1908.250,1464.742,1911.273C1465.035,1911.777,1465.586,1911.918,1466.113,1911.625C1466.605,1911.355,1466.746,1910.805,1466.441,1910.254C1464.871,1907.395,1463.992,1903.984,1463.992,1900.832C1463.992,1894.328,1467.965,1889.992,1473.930,1889.992C1479.883,1889.992,1483.867,1894.328,1483.867,1900.832C1483.867,1903.984,1482.977,1907.395,1481.406,1910.254C1481.102,1910.805,1481.242,1911.355,1481.734,1911.625C1482.262,1911.918,1482.824,1911.777,1483.106,1911.273C1484.828,1908.250,1485.859,1904.359,1485.859,1900.832C1485.859,1893.121,1481.102,1888.000,1473.930,1888.000C1466.746,1888.000,1462.000,1893.121,1462.000,1900.832ZZM1465.668,1910.981C1466.055,1912.328,1467.203,1912.949,1468.563,1912.563C1469.910,1912.176,1470.543,1911.004,1470.144,1909.656L1468.469,1903.938C1468.082,1902.602,1466.934,1901.969,1465.574,1902.356C1464.227,1902.754,1463.594,1903.914,1463.992,1905.273L1465.668,1910.981ZZM1482.180,1910.981L1483.856,1905.273C1484.254,1903.902,1483.633,1902.754,1482.273,1902.356C1480.914,1901.969,1479.777,1902.602,1479.379,1903.938L1477.703,1909.656C1477.305,1911.016,1477.938,1912.176,1479.285,1912.563C1480.656,1912.949,1481.793,1912.328,1482.180,1910.981ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,9 @@
|
|||
<!-- Shape: svg-path -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-08e30ed9-75c0-8079-8003-293d62258e4c" version="1.1" viewBox="264 1889.368 30 26.264">
|
||||
<g id="shape-08e30ed9-75c0-8079-8003-293d62258e4c">
|
||||
<g class="fills" id="fills-08e30ed9-75c0-8079-8003-293d62258e4c">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M264.000,1901.757C264.000,1902.395,264.479,1902.936,265.266,1902.936C265.647,1902.936,265.978,1902.728,266.274,1902.481L278.490,1892.244C278.821,1891.948,279.203,1891.948,279.534,1892.244L291.739,1902.481C292.034,1902.728,292.366,1902.936,292.747,1902.936C293.460,1902.936,294.000,1902.494,294.000,1901.793C294.000,1901.376,293.841,1901.043,293.521,1900.785L290.067,1897.873L290.067,1892.797C290.067,1892.244,289.711,1891.888,289.170,1891.888L287.511,1891.888C286.958,1891.888,286.589,1892.244,286.589,1892.797L286.589,1894.961L280.739,1890.031C279.682,1889.147,278.354,1889.147,277.286,1890.031L264.479,1900.785C264.159,1901.056,264.000,1901.413,264.000,1901.757ZZM267.982,1912.805C267.982,1914.576,269.088,1915.632,270.895,1915.632L287.105,1915.632C288.924,1915.632,290.030,1914.576,290.030,1912.805L290.030,1903.440L279.756,1894.825C279.289,1894.419,278.711,1894.432,278.244,1894.825L267.982,1903.428L267.982,1912.805ZZM282.214,1913.224L275.773,1913.224L275.773,1905.247C275.773,1904.669,276.155,1904.301,276.732,1904.301L281.267,1904.301C281.845,1904.301,282.214,1904.669,282.214,1905.247L282.214,1913.224ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,23 @@
|
|||
<!-- Shape: svg-g -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="18.6" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-f2a697a0-1165-8029-8003-2a7165bc4c8d" version="1.1" viewBox="1131.5 705.5 18.6 20.468" height="20.468">
|
||||
<g id="shape-f2a697a0-1165-8029-8003-2a7165bc4c8d" style="fill:currentcolor" clip-path="url(#rumext-id-329-clip3_9_654)" rx="0" ry="0">
|
||||
<defs>
|
||||
<clipPath id="rumext-id-329-clip3_9_654" class="svg-def" transform="matrix(1.000000, 0.000000, 0.000000, 1.000000, 1116.799974, 531.762000)">
|
||||
<rect width="24" height="24" fill="currentcolor" transform="translate(12 172)">
|
||||
</rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="shape-f2a697a0-1165-8029-8003-2a7165bccc80">
|
||||
<g class="fills" id="fills-f2a697a0-1165-8029-8003-2a7165bccc80">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1149.600,706.000L1149.600,720.759C1149.591,721.415,1149.363,722.050,1148.953,722.562C1148.542,723.075,1147.971,723.437,1147.331,723.588C1146.692,723.740,1146.020,723.674,1145.422,723.400C1144.824,723.126,1144.335,722.661,1144.032,722.077C1143.729,721.494,1143.630,720.826,1143.750,720.179C1143.870,719.533,1144.203,718.945,1144.695,718.509C1145.188,718.073,1145.811,717.815,1146.468,717.774C1147.124,717.733,1147.775,717.913,1148.317,718.284L1149.100,718.821L1149.100,717.872L1149.100,707.252L1149.100,706.623L1148.487,706.765L1138.287,709.125L1137.900,709.214L1137.900,709.612L1137.900,722.512L1137.900,722.513C1137.901,723.172,1137.681,723.813,1137.276,724.333C1136.870,724.853,1136.302,725.222,1135.662,725.381C1135.022,725.540,1134.348,725.480,1133.746,725.211C1133.144,724.942,1132.649,724.479,1132.341,723.896C1132.034,723.313,1131.930,722.643,1132.047,721.994C1132.164,721.346,1132.495,720.755,1132.987,720.316C1133.479,719.877,1134.104,719.616,1134.762,719.574C1135.420,719.532,1136.073,719.712,1136.617,720.084L1137.400,720.621L1137.400,719.672L1137.400,708.810L1149.600,706.000ZZM1134.683,724.903L1134.683,724.903ZL1134.962,724.962C1135.291,724.954,1135.614,724.881,1135.914,724.748C1136.215,724.615,1136.486,724.424,1136.712,724.186C1136.938,723.948,1137.116,723.667,1137.234,723.361C1137.351,723.056,1137.407,722.732,1137.400,722.406C1137.399,721.913,1137.252,721.432,1136.979,721.023C1136.704,720.612,1136.313,720.292,1135.857,720.102C1135.400,719.913,1134.897,719.864,1134.412,719.960C1133.927,720.056,1133.482,720.295,1133.132,720.644C1132.783,720.994,1132.544,721.439,1132.448,721.924C1132.352,722.409,1132.401,722.912,1132.590,723.369C1132.779,723.826,1133.100,724.216,1133.511,724.491C1133.862,724.725,1134.265,724.866,1134.683,724.903ZZM1146.378,723.097L1146.378,723.097ZL1148.600,720.762L1149.099,720.787C1149.099,720.787,1149.099,720.787,1149.099,720.787C1149.124,720.301,1149.003,719.818,1148.752,719.401C1148.502,718.983,1148.132,718.650,1147.691,718.444C1147.250,718.237,1146.757,718.167,1146.276,718.242C1145.794,718.317,1145.346,718.533,1144.989,718.864C1144.631,719.195,1144.380,719.625,1144.268,720.099C1144.156,720.573,1144.188,721.069,1144.359,721.525C1144.531,721.981,1144.834,722.376,1145.231,722.658C1145.571,722.900,1145.966,723.051,1146.378,723.097ZZ">
|
||||
</path>
|
||||
</g>
|
||||
<g id="strokes-f2a697a0-1165-8029-8003-2a7165bccc80" class="strokes">
|
||||
<g class="stroke-shape">
|
||||
<path rx="0" ry="0" style="stroke:currentcolor" d="M1149.600,706.000L1149.600,720.759C1149.591,721.415,1149.363,722.050,1148.953,722.562C1148.542,723.075,1147.971,723.437,1147.331,723.588C1146.692,723.740,1146.020,723.674,1145.422,723.400C1144.824,723.126,1144.335,722.661,1144.032,722.077C1143.729,721.494,1143.630,720.826,1143.750,720.179C1143.870,719.533,1144.203,718.945,1144.695,718.509C1145.188,718.073,1145.811,717.815,1146.468,717.774C1147.124,717.733,1147.775,717.913,1148.317,718.284L1149.100,718.821L1149.100,717.872L1149.100,707.252L1149.100,706.623L1148.487,706.765L1138.287,709.125L1137.900,709.214L1137.900,709.612L1137.900,722.512L1137.900,722.513C1137.901,723.172,1137.681,723.813,1137.276,724.333C1136.870,724.853,1136.302,725.222,1135.662,725.381C1135.022,725.540,1134.348,725.480,1133.746,725.211C1133.144,724.942,1132.649,724.479,1132.341,723.896C1132.034,723.313,1131.930,722.643,1132.047,721.994C1132.164,721.346,1132.495,720.755,1132.987,720.316C1133.479,719.877,1134.104,719.616,1134.762,719.574C1135.420,719.532,1136.073,719.712,1136.617,720.084L1137.400,720.621L1137.400,719.672L1137.400,708.810L1149.600,706.000ZZM1134.683,724.903L1134.683,724.903ZL1134.962,724.962C1135.291,724.954,1135.614,724.881,1135.914,724.748C1136.215,724.615,1136.486,724.424,1136.712,724.186C1136.938,723.948,1137.116,723.667,1137.234,723.361C1137.351,723.056,1137.407,722.732,1137.400,722.406C1137.399,721.913,1137.252,721.432,1136.979,721.023C1136.704,720.612,1136.313,720.292,1135.857,720.102C1135.400,719.913,1134.897,719.864,1134.412,719.960C1133.927,720.056,1133.482,720.295,1133.132,720.644C1132.783,720.994,1132.544,721.439,1132.448,721.924C1132.352,722.409,1132.401,722.912,1132.590,723.369C1132.779,723.826,1133.100,724.216,1133.511,724.491C1133.862,724.725,1134.265,724.866,1134.683,724.903ZZM1146.378,723.097L1146.378,723.097ZL1148.600,720.762L1149.099,720.787C1149.099,720.787,1149.099,720.787,1149.099,720.787C1149.124,720.301,1149.003,719.818,1148.752,719.401C1148.502,718.983,1148.132,718.650,1147.691,718.444C1147.250,718.237,1146.757,718.167,1146.276,718.242C1145.794,718.317,1145.346,718.533,1144.989,718.864C1144.631,719.195,1144.380,719.625,1144.268,720.099C1144.156,720.573,1144.188,721.069,1144.359,721.525C1144.531,721.981,1144.834,722.376,1145.231,722.658C1145.571,722.900,1145.966,723.051,1146.378,723.097ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
|
@ -0,0 +1,9 @@
|
|||
<!-- Shape: svg-path -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="48.947" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-88ca4951-8a7d-80f2-8003-2e6a3ecede70" version="1.1" viewBox="1921.058 3266.8 18.017 24.057" height="24.057">
|
||||
<g id="shape-88ca4951-8a7d-80f2-8003-2e6a3ecede70">
|
||||
<g class="fills" id="fills-88ca4951-8a7d-80f2-8003-2e6a3ecede70">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1923.022,3290.857L1926.389,3290.857C1927.674,3290.857,1928.353,3290.178,1928.353,3288.878L1928.353,3268.764C1928.353,3267.420,1927.674,3266.800,1926.389,3266.800L1923.022,3266.800C1921.737,3266.800,1921.058,3267.479,1921.058,3268.764L1921.058,3288.878C1921.058,3290.178,1921.737,3290.857,1923.022,3290.857ZZM1933.758,3290.857L1937.111,3290.857C1938.410,3290.857,1939.075,3290.178,1939.075,3288.878L1939.075,3268.764C1939.075,3267.420,1938.410,3266.800,1937.111,3266.800L1933.758,3266.800C1932.459,3266.800,1931.779,3267.479,1931.779,3268.764L1931.779,3288.878C1931.779,3290.178,1932.459,3290.857,1933.758,3290.857ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,9 @@
|
|||
<!-- Shape: svg-path -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="43" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-b4e55d81-e415-8079-8003-28125ae0f790" version="1.1" viewBox="223 1753 43 43" height="43">
|
||||
<g id="shape-b4e55d81-e415-8079-8003-28125ae0f790">
|
||||
<g class="fills" id="fills-b4e55d81-e415-8079-8003-28125ae0f790">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M244.500,1796.000C247.437,1796.000,250.198,1795.439,252.784,1794.314C255.384,1793.190,257.674,1791.638,259.655,1789.656C261.637,1787.674,263.190,1785.390,264.314,1782.806C265.438,1780.206,266.000,1777.437,266.000,1774.500C266.000,1771.563,265.438,1768.802,264.314,1766.216C263.190,1763.618,261.637,1761.326,259.655,1759.346C257.674,1757.364,255.384,1755.810,252.784,1754.686C250.184,1753.563,247.416,1753.000,244.479,1753.000C241.542,1753.000,238.774,1753.563,236.174,1754.686C233.588,1755.810,231.305,1757.364,229.323,1759.346C227.356,1761.326,225.810,1763.618,224.686,1766.216C223.562,1768.802,223.000,1771.563,223.000,1774.500C223.000,1777.437,223.562,1780.206,224.686,1782.806C225.810,1785.390,227.363,1787.674,229.345,1789.656C231.326,1791.638,233.610,1793.190,236.195,1794.314C238.795,1795.439,241.563,1796.000,244.500,1796.000ZL244.500,1796.000ZM244.500,1792.417C242.013,1792.417,239.687,1791.954,237.523,1791.026C235.359,1790.098,233.455,1788.820,231.811,1787.190C230.181,1785.545,228.902,1783.642,227.974,1781.478C227.061,1779.313,226.604,1776.988,226.604,1774.500C226.604,1772.014,227.061,1769.687,227.974,1767.522C228.902,1765.360,230.181,1763.455,231.811,1761.810C233.441,1760.166,235.338,1758.889,237.502,1757.974C239.666,1757.048,241.992,1756.583,244.479,1756.583C246.966,1756.583,249.292,1757.048,251.456,1757.974C253.634,1758.889,255.538,1760.166,257.168,1761.810C258.812,1763.455,260.098,1765.360,261.026,1767.522C261.953,1769.687,262.417,1772.014,262.417,1774.500C262.417,1776.988,261.953,1779.313,261.026,1781.478C260.112,1783.642,258.833,1785.545,257.189,1787.190C255.545,1788.820,253.641,1790.098,251.477,1791.026C249.313,1791.954,246.987,1792.417,244.500,1792.417ZL244.500,1792.417ZM240.558,1782.741L252.573,1775.660C253.009,1775.393,253.219,1775.014,253.205,1774.522C253.205,1774.029,252.995,1773.656,252.573,1773.405L240.558,1766.280C240.109,1766.013,239.645,1765.970,239.167,1766.153C238.689,1766.335,238.450,1766.681,238.450,1767.187L238.450,1781.835C238.450,1782.355,238.675,1782.714,239.125,1782.910C239.589,1783.093,240.067,1783.038,240.558,1782.741ZL240.558,1782.741Z">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,23 @@
|
|||
<!-- Shape: svg-g -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="60.407" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-b4e55d81-e415-8079-8003-291b04b22fb6" version="1.1" viewBox="1606.762 1456.5 60.407 81" height="81">
|
||||
<g id="shape-b4e55d81-e415-8079-8003-291b04b22fb6" style="fill:currentcolor" clip-path="url(#rumext-id-15137-clip4_302_1179)" rx="0" ry="0">
|
||||
<defs>
|
||||
<clipPath id="rumext-id-15137-clip4_302_1179" class="svg-def" transform="matrix(4.168817, 0.000000, 0.000000, 4.168817, 189.863929, 1444.889380)">
|
||||
<rect width="24" height="24" fill="white" transform="translate(334.5 0.5)">
|
||||
</rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="shape-b4e55d81-e415-8079-8003-291b04b27892">
|
||||
<g class="fills" id="fills-b4e55d81-e415-8079-8003-291b04b27892">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1610.459,1536.553L1610.459,1536.553ZL1610.367,1536.619C1609.179,1537.534,1607.262,1536.734,1607.262,1534.936L1607.262,1459.064C1607.262,1457.266,1609.179,1456.466,1610.367,1457.381L1610.413,1457.417L1610.463,1457.450L1665.700,1495.178L1665.700,1495.178L1665.721,1495.191C1666.984,1496.033,1666.984,1497.758,1665.721,1498.601L1665.721,1498.600L1665.696,1498.617L1610.459,1536.553ZZ">
|
||||
</path>
|
||||
</g>
|
||||
<g id="strokes-b4e55d81-e415-8079-8003-291b04b27892" class="strokes">
|
||||
<g class="stroke-shape">
|
||||
<path rx="0" ry="0" style="stroke-width:1;stroke:currentcolor;" d="M1610.459,1536.553L1610.459,1536.553ZL1610.367,1536.619C1609.179,1537.534,1607.262,1536.734,1607.262,1534.936L1607.262,1459.064C1607.262,1457.266,1609.179,1456.466,1610.367,1457.381L1610.413,1457.417L1610.463,1457.450L1665.700,1495.178L1665.700,1495.178L1665.721,1495.191C1666.984,1496.033,1666.984,1497.758,1665.721,1498.601L1665.721,1498.600L1665.696,1498.617L1610.459,1536.553ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,9 @@
|
|||
<!-- Shape: svg-path -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="46.667" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-b4e55d81-e415-8079-8003-28248f46222a" version="1.1" viewBox="1468.595 1470.333 46.667 51.102" height="51.102">
|
||||
<g id="shape-b4e55d81-e415-8079-8003-28248f46222a">
|
||||
<g class="fills" id="fills-b4e55d81-e415-8079-8003-28248f46222a">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1491.928,1521.436C1504.693,1521.436,1515.262,1510.867,1515.262,1498.102C1515.262,1486.847,1507.049,1477.308,1496.343,1475.204L1496.343,1471.955C1496.343,1470.331,1495.222,1469.896,1493.987,1470.789L1486.690,1475.890C1485.637,1476.622,1485.615,1477.720,1486.690,1478.475L1493.964,1483.599C1495.222,1484.514,1496.343,1484.079,1496.343,1482.432L1496.343,1479.161C1504.990,1481.128,1511.350,1488.838,1511.350,1498.102C1511.350,1508.900,1502.726,1517.547,1491.928,1517.547C1481.131,1517.547,1472.461,1508.900,1472.484,1498.102C1472.507,1491.606,1475.664,1485.864,1480.536,1482.387C1481.474,1481.678,1481.771,1480.602,1481.200,1479.642C1480.628,1478.704,1479.369,1478.452,1478.363,1479.230C1472.484,1483.508,1468.595,1490.416,1468.595,1498.102C1468.595,1510.867,1479.186,1521.436,1491.928,1521.436ZZM1485.134,1507.482C1486.026,1507.482,1486.598,1506.864,1486.598,1505.903L1486.598,1491.468C1486.598,1490.302,1486.003,1489.684,1484.974,1489.684C1484.334,1489.684,1483.876,1489.890,1483.075,1490.439L1479.918,1492.566C1479.392,1492.955,1479.141,1493.367,1479.141,1493.893C1479.141,1494.648,1479.735,1495.289,1480.468,1495.289C1480.925,1495.289,1481.154,1495.197,1481.588,1494.854L1483.739,1493.275L1483.739,1505.903C1483.739,1506.841,1484.288,1507.482,1485.134,1507.482ZZM1496.755,1507.756C1500.552,1507.756,1503.069,1505.308,1503.069,1501.625C1503.069,1498.262,1500.804,1495.861,1497.533,1495.861C1496.183,1495.861,1494.719,1496.478,1494.033,1497.508L1494.422,1492.566L1501.285,1492.566C1501.948,1492.566,1502.520,1492.017,1502.520,1491.240C1502.520,1490.462,1501.948,1489.959,1501.285,1489.959L1493.964,1489.959C1492.775,1489.959,1492.111,1490.645,1491.997,1491.857L1491.494,1498.446C1491.402,1499.589,1491.997,1500.138,1493.003,1500.138C1493.758,1500.138,1494.079,1500.001,1494.742,1499.498C1495.611,1498.697,1496.320,1498.377,1497.213,1498.377C1498.997,1498.377,1500.187,1499.681,1500.187,1501.671C1500.187,1503.684,1498.791,1505.148,1496.892,1505.148C1495.543,1505.148,1494.376,1504.370,1493.781,1503.181C1493.438,1502.586,1493.026,1502.266,1492.454,1502.266C1491.700,1502.266,1491.173,1502.815,1491.173,1503.593C1491.173,1503.913,1491.242,1504.210,1491.356,1504.508C1491.974,1506.155,1493.987,1507.756,1496.755,1507.756ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -0,0 +1,9 @@
|
|||
<!-- Shape: svg-path -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="46.667" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-b4e55d81-e415-8079-8003-28248f46f412" version="1.1" viewBox="1749.928 1470.333 46.667 51.097" height="51.097">
|
||||
<g id="shape-b4e55d81-e415-8079-8003-28248f46f412">
|
||||
<g class="fills" id="fills-b4e55d81-e415-8079-8003-28248f46f412">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1773.260,1521.430C1786.027,1521.430,1796.595,1510.861,1796.595,1498.096C1796.595,1490.409,1792.707,1483.501,1786.827,1479.223C1785.822,1478.445,1784.563,1478.697,1783.991,1479.635C1783.419,1480.595,1783.716,1481.671,1784.655,1482.380C1789.526,1485.857,1792.684,1491.599,1792.707,1498.096C1792.729,1508.894,1784.059,1517.541,1773.260,1517.541C1762.464,1517.541,1753.839,1508.894,1753.839,1498.096C1753.839,1488.831,1760.198,1481.122,1768.846,1479.154L1768.846,1482.448C1768.846,1484.073,1769.968,1484.507,1771.225,1483.615L1778.522,1478.491C1779.553,1477.782,1779.576,1476.683,1778.522,1475.929L1771.248,1470.804C1769.968,1469.889,1768.846,1470.324,1768.846,1471.971L1768.846,1475.196C1758.141,1477.278,1749.928,1486.841,1749.928,1498.096C1749.928,1510.861,1760.520,1521.430,1773.260,1521.430ZZM1766.467,1507.475C1767.359,1507.475,1767.930,1506.858,1767.930,1505.897L1767.930,1491.462C1767.930,1490.295,1767.337,1489.677,1766.307,1489.677C1765.667,1489.677,1765.208,1489.883,1764.408,1490.432L1761.252,1492.560C1760.725,1492.949,1760.473,1493.361,1760.473,1493.887C1760.473,1494.642,1761.068,1495.282,1761.800,1495.282C1762.257,1495.282,1762.487,1495.191,1762.920,1494.848L1765.071,1493.269L1765.071,1505.897C1765.071,1506.835,1765.620,1507.475,1766.467,1507.475ZZM1778.089,1507.750C1781.885,1507.750,1784.403,1505.302,1784.403,1501.619C1784.403,1498.256,1782.137,1495.854,1778.866,1495.854C1777.517,1495.854,1776.051,1496.472,1775.367,1497.501L1775.755,1492.560L1782.619,1492.560C1783.280,1492.560,1783.852,1492.011,1783.852,1491.233C1783.852,1490.455,1783.280,1489.952,1782.619,1489.952L1775.298,1489.952C1774.108,1489.952,1773.444,1490.638,1773.331,1491.851L1772.827,1498.439C1772.736,1499.583,1773.331,1500.132,1774.336,1500.132C1775.091,1500.132,1775.411,1499.995,1776.075,1499.492C1776.946,1498.691,1777.654,1498.371,1778.546,1498.371C1780.330,1498.371,1781.520,1499.674,1781.520,1501.665C1781.520,1503.678,1780.125,1505.142,1778.226,1505.142C1776.875,1505.142,1775.710,1504.364,1775.115,1503.175C1774.771,1502.580,1774.359,1502.259,1773.788,1502.259C1773.032,1502.259,1772.507,1502.809,1772.507,1503.586C1772.507,1503.907,1772.576,1504.204,1772.689,1504.502C1773.308,1506.149,1775.320,1507.750,1778.089,1507.750ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -0,0 +1,23 @@
|
|||
<!-- Shape: svg-g -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="29.477" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-b4e55d81-e415-8079-8003-291b04b298aa" version="1.1" viewBox="1894.762 1480.5 29.477 31.014" height="31.014">
|
||||
<g id="shape-b4e55d81-e415-8079-8003-291b04b298aa" style="fill:currentcolor" clip-path="url(currentcolor-id-15145-clip5_302_1179)" rx="0" ry="0">
|
||||
<defs>
|
||||
<clipPath id="rumext-id-15145-clip5_302_1179" class="svg-def" transform="matrix(2.308802, 0.000000, 0.000000, 2.308802, 989.442280, 1467.147186)">
|
||||
<rect width="16" height="16" fill="white" transform="translate(390.5 4.5)">
|
||||
</rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="shape-b4e55d81-e415-8079-8003-291b04b2db18">
|
||||
<g class="fills" id="fills-b4e55d81-e415-8079-8003-291b04b2db18">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1916.336,1495.735L1916.544,1495.969L1916.336,1496.203C1916.313,1496.228,1916.286,1496.253,1916.249,1496.278L1916.249,1496.278L1916.235,1496.287L1895.841,1510.294L1895.813,1510.311L1895.790,1510.330C1895.721,1510.383,1895.668,1510.396,1895.626,1510.399C1895.583,1510.401,1895.525,1510.391,1895.467,1510.360C1895.347,1510.297,1895.262,1510.174,1895.262,1510.014L1895.262,1482.000C1895.262,1481.841,1895.347,1481.718,1895.467,1481.654C1895.525,1481.623,1895.583,1481.613,1895.626,1481.616C1895.668,1481.618,1895.721,1481.632,1895.790,1481.684L1895.816,1481.704L1895.843,1481.722L1916.237,1495.652L1916.237,1495.652L1916.249,1495.660C1916.286,1495.684,1916.313,1495.710,1916.336,1495.735ZZM1918.350,1494.965L1918.350,1493.951L1918.350,1481.385C1918.350,1481.176,1918.525,1481.000,1918.735,1481.000L1923.353,1481.000C1923.563,1481.000,1923.738,1481.176,1923.738,1481.385L1923.738,1510.630C1923.738,1510.839,1923.563,1511.014,1923.353,1511.014L1918.735,1511.014C1918.525,1511.014,1918.350,1510.839,1918.350,1510.630L1918.350,1497.987L1918.350,1496.973L1918.350,1494.965ZZ">
|
||||
</path>
|
||||
</g>
|
||||
<g id="strokes-b4e55d81-e415-8079-8003-291b04b2db18" class="strokes">
|
||||
<g class="stroke-shape">
|
||||
<path rx="0" ry="0" style="stroke-width:1;stroke:currentcolor" d="M1916.336,1495.735L1916.544,1495.969L1916.336,1496.203C1916.313,1496.228,1916.286,1496.253,1916.249,1496.278L1916.249,1496.278L1916.235,1496.287L1895.841,1510.294L1895.813,1510.311L1895.790,1510.330C1895.721,1510.383,1895.668,1510.396,1895.626,1510.399C1895.583,1510.401,1895.525,1510.391,1895.467,1510.360C1895.347,1510.297,1895.262,1510.174,1895.262,1510.014L1895.262,1482.000C1895.262,1481.841,1895.347,1481.718,1895.467,1481.654C1895.525,1481.623,1895.583,1481.613,1895.626,1481.616C1895.668,1481.618,1895.721,1481.632,1895.790,1481.684L1895.816,1481.704L1895.843,1481.722L1916.237,1495.652L1916.237,1495.652L1916.249,1495.660C1916.286,1495.684,1916.313,1495.710,1916.336,1495.735ZZM1918.350,1494.965L1918.350,1493.951L1918.350,1481.385C1918.350,1481.176,1918.525,1481.000,1918.735,1481.000L1923.353,1481.000C1923.563,1481.000,1923.738,1481.176,1923.738,1481.385L1923.738,1510.630C1923.738,1510.839,1923.563,1511.014,1923.353,1511.014L1918.735,1511.014C1918.525,1511.014,1918.350,1510.839,1918.350,1510.630L1918.350,1497.987L1918.350,1496.973L1918.350,1494.965ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,23 @@
|
|||
<!-- Shape: svg-g -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="29.477" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-b4e55d81-e415-8079-8003-291b04b1b0a9" version="1.1" viewBox="1338.762 1480.5 29.477 31.014" height="31.014">
|
||||
<g id="shape-b4e55d81-e415-8079-8003-291b04b1b0a9" style="fill:currentcolor" clip-path="url(currentcolor-id-15149-clip3_302_1179)" rx="0" ry="0">
|
||||
<defs>
|
||||
<clipPath id="rumext-id-15149-clip3_302_1179" class="svg-def" transform="matrix(2.308802, 0.000000, 0.000000, 2.308802, 673.557720, 1467.147186)">
|
||||
<rect width="16" height="16" fill="white" transform="translate(286.5 4.5)">
|
||||
</rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="shape-b4e55d81-e415-8079-8003-291b04b22fb5">
|
||||
<g class="fills" id="fills-b4e55d81-e415-8079-8003-291b04b22fb5">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1344.650,1497.985L1344.650,1510.630C1344.650,1510.839,1344.475,1511.014,1344.265,1511.014L1339.647,1511.014C1339.437,1511.014,1339.262,1510.839,1339.262,1510.630L1339.262,1481.385C1339.262,1481.176,1339.437,1481.000,1339.647,1481.000L1344.265,1481.000C1344.475,1481.000,1344.650,1481.176,1344.650,1481.385L1344.650,1493.953L1344.650,1494.965L1344.650,1496.973L1344.650,1497.985ZZM1346.664,1496.203L1346.456,1495.969L1346.664,1495.735C1346.687,1495.710,1346.714,1495.684,1346.751,1495.660L1346.751,1495.660L1346.763,1495.652L1367.157,1481.722L1367.184,1481.704L1367.210,1481.684C1367.279,1481.632,1367.332,1481.618,1367.374,1481.616C1367.417,1481.613,1367.475,1481.623,1367.533,1481.654C1367.653,1481.718,1367.738,1481.841,1367.738,1482.000L1367.738,1510.014C1367.738,1510.174,1367.653,1510.297,1367.533,1510.360C1367.475,1510.391,1367.417,1510.401,1367.374,1510.399C1367.332,1510.396,1367.279,1510.383,1367.210,1510.330L1367.187,1510.311L1367.159,1510.294L1346.765,1496.287L1346.765,1496.287L1346.751,1496.278C1346.714,1496.254,1346.687,1496.228,1346.664,1496.203ZZ">
|
||||
</path>
|
||||
</g>
|
||||
<g id="strokes-b4e55d81-e415-8079-8003-291b04b22fb5" class="strokes">
|
||||
<g class="stroke-shape">
|
||||
<path rx="0" ry="0" style="stroke-width:1;stroke:currentcolor" d="M1344.650,1497.985L1344.650,1510.630C1344.650,1510.839,1344.475,1511.014,1344.265,1511.014L1339.647,1511.014C1339.437,1511.014,1339.262,1510.839,1339.262,1510.630L1339.262,1481.385C1339.262,1481.176,1339.437,1481.000,1339.647,1481.000L1344.265,1481.000C1344.475,1481.000,1344.650,1481.176,1344.650,1481.385L1344.650,1493.953L1344.650,1494.965L1344.650,1496.973L1344.650,1497.985ZZM1346.664,1496.203L1346.456,1495.969L1346.664,1495.735C1346.687,1495.710,1346.714,1495.684,1346.751,1495.660L1346.751,1495.660L1346.763,1495.652L1367.157,1481.722L1367.184,1481.704L1367.210,1481.684C1367.279,1481.632,1367.332,1481.618,1367.374,1481.616C1367.417,1481.613,1367.475,1481.623,1367.533,1481.654C1367.653,1481.718,1367.738,1481.841,1367.738,1482.000L1367.738,1510.014C1367.738,1510.174,1367.653,1510.297,1367.533,1510.360C1367.475,1510.391,1367.417,1510.401,1367.374,1510.399C1367.332,1510.396,1367.279,1510.383,1367.210,1510.330L1367.187,1510.311L1367.159,1510.294L1346.765,1496.287L1346.765,1496.287L1346.751,1496.278C1346.714,1496.254,1346.687,1496.228,1346.664,1496.203ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,23 @@
|
|||
<!-- Shape: svg-g -->
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" width="40" xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" id="screenshot-b4e55d81-e415-8079-8003-2824f34b3d55" version="1.1" viewBox="1357.5 1655 40 31" height="31">
|
||||
<g id="shape-b4e55d81-e415-8079-8003-2824f34b3d55" style="fill:currentcolor" filter="url(#rumext-id-449-filter6_b_71_6738)" rx="0" ry="0">
|
||||
<defs>
|
||||
<filter y="1501.2438669438664" data-old-width="357.2409927942415" filterUnits="userSpaceOnUse" width="357.2409927942415" class="svg-def" data-old-x="1198.877101681342" id="rumext-id-449-filter6_b_71_6738" data-old-height="338.51387733887873" x="1198.877101681342" data-old-y="1501.2438669438664" height="338.51387733887873" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix">
|
||||
</feFlood>
|
||||
<feGaussianBlur in="BackgroundImageFix" stdDeviation="40.7742">
|
||||
</feGaussianBlur>
|
||||
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_71_6738">
|
||||
</feComposite>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_71_6738" result="shape">
|
||||
</feBlend>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="shape-b4e55d81-e415-8079-8003-2824f34b3d56">
|
||||
<g class="fills" id="fills-b4e55d81-e415-8079-8003-2824f34b3d56">
|
||||
<path rx="0" ry="0" style="fill:currentcolor" d="M1376.363,1686.000C1377.644,1686.000,1378.589,1685.033,1378.589,1683.760L1378.589,1657.336C1378.589,1656.031,1377.644,1655.000,1376.315,1655.000C1375.450,1655.000,1374.826,1655.371,1373.881,1656.273L1366.691,1663.040C1366.579,1663.137,1366.451,1663.201,1366.291,1663.201L1361.423,1663.201C1358.877,1663.201,1357.500,1664.619,1357.500,1667.294L1357.500,1673.739C1357.500,1676.413,1358.877,1677.831,1361.423,1677.831L1366.291,1677.831C1366.451,1677.831,1366.563,1677.896,1366.691,1677.992L1373.881,1684.808C1374.746,1685.646,1375.466,1686.000,1376.363,1686.000ZZM1391.639,1682.842C1392.440,1683.325,1393.401,1683.148,1393.945,1682.343C1396.187,1679.152,1397.500,1674.883,1397.500,1670.484C1397.500,1666.085,1396.203,1661.799,1393.945,1658.625C1393.401,1657.820,1392.440,1657.626,1391.639,1658.126C1390.823,1658.641,1390.695,1659.689,1391.319,1660.607C1393.145,1663.298,1394.185,1666.810,1394.185,1670.484C1394.185,1674.141,1393.129,1677.654,1391.319,1680.361C1390.711,1681.279,1390.823,1682.310,1391.639,1682.842ZZM1384.738,1678.202C1385.474,1678.685,1386.435,1678.508,1386.964,1677.751C1388.357,1675.881,1389.189,1673.239,1389.189,1670.484C1389.189,1667.729,1388.357,1665.102,1386.964,1663.217C1386.435,1662.444,1385.474,1662.283,1384.738,1662.782C1383.889,1663.330,1383.713,1664.361,1384.402,1665.360C1385.346,1666.730,1385.891,1668.567,1385.891,1670.484C1385.891,1672.401,1385.330,1674.222,1384.402,1675.608C1383.729,1676.607,1383.889,1677.638,1384.738,1678.202ZZ">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,13 @@
|
|||
@import url(/fonts/stylesheet.css);
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 500;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Reset by Josh W Comeau
|
||||
https://www.joshwcomeau.com/css/custom-css-reset/
|
||||
*/
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
img, picture, video, canvas, svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
input, button, textarea, select {
|
||||
font: inherit;
|
||||
}
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
#root, #__next {
|
||||
isolation: isolate;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
@use 'reset.scss';
|
||||
@use 'fonts.scss';
|
||||
|
||||
body {
|
||||
font-family: 'Theinhardt', sans-serif;
|
||||
line-height: 1;
|
||||
font-size: 16px;
|
||||
|
||||
|
||||
--neutral-900: hsl(0 0% 0%);
|
||||
--neutral-700: hsl(220 10% 44%);
|
||||
--neutral-400: hsl(220 10% 88%);
|
||||
--neutral-200: hsl(230 30% 96%);
|
||||
--neutral-150: hsl(228 33% 94%);
|
||||
--neutral-100: hsl(0 100% 100%);
|
||||
|
||||
--green-500: hsl(147 100% 27%);
|
||||
--green-400: hsl(147 100% 34%);
|
||||
--green-200: hsl(147 100% 50%);
|
||||
|
||||
--neutral-gradient-400: linear-gradient(to bottom, hsl(228, 5%, 82%) 0%, var(--neutral-400) 100%);
|
||||
--green-gradient-400: linear-gradient(to bottom, var(--green-400) 0%, var(--green-500) 100%);
|
||||
|
||||
--box-shadow: 0 0 6px 2px #00000010;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
import { LitElement, css, html, unsafeCSS } from 'lit'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss'
|
||||
|
||||
import './SvgIcon.js'
|
||||
|
||||
class AudioCard extends LitElement {
|
||||
static properties = {
|
||||
idx: { type: Number },
|
||||
icon: { type: String },
|
||||
details: { type: Object },
|
||||
selected: { type: Boolean }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.details = {}
|
||||
this.icon = 'play-circle'
|
||||
}
|
||||
|
||||
clickHandler() {
|
||||
const event = new CustomEvent('select-audio', {
|
||||
bubbles: true, composed: true,
|
||||
detail: { ...this.details, idx: this.idx }
|
||||
})
|
||||
|
||||
this.dispatchEvent(event)
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="card ${this.selected ? 'selected' : ''}" @click=${this.clickHandler}>
|
||||
<mm-icon name=${this.icon}></mm-icon>
|
||||
|
||||
<main>
|
||||
<p class="heading">${this.details?.title}</p>
|
||||
<p class="info">${this.details?.details}</p>
|
||||
</main>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
mm-icon {
|
||||
color: var(--green-400, black);
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-template-columns: min-content 1fr;
|
||||
gap: 1em;
|
||||
align-items: center;
|
||||
padding-inline: 0.75em;
|
||||
padding-block: 0.5em;
|
||||
background: var(--neutral-100, white);
|
||||
min-height: 3.5em;
|
||||
border-radius: 0.5em;
|
||||
box-shadow: var(--box-shadow);
|
||||
outline: 2px solid transparent;
|
||||
transition: outline 0.5s ease;
|
||||
}
|
||||
|
||||
.card.selected {
|
||||
outline: 2px solid var(--green-400, black);
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-acard', AudioCard)
|
|
@ -0,0 +1,368 @@
|
|||
import { LitElement, css, html, unsafeCSS } from 'lit'
|
||||
import { Task } from '@lit-labs/task'
|
||||
import WaveSurfer from 'wavesurfer.js'
|
||||
import { formatSeconds } from '../api/utils'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
// temp bg image
|
||||
import Photo from '/images/Meredith Monk (1974) Photo Lauretta HArris.jpg?url'
|
||||
|
||||
// components
|
||||
import './SvgIcon.js'
|
||||
import './RangeSlider.js'
|
||||
|
||||
class AudioPlayer extends LitElement {
|
||||
static properties = {
|
||||
details: { type: Object },
|
||||
data: { state: true },
|
||||
playing: { state: true },
|
||||
volume: { state: true },
|
||||
track: { state: true },
|
||||
audio: { state: true },
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.details = {}
|
||||
this.data = {}
|
||||
this.audio = null
|
||||
this.playing = false
|
||||
this.audioEl = {}
|
||||
|
||||
this.track = 0
|
||||
}
|
||||
|
||||
_getAudio = new Task(
|
||||
this,
|
||||
async () => {
|
||||
if (!this.details.media) return
|
||||
|
||||
if (this.details.tracks) {
|
||||
console.log(this.details.tracks[0])
|
||||
|
||||
if (!this.audio) {
|
||||
this.initAudio()
|
||||
}
|
||||
|
||||
this.audio.load(this.details.tracks[this.track])
|
||||
|
||||
return this.audio
|
||||
} else {
|
||||
try {
|
||||
if (this.audio.media) {
|
||||
this.audio.destroy()
|
||||
}
|
||||
|
||||
const wsEl = this.initWs()
|
||||
|
||||
this.audio.load(`/media/${this.details.media}.mp3`, this.details.data)
|
||||
|
||||
return wsEl
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
() => [ this.details ]
|
||||
)
|
||||
|
||||
initAudio() {
|
||||
this.audio = document.createElement('audio')
|
||||
|
||||
|
||||
this.audio.addEventListener('loadstart', () => {
|
||||
this.loading = true
|
||||
})
|
||||
|
||||
this.audio.addEventListener('canplay', () => {
|
||||
this.loading = false
|
||||
})
|
||||
|
||||
this.audio.addEventListener('play', () => {
|
||||
this.playing = true
|
||||
})
|
||||
|
||||
this.audio.addEventListener('pause', () => {
|
||||
this.playing = false
|
||||
})
|
||||
|
||||
this.audio.addEventListener('loadedmetadata', () => {
|
||||
this.data = {
|
||||
...this.data,
|
||||
position: 0,
|
||||
duration: this.audio.duration
|
||||
}
|
||||
})
|
||||
|
||||
this.audio.addEventListener('ended', () => {
|
||||
// auto play next track or end if last track
|
||||
})
|
||||
|
||||
this.audio.addEventListener('timeupdate', () => {
|
||||
this.data = {
|
||||
...this.data,
|
||||
position: this.audio.currentTime
|
||||
}
|
||||
})
|
||||
|
||||
this.audio.addEventListener('seeking', () => {
|
||||
this.data = {
|
||||
...this.data,
|
||||
position: this.audio.currentTime
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
initWs() {
|
||||
const div = document.createElement('div')
|
||||
div.classList.add('waveform')
|
||||
|
||||
this.audio = WaveSurfer.create({
|
||||
container: div,
|
||||
waveColor: '#ffffff50',
|
||||
progressColor: 'white',
|
||||
cursorColor: 'transparent',
|
||||
fillParent: true,
|
||||
barWidth: 4,
|
||||
barGap: 6,
|
||||
barRadius: 5,
|
||||
dragToSeek: true,
|
||||
})
|
||||
|
||||
this.audio.setVolume(0.6)
|
||||
this.volume = this.audio.getVolume()
|
||||
|
||||
this.audio.on('ready', (duration) => {
|
||||
this.data = {
|
||||
...this.data,
|
||||
position: 0,
|
||||
duration
|
||||
}
|
||||
})
|
||||
|
||||
this.audio.on('play', () => {
|
||||
this.playing = true
|
||||
})
|
||||
|
||||
this.audio.on('pause', () => {
|
||||
this.playing = false
|
||||
})
|
||||
|
||||
this.audio.on('interaction', (newTime) => {
|
||||
this.data = {
|
||||
...this.data,
|
||||
position: newTime
|
||||
}
|
||||
})
|
||||
|
||||
this.audio.on('timeupdate', (currentTime) => {
|
||||
this.data = {
|
||||
...this.data,
|
||||
position: currentTime
|
||||
}
|
||||
})
|
||||
|
||||
return div
|
||||
}
|
||||
|
||||
togglePlay() {
|
||||
this.audio.playPause()
|
||||
}
|
||||
|
||||
skipForward() {
|
||||
this.audio.skip(15)
|
||||
}
|
||||
|
||||
skipBackward() {
|
||||
this.audio.skip(-15)
|
||||
}
|
||||
|
||||
nextTrack() {
|
||||
this.dispatchEvent(new Event('next-track', { bubbles: true, composed: true }))
|
||||
}
|
||||
|
||||
prevTrack() {
|
||||
this.dispatchEvent(new CustomEvent('prev-track', { bubbles: true, composed: true }))
|
||||
}
|
||||
|
||||
volHandler(evt) {
|
||||
if (evt.detail) {
|
||||
this.audio.setVolume(evt.detail.value)
|
||||
}
|
||||
}
|
||||
|
||||
progressCalc() {
|
||||
const { position, duration } = this.data
|
||||
let percentage = 0
|
||||
|
||||
if (position && duration) {
|
||||
percentage = (position / duration) * 100
|
||||
}
|
||||
|
||||
return `${percentage}%`
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<img class="bg-img" src=${Photo} />
|
||||
<header>
|
||||
<h2>${this.details.title}</h2>
|
||||
<p class="details">${this.details.details}</p>
|
||||
</header>
|
||||
|
||||
<div class="info">
|
||||
<div class="progress">
|
||||
<div class="bar" style="--progress-bar: ${this.progressCalc()}"></div>
|
||||
</div>
|
||||
|
||||
${this._getAudio.render({
|
||||
complete: (div) => div
|
||||
})}
|
||||
|
||||
<span class="time_pos">${formatSeconds(this.data.position)}</span> <span class="time_dur">${formatSeconds(this.data.duration)}</span>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<div>
|
||||
<mm-icon name="skip-prev" @click=${this.prevTrack} ?disabled=${!this.data.duration || this.getAudio?.status == 1}></mm-icon>
|
||||
<mm-icon name="skip-b15" @click=${this.skipBackward} ?disabled=${!this.data.duration || this.getAudio?.status == 1} class="skip15"></mm-icon>
|
||||
<mm-icon name="${this.playing ? 'pause' : 'play'}" @click=${this.togglePlay} ?disabled=${!this.data.duration || this.getAudio?.status == 1} class=${this.playing ? 'pause' : 'play'}></mm-icon>
|
||||
<mm-icon name="skip-f15" @click=${this.skipForward} ?disabled=${!this.data.duration || this.getAudio?.status == 1} class="skip15"></mm-icon>
|
||||
<mm-icon name="skip-next" @click=${this.nextTrack} ?disabled=${!this.data.duration || this.getAudio?.status == 1}></mm-icon>
|
||||
</div>
|
||||
|
||||
<mm-range
|
||||
value=${this.volume}
|
||||
@input=${this.volHandler}
|
||||
?disabled=${!this.data.duration || this.getAudio?.status == 1}
|
||||
style="--track-height: 0.4em; --thumb-size: 1.2em; --track-color: #ffffff50"
|
||||
></mm-range>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
:host {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
padding: 1em;
|
||||
display: grid;
|
||||
grid-template-rows: 0.7fr 0.7fr 1fr;
|
||||
gap: 1em;
|
||||
opacity: 0;
|
||||
animation: fadeIn 0.125s ease-in forwards;
|
||||
overflow: hidden;
|
||||
border-radius: 0.75em 0 0 0.75em;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.bg-img {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
object-fit: cover;
|
||||
object-position: center 25%;
|
||||
opacity: 0.2;
|
||||
mix-blend-mode: overlay;
|
||||
scale: 1.1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
color: var(--netural-100, white);
|
||||
font-size: 1.5em;
|
||||
line-height: 1.2;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
header > p {
|
||||
font-size: 1.5em;
|
||||
max-width: 25ch;
|
||||
}
|
||||
|
||||
.info, .controls {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress {
|
||||
--progress-bar: 0%;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
inset: 0;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.progress > .bar {
|
||||
height: 0.25em;
|
||||
width: var(--progress-bar);
|
||||
background: #ffffff80;
|
||||
border-radius: 100vmax;
|
||||
}
|
||||
|
||||
.time_pos, .time_dur {
|
||||
font-family: sans-serif;
|
||||
font-size: 0.85em;
|
||||
color: var(--neutral-100, white);
|
||||
position: absolute;
|
||||
bottom: 1em;
|
||||
}
|
||||
|
||||
.time_dur {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
width: 65%;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.controls > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
margin-block-end: 1em;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
mm-icon {
|
||||
color: white;
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
mm-icon.skip15 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
mm-icon:not(.skip15, .play, .pause) {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
mm-volctrl {
|
||||
display: block;
|
||||
width: 80%;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.waveform {
|
||||
position: relative;
|
||||
height: 15em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-aplayer', AudioPlayer)
|
|
@ -0,0 +1,50 @@
|
|||
import { LitElement, html, css, unsafeCSS } from 'lit'
|
||||
|
||||
import './SvgIcon.js'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
class Button extends LitElement {
|
||||
static properties = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<button>
|
||||
<mm-icon name="back-arrow"></mm-icon>
|
||||
<span><slot></slot></span>
|
||||
</button>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
:host {
|
||||
--primary-color: currentcolor;
|
||||
--background-color: lightgrey;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25em;
|
||||
padding-block: 0.75em;
|
||||
padding-inline-start: 1em;
|
||||
padding-inline-end: 1.25em;
|
||||
border-radius: 0.25em;
|
||||
color: var(--primary-color);
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
span {
|
||||
text-transform: capitalize;
|
||||
font-weight: 500;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-button', Button)
|
|
@ -0,0 +1,71 @@
|
|||
import { LitElement, css, html, unsafeCSS } from 'lit'
|
||||
import Router from '../api/Router'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
import './SvgIcon.js'
|
||||
|
||||
class Footer extends LitElement {
|
||||
static properties = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<footer>
|
||||
<nav>
|
||||
${Router.routes.map(r => !r.hide ? html`
|
||||
<a href=${r.path} class=${r.path == Router.route.path ? 'selected' : ''}>
|
||||
<mm-icon name=${r.icon}></mm-icon>
|
||||
${r.short}
|
||||
</a>` : '') }
|
||||
</nav>
|
||||
</footer>`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
:host {
|
||||
background: var(--neutral-200, white);
|
||||
box-shadow: 0 0.5em 10px 10px #00000020;
|
||||
}
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
color: var(--green-400, blue);
|
||||
text-decoration: none;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
a > svg {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
margin-inline-end: 0.5em;
|
||||
}
|
||||
|
||||
a.selected {
|
||||
color: var(--neutral-900, black);
|
||||
}
|
||||
|
||||
.unicon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
outline: thin solid red;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding-inline: 0.5em;
|
||||
padding-block-start: 0.5em;
|
||||
padding-block-end: 0.75em;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-footer', Footer)
|
|
@ -0,0 +1,57 @@
|
|||
import { LitElement, html, css, unsafeCSS } from 'lit'
|
||||
import Router from '../api/Router.js'
|
||||
|
||||
import './Button.js'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
class Header extends LitElement {
|
||||
static properties = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
navHome() {
|
||||
Router.navigate('/')
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<header>
|
||||
<div>
|
||||
<mm-button @click=${this.navHome}>back</mm-button>
|
||||
</div>
|
||||
|
||||
<h1>${Router.route.title || ''}</h1>
|
||||
|
||||
</header>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
header {
|
||||
padding-inline: 0.5em;
|
||||
padding-block-end: 0.5em;
|
||||
}
|
||||
|
||||
header > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
mm-button {
|
||||
--primary-color: var(--green-400);
|
||||
--background-color: var(--neutral-400);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
margin-block: 0.25em;
|
||||
font-size: 1.75em;
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-header', Header)
|
|
@ -0,0 +1,40 @@
|
|||
import { LitElement, css, html } from 'lit'
|
||||
|
||||
class HorizontalScroller extends LitElement {
|
||||
static properties = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="scroller">
|
||||
<slot></slot>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
--col-width: 10em;
|
||||
--gap: 1em;
|
||||
}
|
||||
|
||||
.scroller {
|
||||
display: grid;
|
||||
gap: var(--gap);
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: var(--col-width);
|
||||
width: calc(100vw - 1em);
|
||||
overflow: auto;
|
||||
scroll-behaviour: smooth;
|
||||
scroll-padding-inline: 0.5em;
|
||||
padding-inline-start: 0.5em;
|
||||
padding-inline-end: 0.5em;
|
||||
padding-block-end: 0.75em;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
customElements.define('mm-hscroller', HorizontalScroller)
|
|
@ -0,0 +1,87 @@
|
|||
import { LitElement, css, html, unsafeCSS } from 'lit'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
class ImageViewer extends LitElement {
|
||||
static properties = {
|
||||
src: { type: String },
|
||||
imgEl: { state: true },
|
||||
captionWidth: { state: true },
|
||||
loading: { state: true }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.src = ''
|
||||
this.imgEl = new Image()
|
||||
this.captionWidth = 'min-content'
|
||||
this.loading = true
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.imgEl.addEventListener('load', () => {
|
||||
this.captionWidth = `${this.imgEl.offsetWidth}px`
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
set src(val) {
|
||||
if (val) {
|
||||
this.imgEl.src = val
|
||||
console.log(this.imgEl)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<figure class="${this.loading ? 'loading' : ''}">
|
||||
<picture>
|
||||
${this.imgEl}
|
||||
</picture>
|
||||
|
||||
<figcaption style="--width: ${this.captionWidth}">
|
||||
Image Description
|
||||
</figcaption>
|
||||
</figure>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
figure {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 4em;
|
||||
justify-content: center;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.loading {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
picture {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
padding-block: 0.75em;
|
||||
width: var(--width);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-imageviewer', ImageViewer)
|
|
@ -0,0 +1,88 @@
|
|||
import { LitElement, html, css } from 'lit'
|
||||
|
||||
class Loading extends LitElement {
|
||||
static properties = {
|
||||
nobg: { type: Boolean }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.nobg = false
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class=${this.nobg ? '' : 'pulse'}>
|
||||
<span class="loading">Loading<span class="d1">.</span><span class="d2">.</span><span class="d3">.</span></span>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
--fade-duration: 0.125s;
|
||||
--pulse-duration: 0.75s;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div.pulse {
|
||||
opacity: 0;
|
||||
background-color: #ffffff60;
|
||||
animation:
|
||||
fadeIn var(--fade-duration) ease-in forwards,
|
||||
pulse var(--pulse-duration) ease-in-out alternate infinite;
|
||||
}
|
||||
|
||||
.loading {
|
||||
--dot-duration: 1s;
|
||||
font-size: 2em;
|
||||
font-style: italic;
|
||||
font-weight: 200;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.d1, .d2, .d3 {
|
||||
opacity: 0;
|
||||
animation: dot var(--dot-duration) alternate infinite;
|
||||
}
|
||||
|
||||
.d2 {
|
||||
animation-delay: calc(var(--dot-duration) * 0.3);
|
||||
}
|
||||
|
||||
.d3 {
|
||||
animation-delay: calc(calc(var(--dot-duration) * 0.3) * 2);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dot {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
from {
|
||||
background-color: #ffffff20;
|
||||
}
|
||||
to {
|
||||
background-color: #ffffff00;
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
customElements.define('mm-loading', Loading)
|
|
@ -0,0 +1,584 @@
|
|||
import { LitElement, html, css,unsafeCSS } from 'lit'
|
||||
import { Task } from '@lit-labs/task'
|
||||
import { formatSeconds } from '../api/utils'
|
||||
import WaveSurfer from 'wavesurfer.js'
|
||||
|
||||
import Photo from '/images/Meredith Monk (1974) Photo Lauretta Harris.jpg'
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
import './RangeSlider.js'
|
||||
import './Loading.js'
|
||||
|
||||
class ModularPlayer extends LitElement {
|
||||
static properties = {
|
||||
// property
|
||||
details: { type: Object },
|
||||
|
||||
// state
|
||||
duration: { state: true },
|
||||
position: { state: true },
|
||||
playing: { state: true },
|
||||
volume: { state: true },
|
||||
track: { state: true },
|
||||
loading: { state: true },
|
||||
|
||||
// audio element
|
||||
audio: { state: true },
|
||||
ws: { state: true }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.details = {}
|
||||
this.audio = null
|
||||
this.ws = null
|
||||
|
||||
this.track = 0
|
||||
this.position = 0
|
||||
this.duration = 0
|
||||
this.loading = true
|
||||
|
||||
this.initAudio()
|
||||
}
|
||||
|
||||
_getTrack = new Task(
|
||||
this,
|
||||
async () => {
|
||||
if (this.details.tracks) {
|
||||
this.audio.src = `/media${this.details.media}/${this.details.tracks[this.track]}`
|
||||
} else {
|
||||
this.audio.src = `/media${this.details.media}.mp3`
|
||||
}
|
||||
this.audio.load()
|
||||
if (this.playing && this.audio.paused) {
|
||||
this.audio.play()
|
||||
}
|
||||
},
|
||||
() => [ this.track ]
|
||||
)
|
||||
|
||||
_renderWave = new Task(
|
||||
this,
|
||||
{
|
||||
task: async () => {
|
||||
return await this.initWs(this.details.data)
|
||||
},
|
||||
autoRun: false
|
||||
}
|
||||
)
|
||||
|
||||
initAudio() {
|
||||
this.audio = document.createElement('audio')
|
||||
|
||||
if (!localStorage.getItem('deviceVolume')) {
|
||||
localStorage.setItem('deviceVolume', 0.6)
|
||||
}
|
||||
|
||||
this.audio.addEventListener('loadstart', () => {
|
||||
this.loading = true
|
||||
this.audio.volume = localStorage.getItem('deviceVolume')
|
||||
this.volume = this.audio.volume
|
||||
})
|
||||
|
||||
this.audio.addEventListener('canplay', () => {
|
||||
if (this.details.tracks) {
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
|
||||
this.audio.addEventListener('play', () => {
|
||||
this.playing = true
|
||||
})
|
||||
|
||||
this.audio.addEventListener('pause', () => {
|
||||
this.playing = false
|
||||
})
|
||||
|
||||
this.audio.addEventListener('loadedmetadata', () => {
|
||||
this.position = 0
|
||||
this.duration = this.audio.duration
|
||||
|
||||
if (this.duration > 0) {
|
||||
this._renderWave.run()
|
||||
}
|
||||
})
|
||||
|
||||
this.audio.addEventListener('ended', () => {
|
||||
this.audio.currentTime = 0
|
||||
this.position = this.audio.currentTime
|
||||
|
||||
if (this.tracks) {
|
||||
this.track += 1
|
||||
if (this.track >= this.details.tracks.length) {
|
||||
this.track = 0
|
||||
}
|
||||
|
||||
this.audio.src = `/media${this.details.media}/${this.details.tracks[this.track]}`
|
||||
this.audio.load()
|
||||
}
|
||||
})
|
||||
|
||||
this.audio.addEventListener('timeupdate', () => {
|
||||
this.position = this.audio.currentTime
|
||||
})
|
||||
|
||||
this.audio.addEventListener('seeking', () => {
|
||||
this.position = this.audio.currentTime
|
||||
})
|
||||
}
|
||||
|
||||
initWs(peakData) {
|
||||
return new Promise((go, no) => {
|
||||
try {
|
||||
const div = document.createElement('div')
|
||||
div.classList.add('waveform')
|
||||
|
||||
this.ws = WaveSurfer.create({
|
||||
container: div,
|
||||
waveColor: '#ffffff50',
|
||||
progressColor: 'white',
|
||||
cursorColor: 'transparent',
|
||||
fillParent: true,
|
||||
normalize: true,
|
||||
barWidth: 4,
|
||||
barGap: 6,
|
||||
barRadius: 5,
|
||||
dragToSeek: true,
|
||||
media: this.audio,
|
||||
peaks: peakData
|
||||
})
|
||||
|
||||
this.ws.on('interaction', (currentTime) => {
|
||||
this.position = currentTime
|
||||
})
|
||||
|
||||
this.ws.on('ready', () => {
|
||||
setTimeout(() => go(div), 500)
|
||||
})
|
||||
} catch (err) {
|
||||
no(new Error(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
togglePlay() {
|
||||
if (this.audio.paused) {
|
||||
this.playing = true
|
||||
this.audio.play()
|
||||
} else {
|
||||
this.playing = false
|
||||
this.audio.pause()
|
||||
}
|
||||
}
|
||||
|
||||
seekTrack({ detail }) {
|
||||
if (detail) {
|
||||
const { value } = detail
|
||||
this.audio.currentTime = value
|
||||
this.position = value
|
||||
}
|
||||
}
|
||||
|
||||
skipForward() {
|
||||
this.audio.currentTime = this.audio.currentTime + 15
|
||||
}
|
||||
|
||||
skipBackward() {
|
||||
this.audio.currentTime = this.audio.currentTime - 15
|
||||
}
|
||||
|
||||
selectTrack(evt) {
|
||||
const trackNumber = evt.target.dataset.trackNumber
|
||||
this.track = parseInt(trackNumber)
|
||||
}
|
||||
|
||||
nextTrack() {
|
||||
if (this.details.tracks) {
|
||||
if (this.track + 1 < this.details.tracks.length) {
|
||||
this.track += 1
|
||||
}
|
||||
} else {
|
||||
this.dispatchEvent(new Event('next-track', { bubbles: true, composed: true }))
|
||||
}
|
||||
}
|
||||
|
||||
prevTrack() {
|
||||
if (this.details.tracks) {
|
||||
if (this.track - 1 >= 0) {
|
||||
this.track -= 1
|
||||
}
|
||||
} else {
|
||||
this.dispatchEvent(new Event('prev-track', { bubbles: true, composed: true }))
|
||||
}
|
||||
}
|
||||
|
||||
volHandler(evt) {
|
||||
if (evt.detail) {
|
||||
this.audio.volume = evt.detail.value
|
||||
localStorage.setItem('deviceVolume', evt.detail.value)
|
||||
}
|
||||
}
|
||||
|
||||
progressCalc() {
|
||||
let percentage = 0
|
||||
|
||||
if (this.position && this.duration) {
|
||||
percentage = (this.position / this.duration) * 100
|
||||
}
|
||||
|
||||
return `${percentage}%`
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="player">
|
||||
${this.details.tracks ? html`
|
||||
<div class="tracklist">
|
||||
<h2>${this.details.title}</h2>
|
||||
|
||||
<div class="list">
|
||||
<ul>
|
||||
${this.details.tracks.map((t, i) => html`
|
||||
<li class="${this.track === i ? 'selected' : ''}" data-track-number=${i} @click=${this.selectTrack}>
|
||||
<mm-icon name="notes" class=${this.playing ? 'playing' : ''}></mm-icon>
|
||||
<span>${t}</span>
|
||||
</li>`)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
` : html`<img class="bg-img" src=${Photo} />`}
|
||||
|
||||
<header>
|
||||
<h2>${this.details.tracks ? this.details?.tracks[this.track] : this.details.title}</h2>
|
||||
${this.details.tracks ? '' : html`<p class="details">${this.details.details}</p>`}
|
||||
</header>
|
||||
|
||||
<div class="info">
|
||||
${this._getTrack.render({
|
||||
complete: () => html`
|
||||
<span class="time_pos">${formatSeconds(this.position)}</span>
|
||||
|
||||
${!this.details.tracks ?
|
||||
this._renderWave.render({
|
||||
initial: () => html`<mm-loading class="waveform" nobg></mm-loading>`,
|
||||
pending: () => html`<mm-loading class="waveform" nobg></mm-loading>`,
|
||||
complete: (ws) => html`
|
||||
<div class="progress">
|
||||
<div class="bar" style="--progress-bar: ${this.progressCalc()}">
|
||||
<div class="blob"></div>
|
||||
</div>
|
||||
</div>
|
||||
${ws}`
|
||||
}) :
|
||||
html`<mm-range
|
||||
value=${this.position}
|
||||
max=${this.duration}
|
||||
step="0.1"
|
||||
@input=${this.seekTrack}
|
||||
></mm-range>`
|
||||
}
|
||||
|
||||
<span class="time_dur">${formatSeconds(this.duration)}</span>
|
||||
`
|
||||
})}
|
||||
|
||||
${this.audio}
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="buttons">
|
||||
<mm-icon name="skip-prev" @click=${this.prevTrack} ?disabled=${!this.duration || this.getAudio?.status == 1}></mm-icon>
|
||||
<mm-icon name="skip-b15" @click=${this.skipBackward} ?disabled=${!this.duration || this.getAudio?.status == 1} class="skip15"></mm-icon>
|
||||
<mm-icon name="${this.playing ? 'pause' : 'play'}" @click=${this.togglePlay} ?disabled=${!this.duration || this.getAudio?.status == 1} class=${this.playing ? 'pause' : 'play'}></mm-icon>
|
||||
<mm-icon name="skip-f15" @click=${this.skipForward} ?disabled=${!this.duration || this.getAudio?.status == 1} class="skip15"></mm-icon>
|
||||
<mm-icon name="skip-next" @click=${this.nextTrack} ?disabled=${!this.duration || this.getAudio?.status == 1}></mm-icon>
|
||||
</div>
|
||||
|
||||
<div class="volume">
|
||||
<mm-icon name="volume"></mm-icon>
|
||||
<mm-range
|
||||
value=${this.volume}
|
||||
@input=${this.volHandler}
|
||||
?disabled=${!this.duration || this.getAudio?.status == 1}
|
||||
></mm-range>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
:host {
|
||||
--padding: 1rem;
|
||||
--tracklist-background: lightgrey;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.player {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
gap: 1em;
|
||||
opacity: 0;
|
||||
animation: fadeIn 0.125s ease-in forwards;
|
||||
overflow: hidden;
|
||||
border-radius: 0.75em 0 0 0.75em;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.player:has(.tracklist) {
|
||||
grid-template-rows: 0.7fr 0.5fr 0.3fr 1fr;
|
||||
padding-block-end: 2em;
|
||||
gap: 0;
|
||||
|
||||
& header {
|
||||
margin-block-start: 1.5em;
|
||||
margin-block-end: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.player:has(.waveform) {
|
||||
grid-template-rows: 0.7fr 0.7fr 1fr;
|
||||
|
||||
& header {
|
||||
padding-block-start: 1em;
|
||||
padding-inline: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-img {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
object-fit: cover;
|
||||
object-position: center 25%;
|
||||
opacity: 0.2;
|
||||
mix-blend-mode: overlay;
|
||||
scale: 1.1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
color: var(--netural-100, white);
|
||||
font-size: 1.5em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
header > p {
|
||||
font-size: 1.5em;
|
||||
max-width: 25ch;
|
||||
}
|
||||
|
||||
.tracklist + header {
|
||||
text-align: center;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.tracklist {
|
||||
--tracklist-padding: calc(var(--padding) * 2);
|
||||
position: relative;
|
||||
background: var(--neutral-gradient-400);
|
||||
height: 20em;
|
||||
|
||||
> h2 {
|
||||
margin-inline: var(--tracklist-padding);
|
||||
margin-block-start: 0.75em;
|
||||
padding-block-start: 0.75em;
|
||||
border-block-start: thin solid var(--green-400);
|
||||
}
|
||||
|
||||
> .list {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
top: calc(var(--tracklist-padding) * 2.5);
|
||||
right: var(--tracklist-padding);
|
||||
left: var(--tracklist-padding);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
border-radius: 0.75em 0.75em 0 0;
|
||||
background: var(--neutral-150);
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
.list ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin-block: 0;
|
||||
margin-inline: 0;
|
||||
|
||||
& li {
|
||||
display: grid;
|
||||
grid-template-columns: 1em auto;
|
||||
gap: 0.5em;
|
||||
padding-block: 0.5em;
|
||||
padding-inline: 0.5em;
|
||||
|
||||
&:nth-child(2n-1) {
|
||||
box-shadow: var(--box-shadow);
|
||||
background: var(--neutral-100);
|
||||
}
|
||||
|
||||
& > * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
& > mm-icon {
|
||||
font-size: 1em;
|
||||
opacity: 0;
|
||||
color: var(--neutral-700);
|
||||
transition: opacity 0.25s ease, color 0.25s ease;
|
||||
|
||||
&.playing {
|
||||
color: var(--green-400);
|
||||
}
|
||||
}
|
||||
|
||||
&.selected > mm-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info, .controls {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding-inline: var(--padding);
|
||||
}
|
||||
|
||||
mm-range {
|
||||
--track-color: #ffffff50
|
||||
}
|
||||
|
||||
.info:not(:has(.waveform)) {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
align-items: center;
|
||||
|
||||
|
||||
& > mm-range {
|
||||
flex-grow: 1;
|
||||
--track-height: 0.4em;
|
||||
--thumb-size: 0;
|
||||
}
|
||||
|
||||
& .time_dur {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.info:has(.waveform) {
|
||||
& .time_pos,
|
||||
& .time_dur {
|
||||
position: absolute;
|
||||
bottom: var(--padding);
|
||||
}
|
||||
|
||||
& .time_dur {
|
||||
right: var(--padding);
|
||||
}
|
||||
}
|
||||
|
||||
.time_pos,
|
||||
.time_dur {
|
||||
font-family: sans-serif;
|
||||
font-size: 0.85em;
|
||||
color: var(--neutral-100, white);
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
width: 65%;
|
||||
margin-inline: auto;
|
||||
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
margin-block-end: 1em;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
& .volume {
|
||||
display: flex;
|
||||
width: 70%;
|
||||
align-self: center;
|
||||
gap: 0.75em;
|
||||
|
||||
& mm-icon {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
& mm-range {
|
||||
--track-height: 0.25em;
|
||||
--thumb-size: 0.8em;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
--progress-bar: 0%;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
inset: 0;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
padding-inline: var(--padding);
|
||||
|
||||
> .bar {
|
||||
--bar-height: 0.25em;
|
||||
height: var(--bar-height);
|
||||
width: var(--progress-bar);
|
||||
background: #ffffff80;
|
||||
border-radius: 100vmax;
|
||||
position: relative;
|
||||
|
||||
> .blob {
|
||||
position: absolute;
|
||||
top: calc(var(--bar-height) * -0.5);
|
||||
right: calc(var(--bar-height) * -1);
|
||||
width: 0.5em;
|
||||
height: 0.5em;
|
||||
border-radius: 100vmax;
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mm-icon {
|
||||
color: white;
|
||||
font-size: 2.5em;
|
||||
|
||||
&.skip15 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
&:not(.skip15, .play, .pause) {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.waveform {
|
||||
position: absolute;
|
||||
inset: 0 var(--padding);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-audio-player', ModularPlayer)
|
|
@ -0,0 +1,67 @@
|
|||
import { LitElement, html, css, unsafeCSS } from 'lit'
|
||||
import Router from '../api/Router'
|
||||
|
||||
import './SvgIcon.js'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
class NavCard extends LitElement {
|
||||
static properties = {
|
||||
route: { type: Object }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.route = {}
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.shadowRoot.host.addEventListener('click', () => {
|
||||
Router.navigate(this.route.path)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="card">
|
||||
<mm-icon name=${this.route?.icon}></mm-icon>
|
||||
<p class="title">${this.route?.title}</p>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [css`${unsafeCSS(MainCSS)}`, css`
|
||||
:host {
|
||||
--highlight-color: black;
|
||||
--shadow: 0 0 0px transparent;
|
||||
height: 100%;
|
||||
background: linear-gradient(to bottom, rgb(255 255 255 / 0.9) 0%, rgb(159 159 159 / 0.6) 100%);
|
||||
border-radius: 0.75em;
|
||||
padding: 1em;
|
||||
padding-block-start: 1.25em;
|
||||
box-shadow: 0px 0px 10px #00000060;
|
||||
text-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.card {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
border-top: thin solid var(--highlight-color);
|
||||
padding-block-start: 0.3em;
|
||||
cursor: pointer;
|
||||
font-size: 1.65em;
|
||||
}
|
||||
|
||||
mm-icon {
|
||||
--svg-shadow: var(--shadow);
|
||||
margin-inline-end: 0.5em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.title {
|
||||
max-width: 12ch;
|
||||
}
|
||||
`]
|
||||
}
|
||||
|
||||
customElements.define('mm-ncard', NavCard)
|
|
@ -0,0 +1,117 @@
|
|||
import { LitElement, css, html, unsafeCSS } from 'lit'
|
||||
import { live } from 'lit/directives/live.js'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
class RangeSlider extends LitElement {
|
||||
static properties = {
|
||||
min: { type: Number },
|
||||
max: { type: Number },
|
||||
value: { type: Number },
|
||||
step: { type: Number },
|
||||
disabled: { type: Boolean },
|
||||
thumb: { type: Boolean },
|
||||
sliderEl: { state: true }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.min = 0
|
||||
this.max = 1
|
||||
this.value = 0
|
||||
this.step = 0.01
|
||||
this.disabled = false
|
||||
this.thumb = true
|
||||
this.sliderEl = null
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.sliderEl = this.shadowRoot.querySelector('input')
|
||||
this.sliderEl.style.setProperty('--progress', `${(this.sliderEl.value / this.sliderEl.max) * 100}%`)
|
||||
}
|
||||
|
||||
draw({ target: sliderEl }) {
|
||||
const progress = (sliderEl.value / sliderEl.max) * 100
|
||||
sliderEl.style.setProperty('--progress', `${progress}%`)
|
||||
|
||||
const event = new CustomEvent('input', {
|
||||
bubbles: true, composed: true,
|
||||
detail: { value: sliderEl.value }
|
||||
})
|
||||
|
||||
this.dispatchEvent(event)
|
||||
}
|
||||
|
||||
willUpdate(changedProperties) {
|
||||
if (changedProperties.has('value') && this.sliderEl) {
|
||||
const progress = (this.value / this.max) * 100
|
||||
this.sliderEl.style.setProperty('--progress', `${progress}%`)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<input type="range"
|
||||
min=${live(this.min || 0)}
|
||||
.max=${live(this.max || 1)}
|
||||
.step=${live(this.step || 0.01)}
|
||||
.value=${live(this.value || 0)}
|
||||
@input=${this.draw}
|
||||
></input>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
:host {
|
||||
--thumb-size: 1.5em;
|
||||
--track-height: 0.5em;
|
||||
--track-progress: white;
|
||||
--track-color: grey;
|
||||
--thumb-color: white;
|
||||
--progress: 0%;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
appearance: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
border-radius: 100vmax;
|
||||
|
||||
height: var(--track-height);
|
||||
background: linear-gradient(to right, var(--track-progress), var(--progress), var(--track-color) var(--progress));
|
||||
}
|
||||
|
||||
input::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: var(--thumb-size);
|
||||
aspect-ratio: 1;
|
||||
background-color: var(--thumb-color);
|
||||
border-radius: 100vmax;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: var(--thumb-size);
|
||||
aspect-ratio: 1;
|
||||
background-color: var(--thumb-color);
|
||||
border-radius: 100vmax;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input::-moz-range-thumb {
|
||||
appearance: none;
|
||||
height: var(--thumb-size);
|
||||
aspect-ratio: 1;
|
||||
background-color: var(--thumb-color);
|
||||
border-radius: 100vmax;
|
||||
border: none;
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-range', RangeSlider)
|
|
@ -0,0 +1,47 @@
|
|||
import { LitElement, css, html } from 'lit'
|
||||
import { unsafeSVG } from 'lit/directives/unsafe-svg.js'
|
||||
|
||||
class SvgIcon extends LitElement {
|
||||
static properties = {
|
||||
name: { type: String },
|
||||
element: { state: true },
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.name = ''
|
||||
}
|
||||
|
||||
attributeChangedCallback(att, _, nv) {
|
||||
if (att === 'name' && nv) {
|
||||
this.loadSVG(nv)
|
||||
}
|
||||
}
|
||||
|
||||
async loadSVG(name) {
|
||||
const { default: svg } = await import(`../assets/icons/${name}.svg?raw`)
|
||||
this.element = html`${unsafeSVG(svg)}`
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.element
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
--svg-shadow: 0px 0px 0px transparent;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: currentcolor;
|
||||
display: block;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
filter: drop-shadow(var(--svg-shadow));
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
customElements.define('mm-icon', SvgIcon)
|
|
@ -0,0 +1,98 @@
|
|||
import { LitElement, html, css, unsafeCSS } from 'lit'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
class VerticalCard extends LitElement {
|
||||
static properties = {
|
||||
details: { type: Object },
|
||||
selected: { type: Boolean }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.details = {}
|
||||
this.selected = false
|
||||
}
|
||||
|
||||
select() {
|
||||
const event = new CustomEvent('video-select', {
|
||||
bubbles: true, composed: true,
|
||||
detail: {
|
||||
...this.details
|
||||
}
|
||||
})
|
||||
|
||||
this.dispatchEvent(event)
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div @click=${this.select} class=${this.selected ? 'selected' : ''}>
|
||||
<picture>
|
||||
<img src="/media/documentaries_interviews/thumbs/girlchild.png">
|
||||
</picture>
|
||||
<aside>
|
||||
<p class="title">${this.details?.title}</p>
|
||||
<p class="detail">${this.details?.detail}</p>
|
||||
</aside>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
:host {
|
||||
--background-color: lightgrey;
|
||||
--font-color: black;
|
||||
--image-placeholder: lightgrey;
|
||||
--color: black;
|
||||
}
|
||||
|
||||
div {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: auto 0.2fr;
|
||||
background: lightgrey;
|
||||
line-height: 1;
|
||||
overflow: hidden;
|
||||
border-radius: var(--border-radius, 0.75em);
|
||||
background: var(--background-color);
|
||||
color: var(--font-color);
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.selected {
|
||||
outline: 2px solid var(--color);
|
||||
}
|
||||
|
||||
picture {
|
||||
display: block;
|
||||
background: var(--image-placeholder);
|
||||
aspect-ratio: 1 / 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
picture > img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
aside {
|
||||
padding: 0.5em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: 0.7em;
|
||||
line-height: 1.2;
|
||||
margin-block-start: 0.125em;
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-vcard', VerticalCard)
|
|
@ -0,0 +1,235 @@
|
|||
import { LitElement, css, html } from 'lit'
|
||||
|
||||
import './RangeSlider.js'
|
||||
import './SvgIcon.js'
|
||||
import { formatSeconds } from '../api/utils.js'
|
||||
|
||||
class VideoPlayer extends LitElement {
|
||||
static properties = {
|
||||
media: { type: String },
|
||||
autoplay: { type: Boolean },
|
||||
videoEl: { state: true },
|
||||
playing: { state: true },
|
||||
controls: { state: true },
|
||||
ctrlTimer: { state: true },
|
||||
position: { state: true },
|
||||
duration: { state: true },
|
||||
remain: { state: true }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.media = ''
|
||||
this.hostHeight = 0
|
||||
this.hostWidth = 0
|
||||
this.playing = false
|
||||
this.controls = true
|
||||
this.ctrlTimer = {}
|
||||
this.position = 0
|
||||
this.duration = 0
|
||||
this.remain = true
|
||||
this.autoplay = false
|
||||
this.videoEl = document.createElement('video')
|
||||
this.videoEl.playsInline = true
|
||||
this.videoEl.preload = 'metadata'
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.videoEl.addEventListener('loadeddata', () => {
|
||||
this.duration = this.videoEl.duration
|
||||
})
|
||||
|
||||
this.videoEl.addEventListener('timeupdate', () => {
|
||||
this.position = this.videoEl.currentTime
|
||||
})
|
||||
}
|
||||
|
||||
willUpdate(att) {
|
||||
if (att.has('media')) {
|
||||
this.videoEl.src = this.media
|
||||
|
||||
if (this.autoplay) {
|
||||
this.togglePlay()
|
||||
this.interact()
|
||||
}
|
||||
}
|
||||
this.update()
|
||||
}
|
||||
|
||||
togglePlay() {
|
||||
if (this.playing) {
|
||||
this.videoEl.pause()
|
||||
} else {
|
||||
this.videoEl.play()
|
||||
}
|
||||
this.playing = !this.playing
|
||||
}
|
||||
|
||||
interact() {
|
||||
if (this.playing) {
|
||||
// show controls
|
||||
this.controls = true
|
||||
clearTimeout(this.ctrlTimer)
|
||||
|
||||
// timeout
|
||||
this.ctrlTimer = setTimeout(() => {
|
||||
if (this.playing) {
|
||||
this.controls = false
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
toggleFullscreen() {
|
||||
const container = this.shadowRoot.querySelector('.videoContainer')
|
||||
|
||||
if (container.webkitSupportsFullscreen) {
|
||||
container.webkitEnterFullscreen()
|
||||
return
|
||||
}
|
||||
|
||||
if (document.fullscreenElement !== null) {
|
||||
document.exitFullscreen()
|
||||
container.setAttribute('data-fullscreen', false)
|
||||
} else {
|
||||
container.requestFullscreen()
|
||||
container.setAttribute('data-fullscreen', true)
|
||||
}
|
||||
}
|
||||
|
||||
toggleDuration() {
|
||||
console.log(this.remain)
|
||||
this.remain = !this.remain
|
||||
}
|
||||
|
||||
seek({ detail }) {
|
||||
if (detail) {
|
||||
this.interact()
|
||||
|
||||
this.videoEl.currentTime = detail.value
|
||||
this.position = detail.value
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="videoContainer">
|
||||
${this.videoEl}
|
||||
|
||||
<div class="touchbox" @click=${this.interact}>
|
||||
<div class="playPause ${this.controls ? '' : 'invisible'}" @click=${this.togglePlay}>
|
||||
<mm-icon class="play ${this.playing ? '' : 'invisible'}" name="pause"></mm-icon>
|
||||
<mm-icon class="pause ${this.playing ? 'invisible' : ''}" name="play"></mm-icon>
|
||||
</div>
|
||||
|
||||
<div class="controls ${this.controls ? '' : 'invisible'}">
|
||||
|
||||
<mm-icon @click=${this.toggleFullscreen} class="fullscreen" name="fullscreen"></mm-icon>
|
||||
|
||||
<mm-range
|
||||
value="${this.position}"
|
||||
max="${this.duration}"
|
||||
step="0.1"
|
||||
@input=${this.seek}
|
||||
></mm-range>
|
||||
|
||||
<div class="time">
|
||||
<span class="pos">${formatSeconds(this.position)}</span>
|
||||
<span class="dur" @click=${this.toggleDuration}>${ this.remain ? `-${formatSeconds(this.duration - this.position)}` : formatSeconds(this.duration)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
video {
|
||||
position: absolute;
|
||||
display: block;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: black;
|
||||
}
|
||||
|
||||
.controls {
|
||||
position: absolute;
|
||||
padding-inline: 0.75em;
|
||||
padding-block: 0.5em;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 5;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.time {
|
||||
display: flex;
|
||||
color: white;
|
||||
font-size: 0.65em;
|
||||
padding-inline: 0.125em;
|
||||
padding-block: 1em 0.5em;
|
||||
}
|
||||
|
||||
.dur {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
mm-icon {
|
||||
display: block;
|
||||
color: white;
|
||||
}
|
||||
|
||||
mm-icon.fullscreen {
|
||||
font-size: 1.25em;
|
||||
width: 1em;
|
||||
margin-inline-start: auto;
|
||||
margin-block-end: 1em;
|
||||
}
|
||||
|
||||
mm-range {
|
||||
--track-height: 0.3em;
|
||||
--thumb-size: 0.8em;
|
||||
--track-color: #ffffff50;
|
||||
}
|
||||
|
||||
.touchbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 1;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.playPause {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2.25em;
|
||||
transition: opacity 0.25 ease;
|
||||
}
|
||||
|
||||
.playPause > mm-icon {
|
||||
inset: 0;
|
||||
grid-column: 1 / span 1;
|
||||
grid-row: 1 / span 1;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
`
|
||||
}
|
||||
|
||||
customElements.define('mm-vplayer', VideoPlayer)
|
|
@ -0,0 +1,162 @@
|
|||
import { LitElement, css, html } from 'lit'
|
||||
import { Task } from '@lit-labs/task'
|
||||
|
||||
// components
|
||||
import '../components/Header.js'
|
||||
import '../components/Footer.js'
|
||||
import '../components/Loading.js'
|
||||
import '../components/AudioCard.js'
|
||||
import '../components/ModularPlayer.js'
|
||||
|
||||
import Router from '../api/Router.js'
|
||||
|
||||
class AudioView extends LitElement {
|
||||
static properties = {
|
||||
path: { type: String },
|
||||
tracks: { type: Array },
|
||||
selected: { state: true }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.path = ''
|
||||
this.tracks = []
|
||||
this.selected = null
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.addEventListener('select-audio', ({ detail }) => {
|
||||
this.selected = { ...detail, media: `${Router.route.path}/${detail.media}` }
|
||||
console.log(this.selected)
|
||||
this._selectAudio.run()
|
||||
})
|
||||
|
||||
this.addEventListener('next-track', () => {
|
||||
this._skipTrack(1)
|
||||
})
|
||||
|
||||
this.addEventListener('prev-track', () => {
|
||||
this._skipTrack(-1)
|
||||
})
|
||||
}
|
||||
|
||||
_getAudio = new Task(
|
||||
this,
|
||||
async () => {
|
||||
const res = await fetch(`/data${Router.route.path}.json`)
|
||||
const json = await res.json()
|
||||
|
||||
this.tracks = json
|
||||
// start track (0 is not currently available)
|
||||
const idx = 1
|
||||
this.selected = { ...this.tracks[idx], idx, media: `${Router.route.path}/${this.tracks[idx].media}` }
|
||||
},
|
||||
() => [ this.path ]
|
||||
)
|
||||
|
||||
_selectAudio = new Task(
|
||||
this,
|
||||
{
|
||||
task: async ([ selected ]) => {
|
||||
const res = await fetch(`/media${selected.media}.json`)
|
||||
return { ...selected, ...await res.json() }
|
||||
},
|
||||
args: () => [ this.selected ]
|
||||
}
|
||||
)
|
||||
|
||||
_skipTrack(direction) {
|
||||
const idx = (this.selected.idx + direction) % this.tracks.length
|
||||
const detail = { ...this.tracks[idx], idx }
|
||||
|
||||
this.selected = { ...detail, media: `${Router.route.path}/${detail.media}` }
|
||||
this._selectAudio.run()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<mm-header></mm-header>
|
||||
|
||||
<main>
|
||||
<nav class="tracks">
|
||||
<div class="scroll-items">
|
||||
${this._getAudio.render({
|
||||
pending: () => html`<mm-loading style="--fill-color: grey"></mm-loading>`,
|
||||
complete: () => html`${this.tracks.map((t, i) => html`<mm-acard idx=${i} icon=${t.album ? 'album' : 'play-circle'} ?selected=${t.title == this.selected.title && t.details == this.selected.details} .details=${t}></mm-acard>`)}`,
|
||||
error: (err) => html`Error: ${err}`
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="player">
|
||||
<div>
|
||||
${this._selectAudio.render({
|
||||
pending: () => html`<mm-loading style="--fill-color: grey"></mm-loading>`,
|
||||
complete: (selected) => html`<mm-audio-player .details=${selected}></mm-audio-player>`
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<mm-footer></mm-footer>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
|
||||
}
|
||||
|
||||
main {
|
||||
flex-grow: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 0.7fr 1fr;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.tracks {
|
||||
--border-radius: 0.75em;
|
||||
--padding: 0.5em;
|
||||
position: relative;
|
||||
background: var(--neutral-400, lightgrey);
|
||||
border-radius: 0 var(--border-radius) 0 0;
|
||||
}
|
||||
|
||||
.scroll-items {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
padding-block: 0.75em;
|
||||
padding-inline-start: 0.5em;
|
||||
padding-inline-end: 0.75em;
|
||||
border-radius: 0 calc(var(--border-radius) * 0.5) 0 0;
|
||||
display: grid;
|
||||
grid-auto-flow: rows;
|
||||
align-content: start;
|
||||
gap: 0.75em;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.player {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.player > div {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 0.75em 0 0 0.75em;
|
||||
background: var(--green-gradient-400, lightgrey);
|
||||
margin-block-end: 1em;
|
||||
}
|
||||
|
||||
mm-footer {
|
||||
z-index: 1;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
customElements.define('mm-audio', AudioView)
|
|
@ -0,0 +1,48 @@
|
|||
import { LitElement, html, css } from 'lit'
|
||||
|
||||
import '../components/Header.js'
|
||||
import '../components/Footer.js'
|
||||
|
||||
class FourOhFour extends LitElement {
|
||||
static properties = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<mm-header></mm-header>
|
||||
|
||||
<main>
|
||||
<h1>404</h1>
|
||||
<p>The page you're looking for cannot be found</p>
|
||||
</main>
|
||||
|
||||
<mm-footer></mm-footer>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
gap: 0.5em;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0em 0em 10em;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
main > * {
|
||||
margin: 0.25em;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
customElements.define('mm-404', FourOhFour)
|
|
@ -0,0 +1,107 @@
|
|||
import { LitElement, html, css, unsafeCSS } from 'lit'
|
||||
import Router from '../api/Router.js'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
// components
|
||||
import '../components/NavCard.js'
|
||||
|
||||
class Home extends LitElement {
|
||||
static properties = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<header>
|
||||
<div>
|
||||
<p class="sub">Oude Kerk and Hartwig Art Foundation present</p>
|
||||
<h1>Meredith Monk: Calling</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="home-nav">
|
||||
${Router.routes.map(r => (r.title != 'Not found' && r.title != 'home') ? html`<mm-ncard .route=${r}></mm-ncard>` : '')}
|
||||
</nav>
|
||||
|
||||
<footer>
|
||||
<div>
|
||||
<p>Rooms for Listening & Watching</p>
|
||||
</div>
|
||||
</footer>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
:host {
|
||||
--margin: 0.75em;
|
||||
display: block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - calc(var(--margin) * 2));
|
||||
background-image: url('/images/home-bg.png');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
margin: var(--margin);
|
||||
}
|
||||
|
||||
div {
|
||||
background-color: white;
|
||||
color: var(--green-400, black);
|
||||
text-shadow: 0 0 5px rgba(0, 173, 78, 0.4);
|
||||
padding-inline: 1em;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
header > div {
|
||||
margin-inline: 4em;
|
||||
border-radius: 0 0 0.75em 0.75em;
|
||||
padding-block-start: 0.5em;
|
||||
padding-block-end: 1em;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2.25em;
|
||||
}
|
||||
|
||||
.home-nav {
|
||||
display: grid;
|
||||
margin: 4em;
|
||||
flex-grow: 1;
|
||||
gap: 2.5em;
|
||||
grid-template-columns: repeat(auto-fill, minmax(25vw, 1fr));
|
||||
grid-auto-rows: 1fr;
|
||||
}
|
||||
|
||||
mm-ncard {
|
||||
--highlight-color: var(--green-400);
|
||||
--shadow: 0px 0px 5px #00000099;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer > div {
|
||||
margin-inline-end: 4em;
|
||||
margin-inline-start: auto;
|
||||
border-radius: 0.75em 0.75em 0 0;
|
||||
padding-block-start: 1em;
|
||||
padding-block-end: 0.5em;
|
||||
}
|
||||
|
||||
footer p {
|
||||
font-size: 1.5em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-home', Home)
|
|
@ -0,0 +1,69 @@
|
|||
import { LitElement, css, html } from "lit";
|
||||
|
||||
import '../components/Header.js'
|
||||
import '../components/Footer.js'
|
||||
import '../components/HorizontalScroller.js'
|
||||
import '../components/VerticalCard.js'
|
||||
import '../components/ImageViewer.js'
|
||||
|
||||
class ImageView extends LitElement {
|
||||
static properties = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<mm-header></mm-header>
|
||||
<main>
|
||||
<div class="image">
|
||||
<mm-imageviewer src="https://picsum.photos/${parseInt((Math.random() * 400) + 300)}/${parseInt((Math.random() * 400) + 400)}"></mm-imageviewer>
|
||||
</div>
|
||||
|
||||
<mm-hscroller>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
<mm-vcard .details=${{'title': 'Image'}}></mm-vcard>
|
||||
</mm-hscroller>
|
||||
</main>
|
||||
<mm-footer></mm-footer>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: grid;
|
||||
grid-template-rows: min-content 1fr min-content;
|
||||
gap: 0.25em;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
mm-hscroller {
|
||||
--col-width: 7.5em;
|
||||
--gap: 0.75em;
|
||||
}
|
||||
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.image {
|
||||
position: relative;
|
||||
inline-margin: auto;
|
||||
height: 100%;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
customElements.define('mm-images', ImageView)
|
|
@ -0,0 +1,124 @@
|
|||
import { LitElement, html, css, unsafeCSS } from 'lit'
|
||||
import { Task } from '@lit-labs/task'
|
||||
|
||||
import MainCSS from '../assets/styles/main.scss?inline'
|
||||
|
||||
// components
|
||||
import '../components/Header.js'
|
||||
import '../components/Footer.js'
|
||||
import '../components/VerticalCard.js'
|
||||
import '../components/VideoPlayer.js'
|
||||
import '../components/HorizontalScroller.js'
|
||||
|
||||
import Router from '../api/Router.js'
|
||||
|
||||
class VideoPage extends LitElement {
|
||||
static properties = {
|
||||
data: { type: Array },
|
||||
fims: { type: Array, state: true },
|
||||
selected: { state: true }
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.data = []
|
||||
this.selected = {}
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.addEventListener('video-select', ({ detail }) => {
|
||||
this.selected = { ...detail, media: `${Router.route.path}/${detail.media}`, autoplay: true }
|
||||
})
|
||||
}
|
||||
|
||||
_getVideos = new Task(
|
||||
this,
|
||||
async () => {
|
||||
const res = await fetch(`/data/${Router.route.path}.json`)
|
||||
this.films = await res.json()
|
||||
this.selected = { ...this.films[0], media: `${Router.route.path}/${this.films[0].media}` }
|
||||
},
|
||||
() => [ this.data ]
|
||||
)
|
||||
|
||||
// TODO@mx this might not be necessary
|
||||
_selectVideo = new Task(
|
||||
this,
|
||||
{
|
||||
task: () => {
|
||||
// console.log(this.selected)
|
||||
},
|
||||
args: () => [ this.selected ],
|
||||
}
|
||||
)
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<mm-header></mm-header>
|
||||
|
||||
<main>
|
||||
${this._selectVideo.render({
|
||||
pending: () => html`Loading...`,
|
||||
complete: () => html`
|
||||
<aside>
|
||||
<h2>${this.selected.title}</h2>
|
||||
<p class="detail">${this.selected.detail}</p>
|
||||
</aside>
|
||||
<mm-vplayer media='/media/${this.selected.media}' ?autoplay=${this.selected.autoplay}></mm-vplayer>
|
||||
`
|
||||
})}
|
||||
</main>
|
||||
|
||||
<mm-hscroller>
|
||||
${this._getVideos.render({
|
||||
pending: () => html`Loading...`,
|
||||
complete: () => html`${this.films.map(t => html`
|
||||
<mm-vcard
|
||||
.details=${t}
|
||||
?selected=${t.title == this.selected.title && t.detail == this.selected.detail}
|
||||
></mm-vcard>`
|
||||
)}`
|
||||
})}
|
||||
</mm-hscroller>
|
||||
|
||||
<mm-footer></mm-footer>`
|
||||
}
|
||||
|
||||
static styles = [ css`${unsafeCSS(MainCSS)}`, css`
|
||||
:host {
|
||||
display: grid;
|
||||
grid-template-rows: min-content 1fr min-content min-content;
|
||||
gap: 0.25em;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
mm-vcard {
|
||||
--color: var(--green-400);
|
||||
--background-color: var(--neutral-200);
|
||||
}
|
||||
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-columns: 0.4fr 1fr;
|
||||
margin-inline: 0.5em;
|
||||
margin-block-end: 0.5em;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
aside {
|
||||
font-size: 1.5em;
|
||||
padding-inline-start: 0.5em;
|
||||
|
||||
> h2 {
|
||||
max-width: 12ch;
|
||||
margin-block-end: 0.2em;
|
||||
}
|
||||
|
||||
> .detail {
|
||||
line-height: 1.1;
|
||||
}
|
||||
}
|
||||
` ]
|
||||
}
|
||||
|
||||
customElements.define('mm-videos', VideoPage)
|
|
@ -0,0 +1,9 @@
|
|||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
base: '/',
|
||||
build: {
|
||||
sourcemap: true,
|
||||
target: [ 'esnext', 'edge100', 'firefox100', 'chrome100', 'safari18' ],
|
||||
},
|
||||
})
|