refactor(web): use svelte builtin id generator (#32631)

(cherry picked from commit 285552785f1bd7cb5ad431c8b3423d1d5e858188)
This commit is contained in:
Mees Frensel
2026-10-27 23:12:13 +03:00
committed by Codex
parent 3458767d66
commit 27ccbee82d
10 changed files with 20 additions and 36 deletions
@@ -22,7 +22,6 @@
<script lang="ts"> <script lang="ts">
import { focusOutside } from '#lib/actions/focus-outside.ts'; import { focusOutside } from '#lib/actions/focus-outside.ts';
import { shortcuts } from '#lib/actions/shortcut.ts'; import { shortcuts } from '#lib/actions/shortcut.ts';
import { generateId } from '#lib/utils/generate-id.ts';
import { Icon, IconButton, Label } from '@immich/ui'; import { Icon, IconButton, Label } from '@immich/ui';
import { mdiChevronDown, mdiClose, mdiMagnify } from '@mdi/js'; import { mdiChevronDown, mdiClose, mdiMagnify } from '@mdi/js';
import { onMount, tick } from 'svelte'; import { onMount, tick } from 'svelte';
@@ -62,10 +61,6 @@
forceFocus = false, forceFocus = false,
}: Props = $props(); }: Props = $props();
/**
* Unique identifier for the combobox.
*/
let id: string = generateId();
/** /**
* Indicates whether or not the dropdown autocomplete list should be visible. * Indicates whether or not the dropdown autocomplete list should be visible.
*/ */
@@ -80,6 +75,7 @@
let input = $state<HTMLInputElement>(); let input = $state<HTMLInputElement>();
let bounds: DOMRect | undefined = $state(); let bounds: DOMRect | undefined = $state();
const id = $props.id();
const inputId = `combobox-${id}`; const inputId = `combobox-${id}`;
const listboxId = `listbox-${id}`; const listboxId = `listbox-${id}`;
/** /**
@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import NumberRangeInput from '#lib/components/shared-components/NumberRangeInput.svelte'; import NumberRangeInput from '#lib/components/shared-components/NumberRangeInput.svelte';
import { generateId } from '#lib/utils/generate-id.ts';
import { convert } from 'geo-coordinates-parser'; import { convert } from 'geo-coordinates-parser';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
@@ -12,7 +11,7 @@
let { lat = $bindable(), lng = $bindable(), onUpdate }: Props = $props(); let { lat = $bindable(), lng = $bindable(), onUpdate }: Props = $props();
const id = generateId(); const id = $props.id();
const onInput = () => { const onInput = () => {
if (lat !== undefined && lng !== undefined) { if (lat !== undefined && lng !== undefined) {
@@ -9,7 +9,6 @@
getContextMenuPositionFromEvent, getContextMenuPositionFromEvent,
type Align, type Align,
} from '#lib/utils/context-menu.ts'; } from '#lib/utils/context-menu.ts';
import { generateId } from '#lib/utils/generate-id.ts';
import { IconButton, type Color, type Size, type Variants } from '@immich/ui'; import { IconButton, type Color, type Size, type Variants } from '@immich/ui';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
@@ -61,7 +60,7 @@
let menuContainer: HTMLUListElement | undefined = $state(); let menuContainer: HTMLUListElement | undefined = $state();
let buttonContainer: HTMLDivElement | undefined = $state(); let buttonContainer: HTMLDivElement | undefined = $state();
const id = generateId(); const id = $props.id();
const buttonId = `context-menu-button-${id}`; const buttonId = `context-menu-button-${id}`;
const menuId = `context-menu-${id}`; const menuId = `context-menu-${id}`;
@@ -2,7 +2,6 @@
import type { Shortcut } from '#lib/actions/shortcut.ts'; import type { Shortcut } from '#lib/actions/shortcut.ts';
import { shortcut as bindShortcut, shortcutLabel as computeShortcutLabel } from '#lib/actions/shortcut.ts'; import { shortcut as bindShortcut, shortcutLabel as computeShortcutLabel } from '#lib/actions/shortcut.ts';
import { optionClickCallbackStore, selectedIdStore } from '#lib/stores/context-menu.store.ts'; import { optionClickCallbackStore, selectedIdStore } from '#lib/stores/context-menu.store.ts';
import { generateId } from '#lib/utils/generate-id.ts';
import { Icon, type IconLike } from '@immich/ui'; import { Icon, type IconLike } from '@immich/ui';
interface Props { interface Props {
@@ -27,9 +26,8 @@
shortcutLabel = '', shortcutLabel = '',
}: Props = $props(); }: Props = $props();
let id: string = generateId(); const id = $props.id();
const isActive = $derived($selectedIdStore === id);
let isActive = $derived($selectedIdStore === id);
const handleClick = (event: MouseEvent) => { const handleClick = (event: MouseEvent) => {
// eslint-disable-next-line unicorn/no-optional-chaining-on-undeclared-variable // eslint-disable-next-line unicorn/no-optional-chaining-on-undeclared-variable
@@ -4,7 +4,6 @@
import { Route } from '#lib/route.ts'; import { Route } from '#lib/route.ts';
import { searchStore } from '#lib/stores/search.svelte.ts'; import { searchStore } from '#lib/stores/search.svelte.ts';
import { handlePromiseError } from '#lib/utils.ts'; import { handlePromiseError } from '#lib/utils.ts';
import { generateId } from '#lib/utils/generate-id.ts';
import { IconButton } from '@immich/ui'; import { IconButton } from '@immich/ui';
import { mdiClose, mdiMagnify } from '@mdi/js'; import { mdiClose, mdiMagnify } from '@mdi/js';
import { onDestroy, tick } from 'svelte'; import { onDestroy, tick } from 'svelte';
@@ -29,8 +28,7 @@
let showSuggestions = $state(false); let showSuggestions = $state(false);
let selectedId: string | undefined = $state(); let selectedId: string | undefined = $state();
const listboxId = generateId(); const listboxId = $props.id();
const searchTypeId = generateId();
onDestroy(() => { onDestroy(() => {
searchStore.isSearchEnabled = false; searchStore.isSearchEnabled = false;
@@ -173,7 +171,6 @@
aria-activedescendant={selectedId ?? ''} aria-activedescendant={selectedId ?? ''}
aria-expanded={showSuggestions} aria-expanded={showSuggestions}
aria-autocomplete="list" aria-autocomplete="list"
aria-describedby={searchTypeId}
use:shortcuts={[ use:shortcuts={[
{ shortcut: { key: 'Escape' }, onShortcut: onEscape }, { shortcut: { key: 'Escape' }, onShortcut: onEscape },
{ shortcut: { key: 'ArrowUp' }, onShortcut: () => onArrow(-1) }, { shortcut: { key: 'ArrowUp' }, onShortcut: () => onArrow(-1) },
@@ -1,12 +1,11 @@
<script lang="ts"> <script lang="ts">
import { generateId } from '#lib/utils/generate-id.ts';
import { Switch } from '@immich/ui'; import { Switch } from '@immich/ui';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import { quintOut } from 'svelte/easing'; import { quintOut } from 'svelte/easing';
import { fly } from 'svelte/transition'; import { fly } from 'svelte/transition';
interface Props { type Props = {
title: string; title: string;
subtitle?: string; subtitle?: string;
checked?: boolean; checked?: boolean;
@@ -14,7 +13,7 @@
isEdited?: boolean; isEdited?: boolean;
onToggle?: (isChecked: boolean) => void; onToggle?: (isChecked: boolean) => void;
children?: Snippet; children?: Snippet;
} };
let { let {
title, title,
@@ -26,10 +25,9 @@
children, children,
}: Props = $props(); }: Props = $props();
let id: string = generateId(); const id = $props.id();
const switchId = `input-${id}`;
let switchId = $derived(`input-${id}`); const subtitleId = $derived(subtitle ? `${id}-subtitle` : undefined);
let subtitleId = $derived(subtitle ? `${id}-subtitle` : undefined);
</script> </script>
<div class="flex place-items-center justify-between"> <div class="flex place-items-center justify-between">
+2 -4
View File
@@ -1,6 +1,4 @@
<script lang="ts"> <script lang="ts">
import { generateId } from '#lib/utils/generate-id.ts';
interface Props { interface Props {
filters: string[]; filters: string[];
labels?: string[]; labels?: string[];
@@ -9,9 +7,9 @@
onSelect: (selected: string) => void; onSelect: (selected: string) => void;
} }
let { filters, selected, label, labels, onSelect }: Props = $props(); const { filters, selected, label, labels, onSelect }: Props = $props();
const id = `group-tab-${generateId()}`; const id = $props.id();
</script> </script>
<fieldset <fieldset
+4 -5
View File
@@ -1,28 +1,27 @@
<script lang="ts"> <script lang="ts">
import { focusOutside } from '#lib/actions/focus-outside.ts'; import { focusOutside } from '#lib/actions/focus-outside.ts';
import { shortcuts } from '#lib/actions/shortcut.ts'; import { shortcuts } from '#lib/actions/shortcut.ts';
import { generateId } from '#lib/utils/generate-id.ts';
import { Icon } from '@immich/ui'; import { Icon } from '@immich/ui';
import { mdiStar, mdiStarOutline } from '@mdi/js'; import { mdiStar, mdiStarOutline } from '@mdi/js';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
export type Rating = -1 | 1 | 2 | 3 | 4 | 5 | null; export type Rating = -1 | 1 | 2 | 3 | 4 | 5 | null;
interface Props { type Props = {
count?: number; count?: number;
rating: Rating; rating: Rating;
readOnly?: boolean; readOnly?: boolean;
onRating: (rating: Rating) => void | undefined; onRating: (rating: Rating) => void | undefined;
} };
let { count = 5, rating, readOnly = false, onRating }: Props = $props(); const { count = 5, rating, readOnly = false, onRating }: Props = $props();
let ratingSelection = $derived(rating); let ratingSelection = $derived(rating);
let hoverRating: Rating = $state(null); let hoverRating: Rating = $state(null);
let focusRating: Rating = $state(null); let focusRating: Rating = $state(null);
let timeoutId: ReturnType<typeof setTimeout> | undefined; let timeoutId: ReturnType<typeof setTimeout> | undefined;
const id = generateId(); const id = $props.id();
const handleSelect = (newRating: Rating) => { const handleSelect = (newRating: Rating) => {
if (readOnly) { if (readOnly) {
-2
View File
@@ -1,2 +0,0 @@
let _count = 0;
export const generateId = (): string => `id-${_count++}`;
@@ -7,7 +7,6 @@
import { getWorkflowQuery, updateWorkflowMutation } from '#lib/queries/workflow.query.ts'; import { getWorkflowQuery, updateWorkflowMutation } from '#lib/queries/workflow.query.ts';
import { Route } from '#lib/route.ts'; import { Route } from '#lib/route.ts';
import { getWorkflowActions } from '#lib/services/workflow.service.ts'; import { getWorkflowActions } from '#lib/services/workflow.service.ts';
import { generateId } from '#lib/utils/generate-id.ts';
import { getTriggerDescription, getTriggerName } from '#lib/utils/workflow.ts'; import { getTriggerDescription, getTriggerName } from '#lib/utils/workflow.ts';
import type { WorkflowUpdateDto } from '@immich/sdk'; import type { WorkflowUpdateDto } from '@immich/sdk';
import { import {
@@ -63,6 +62,9 @@
const query = createPreloadedQuery(() => getWorkflowQuery(params.workflowId)); const query = createPreloadedQuery(() => getWorkflowQuery(params.workflowId));
const workflow = $derived(query.data); const workflow = $derived(query.data);
let stepCounter = 0;
const generateId = () => `step-${stepCounter++}`;
let { id, enabled, name, description, trigger } = $state(query.data); let { id, enabled, name, description, trigger } = $state(query.data);
let steps = $state(query.data.steps.map((step) => ({ ...step, id: generateId() }))); let steps = $state(query.data.steps.map((step) => ({ ...step, id: generateId() })));
const savedWorkflow = $derived(cloneDeep(workflow)); const savedWorkflow = $derived(cloneDeep(workflow));