mirror of
https://github.com/koel/koel
synced 2024-11-24 13:13:05 +00:00
more good stuff
This commit is contained in:
parent
014e109b3a
commit
ac83736192
19 changed files with 74 additions and 102 deletions
|
@ -38,6 +38,7 @@
|
|||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/ban-ts-comment": 0,
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"vue/no-side-effects-in-computed-properties": 0,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"standard/no-callback-literal": 0,
|
||||
|
|
|
@ -31,7 +31,7 @@ import LoginForm from '@/components/auth/login-form.vue'
|
|||
import MainWrapper from '@/components/layout/main-wrapper/index.vue'
|
||||
import Overlay from '@/components/ui/overlay.vue'
|
||||
|
||||
import { $, eventBus, hideOverlay, showOverlay } from '@/utils'
|
||||
import { $, eventBus, hideOverlay, showOverlay, arrayify } from '@/utils'
|
||||
import { favoriteStore, preferenceStore as preferences, queueStore, sharedStore } from '@/stores'
|
||||
import { auth, playback, socket } from '@/services'
|
||||
|
||||
|
@ -84,8 +84,8 @@ onMounted(async () => {
|
|||
$.addClass(document.documentElement, navigator.userAgent.includes('Mac') ? 'mac' : 'non-mac')
|
||||
})
|
||||
|
||||
eventBus.on('SONG_CONTEXT_MENU_REQUESTED', async (e: MouseEvent, songs: Song[]) => {
|
||||
contextMenuSongs.value = ([] as Song[]).concat(songs)
|
||||
eventBus.on('SONG_CONTEXT_MENU_REQUESTED', async (e: MouseEvent, songs: Song | Song[]) => {
|
||||
contextMenuSongs.value = arrayify(songs)
|
||||
await nextTick()
|
||||
songContextMenu.value?.open(e.pageY, e.pageX)
|
||||
})
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<section id="queueWrapper">
|
||||
<screen-header>
|
||||
<ScreenHeader>
|
||||
Current Queue
|
||||
<controls-toggler :showing-controls="showingControls" @toggleControls="toggleControls"/>
|
||||
<ControlsToggler :showing-controls="showingControls" @toggleControls="toggleControls"/>
|
||||
|
||||
<template v-slot:meta>
|
||||
<span v-if="meta.songCount" data-test="list-meta">
|
||||
|
@ -11,27 +11,27 @@
|
|||
</template>
|
||||
|
||||
<template v-slot:controls>
|
||||
<song-list-controls
|
||||
v-if="state.songs.length && (!isPhone || showingControls)"
|
||||
<SongListControls
|
||||
v-if="songs.length && (!isPhone || showingControls)"
|
||||
@playAll="playAll"
|
||||
@playSelected="playSelected"
|
||||
@clearQueue="clearQueue"
|
||||
:songs="state.songs"
|
||||
:songs="songs"
|
||||
:config="songListControlConfig"
|
||||
:selectedSongs="selectedSongs"
|
||||
/>
|
||||
</template>
|
||||
</screen-header>
|
||||
</ScreenHeader>
|
||||
|
||||
<song-list
|
||||
v-if="state.songs.length"
|
||||
:items="state.songs"
|
||||
<SongList
|
||||
v-if="songs.length"
|
||||
:items="songs"
|
||||
:config="{ sortable: false }"
|
||||
type="queue"
|
||||
ref="songList"
|
||||
/>
|
||||
|
||||
<screen-placeholder v-else>
|
||||
<ScreenPlaceholder v-else>
|
||||
<template v-slot:icon>
|
||||
<i class="fa fa-coffee"></i>
|
||||
</template>
|
||||
|
@ -41,7 +41,7 @@
|
|||
How about
|
||||
<a class="start" @click.prevent="shuffleAll">shuffling all songs</a>?
|
||||
</span>
|
||||
</screen-placeholder>
|
||||
</ScreenPlaceholder>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
@ -50,7 +50,7 @@ import { pluralize } from '@/utils'
|
|||
import { queueStore, songStore } from '@/stores'
|
||||
import { playback } from '@/services'
|
||||
import { useSongList } from '@/composables'
|
||||
import { computed, defineAsyncComponent, reactive } from 'vue'
|
||||
import { computed, defineAsyncComponent, reactive, toRef } from 'vue'
|
||||
|
||||
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
|
||||
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
|
||||
|
@ -71,14 +71,13 @@ const {
|
|||
clearQueue: true
|
||||
})
|
||||
|
||||
const state = reactive(queueStore.state)
|
||||
const songs = toRef(queueStore.state, 'songs')
|
||||
const songState = reactive(songStore.state)
|
||||
|
||||
const shouldShowShufflingAllLink = computed(() => songState.songs.length > 0)
|
||||
|
||||
const playAll = () => {
|
||||
// @ts-ignore
|
||||
playback.queueAndPlay(state.songs.length ? songList.value?.getAllSongsWithSort() : songStore.all)
|
||||
playback.queueAndPlay(songs.value.length ? songList.value.getAllSongsWithSort() : songStore.all)
|
||||
}
|
||||
|
||||
const shuffleAll = async () => await playback.queueAndPlay(songStore.all, true)
|
||||
|
|
|
@ -31,23 +31,14 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineAsyncComponent, PropType, toRefs } from 'vue'
|
||||
import { computed, defineAsyncComponent, toRefs } from 'vue'
|
||||
import { eventBus, pluralize, startDragging } from '@/utils'
|
||||
import { queueStore } from '@/stores'
|
||||
import { playback } from '@/services'
|
||||
|
||||
const LikeButton = defineAsyncComponent(() => import('@/components/song/like-button.vue'))
|
||||
|
||||
const props = defineProps({
|
||||
song: {
|
||||
type: Object as PropType<Song>,
|
||||
required: true
|
||||
},
|
||||
topPlayCount: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
const props = withDefaults(defineProps<{ song: Song, topPlayCount?: number }>(), { topPlayCount: 0 })
|
||||
|
||||
const { song, topPlayCount } = toRefs(props)
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
import { computed, defineAsyncComponent, nextTick, reactive, ref, toRefs } from 'vue'
|
||||
import { isEqual, union } from 'lodash'
|
||||
|
||||
import { alerts, br2nl, getDefaultCover } from '@/utils'
|
||||
import { alerts, br2nl, getDefaultCover, arrayify } from '@/utils'
|
||||
import { songInfo } from '@/services/info'
|
||||
import { albumStore, artistStore, songStore } from '@/stores'
|
||||
|
||||
|
@ -234,7 +234,7 @@ const initCompilationStateCheckbox = async () => {
|
|||
}
|
||||
|
||||
const open = async () => {
|
||||
mutatedSongs.value = ([] as Song[]).concat(songs.value)
|
||||
mutatedSongs.value = arrayify(songs.value)
|
||||
currentView.value = initialTab!.value
|
||||
const firstSong = mutatedSongs.value[0]
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ import {
|
|||
} from 'vue'
|
||||
import { RecycleScroller } from 'vue-virtual-scroller'
|
||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||
import { $, eventBus, orderBy, startDragging } from '@/utils'
|
||||
import { $, eventBus, orderBy, startDragging, arrayify } from '@/utils'
|
||||
import { favoriteStore, playlistStore, queueStore } from '@/stores'
|
||||
import { playback } from '@/services'
|
||||
import router from '@/router'
|
||||
|
@ -101,26 +101,10 @@ interface SongRow {
|
|||
|
||||
const SongItem = defineAsyncComponent(() => import('@/components/song/item.vue'))
|
||||
|
||||
// @ts-ignore
|
||||
getCurrentInstance()!.__IS_SONG_LIST__ = true
|
||||
|
||||
const props = defineProps({
|
||||
items: {
|
||||
type: Array as PropType<Song[]>,
|
||||
required: true
|
||||
},
|
||||
playlist: {
|
||||
type: Object as PropType<Playlist>
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<SongListType>,
|
||||
default: 'all-songs'
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<Partial<SongListConfig>>,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const props = withDefaults(
|
||||
defineProps<{ items: Song[], playlist?: Playlist, type?: SongListType, config?: Partial<SongListConfig> }>(),
|
||||
{ type: 'all-songs', config: () => ({}) }
|
||||
)
|
||||
|
||||
const { items, playlist, type, config } = toRefs(props)
|
||||
|
||||
|
@ -170,7 +154,7 @@ const sort = (field: SortField | SortField[] = [], order: SortOrder | null = nul
|
|||
return
|
||||
}
|
||||
|
||||
sortFields.value = ([] as SortField[]).concat(field)
|
||||
sortFields.value = arrayify(field)
|
||||
|
||||
if (!sortFields.value.length && ['album', 'artist'].includes(type.value)) {
|
||||
// by default, sort Album/Artist by track numbers for a more friendly UX
|
||||
|
|
|
@ -25,15 +25,9 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType, ref, toRefs, watchEffect } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String as PropType<ArtistAlbumViewMode>,
|
||||
default: 'thumbnails'
|
||||
}
|
||||
})
|
||||
import { ref, toRefs, watchEffect } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<{ value?: ArtistAlbumViewMode }>(), { value: 'thumbnails' })
|
||||
const { value } = toRefs(props)
|
||||
|
||||
let modelValue = ref<ArtistAlbumViewMode>()
|
||||
|
|
|
@ -13,7 +13,7 @@ import SongListControls from '@/components/song/list-controls.vue'
|
|||
import { songStore } from '@/stores'
|
||||
|
||||
export const useSongList = (controlsConfig: Partial<SongListControlsConfig> = {}) => {
|
||||
const songList = ref(null)
|
||||
const songList = ref<InstanceType<typeof SongList>>()
|
||||
const state = reactive<SongListState>({ songs: [] })
|
||||
|
||||
const meta = reactive<SongListMeta>({
|
||||
|
@ -35,7 +35,7 @@ export const useSongList = (controlsConfig: Partial<SongListControlsConfig> = {}
|
|||
meta.totalLength = songStore.getFormattedLength(state.songs)
|
||||
})
|
||||
|
||||
const getSongsToPlay = (): Song[] => (songList.value as any).getAllSongsWithSort()
|
||||
const getSongsToPlay = (): Song[] => songList.value.getAllSongsWithSort()
|
||||
const playAll = (shuffled: boolean) => playback.queueAndPlay(getSongsToPlay(), shuffled)
|
||||
const playSelected = (shuffled: boolean) => playback.queueAndPlay(selectedSongs.value, shuffled)
|
||||
const toggleControls = () => (showingControls.value = !showingControls.value)
|
||||
|
|
|
@ -5,6 +5,8 @@ import { favoriteStore, playlistStore, queueStore } from '@/stores'
|
|||
* Each component including this mixin must have a `songs` array as either data, prop, or computed.
|
||||
*/
|
||||
export const useSongMenuMethods = (songs: Song[], close: TAnyFunction) => {
|
||||
console.log(songs)
|
||||
|
||||
const queueSongsAfterCurrent = () => {
|
||||
queueStore.queueAfterCurrent(songs)
|
||||
close()
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
import { playlistStore, favoriteStore } from '@/stores'
|
||||
import { favoriteStore, playlistStore } from '@/stores'
|
||||
import { auth } from '.'
|
||||
import { alerts } from '@/utils'
|
||||
|
||||
let events: any
|
||||
|
||||
if (KOEL_ENV === 'app') {
|
||||
events = require('&/events').default
|
||||
}
|
||||
import { alerts, arrayify } from '@/utils'
|
||||
|
||||
export const download = {
|
||||
fromSongs (songs: Song | Song[]): void {
|
||||
const query = (<Song[]>[]).concat(songs).reduce((q, song) => `songs[]=${song.id}&${q}`, '')
|
||||
const query = arrayify(songs).reduce((q, song) => `songs[]=${song.id}&${q}`, '')
|
||||
this.trigger(`songs?${query}`)
|
||||
},
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { union, difference, take, orderBy } from 'lodash'
|
|||
import stub from '@/stubs/album'
|
||||
import { artistStore } from '.'
|
||||
import { http } from '@/services'
|
||||
import { use } from '@/utils'
|
||||
import { arrayify, use } from '@/utils'
|
||||
|
||||
const UNKNOWN_ALBUM_ID = 1
|
||||
|
||||
|
@ -53,7 +53,7 @@ export const albumStore = {
|
|||
},
|
||||
|
||||
add (albums: Album | Album[]): void {
|
||||
(<Album[]>[]).concat(albums).forEach(album => {
|
||||
arrayify(albums).forEach(album => {
|
||||
this.setupAlbum(album)
|
||||
album.playCount = album.songs.reduce((count, song) => count + song.playCount, 0)
|
||||
this.all.push(album)
|
||||
|
|
|
@ -2,7 +2,7 @@ import { difference, take, orderBy } from 'lodash'
|
|||
|
||||
import { http } from '@/services'
|
||||
import stub from '@/stubs/artist'
|
||||
import { use } from '@/utils'
|
||||
import { arrayify, use } from '@/utils'
|
||||
|
||||
const UNKNOWN_ARTIST_ID = 1
|
||||
const VARIOUS_ARTISTS_ID = 2
|
||||
|
@ -50,7 +50,7 @@ export const artistStore = {
|
|||
},
|
||||
|
||||
add (artists: Artist | Artist[]) {
|
||||
(<Artist[]>[]).concat(artists).forEach(artist => {
|
||||
arrayify(artists).forEach(artist => {
|
||||
this.setupArtist(artist)
|
||||
artist.playCount = artist.songs.reduce((count, song) => count + song.playCount, 0)
|
||||
this.all.push(artist)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { difference, union } from 'lodash'
|
||||
import { http } from '@/services'
|
||||
import { arrayify } from '@/utils'
|
||||
|
||||
export const favoriteStore = {
|
||||
state: {
|
||||
|
@ -29,14 +30,14 @@ export const favoriteStore = {
|
|||
* Add a song/songs into the store.
|
||||
*/
|
||||
add (songs: Song | Song[]): void {
|
||||
this.all = union(this.all, (<Song[]>[]).concat(songs))
|
||||
this.all = union(this.all, arrayify(songs))
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a song/songs from the store.
|
||||
*/
|
||||
remove (songs: Song | Song[]): void {
|
||||
this.all = difference(this.all, (<Song[]>[]).concat(songs))
|
||||
this.all = difference(this.all, arrayify(songs))
|
||||
},
|
||||
|
||||
clear (): void {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { difference, union, orderBy } from 'lodash'
|
|||
|
||||
import stub from '@/stubs/playlist'
|
||||
import { http } from '@/services'
|
||||
import { alerts, pluralize } from '@/utils'
|
||||
import { alerts, pluralize, arrayify } from '@/utils'
|
||||
import { songStore } from '.'
|
||||
import models from '@/config/smart-playlist/models'
|
||||
import operators from '@/config/smart-playlist/operators'
|
||||
|
@ -79,7 +79,7 @@ export const playlistStore = {
|
|||
* Add a playlist/playlists into the store.
|
||||
*/
|
||||
add (playlists: Playlist | Playlist[]) {
|
||||
const playlistsToAdd = (<Playlist[]>[]).concat(playlists)
|
||||
const playlistsToAdd = arrayify(playlists)
|
||||
playlistsToAdd.forEach(playlist => this.setupPlaylist(playlist))
|
||||
this.all = this.sort(union(this.all, playlistsToAdd))
|
||||
},
|
||||
|
@ -88,7 +88,7 @@ export const playlistStore = {
|
|||
* Remove a playlist/playlists from the store.
|
||||
*/
|
||||
remove (playlists: Playlist | Playlist[]) {
|
||||
this.all = difference(this.all, (<Playlist[]>[]).concat(playlists))
|
||||
this.all = difference(this.all, arrayify(playlists))
|
||||
},
|
||||
|
||||
async store (name: string, songs: Song[] = [], rules: SmartPlaylistRuleGroup[] = []): Promise<Playlist> {
|
||||
|
@ -169,7 +169,7 @@ export const playlistStore = {
|
|||
/**
|
||||
* Serialize the rule (groups) to be ready for database.
|
||||
*/
|
||||
serializeSmartPlaylistRulesForStorage: (ruleGroups: SmartPlaylistRuleGroup[]): object[] | null => {
|
||||
serializeSmartPlaylistRulesForStorage: (ruleGroups: SmartPlaylistRuleGroup[]): any[] | null => {
|
||||
if (!ruleGroups || !ruleGroups.length) {
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { union, difference, shuffle } from 'lodash'
|
||||
import { reactive } from 'vue'
|
||||
import { arrayify } from '@/utils'
|
||||
|
||||
export const queueStore = {
|
||||
state: {
|
||||
state: reactive({
|
||||
songs: [] as Song[],
|
||||
current: undefined as Song | undefined
|
||||
},
|
||||
}),
|
||||
|
||||
init () {
|
||||
// We don't have anything to do here yet.
|
||||
|
@ -57,7 +59,7 @@ export const queueStore = {
|
|||
*/
|
||||
queue (songs: Song | Song[]): void {
|
||||
this.unqueue(songs)
|
||||
this.all = union(this.all, (<Song[]>[]).concat(songs))
|
||||
this.all = union(this.all, arrayify(songs))
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -65,7 +67,7 @@ export const queueStore = {
|
|||
* @param {Song|Song[]} songs The song, or an array of songs
|
||||
*/
|
||||
queueToTop (songs: Song | Song[]): void {
|
||||
this.all = union((<Song[]>[]).concat(songs), this.all)
|
||||
this.all = union(arrayify(songs), this.all)
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -73,7 +75,7 @@ export const queueStore = {
|
|||
* @param {Song|Song[]} songs The song, or an array of songs
|
||||
*/
|
||||
replaceQueueWith (songs: Song | Song[]): void {
|
||||
this.all = (<Song[]>[]).concat(songs)
|
||||
this.all = arrayify(songs)
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -81,7 +83,7 @@ export const queueStore = {
|
|||
* @param {Song|Song[]} songs The song, or an array of songs
|
||||
*/
|
||||
queueAfterCurrent (songs: Song | Song[]): void {
|
||||
songs = (<Song[]>[]).concat(songs)
|
||||
songs = arrayify(songs)
|
||||
|
||||
if (!this.current || !this.all.length) {
|
||||
return this.queue(songs)
|
||||
|
@ -95,7 +97,7 @@ export const queueStore = {
|
|||
},
|
||||
|
||||
unqueue (songs: Song | Song[]): void {
|
||||
this.all = difference(this.all, (<Song[]>[]).concat(songs))
|
||||
this.all = difference(this.all, arrayify(songs))
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -106,7 +108,7 @@ export const queueStore = {
|
|||
*/
|
||||
move (songs: Song | Song[], target: Song): void {
|
||||
const targetIndex = this.indexOf(target)
|
||||
const movedSongs = (<Song[]>[]).concat(songs)
|
||||
const movedSongs = arrayify(songs)
|
||||
|
||||
movedSongs.forEach(song => {
|
||||
this.all.splice(this.indexOf(song), 1)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import Vue from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import slugify from 'slugify'
|
||||
import { without, take, remove, orderBy, unionBy } from 'lodash'
|
||||
import { orderBy, remove, take, unionBy, without } from 'lodash'
|
||||
import isMobile from 'ismobilejs'
|
||||
|
||||
import { secondsToHis, alerts, pluralize, use } from '@/utils'
|
||||
import { http, auth, ls } from '@/services'
|
||||
import { sharedStore, favoriteStore, albumStore, artistStore, preferenceStore } from '.'
|
||||
import { alerts, arrayify, pluralize, secondsToHis, use } from '@/utils'
|
||||
import { auth, http, ls } from '@/services'
|
||||
import { albumStore, artistStore, favoriteStore, preferenceStore, sharedStore } from '.'
|
||||
import stub from '@/stubs/song'
|
||||
|
||||
interface BroadcastSongData {
|
||||
|
@ -34,10 +34,10 @@ export const songStore = {
|
|||
stub,
|
||||
cache: {} as { [key: string]: Song },
|
||||
|
||||
state: {
|
||||
state: reactive({
|
||||
songs: [] as Song[],
|
||||
recentlyPlayed: [] as Song[]
|
||||
},
|
||||
}),
|
||||
|
||||
init (songs: Song[]): void {
|
||||
this.all = songs
|
||||
|
@ -107,7 +107,7 @@ export const songStore = {
|
|||
},
|
||||
|
||||
getFormattedLength (songs: Song[]): string {
|
||||
return <string>this.getLength(songs, true)
|
||||
return String(this.getLength(songs, true))
|
||||
},
|
||||
|
||||
get all (): Song[] {
|
||||
|
@ -124,7 +124,7 @@ export const songStore = {
|
|||
|
||||
byIds (ids: string[]): Song[] {
|
||||
const songs = [] as Song[]
|
||||
([] as string[]).concat(ids).forEach(id => use(this.byId(id), song => songs.push(song!)))
|
||||
arrayify(ids).forEach(id => use(this.byId(id), song => songs.push(song!)))
|
||||
return songs
|
||||
},
|
||||
|
||||
|
|
1
resources/assets/js/types.d.ts
vendored
1
resources/assets/js/types.d.ts
vendored
|
@ -1,5 +1,6 @@
|
|||
declare module '*.jpg'
|
||||
declare module '*.png'
|
||||
declare module '*.svg'
|
||||
|
||||
declare type TAnyFunction = (...args: Array<unknown|any>) => unknown|any
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import select from 'select'
|
||||
import { eventBus, noop, pluralize } from '@/utils'
|
||||
import { eventBus, noop, pluralize, arrayify } from '@/utils'
|
||||
import defaultCover from '@/../img/covers/unknown-album.png'
|
||||
|
||||
/**
|
||||
|
@ -81,7 +81,7 @@ export const startDragging = (event: DragEvent, dragged: Song | Song[] | Album |
|
|||
|
||||
switch (type) {
|
||||
case 'Song':
|
||||
dragged = (<Song[]>[]).concat(<Song>dragged)
|
||||
dragged = arrayify(<Song>dragged)
|
||||
text = dragged.length === 1
|
||||
? `${dragged[0].title} by ${dragged[0].artist.name}`
|
||||
: pluralize(dragged.length, 'song')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export const use = <T>(value: T, cb: (arg: T) => void): void => {
|
||||
export const use = <T> (value: T, cb: (arg: T) => void) => {
|
||||
if (typeof value === 'undefined' || value === null) {
|
||||
return
|
||||
}
|
||||
|
@ -6,4 +6,7 @@ export const use = <T>(value: T, cb: (arg: T) => void): void => {
|
|||
cb(value)
|
||||
}
|
||||
|
||||
export const arrayify = <T> (maybeArray: T | Array<T>) => ([] as Array<T>).concat(maybeArray)
|
||||
|
||||
// @ts-ignore
|
||||
export const noop = () => {}
|
||||
|
|
Loading…
Reference in a new issue