Parcourir la source

add hover overlay with upload/trash icons to project icon in edit dialog

David Hill il y a 1 mois
Parent
commit
2dbdd18483

+ 52 - 13
packages/app/src/components/dialog-edit-project.tsx

@@ -27,11 +27,15 @@ export function DialogEditProject(props: { project: LocalProject }) {
   })
 
   const [dragOver, setDragOver] = createSignal(false)
+  const [iconHover, setIconHover] = createSignal(false)
 
   function handleFileSelect(file: File) {
     if (!file.type.startsWith("image/")) return
     const reader = new FileReader()
-    reader.onload = (e) => setStore("iconUrl", e.target?.result as string)
+    reader.onload = (e) => {
+      setStore("iconUrl", e.target?.result as string)
+      setIconHover(false)
+    }
     reader.readAsDataURL(file)
   }
 
@@ -92,9 +96,9 @@ export function DialogEditProject(props: { project: LocalProject }) {
           <div class="flex flex-col gap-2">
             <label class="text-12-medium text-text-weak">Icon</label>
             <div class="flex gap-3 items-start">
-              <div class="relative">
+              <div class="relative" onMouseEnter={() => setIconHover(true)} onMouseLeave={() => setIconHover(false)}>
                 <div
-                  class="size-16 rounded-md transition-colors cursor-pointer"
+                  class="relative size-16 rounded-md transition-colors cursor-pointer"
                   classList={{
                     "border-text-interactive-base bg-surface-info-base/20": dragOver(),
                     "border-border-base hover:border-border-strong": !dragOver(),
@@ -103,7 +107,13 @@ export function DialogEditProject(props: { project: LocalProject }) {
                   onDrop={handleDrop}
                   onDragOver={handleDragOver}
                   onDragLeave={handleDragLeave}
-                  onClick={() => document.getElementById("icon-upload")?.click()}
+                  onClick={() => {
+                    if (store.iconUrl && iconHover()) {
+                      clearIcon()
+                    } else {
+                      document.getElementById("icon-upload")?.click()
+                    }
+                  }}
                 >
                   <Show
                     when={store.iconUrl}
@@ -120,15 +130,44 @@ export function DialogEditProject(props: { project: LocalProject }) {
                     <img src={store.iconUrl} alt="Project icon" class="size-full object-cover" />
                   </Show>
                 </div>
-                <Show when={store.iconUrl}>
-                  <button
-                    type="button"
-                    class="absolute -top-1.5 -right-1.5 size-5 rounded-full bg-surface-raised-base border border-border-base flex items-center justify-center hover:bg-surface-raised-base-hover"
-                    onClick={clearIcon}
-                  >
-                    <Icon name="close" class="size-3 text-icon-base" />
-                  </button>
-                </Show>
+                <div
+                  style={{
+                    position: "absolute",
+                    top: 0,
+                    left: 0,
+                    width: "64px",
+                    height: "64px",
+                    background: "rgba(0,0,0,0.6)",
+                    "border-radius": "6px",
+                    "z-index": 10,
+                    "pointer-events": "none",
+                    opacity: iconHover() && !store.iconUrl ? 1 : 0,
+                    display: "flex",
+                    "align-items": "center",
+                    "justify-content": "center",
+                  }}
+                >
+                  <Icon name="cloud-upload" size="large" class="text-icon-invert-base" />
+                </div>
+                <div
+                  style={{
+                    position: "absolute",
+                    top: 0,
+                    left: 0,
+                    width: "64px",
+                    height: "64px",
+                    background: "rgba(0,0,0,0.6)",
+                    "border-radius": "6px",
+                    "z-index": 10,
+                    "pointer-events": "none",
+                    opacity: iconHover() && store.iconUrl ? 1 : 0,
+                    display: "flex",
+                    "align-items": "center",
+                    "justify-content": "center",
+                  }}
+                >
+                  <Icon name="trash" size="large" class="text-icon-invert-base" />
+                </div>
               </div>
               <input id="icon-upload" type="file" accept="image/*" class="hidden" onChange={handleInputChange} />
               <div class="flex flex-col gap-1.5 text-12-regular text-text-weak self-center">

+ 2 - 0
packages/ui/src/components/icon.tsx

@@ -64,6 +64,8 @@ const icons = {
   help: `<path d="M7.91683 7.91927V6.2526H12.0835V8.7526L10.0002 10.0026V12.0859M10.0002 13.7526V13.7609M17.9168 10.0026C17.9168 14.3749 14.3724 17.9193 10.0002 17.9193C5.62791 17.9193 2.0835 14.3749 2.0835 10.0026C2.0835 5.63035 5.62791 2.08594 10.0002 2.08594C14.3724 2.08594 17.9168 5.63035 17.9168 10.0026Z" stroke="currentColor" stroke-linecap="square"/>`,
   "settings-gear": `<path d="M7.62516 4.46094L5.05225 3.86719L3.86475 5.05469L4.4585 7.6276L2.0835 9.21094V10.7943L4.4585 12.3776L3.86475 14.9505L5.05225 16.138L7.62516 15.5443L9.2085 17.9193H10.7918L12.3752 15.5443L14.9481 16.138L16.1356 14.9505L15.5418 12.3776L17.9168 10.7943V9.21094L15.5418 7.6276L16.1356 5.05469L14.9481 3.86719L12.3752 4.46094L10.7918 2.08594H9.2085L7.62516 4.46094Z" stroke="currentColor"/><path d="M12.5002 10.0026C12.5002 11.3833 11.3809 12.5026 10.0002 12.5026C8.61945 12.5026 7.50016 11.3833 7.50016 10.0026C7.50016 8.62189 8.61945 7.5026 10.0002 7.5026C11.3809 7.5026 12.5002 8.62189 12.5002 10.0026Z" stroke="currentColor"/>`,
   dash: `<rect x="5" y="9.5" width="10" height="1" fill="currentColor"/>`,
+  "cloud-upload": `<path d="M12.0833 16.25H15C17.0711 16.25 18.75 14.5711 18.75 12.5C18.75 10.5649 17.2843 8.97217 15.4025 8.77133C15.2 6.13103 12.8586 4.08333 10 4.08333C7.71532 4.08333 5.76101 5.49781 4.96501 7.49881C2.84892 7.90461 1.25 9.76559 1.25 11.6667C1.25 13.9813 3.30203 16.25 5.83333 16.25H7.91667M10 16.25V10.4167M12.0833 11.875L10 9.79167L7.91667 11.875" stroke="currentColor" stroke-linecap="square"/>`,
+  trash: `<path d="M4.58342 17.9134L4.58369 17.4134L4.22787 17.5384L4.22766 18.0384H4.58342V17.9134ZM15.4167 17.9134V18.0384H15.7725L15.7723 17.5384L15.4167 17.9134ZM2.08342 3.95508V3.45508H1.58342V3.95508H2.08342V4.45508V3.95508ZM17.9167 4.45508V4.95508H18.4167V4.45508H17.9167V3.95508V4.45508ZM4.16677 4.58008L3.66701 4.5996L4.22816 17.5379L4.72792 17.4934L5.22767 17.4489L4.66652 4.54055L4.16677 4.58008ZM4.58342 18.0384V17.9134H15.4167V18.0384V18.5384H4.58342V18.0384ZM15.4167 17.9134L15.8332 17.5379L16.2498 4.5996L15.7501 4.58008L15.2503 4.56055L14.8337 17.4989L15.4167 17.9134ZM15.8334 4.58008V4.08008H4.16677V4.58008V5.08008H15.8334V4.58008ZM2.08342 4.45508V4.95508H4.16677V4.58008V4.08008H2.08342V4.45508ZM15.8334 4.58008V5.08008H17.9167V4.45508V3.95508H15.8334V4.58008ZM6.83951 4.35149L7.432 4.55047C7.79251 3.47701 8.80699 2.70508 10.0001 2.70508V2.20508V1.70508C8.25392 1.70508 6.77335 2.83539 6.24702 4.15251L6.83951 4.35149ZM10.0001 2.20508V2.70508C11.1932 2.70508 12.2077 3.47701 12.5682 4.55047L13.1607 4.35149L13.7532 4.15251C13.2269 2.83539 11.7463 1.70508 10.0001 1.70508V2.20508Z" fill="currentColor"/>`,
 }
 
 export interface IconProps extends ComponentProps<"svg"> {