- {/* Drop indicator - absolutely positioned so it doesn't affect layout */}
+ {/* Drop indicator */}
{showIndicatorBefore && (
)}
handleDragStart(e, index)}
onDragEnd={handleDragEnd}
onDragOver={(e) => handleDragOver(e, index)}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={`
- flex items-center gap-3 p-3 rounded-lg border
+ flex items-center gap-3 p-3 rounded-lg
transition-all duration-150
- ${isDragging ? 'opacity-50 cursor-grabbing' : 'cursor-grab'}
- border-[var(--border-muted)]
- ${isDisabled ? 'opacity-60' : 'hover:bg-[var(--hover-surface)]'}
+ ${isDragging ? 'opacity-50 cursor-grabbing' : isPinned ? 'cursor-default' : 'cursor-grab'}
+ border border-[var(--border-muted)]
+ ${isDisabled ? 'opacity-60' : !isPinned ? 'hover:bg-[var(--hover-surface)]' : ''}
`}
>
- {/* Reorder Controls */}
-
-
{
- e.stopPropagation();
- moveItem(index, 'up');
- }}
- disabled={index === 0}
- className={`
- p-1.5 sm:p-0.5 rounded transition-colors
- ${index === 0
- ? 'text-gray-300 dark:text-gray-600 cursor-not-allowed'
- : 'text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 sm:hover:bg-gray-100 sm:dark:hover:bg-gray-700'
- }
- `}
- aria-label="Move up"
- >
-
-
-
-
-
{
- e.stopPropagation();
- moveItem(index, 'down');
- }}
- disabled={index === items.length - 1}
- className={`
- p-1.5 sm:p-0.5 rounded transition-colors
- ${index === items.length - 1
- ? 'text-gray-300 dark:text-gray-600 cursor-not-allowed'
- : 'text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 sm:hover:bg-gray-100 sm:dark:hover:bg-gray-700'
- }
- `}
- aria-label="Move down"
- >
-
-
-
-
-
-
- {/* Label and Description */}
-
-
{item.label}
- {item.description && (
-
- {item.description}
-
- )}
- {item.isLocked && item.disabledReason && (
-
-
-
-
- {item.disabledReason}
-
- )}
-
-
- {/* Toggle Switch */}
- {(() => {
- // Locked items always show as "off" regardless of enabled state
- const showAsEnabled = item.enabled && !item.isLocked;
- return (
-
{
- e.stopPropagation();
- toggleItem(index);
- }}
- disabled={isItemDisabled}
- className={`
- relative inline-flex h-6 w-11 flex-shrink-0 items-center rounded-full
- transition-colors duration-200 focus:outline-none focus:ring-2
- focus:ring-sky-500/50 disabled:opacity-60 disabled:cursor-not-allowed
- ${showAsEnabled ? 'bg-sky-600' : 'bg-gray-300 dark:bg-gray-600'}
- `}
- >
-
+ {
+ e.stopPropagation();
+ moveItem(index, 'up');
+ }}
+ disabled={!canMoveUp(index)}
className={`
- inline-block h-4 w-4 transform rounded-full bg-white
- shadow-sm transition-transform duration-200
- ${showAsEnabled ? 'translate-x-6' : 'translate-x-1'}
+ p-1.5 sm:p-0.5 rounded transition-colors
+ ${!canMoveUp(index)
+ ? 'text-gray-300 dark:text-gray-600 cursor-not-allowed'
+ : 'text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 sm:hover:bg-gray-100 sm:dark:hover:bg-gray-700'
+ }
`}
- />
-
- );
- })()}
+ aria-label="Move up"
+ >
+
+
+
+
+
{
+ e.stopPropagation();
+ moveItem(index, 'down');
+ }}
+ disabled={!canMoveDown(index)}
+ className={`
+ p-1.5 sm:p-0.5 rounded transition-colors
+ ${!canMoveDown(index)
+ ? 'text-gray-300 dark:text-gray-600 cursor-not-allowed'
+ : 'text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 sm:hover:bg-gray-100 sm:dark:hover:bg-gray-700'
+ }
+ `}
+ aria-label="Move down"
+ >
+
+
+
+
+
+ ) : (
+
+ )}
+
+ {/* Label and Description */}
+
+
{item.label}
+ {item.description && (
+
+ {item.description}
+
+ )}
+ {item.isLocked && item.disabledReason && (
+
+
+
+
+ {item.disabledReason}
+
+ )}
+
+
+ {/* Toggle Switch */}
+
{
+ e.stopPropagation();
+ toggleItem(index);
+ }}
+ disabled={isItemDisabled}
+ className={`
+ relative inline-flex h-6 w-11 flex-shrink-0 items-center rounded-full
+ transition-colors duration-200 focus:outline-none focus:ring-2
+ focus:ring-sky-500/50 disabled:opacity-60 disabled:cursor-not-allowed
+ ${item.enabled && !item.isLocked ? 'bg-sky-600' : 'bg-gray-300 dark:bg-gray-600'}
+ `}
+ >
+
+
);
})}
- {/* Drop indicator after last item - use relative container with absolute indicator */}
+ {/* Drop indicator after last item */}
{dropGapIndex === items.length && (
diff --git a/src/frontend/src/types/settings.ts b/src/frontend/src/types/settings.ts
index 4c0f1607..c189e413 100644
--- a/src/frontend/src/types/settings.ts
+++ b/src/frontend/src/types/settings.ts
@@ -101,6 +101,7 @@ export interface OrderableListOption {
description?: string;
disabledReason?: string; // Explanation when item cannot be enabled
isLocked?: boolean; // Item cannot be toggled (e.g., missing dependency)
+ isPinned?: boolean; // Item cannot be reordered (but can still be toggled if not locked)
}
export interface OrderableListFieldConfig extends BaseField {