ux: frontend: clicking select made the button move out of the mouse pointer

Massimo Melina committed Feb 17, 2023 at 11:41 UTC 36f6b8c4704758c7384d80d396c2c55e253f6840
2 files changed +37 -20
frontend/src/index.scss
+17 -1
@@ -117,6 +117,22 @@ header {
117 z-index: 1; /* necessary to not be covered by checkboxes */
118 }
119
120 +.before-sliding {
121 + width: 0 !important;
122 + flex: 0 !important;
123 + margin: 0 !important;
124 + height: 0 !important;
125 + padding: 0 !important;
126 + overflow: hidden !important;
127 + transition: all .5s;
128 +}
129 +.show-sliding {
130 + transition: all .5s;
131 + overflow: clip;
132 + flex: 1;
133 + white-space: nowrap;
134 +}
135 +
136 .ani-working { animation:1s blink infinite }
137
138 @keyframes blink {
@@ -237,7 +253,7 @@ ul.dir {
253 justify-content: space-evenly;
254 flex-wrap: wrap;
255 &>* {
240 - flex: auto;
256 + flex: 1;
257 margin: 0.1em;
258 }
259 & button { /* no need for horizontal padding as we are using flex to grow */
frontend/src/menu.ts
+20 -19
@@ -1,7 +1,7 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import { state, useSnapState } from './state'
4 -import { createElement as h, Fragment, useEffect, useMemo, useState } from 'react'
4 +import { ComponentPropsWithoutRef, createElement as h, Fragment, useEffect, useMemo, useState } from 'react'
5 import { alertDialog, confirmDialog, ConfirmOptions, promptDialog } from './dialog'
6 import { err2msg, hError, hIcon, onlyTruthy, prefix, useStateMounted } from './misc'
7 import { loginDialog } from './login'
@@ -42,26 +42,26 @@ export function MenuPanel() {
42 return h('div', { id: 'menu-panel' },
43 h('div', { id: 'menu-bar' },
44 h(LoginButton),
45 - showFilter && can_delete ? h(MenuButton, {
46 - icon: 'trash',
47 - label: "Delete",
48 - onClick: () => deleteFiles(Object.keys(selected), pathname)
49 - })
50 - : (can_upload || qs.length > 0) && h(MenuButton, {
51 - icon: 'upload',
52 - label: "Upload",
53 - className: uploading && 'ani-working',
54 - onClick: showUpload,
55 - }),
45 h(MenuButton, {
46 icon: 'check',
47 label: "Select",
59 - tooltip: `Selection applies to "Download zip", but you can also filter the list`,
48 + tooltip: `Selection applies to "Zip" and "Delete" (when available), but you can also filter the list`,
49 toggled: showFilter,
50 onClick() {
51 state.showFilter = !showFilter
52 }
53 }),
54 + h(MenuButton, showFilter && can_delete ? {
55 + icon: 'trash',
56 + label: "Delete",
57 + className: 'show-sliding',
58 + onClick: () => deleteFiles(Object.keys(selected), pathname)
59 + } : (can_upload || qs.length > 0) ? {
60 + icon: 'upload',
61 + label: "Upload",
62 + className: 'show-sliding ' + (uploading ? 'ani-working' : ''),
63 + onClick: showUpload,
64 + } : { icon: '', label: '', className: 'before-sliding' }),
65 h(MenuButton, getSearchProps()),
66 h(MenuButton, {
67 icon: 'settings',
@@ -70,7 +70,7 @@ export function MenuPanel() {
70 }),
71 h(MenuLink, {
72 icon: 'archive',
73 - label: "Download zip",
73 + label: "Zip",
74 tooltip: list ? "Download selected elements as a single zip file"
75 : "Download whole list (unfiltered) as a single zip file. If you select some elements, only those will be downloaded.",
76 href: '?'+String(new URLSearchParams(_.pickBy({
@@ -85,7 +85,7 @@ export function MenuPanel() {
85 onClick() {
86 state.showFilter = true
87 closeDialog(false)
88 - return alertDialog("Use checkboxes to select the files, then you can use Download-zip again")
88 + return alertDialog("Use checkboxes to select the files, then you can use Zip again")
89 },
90 }, "Select some files"),
91 }
@@ -122,17 +122,17 @@ export function MenuPanel() {
122 }
123 }
124
125 -interface MenuButtonProps {
125 +interface MenuButtonProps extends ComponentPropsWithoutRef<"button"> {
126 icon: string,
127 label: string,
128 tooltip?: string,
129 toggled?: boolean,
130 className?: string,
131 - onClick?: () => void
131 + onClick?: () => unknown
132 onClickAnimation?: boolean
133 }
134
135 -export function MenuButton({ icon, label, tooltip, toggled, onClick, onClickAnimation, className = '' }: MenuButtonProps) {
135 +export function MenuButton({ icon, label, tooltip, toggled, onClick, onClickAnimation, ...rest }: MenuButtonProps) {
136 const [working, setWorking] = useState(false)
137 return h('button', {
138 title: tooltip || label,
@@ -142,7 +142,8 @@ export function MenuButton({ icon, label, tooltip, toggled, onClick, onClickAnim
142 setWorking(true)
143 Promise.resolve(onClick()).finally(() => setWorking(false))
144 },
145 - className: [className, toggled && 'toggled', working && 'ani-working'].filter(Boolean).join(' ')
145 + className: [rest.className, toggled && 'toggled', working && 'ani-working'].filter(Boolean).join(' '),
146 + ...rest,
147 }, hIcon(icon), h('label', {}, label) )
148 }
149