mirror of
https://github.com/immich-app/immich.git
synced 2026-03-12 21:42:54 -07:00
refactor: clean class (#26879)
This commit is contained in:
20
pnpm-lock.yaml
generated
20
pnpm-lock.yaml
generated
@@ -845,6 +845,12 @@ importers:
|
||||
tabbable:
|
||||
specifier: ^6.2.0
|
||||
version: 6.4.0
|
||||
tailwind-merge:
|
||||
specifier: ^3.5.0
|
||||
version: 3.5.0
|
||||
tailwind-variants:
|
||||
specifier: ^3.2.2
|
||||
version: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.1)
|
||||
thumbhash:
|
||||
specifier: ^0.1.1
|
||||
version: 0.1.1
|
||||
@@ -11252,8 +11258,8 @@ packages:
|
||||
tabbable@6.4.0:
|
||||
resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==}
|
||||
|
||||
tailwind-merge@3.4.0:
|
||||
resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==}
|
||||
tailwind-merge@3.5.0:
|
||||
resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==}
|
||||
|
||||
tailwind-variants@3.2.2:
|
||||
resolution: {integrity: sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==}
|
||||
@@ -14959,8 +14965,8 @@ snapshots:
|
||||
simple-icons: 16.9.0
|
||||
svelte: 5.53.7
|
||||
svelte-highlight: 7.9.0
|
||||
tailwind-merge: 3.4.0
|
||||
tailwind-variants: 3.2.2(tailwind-merge@3.4.0)(tailwindcss@4.2.1)
|
||||
tailwind-merge: 3.5.0
|
||||
tailwind-variants: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.1)
|
||||
tailwindcss: 4.2.1
|
||||
transitivePeerDependencies:
|
||||
- '@sveltejs/kit'
|
||||
@@ -24554,13 +24560,13 @@ snapshots:
|
||||
|
||||
tabbable@6.4.0: {}
|
||||
|
||||
tailwind-merge@3.4.0: {}
|
||||
tailwind-merge@3.5.0: {}
|
||||
|
||||
tailwind-variants@3.2.2(tailwind-merge@3.4.0)(tailwindcss@4.2.1):
|
||||
tailwind-variants@3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.1):
|
||||
dependencies:
|
||||
tailwindcss: 4.2.1
|
||||
optionalDependencies:
|
||||
tailwind-merge: 3.4.0
|
||||
tailwind-merge: 3.5.0
|
||||
|
||||
tailwindcss-email-variants@3.0.5(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2)):
|
||||
dependencies:
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
"svelte-maplibre": "^1.2.5",
|
||||
"svelte-persisted-store": "^0.12.0",
|
||||
"tabbable": "^6.2.0",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwind-variants": "^3.2.2",
|
||||
"thumbhash": "^0.1.1",
|
||||
"transformation-matrix": "^3.1.0",
|
||||
"uplot": "^1.6.32"
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { cleanClass } from '$lib';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
interface Props {
|
||||
class?: ClassValue;
|
||||
}
|
||||
|
||||
let { class: className = '' }: Props = $props();
|
||||
let { class: className }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="absolute h-full w-full bg-gray-300 dark:bg-gray-700 {className}"></div>
|
||||
<div class={cleanClass('absolute h-full w-full bg-gray-300 dark:bg-gray-700', className)}></div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { cleanClass } from '$lib';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
interface Props {
|
||||
@@ -8,7 +9,7 @@
|
||||
let { class: className }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="delayed inline-flex items-center gap-1 {className}">
|
||||
<div class={cleanClass('delayed inline-flex items-center gap-1', className)}>
|
||||
{#each [0, 1, 2] as i (i)}
|
||||
<span class="dot block size-1.5 rounded-full bg-white shadow-[0_0_3px_rgba(0,0,0,0.6)]" style:--delay="{i * 0.25}s"
|
||||
></span>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { cleanClass } from '$lib';
|
||||
import QueueCardBadge from '$lib/components/QueueCardBadge.svelte';
|
||||
import QueueCardButton from '$lib/components/QueueCardButton.svelte';
|
||||
import Badge from '$lib/elements/Badge.svelte';
|
||||
@@ -105,7 +106,10 @@
|
||||
|
||||
<div class="mt-2 flex w-full max-w-md flex-col sm:flex-row">
|
||||
<div
|
||||
class="{commonClasses} rounded-t-lg bg-immich-primary text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray sm:rounded-s-lg sm:rounded-e-none"
|
||||
class={cleanClass(
|
||||
commonClasses,
|
||||
'rounded-t-lg bg-immich-primary text-white dark:bg-immich-dark-primary dark:text-immich-dark-gray sm:rounded-s-lg sm:rounded-e-none',
|
||||
)}
|
||||
>
|
||||
<p>{$t('active')}</p>
|
||||
<p class="text-2xl">
|
||||
@@ -114,7 +118,10 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="{commonClasses} flex-row-reverse rounded-b-lg bg-gray-200 text-immich-dark-bg dark:bg-gray-700 dark:text-immich-gray sm:rounded-s-none sm:rounded-e-lg"
|
||||
class={cleanClass(
|
||||
commonClasses,
|
||||
'flex-row-reverse rounded-b-lg bg-gray-200 text-immich-dark-bg dark:bg-gray-700 dark:text-immich-gray sm:rounded-s-none sm:rounded-e-lg',
|
||||
)}
|
||||
>
|
||||
<p class="text-2xl">
|
||||
{waitingCount.toLocaleString($locale)}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
<script lang="ts" module>
|
||||
export type Color = 'success' | 'warning';
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
import { tv } from 'tailwind-variants';
|
||||
|
||||
interface Props {
|
||||
color: Color;
|
||||
type Props = {
|
||||
color: 'success' | 'warning';
|
||||
children?: Snippet;
|
||||
}
|
||||
};
|
||||
|
||||
let { color, children }: Props = $props();
|
||||
|
||||
const colorClasses: Record<Color, string> = {
|
||||
success: 'bg-green-500/70 text-gray-900 dark:bg-green-700/90 dark:text-gray-100',
|
||||
warning: 'bg-orange-400/70 text-gray-900 dark:bg-orange-900 dark:text-gray-100',
|
||||
};
|
||||
const styles = tv({
|
||||
base: 'w-full p-2 text-center text-sm ',
|
||||
variants: {
|
||||
color: {
|
||||
success: 'bg-green-500/70 text-gray-900 dark:bg-green-700/90 dark:text-gray-100',
|
||||
warning: 'bg-orange-400/70 text-gray-900 dark:bg-orange-900 dark:text-gray-100',
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full p-2 text-center text-sm {colorClasses[color]}">
|
||||
<div class={styles({ color })}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
import { tv } from 'tailwind-variants';
|
||||
|
||||
interface Props {
|
||||
color: Colors;
|
||||
@@ -14,24 +15,22 @@
|
||||
|
||||
let { color, disabled = false, onClick = () => {}, children }: Props = $props();
|
||||
|
||||
const colorClasses: Record<Colors, string> = {
|
||||
'light-gray': 'bg-gray-300/80 dark:bg-gray-700',
|
||||
gray: 'bg-gray-300/90 dark:bg-gray-700/90',
|
||||
'dark-gray': 'bg-gray-300 dark:bg-gray-700/80',
|
||||
};
|
||||
|
||||
const hoverClasses = disabled
|
||||
? 'cursor-not-allowed'
|
||||
: 'hover:bg-immich-primary hover:text-white dark:hover:bg-immich-dark-primary dark:hover:text-black';
|
||||
const styles = tv({
|
||||
base: 'flex h-full w-full flex-col place-content-center place-items-center gap-2 px-8 py-2 text-xs text-gray-600 transition-colors dark:text-gray-200 ',
|
||||
variants: {
|
||||
color: {
|
||||
'light-gray': 'bg-gray-300/80 dark:bg-gray-700',
|
||||
gray: 'bg-gray-300/90 dark:bg-gray-700/90',
|
||||
'dark-gray': 'bg-gray-300 dark:bg-gray-700/80',
|
||||
},
|
||||
disabled: {
|
||||
true: 'cursor-not-allowed',
|
||||
false: 'hover:bg-immich-primary hover:text-white dark:hover:bg-immich-dark-primary dark:hover:text-black',
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
{disabled}
|
||||
class="flex h-full w-full flex-col place-content-center place-items-center gap-2 px-8 py-2 text-xs text-gray-600 transition-colors dark:text-gray-200 {colorClasses[
|
||||
color
|
||||
]} {hoverClasses}"
|
||||
onclick={onClick}
|
||||
>
|
||||
<button type="button" {disabled} class={styles({ disabled, color })} onclick={onClick}>
|
||||
{@render children?.()}
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { cleanClass } from '$lib';
|
||||
import { queueManager } from '$lib/managers/queue-manager.svelte';
|
||||
import type { QueueSnapshot } from '$lib/types';
|
||||
import type { QueueResponseDto } from '@immich/sdk';
|
||||
@@ -13,7 +14,7 @@
|
||||
class?: string;
|
||||
};
|
||||
|
||||
const { queue, class: className = '' }: Props = $props();
|
||||
const { queue, class: className }: Props = $props();
|
||||
|
||||
type Data = number | null;
|
||||
type NormalizedData = [
|
||||
@@ -159,7 +160,7 @@
|
||||
requestAnimationFrame(update);
|
||||
</script>
|
||||
|
||||
<div class="w-full {className}" bind:this={chartElement}>
|
||||
<div class={cleanClass('w-full', className)} bind:this={chartElement}>
|
||||
{#if data[0].length === 0}
|
||||
<LoadingSpinner size="giant" />
|
||||
{/if}
|
||||
|
||||
15
web/src/lib/index.spec.ts
Normal file
15
web/src/lib/index.spec.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { cleanClass } from '$lib';
|
||||
|
||||
describe('cleanClass', () => {
|
||||
it('should return a string of class names', () => {
|
||||
expect(cleanClass('class1', 'class2', 'class3')).toBe('class1 class2 class3');
|
||||
});
|
||||
|
||||
it('should filter out undefined, null, and false values', () => {
|
||||
expect(cleanClass('class1', undefined, 'class2', null, 'class3', false)).toBe('class1 class2 class3');
|
||||
});
|
||||
|
||||
it('should unnest arrays', () => {
|
||||
expect(cleanClass('class1', ['class2', 'class3'])).toBe('class1 class2 class3');
|
||||
});
|
||||
});
|
||||
16
web/src/lib/index.ts
Normal file
16
web/src/lib/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export const cleanClass = (...classNames: unknown[]) => {
|
||||
return twMerge(
|
||||
classNames
|
||||
.flatMap((className) => (Array.isArray(className) ? className : [className]))
|
||||
.filter((className) => {
|
||||
if (!className || typeof className === 'boolean') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return typeof className === 'string';
|
||||
})
|
||||
.join(' '),
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user