mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 22:20:51 +01:00
219 lines
6.9 KiB
TypeScript
219 lines
6.9 KiB
TypeScript
import { platform } from '@tauri-apps/plugin-os'
|
|
import type { RouteLocationRawI18n } from 'vue-router'
|
|
|
|
/* export const bytesToBase64DataUrlAsync = async (
|
|
bytes: Uint8Array,
|
|
type = 'application/octet-stream',
|
|
) => {
|
|
return await new Promise((resolve, reject) => {
|
|
const reader = Object.assign(new FileReader(), {
|
|
onload: () => resolve(reader.result),
|
|
onerror: () => reject(reader.error),
|
|
})
|
|
reader.readAsDataURL(new File([new Blob([bytes])], '', { type }))
|
|
})
|
|
} */
|
|
|
|
export const blobToImageAsync = (blob: Blob): Promise<HTMLImageElement> => {
|
|
return new Promise((resolve) => {
|
|
console.log('transform blob', blob)
|
|
const url = URL.createObjectURL(blob)
|
|
const img = new Image()
|
|
img.onload = () => {
|
|
URL.revokeObjectURL(url)
|
|
resolve(img)
|
|
}
|
|
img.src = url
|
|
})
|
|
}
|
|
|
|
export const readableFileSize = (sizeInByte: number | string = 0) => {
|
|
if (!sizeInByte) {
|
|
return '0 KB'
|
|
}
|
|
const size =
|
|
typeof sizeInByte === 'string' ? parseInt(sizeInByte) : sizeInByte
|
|
const sizeInKb = size / 1024
|
|
const sizeInMb = sizeInKb / 1024
|
|
const sizeInGb = sizeInMb / 1024
|
|
const sizeInTb = sizeInGb / 1024
|
|
|
|
if (sizeInTb > 1) return `${sizeInTb.toFixed(2)} TB`
|
|
if (sizeInGb > 1) return `${sizeInGb.toFixed(2)} GB`
|
|
if (sizeInMb > 1) return `${sizeInMb.toFixed(2)} MB`
|
|
|
|
return `${sizeInKb.toFixed(2)} KB`
|
|
}
|
|
|
|
export const getSingleRouteParam = (
|
|
param: string | string[] | undefined,
|
|
): string => {
|
|
const _param = Array.isArray(param) ? (param.at(0) ?? '') : (param ?? '')
|
|
//console.log('getSingleRouteParam found:', _param, param)
|
|
return decodeURIComponent(_param)
|
|
}
|
|
|
|
export const isRouteActive = (
|
|
to: RouteLocationRawI18n,
|
|
exact: boolean = false,
|
|
) =>
|
|
computed(() => {
|
|
const found = useRouter()
|
|
.getRoutes()
|
|
.find((route) => route.name === useLocaleRoute()(to)?.name)
|
|
//console.log('found route', found, useRouter().currentRoute.value, to);
|
|
return exact
|
|
? found?.name === useRouter().currentRoute.value.name
|
|
: found?.name === useRouter().currentRoute.value.name ||
|
|
found?.children.some(
|
|
(child) => child.name === useRouter().currentRoute.value.name,
|
|
)
|
|
})
|
|
|
|
export const isKey = <T extends object>(x: T, k: PropertyKey): k is keyof T => {
|
|
return k in x
|
|
}
|
|
|
|
export const filterAsync = async <T>(
|
|
arr: T[],
|
|
predicate: (value: T, index: number, array: T[]) => Promise<boolean>,
|
|
) => {
|
|
// 1. Mappe jedes Element auf ein Promise, das zu true/false auflöst
|
|
const results = await Promise.all(arr.map(predicate))
|
|
|
|
// 2. Filtere das ursprüngliche Array basierend auf den Ergebnissen
|
|
return arr.filter((_value, index) => results[index])
|
|
}
|
|
|
|
export const stringToHex = (str: string) =>
|
|
str
|
|
.split('')
|
|
.map((char) => char.charCodeAt(0).toString(16).padStart(2, '0'))
|
|
.join('') // Join array into a single string
|
|
|
|
export const hexToString = (hex: string) => {
|
|
if (!hex) return ''
|
|
const parsedValue = hex
|
|
.match(/.{1,2}/g) // Split hex into pairs
|
|
?.map((byte) => String.fromCharCode(parseInt(byte, 16))) // Convert hex to char
|
|
.join('') // Join array into a single string
|
|
|
|
return parsedValue ? parsedValue : ''
|
|
}
|
|
|
|
export const getContrastingTextColor = (
|
|
hexColor?: string | null,
|
|
): 'black' | 'white' => {
|
|
if (!hexColor) {
|
|
return 'black' // Fallback
|
|
}
|
|
|
|
// Entferne das '#' vom Anfang
|
|
let color = hexColor.startsWith('#') ? hexColor.slice(1) : hexColor
|
|
|
|
// Handle Kurzform-Hex-Werte (z.B. "F0C" -> "FF00CC")
|
|
if (color.length === 3) {
|
|
color = color
|
|
.split('')
|
|
.map((char) => char + char)
|
|
.join('')
|
|
}
|
|
|
|
if (color.length !== 6) {
|
|
return 'black' // Fallback für ungültige Farben
|
|
}
|
|
|
|
// Konvertiere Hex zu RGB
|
|
const r = parseInt(color.substring(0, 2), 16)
|
|
const g = parseInt(color.substring(2, 4), 16)
|
|
const b = parseInt(color.substring(4, 6), 16)
|
|
|
|
// Berechne die wahrgenommene Luminanz nach der WCAG-Formel.
|
|
// Werte von 0 (schwarz) bis 255 (weiß).
|
|
const luminance = 0.299 * r + 0.587 * g + 0.114 * b
|
|
|
|
// Wähle die Textfarbe basierend auf einem Schwellenwert.
|
|
// Ein Wert > 186 wird oft als "hell" genug für schwarzen Text angesehen.
|
|
return luminance > 186 ? 'black' : 'white'
|
|
}
|
|
|
|
/**
|
|
* Eine "Type Guard"-Funktion, die prüft, ob ein Wert ein Objekt (aber nicht null) ist.
|
|
* Wenn sie `true` zurückgibt, weiß TypeScript, dass der Wert sicher als Objekt behandelt werden kann.
|
|
* @param value Der zu prüfende Wert vom Typ `unknown`.
|
|
* @returns {boolean} `true`, wenn der Wert ein Objekt ist.
|
|
*/
|
|
export const isObject = (value: unknown): value is Record<string, unknown> => {
|
|
return typeof value === 'object' && value !== null
|
|
}
|
|
|
|
/**
|
|
* Führt einen typsicheren, tiefen Vergleich (deep comparison) von zwei Werten durch.
|
|
* Gibt `true` zurück, wenn die Werte als gleich angesehen werden.
|
|
*
|
|
* @param valueA Der erste Wert für den Vergleich.
|
|
* @param valueB Der zweite Wert für den Vergleich.
|
|
* @returns {boolean} `true`, wenn die Werte gleich sind, andernfalls `false`.
|
|
*/
|
|
export const areObjectsEqual = (valueA: unknown, valueB: unknown): boolean => {
|
|
console.log('areObjectsEqual', valueA, valueB)
|
|
// 1. Schneller Check für exakt die gleiche Referenz oder primitive Gleichheit
|
|
if (valueA === valueB) {
|
|
return true
|
|
}
|
|
|
|
// DEINE SONDERREGEL: Behandle `null` und einen leeren String `""` als gleichwertig.
|
|
const areNullAndEmptyString =
|
|
(valueA === null && valueB === '') || (valueA === '' && valueB === null)
|
|
|
|
if (areNullAndEmptyString) {
|
|
return true
|
|
}
|
|
|
|
// 2. Nutzen der Type Guard: Wenn beide Werte keine Objekte sind,
|
|
// und die vorherigen Checks fehlschlugen, sind sie ungleich.
|
|
if (!isObject(valueA) || !isObject(valueB)) {
|
|
console.log('areObjectsEqual no objects', valueA, valueB)
|
|
return false
|
|
}
|
|
|
|
// Ab hier weiß TypeScript dank der Type Guard, dass valueA und valueB Objekte sind.
|
|
|
|
// 3. Holen der Schlüssel und Vergleich der Anzahl
|
|
const keysA = Object.keys(valueA)
|
|
const keysB = Object.keys(valueB)
|
|
|
|
if (keysA.length !== keysB.length) {
|
|
console.log('areObjectsEqual length')
|
|
return false
|
|
}
|
|
|
|
// 4. Iteration über alle Schlüssel und rekursiver Vergleich der Werte
|
|
for (const key of keysA) {
|
|
// Prüfen, ob der Schlüssel auch im zweiten Objekt überhaupt existiert
|
|
if (!keysB.includes(key)) {
|
|
console.log('areObjectsEqual keys')
|
|
return false
|
|
}
|
|
|
|
// Die Werte der Schlüssel sind wieder `unknown`, daher nutzen wir Rekursion.
|
|
const nestedValueA = valueA[key]
|
|
const nestedValueB = valueB[key]
|
|
|
|
// Wenn der rekursive Aufruf für einen der Werte `false` zurückgibt,
|
|
// sind die gesamten Objekte ungleich.
|
|
if (!areObjectsEqual(nestedValueA, nestedValueB)) {
|
|
console.log('areObjectsEqual nested')
|
|
return false
|
|
}
|
|
}
|
|
|
|
// 5. Wenn die Schleife durchläuft, sind die Objekte gleich
|
|
return true
|
|
}
|
|
|
|
export const getFileName = (fullPath: string) => {
|
|
const seperator = platform() === 'windows' ? '\\' : '/'
|
|
return fullPath.split(seperator).pop()
|
|
}
|