feat: mark already-added images as disabled in EntityPicker
This commit is contained in:
@@ -104,7 +104,7 @@
|
|||||||
case 'Enter': {
|
case 'Enter': {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const item = flatFiltered[highlightIndex];
|
const item = flatFiltered[highlightIndex];
|
||||||
if (item) {
|
if (item && !item.disabled) {
|
||||||
onselect(item.value);
|
onselect(item.value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -121,8 +121,9 @@
|
|||||||
onclose();
|
onclose();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleItemClick(value: string) {
|
function handleItemClick(item: EntityPickerItem) {
|
||||||
onselect(value);
|
if (item.disabled) return;
|
||||||
|
onselect(item.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Track the flat index across groups for highlight matching. */
|
/** Track the flat index across groups for highlight matching. */
|
||||||
@@ -204,18 +205,22 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="entity-picker-item"
|
class="entity-picker-item"
|
||||||
class:entity-picker-item--highlighted={isHighlighted}
|
class:entity-picker-item--highlighted={isHighlighted && !item.disabled}
|
||||||
class:entity-picker-item--current={isCurrent}
|
class:entity-picker-item--current={isCurrent}
|
||||||
|
class:entity-picker-item--disabled={item.disabled}
|
||||||
data-highlighted={isHighlighted}
|
data-highlighted={isHighlighted}
|
||||||
onclick={() => handleItemClick(item.value)}
|
onclick={() => handleItemClick(item)}
|
||||||
onmouseenter={() => { highlightIndex = flatIdx; }}
|
onmouseenter={() => { highlightIndex = flatIdx; }}
|
||||||
|
disabled={item.disabled}
|
||||||
>
|
>
|
||||||
{#if item.icon}
|
{#if item.icon}
|
||||||
<span class="entity-picker-item-icon">{@html item.icon}</span>
|
<span class="entity-picker-item-icon">{@html item.icon}</span>
|
||||||
{/if}
|
{/if}
|
||||||
<span class="entity-picker-item-content">
|
<span class="entity-picker-item-content">
|
||||||
<span class="entity-picker-item-label">{item.label}</span>
|
<span class="entity-picker-item-label">{item.label}</span>
|
||||||
{#if item.description}
|
{#if item.disabledHint}
|
||||||
|
<span class="entity-picker-item-hint">{item.disabledHint}</span>
|
||||||
|
{:else if item.description}
|
||||||
<span class="entity-picker-item-description">{item.description}</span>
|
<span class="entity-picker-item-description">{item.description}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
@@ -422,4 +427,17 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entity-picker-item--disabled {
|
||||||
|
opacity: 0.45;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity-picker-item-hint {
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-style: italic;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -144,6 +144,8 @@ export interface EntityPickerItem {
|
|||||||
description?: string;
|
description?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
group?: string;
|
group?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
disabledHint?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Volume mount configuration for a project. */
|
/** Volume mount configuration for a project. */
|
||||||
|
|||||||
@@ -33,17 +33,22 @@
|
|||||||
imagePickerLoading = true;
|
imagePickerLoading = true;
|
||||||
try {
|
try {
|
||||||
const registries = await api.listRegistries();
|
const registries = await api.listRegistries();
|
||||||
|
// Collect existing project images to mark as already added.
|
||||||
|
const existingImages = new Set(projects.map(p => p.image.toLowerCase()));
|
||||||
const items: EntityPickerItem[] = [];
|
const items: EntityPickerItem[] = [];
|
||||||
for (const reg of registries) {
|
for (const reg of registries) {
|
||||||
if (!reg.owner) continue;
|
if (!reg.owner) continue;
|
||||||
try {
|
try {
|
||||||
const images = await api.listRegistryImages(reg.id);
|
const images = await api.listRegistryImages(reg.id);
|
||||||
for (const img of images) {
|
for (const img of images) {
|
||||||
|
const alreadyAdded = existingImages.has(img.full_ref.toLowerCase());
|
||||||
items.push({
|
items.push({
|
||||||
value: JSON.stringify({ full_ref: img.full_ref, registryName: reg.name }),
|
value: JSON.stringify({ full_ref: img.full_ref, registryName: reg.name }),
|
||||||
label: img.full_ref,
|
label: img.full_ref,
|
||||||
description: reg.name,
|
description: alreadyAdded ? undefined : reg.name,
|
||||||
group: reg.name
|
group: reg.name,
|
||||||
|
disabled: alreadyAdded,
|
||||||
|
disabledHint: alreadyAdded ? 'Already added' : undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
Reference in New Issue
Block a user