Parcourir la source

feat(admin): единая анимация кнопок подтверждения при редактировании и удалении

neyrogovnarik il y a 2 semaines
Parent
commit
f137544902
1 fichiers modifiés avec 102 ajouts et 124 suppressions
  1. 102 124
      frontend/src/app/admin/tags/page.tsx

+ 102 - 124
frontend/src/app/admin/tags/page.tsx

@@ -160,71 +160,60 @@ export default function AdminTagsPage() {
           </button>
         </div>
         <div className="max-h-[calc(100vh-13rem)] space-y-1 overflow-y-auto pb-8 pr-1">
-          {tags.map((tag) => (
-            <div key={tag.id} className="flex items-center justify-between rounded-lg bg-white/5 px-3 py-2">
-              {editingTagId === tag.id ? (
-                <div className="flex flex-1 items-center gap-2">
-                  <input value={editTagName} onChange={(e) => setEditTagName(e.target.value)}
-                    className="flex-1 rounded border border-white/10 bg-white/10 px-2 py-1 text-sm text-white outline-none"
-                    autoFocus
-                    onKeyDown={(e) => {
-                      if (e.key === 'Enter') saveTag(tag.id)
-                      if (e.key === 'Escape') cancelEditTag()
-                    }}
-                  />
-                  <button onClick={() => saveTag(tag.id)}
-                    className="rounded bg-green-600 px-2 py-1 text-xs text-white hover:bg-green-500">
-                    ✓
-                  </button>
-                  <button onClick={cancelEditTag}
-                    className="rounded bg-white/10 px-2 py-1 text-xs text-white/60 hover:text-white">
-                    ✕
-                  </button>
-                </div>
+          {tags.map((tag) => {
+            const isEditing = editingTagId === tag.id
+            const isDeleting = deletingTagId === tag.id
+            const showConfirm = isEditing || isDeleting
+            return (
+            <div key={tag.id} className="flex items-center justify-between rounded-lg bg-white/5 px-3 py-2 gap-2">
+              {isEditing ? (
+                <input value={editTagName} onChange={(e) => setEditTagName(e.target.value)}
+                  className="flex-1 rounded border border-white/10 bg-white/10 px-2 py-1 text-sm text-white outline-none"
+                  autoFocus
+                  onKeyDown={(e) => {
+                    if (e.key === 'Enter') saveTag(tag.id)
+                    if (e.key === 'Escape') cancelEditTag()
+                  }}
+                />
               ) : (
-                <>
-                  <span className="text-sm text-white">{tag.name}</span>
-                  <div className="flex items-center gap-2">
-                    <span className="text-xs text-white/40">{tag.id}</span>
-                    <div className="relative h-5 w-12 overflow-hidden">
-                      <div className={`absolute inset-0 flex items-center gap-2 transition-all duration-200 ${
-                        deletingTagId === tag.id
-                          ? '-translate-y-full opacity-0'
-                          : 'translate-y-0 opacity-100'
-                      }`}>
-                        <button onClick={() => startEditTag(tag)}
-                          className="text-xs text-white/40 hover:text-white"
-                          title="Редактировать">
-                          ✏️
-                        </button>
-                        <button onClick={() => setDeletingTagId(tag.id)}
-                          className="text-xs text-white/40 hover:text-red-400"
-                          title="Удалить">
-                          🗑️
-                        </button>
-                      </div>
-                      <div className={`absolute inset-0 flex items-center gap-2 transition-all duration-200 ${
-                        deletingTagId === tag.id
-                          ? 'translate-y-0 opacity-100'
-                          : 'translate-y-full opacity-0'
-                      }`}>
-                        <button onClick={() => confirmDeleteTag(tag.id)}
-                          className="text-xs text-green-400 hover:text-green-300"
-                          title="Подтвердить удаление">
-                          ✓
-                        </button>
-                        <button onClick={() => setDeletingTagId(null)}
-                          className="text-xs text-white/40 hover:text-white"
-                          title="Отмена">
-                          ✕
-                        </button>
-                      </div>
-                    </div>
-                  </div>
-                </>
+                <span className="text-sm text-white truncate">{tag.name}</span>
               )}
+              <div className="flex items-center gap-2 shrink-0">
+                <span className="text-xs text-white/40">{tag.id}</span>
+                <div className="relative h-5 w-12 overflow-hidden">
+                  <div className={`absolute inset-0 flex items-center gap-2 transition-all duration-200 ${
+                    showConfirm ? '-translate-y-full opacity-0' : 'translate-y-0 opacity-100'
+                  }`}>
+                    <button onClick={() => startEditTag(tag)}
+                      className="text-xs text-white/40 hover:text-white"
+                      title="Редактировать">
+                      ✏️
+                    </button>
+                    <button onClick={() => setDeletingTagId(tag.id)}
+                      className="text-xs text-white/40 hover:text-red-400"
+                      title="Удалить">
+                      🗑️
+                    </button>
+                  </div>
+                  <div className={`absolute inset-0 flex items-center gap-2 transition-all duration-200 ${
+                    showConfirm ? 'translate-y-0 opacity-100' : 'translate-y-full opacity-0'
+                  }`}>
+                    <button onClick={() => isEditing ? saveTag(tag.id) : confirmDeleteTag(tag.id)}
+                      className={`text-xs hover:brightness-125 ${isEditing ? 'text-green-400 hover:text-green-300' : 'text-red-400 hover:text-red-300'}`}
+                      title={isEditing ? 'Сохранить' : 'Подтвердить удаление'}>
+                      ✓
+                    </button>
+                    <button onClick={() => isEditing ? cancelEditTag() : setDeletingTagId(null)}
+                      className="text-xs text-white/40 hover:text-white"
+                      title="Отмена">
+                      ✕
+                    </button>
+                  </div>
+                </div>
+              </div>
             </div>
-          ))}
+            )
+          })}
         </div>
       </div>
 
@@ -244,71 +233,60 @@ export default function AdminTagsPage() {
           </button>
         </div>
         <div className="max-h-[calc(100vh-13rem)] space-y-1 overflow-y-auto pb-8 pr-1">
-          {features.map((feat) => (
-            <div key={feat.id} className="flex items-center justify-between rounded-lg bg-white/5 px-3 py-2">
-              {editingFeatureId === feat.id ? (
-                <div className="flex flex-1 items-center gap-2">
-                  <input value={editFeatureName} onChange={(e) => setEditFeatureName(e.target.value)}
-                    className="flex-1 rounded border border-white/10 bg-white/10 px-2 py-1 text-sm text-white outline-none"
-                    autoFocus
-                    onKeyDown={(e) => {
-                      if (e.key === 'Enter') saveFeature(feat.id)
-                      if (e.key === 'Escape') cancelEditFeature()
-                    }}
-                  />
-                  <button onClick={() => saveFeature(feat.id)}
-                    className="rounded bg-green-600 px-2 py-1 text-xs text-white hover:bg-green-500">
-                    ✓
-                  </button>
-                  <button onClick={cancelEditFeature}
-                    className="rounded bg-white/10 px-2 py-1 text-xs text-white/60 hover:text-white">
-                    ✕
-                  </button>
-                </div>
+          {features.map((feat) => {
+            const isEditingFeat = editingFeatureId === feat.id
+            const isDeletingFeat = deletingFeatureId === feat.id
+            const showConfirmFeat = isEditingFeat || isDeletingFeat
+            return (
+            <div key={feat.id} className="flex items-center justify-between rounded-lg bg-white/5 px-3 py-2 gap-2">
+              {isEditingFeat ? (
+                <input value={editFeatureName} onChange={(e) => setEditFeatureName(e.target.value)}
+                  className="flex-1 rounded border border-white/10 bg-white/10 px-2 py-1 text-sm text-white outline-none"
+                  autoFocus
+                  onKeyDown={(e) => {
+                    if (e.key === 'Enter') saveFeature(feat.id)
+                    if (e.key === 'Escape') cancelEditFeature()
+                  }}
+                />
               ) : (
-                <>
-                  <span className="text-sm text-white">{feat.name}</span>
-                  <div className="flex items-center gap-2">
-                    <span className="text-xs text-white/40">{feat.id}</span>
-                    <div className="relative h-5 w-12 overflow-hidden">
-                      <div className={`absolute inset-0 flex items-center gap-2 transition-all duration-200 ${
-                        deletingFeatureId === feat.id
-                          ? '-translate-y-full opacity-0'
-                          : 'translate-y-0 opacity-100'
-                      }`}>
-                        <button onClick={() => startEditFeature(feat)}
-                          className="text-xs text-white/40 hover:text-white"
-                          title="Редактировать">
-                          ✏️
-                        </button>
-                        <button onClick={() => setDeletingFeatureId(feat.id)}
-                          className="text-xs text-white/40 hover:text-red-400"
-                          title="Удалить">
-                          🗑️
-                        </button>
-                      </div>
-                      <div className={`absolute inset-0 flex items-center gap-2 transition-all duration-200 ${
-                        deletingFeatureId === feat.id
-                          ? 'translate-y-0 opacity-100'
-                          : 'translate-y-full opacity-0'
-                      }`}>
-                        <button onClick={() => confirmDeleteFeature(feat.id)}
-                          className="text-xs text-green-400 hover:text-green-300"
-                          title="Подтвердить удаление">
-                          ✓
-                        </button>
-                        <button onClick={() => setDeletingFeatureId(null)}
-                          className="text-xs text-white/40 hover:text-white"
-                          title="Отмена">
-                          ✕
-                        </button>
-                      </div>
-                    </div>
-                  </div>
-                </>
+                <span className="text-sm text-white truncate">{feat.name}</span>
               )}
+              <div className="flex items-center gap-2 shrink-0">
+                <span className="text-xs text-white/40">{feat.id}</span>
+                <div className="relative h-5 w-12 overflow-hidden">
+                  <div className={`absolute inset-0 flex items-center gap-2 transition-all duration-200 ${
+                    showConfirmFeat ? '-translate-y-full opacity-0' : 'translate-y-0 opacity-100'
+                  }`}>
+                    <button onClick={() => startEditFeature(feat)}
+                      className="text-xs text-white/40 hover:text-white"
+                      title="Редактировать">
+                      ✏️
+                    </button>
+                    <button onClick={() => setDeletingFeatureId(feat.id)}
+                      className="text-xs text-white/40 hover:text-red-400"
+                      title="Удалить">
+                      🗑️
+                    </button>
+                  </div>
+                  <div className={`absolute inset-0 flex items-center gap-2 transition-all duration-200 ${
+                    showConfirmFeat ? 'translate-y-0 opacity-100' : 'translate-y-full opacity-0'
+                  }`}>
+                    <button onClick={() => isEditingFeat ? saveFeature(feat.id) : confirmDeleteFeature(feat.id)}
+                      className={`text-xs hover:brightness-125 ${isEditingFeat ? 'text-green-400 hover:text-green-300' : 'text-red-400 hover:text-red-300'}`}
+                      title={isEditingFeat ? 'Сохранить' : 'Подтвердить удаление'}>
+                      ✓
+                    </button>
+                    <button onClick={() => isEditingFeat ? cancelEditFeature() : setDeletingFeatureId(null)}
+                      className="text-xs text-white/40 hover:text-white"
+                      title="Отмена">
+                      ✕
+                    </button>
+                  </div>
+                </div>
+              </div>
             </div>
-          ))}
+            )
+          })}
         </div>
       </div>
     </div>