Newer
Older
import {
computed,
ComputedGetter,
ComputedRef,
readonly,
Ref,
ref,
shallowRef,
watch,
watchEffect,
} from 'vue'
import { formatISO, parseISO } from 'date-fns'
import DOMPurify from 'dompurify'
import { useStore } from 'vuex'
import { Show } from '@/types'
export function computedIter<T>(fn: ComputedGetter<Iterable<T>>): ComputedRef<T[]> {
return computed(() => Array.from(fn()))
}
export const useId = (() => {
let _id = 0
return function useId(prefix = 'component') {
return readonly(ref(`${prefix}-${_id++}`))
}
})()
export function useAsyncFunction<F extends (...args: never[]) => Promise<unknown>>(fn: F) {
const isProcessing = ref(false)
async function wrapper(...args: Parameters<F>): Promise<Awaited<ReturnType<F>>> {
return (await fn(...args)) as Awaited<ReturnType<F>>
return { fn: wrapper, isProcessing }
}
export function useUpdatableState<T>(
externalStateRef: Ref<T>,
onUpdate: (value: T) => void,
clone: (value: T) => T = cloneDeep,
): Ref<T> {
const localRef = ref()
watchEffect(() => {
localRef.value = clone(externalStateRef.value)
})
watch(
localRef,
(newValue) => {
if (!isEqual(newValue, externalStateRef.value)) {
onUpdate(newValue)
}
},
{ deep: true },
)
return localRef
}
export function useCopy<T, R = T>(externalStateRef: Ref<T>, transform?: (value: T) => R) {
const _transform = transform ?? ((v: T) => v as unknown as R)
const localRef = shallowRef<R>(_transform(externalStateRef.value))
watch(externalStateRef, (newValue) => {
localRef.value = _transform(newValue)
})
return localRef
}
export function useFormattedISODate(date: Ref<Date>) {
return computed({
get() {
return formatISO(date.value, { representation: 'date' })
},
set(dateValue: string) {
date.value = parseISO(dateValue)
},
})
}

Konrad Mohrfeldt
committed
export function getClosestSlot(slotDurationMinutes: number, date?: Date): Date {
date = date ?? new Date()
const slotDurationMillis = slotDurationMinutes * 60 * 1000
const slots = Math.floor(date.getTime() / slotDurationMillis)
const closestSlotTimestamp = slots * slotDurationMillis
return new Date(closestSlotTimestamp)
}
export function getNextAvailableSlot(slotDurationMinutes: number, date?: Date): Date {
date = date ?? new Date()
const slotDurationMillis = slotDurationMinutes * 60 * 1000
const slots = Math.ceil(date.getTime() / slotDurationMillis)
const nextSlotTimestamp = slots * slotDurationMillis
return new Date(nextSlotTimestamp)
}
export function calculateDurationSeconds(start: Date, end: Date): number {
return Math.abs(end.getTime() - start.getTime()) / 1000
}
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
export function sanitizeHTML(
html: string,
preset?: 'inline-noninteractive' | 'safe-html' | 'strip',
): string {
const inlineElements = [
'b',
'big',
'i',
'small',
'tt',
'abbr',
'acronym',
'cite',
'code',
'dfn',
'em',
'kbd',
'strong',
'samp',
'var',
'br',
'q',
's',
'span',
'sub',
'sup',
]
if (preset === 'inline-noninteractive') {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: inlineElements,
ALLOWED_ATTR: ['style', 'title'],
})
}
if (preset === 'safe-html') {
return DOMPurify.sanitize(html, { USE_PROFILES: { html: true } })
}
return DOMPurify.sanitize(html, { ALLOWED_TAGS: [] })
}
export function useSelectedShow() {
const store = useStore()
return computed<Show>({
get() {
return store.state.shows.shows[store.state.shows.selected.index]
},
set(show) {
store.commit('shows/switchShowById', show.id)
},
})
}
export function secondsToDurationString(seconds: number): string {
seconds = Math.round(seconds)
const h = Math.floor(seconds / 3600)
const m = Math.floor((seconds % 3600) / 60)
const s = Math.round(seconds % 60)
return `${h ? h + 'h ' : ''}${m ? m + 'min ' : ''}${s ? s + 's' : ''}`
}
export function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max)
}
export function mapToDomain(
value: number,
inputDomain: [number, number],
outputDomain: [number, number],
) {
const [x1, y1] = inputDomain
const [x2, y2] = outputDomain
value = clamp(value, x1, y1)
return ((value - x1) * (y2 - x2)) / (y1 - x1) + x2
}
export function asyncWritableComputed<T>(
initialValue: T,
config: {
get: () => Promise<T>
set: (value: T) => Promise<void> | void
},
) {
const data = shallowRef(initialValue)
watchEffect(async () => {
data.value = await config.get()
})
return computed<T>({
get: () => data.value,
set: (value: T) => {
config.set(value)
},
})
}