Sortable playlist (#135)

* Sortable playlist
* Year field
This commit is contained in:
Ilya Y 2023-07-18 22:11:00 +03:00 committed by GitHub
parent 9b18587c9e
commit 6bce7d576c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 85 additions and 13 deletions

View File

@ -167,6 +167,8 @@
"More": "More",
"Most commented": "Commented",
"Most read": "Readable",
"Move down": "Move down",
"Move up": "Move up",
"My feed": "My feed",
"My subscriptions": "Subscriptions",
"Name": "Name",

View File

@ -177,6 +177,8 @@
"More": "Ещё",
"Most commented": "Комментируемое",
"Most read": "Читаемое",
"Move down": "Переместить вниз",
"Move up": "Переместить вверх",
"My feed": "Моя лента",
"My subscriptions": "Подписки",
"Name": "Имя",

View File

@ -309,8 +309,25 @@ $vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thum
.actions {
margin-left: auto;
display: flex;
align-items: center;
flex-direction: row;
gap: 16px;
.action {
border: 1px solid transparent;
&:not([disabled]):hover {
border-color: var(--background-color-invert);
background: var(--background-color-invert);
img {
filter: var(--icon-filter-hover);
}
}
}
.moveIconDown {
transform: rotate(180deg);
}
}
.descriptionBlock {

View File

@ -11,6 +11,7 @@ type Props = {
body?: string
editorMode?: boolean
onMediaItemFieldChange?: (index: number, field: keyof MediaItem, value: string) => void
onChangeMediaIndex?: (direction: 'up' | 'down', index) => void
}
const getFormattedTime = (point) => new Date(point * 1000).toISOString().slice(14, -5)
@ -159,6 +160,7 @@ export const AudioPlayer = (props: Props) => {
<PlayerPlaylist
editorMode={props.editorMode}
onPlayMedia={handlePlayMedia}
onChangeMediaIndex={(direction, index) => props.onChangeMediaIndex(direction, index)}
isPlaying={isPlaying()}
media={props.media}
currentTrackIndex={currentTrackIndex()}

View File

@ -18,6 +18,7 @@ type Props = {
body?: string
editorMode?: boolean
onMediaItemFieldChange?: (index: number, field: keyof MediaItem, value: string) => void
onChangeMediaIndex?: (direction: 'up' | 'down', index) => void
}
export const PlayerPlaylist = (props: Props) => {
@ -72,6 +73,34 @@ export const PlayerPlaylist = (props: Props) => {
</Show>
</div>
<div class={styles.actions}>
<Show when={props.editorMode}>
<Popover content={t('Move up')}>
{(triggerRef: (el) => void) => (
<button
type="button"
ref={triggerRef}
class={styles.action}
disabled={index() === 0}
onClick={() => props.onChangeMediaIndex('up', index())}
>
<Icon name="up-button" />
</button>
)}
</Popover>
<Popover content={t('Move down')}>
{(triggerRef: (el) => void) => (
<button
type="button"
ref={triggerRef}
class={styles.action}
disabled={index() === props.media.length - 1}
onClick={() => props.onChangeMediaIndex('down', index())}
>
<Icon name="up-button" class={styles.moveIconDown} />
</button>
)}
</Popover>
</Show>
<Show when={(mi.lyrics || mi.body) && !props.editorMode}>
<Popover content={t('Show lyrics')}>
{(triggerRef: (el) => void) => (

View File

@ -190,12 +190,6 @@ export const FullArticle = (props: ArticleProps) => {
</div>
</Show>
<Show when={media().length > 0 && props.article.layout === 'audio'}>
<div class="media-items">
<AudioPlayer media={media()} articleSlug={props.article.slug} body={body()} />
</div>
</Show>
<Show when={body()}>
<div class={styles.shoutBody}>
<Show when={!body().startsWith('<')} fallback={<div innerHTML={body()} />}>

View File

@ -2,8 +2,8 @@ import { clsx } from 'clsx'
import styles from './AudioUploader.module.scss'
import { DropArea } from '../../_shared/DropArea'
import { useLocalize } from '../../../context/localize'
import { createEffect, createSignal, on, Show } from 'solid-js'
import { MediaItem, UploadedFile } from '../../../pages/types'
import { Show } from 'solid-js'
import { MediaItem } from '../../../pages/types'
import { composeMediaItems } from '../../../utils/composeMediaItems'
import { AudioPlayer } from '../../Article/AudioPlayer'
import { Buffer } from 'buffer'
@ -20,6 +20,7 @@ type Props = {
}
onAudioChange: (index: number, value: MediaItem) => void
onAudioAdd: (value: MediaItem[]) => void
onAudioSorted: (value: MediaItem[]) => void
}
export const AudioUploader = (props: Props) => {
@ -29,6 +30,18 @@ export const AudioUploader = (props: Props) => {
props.onAudioChange(index, { ...props.audio[index], [field]: value })
}
const handleChangeIndex = (direction: 'up' | 'down', index: number) => {
const media = [...props.audio]
if (direction === 'up' && index > 0) {
const copy = media.splice(index, 1)[0]
media.splice(index - 1, 0, copy)
} else if (direction === 'down' && index < media.length - 1) {
const copy = media.splice(index, 1)[0]
media.splice(index + 1, 0, copy)
}
props.onAudioSorted(media)
}
return (
<div class={clsx(styles.AudioUploader, props.class)}>
<Show when={props.audio.length > 0}>
@ -36,6 +49,7 @@ export const AudioUploader = (props: Props) => {
editorMode={true}
media={props.audio}
onMediaItemFieldChange={handleMediaItemFieldChange}
onChangeMediaIndex={handleChangeIndex}
/>
</Show>
<DropArea

View File

@ -96,6 +96,13 @@
.additionalInput {
@include font-size(1.4rem);
-moz-appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
font-weight: 600;
padding: 0;
margin: 14px 0 0;

View File

@ -1,4 +1,4 @@
import { Accessor, createEffect, createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js'
import { Accessor, createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js'
import { useLocalize } from '../../context/localize'
import { clsx } from 'clsx'
import { Title } from '@solidjs/meta'
@ -132,7 +132,7 @@ export const EditView = (props: Props) => {
const newMedia = [...mediaItems(), ...data]
setForm('media', JSON.stringify(newMedia))
}
const handleSortedImages = (data) => {
const handleSortedMedia = (data) => {
setForm('media', JSON.stringify(data))
}
@ -244,6 +244,10 @@ export const EditView = (props: Props) => {
onChange={(event) => handleBaseFieldsChange('artist', event.target.value)}
/>
<input
type="number"
min="1900"
max={new Date().getFullYear()}
step="1"
class={styles.additionalInput}
placeholder={t('Release date...')}
value={mediaItems()[0]?.date || ''}
@ -305,7 +309,7 @@ export const EditView = (props: Props) => {
onImageChange={handleMediaChange}
onImageDelete={(index) => handleImageDelete(index)}
onImagesAdd={(value) => handleAddMedia(value)}
onImagesSorted={(value) => handleSortedImages(value)}
onImagesSorted={(value) => handleSortedMedia(value)}
/>
</Show>
@ -341,6 +345,7 @@ export const EditView = (props: Props) => {
baseFields={baseAudioFields()}
onAudioAdd={(value) => handleAddMedia(value)}
onAudioChange={handleMediaChange}
onAudioSorted={(value) => handleSortedMedia(value)}
/>
</Show>
@ -387,7 +392,7 @@ export const EditView = (props: Props) => {
{/* />*/}
{/*</div>*/}
<h4>Темы</h4>
<h4>{t('Topics')}</h4>
{/*<p class="description">*/}
{/* Добавьте несколько тем, чтобы читатель знал, о&nbsp;чем ваш материал, и&nbsp;мог найти*/}
{/* его на&nbsp;страницах интересных ему тем. Темы можно менять местами, первая тема*/}

View File

@ -295,7 +295,7 @@ export const SolidSwiper = (props: Props) => {
</div>
<div
class={clsx(styles.action, {
[styles.hidden]: index() + 1 === Number(props.images.length)
[styles.hidden]: index() === props.images.length - 1
})}
onClick={() => handleChangeIndex('right', index())}
>