fix: (regression 0.49.0) losing focus after selecting an item
Massimo Melina committed
Apr 2, 2024 at 20:11 UTC
a219294e0bd10d1e91541b7af95d1fabcf02321a
2 files changed
+45
-53
frontend/src/BrowseFiles.ts
+37
-47
@@ -32,9 +32,7 @@ export function BrowseFiles() {
32
),
33
}), [props])
34
if (!useAuthorized())
35
- return h(CustomCode, { name: 'unauthorized',
36
- ifEmpty: () => h('h1', { className: 'unauthorized' }, t`Unauthorized`)
37
- })
35
+ return h(CustomCode, { name: 'unauthorized' }, h('h1', { className: 'unauthorized' }, t`Unauthorized`) )
36
return h('div', propsDropFiles, // element dedicated to drop-files to cover full screen
37
h('div', {
38
className: 'list-wrapper ' + (tile_size ? 'tiles-mode' : 'list-mode'),
@@ -209,44 +207,40 @@ const Entry = ({ entry, midnight, separator }: EntryProps) => {
207
const ariaId = useId()
208
const ariaProps = { id: ariaId, 'aria-label': prefix(name + ', ', isFolder ? t`Folder` : entry.web ? t`Web page` : isLink ? t`Link` : '') }
209
return h('li', { className, label: separator },
212
- h(CustomCode, {
213
- name: 'entry',
214
- props: { entry },
215
- ifEmpty: () => h(Fragment, {},
216
- showFilter && h(Checkbox, {
217
- disabled: isLink,
218
- 'aria-labelledby': ariaId,
219
- value: selected[uri],
220
- onChange(v) {
221
- if (v)
222
- return state.selected[uri] = true
223
- delete state.selected[uri]
224
- },
225
- }),
226
- h('span', { className: 'link-wrapper' }, // container to handle mouse over for both children
227
- ...isFolder || entry.web ? [ // internal navigation, use Link component
228
- h(Link, { to: uri, reloadDocument: entry.web, ...ariaProps }, // without reloadDocument, once you enter the web page, the back button won't bring you back to the frontend
229
- ico, entry.n.slice(0, -1)), // don't use name, as we want to include whole path in case of search
230
- // popup button is here to be able to detect link-wrapper:hover
231
- file_menu_on_link && !showingButton && h('button', {
232
- className: 'popup-menu-button',
233
- onClick: fileMenu
234
- }, hIcon('menu'), t`Menu`)
235
- ] : containerName ? [
236
- h('a', { href: uri, onClick, tabIndex: -1, 'aria-hidden': true }, ico),
237
- h(Link, { to: containerDir, className: 'container-folder', tabIndex: -1 }, containerName),
238
- h('a', { href: uri, onClick, ...ariaProps }, name)
239
- ] : [h('a', { href: uri, onClick, ...ariaProps }, ico, name)],
240
- ),
241
- h(CustomCode, { name: 'afterEntryName', props: { entry } }),
242
- entry.comment && h('div', { className: 'entry-comment' }, entry.comment),
243
- h('div', { className: 'entry-panel' },
244
- h(EntryDetails, { entry, midnight }),
245
- showingButton && iconBtn('menu', fileMenu, { className: 'file-menu-button' }),
246
- ),
247
- h('div'),
248
- )
249
- }),
210
+ h(CustomCode, { name: 'entry', entry },
211
+ showFilter && h(Checkbox, {
212
+ disabled: isLink,
213
+ 'aria-labelledby': ariaId,
214
+ value: selected[uri],
215
+ onChange(v) {
216
+ if (v)
217
+ return state.selected[uri] = true
218
+ delete state.selected[uri]
219
+ },
220
+ }),
221
+ h('span', { className: 'link-wrapper' }, // container to handle mouse over for both children
222
+ ...isFolder || entry.web ? [ // internal navigation, use Link component
223
+ h(Link, { to: uri, reloadDocument: entry.web, ...ariaProps }, // without reloadDocument, once you enter the web page, the back button won't bring you back to the frontend
224
+ ico, entry.n.slice(0, -1)), // don't use name, as we want to include whole path in case of search
225
+ // popup button is here to be able to detect link-wrapper:hover
226
+ file_menu_on_link && !showingButton && h('button', {
227
+ className: 'popup-menu-button',
228
+ onClick: fileMenu
229
+ }, hIcon('menu'), t`Menu`)
230
+ ] : containerName ? [
231
+ h('a', { href: uri, onClick, tabIndex: -1, 'aria-hidden': true }, ico),
232
+ h(Link, { to: containerDir, className: 'container-folder', tabIndex: -1 }, containerName),
233
+ h('a', { href: uri, onClick, ...ariaProps }, name)
234
+ ] : [h('a', { href: uri, onClick, ...ariaProps }, ico, name)],
235
+ ),
236
+ h(CustomCode, { name: 'afterEntryName', entry }),
237
+ entry.comment && h('div', { className: 'entry-comment' }, entry.comment),
238
+ h('div', { className: 'entry-panel' },
239
+ h(EntryDetails, { entry, midnight }),
240
+ showingButton && iconBtn('menu', fileMenu, { className: 'file-menu-button' }),
241
+ ),
242
+ h('div'),
243
+ ),
244
)
245
246
function fileMenu(ev: MouseEvent) {
@@ -266,11 +260,7 @@ const Entry = ({ entry, midnight, separator }: EntryProps) => {
260
}
261
262
export function getEntryIcon(entry: DirEntry) {
269
- return h(CustomCode, {
270
- name: 'entryIcon',
271
- props: { entry },
272
- ifEmpty: () => entry.getDefaultIcon()
273
- })
263
+ return h(CustomCode, { name: 'entryIcon', entry }, entry.getDefaultIcon())
264
}
265
266
export const EntryDetails = memo(({ entry, midnight }: { entry: DirEntry, midnight: Date }) => {
@@ -280,7 +270,7 @@ export const EntryDetails = memo(({ entry, midnight }: { entry: DirEntry, midnig
270
const {t} = useI18N()
271
const dd = '2-digit'
272
return h('div', { className: 'entry-details' },
283
- h(CustomCode, { name: 'additionalEntryDetails', props: { entry } }),
273
+ h(CustomCode, { name: 'additionalEntryDetails', entry }),
274
entry.p?.match(entry.isFolder ? /l/i : /r/i) && hIcon('password', { className: 'miss-perm', title: t(MISSING_PERM) }),
275
h(EntrySize, { s }),
276
time && h('span', {
frontend/src/components.ts
+8
-6
@@ -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 { getHFS, hfsEvent, hIcon, Html, isPrimitive, onlyTruthy, prefix } from './misc'
4
-import { ButtonHTMLAttributes, ChangeEvent, createElement as h, CSSProperties, FC, forwardRef, Fragment,
4
+import { ButtonHTMLAttributes, ChangeEvent, createElement as h, CSSProperties, forwardRef, Fragment,
5
HTMLAttributes, InputHTMLAttributes, isValidElement, MouseEventHandler, ReactNode, SelectHTMLAttributes,
6
useMemo, useState, ComponentPropsWithoutRef } from 'react'
7
import _ from 'lodash'
@@ -66,18 +66,20 @@ export function Select<T extends string>({ onChange, value, options, ...props }:
66
}, options.map(({ value, label }) => h('option', { key: value, value }, label)))
67
}
68
69
-export function CustomCode({ name, props, ifEmpty }: { name: string, props?: any, ifEmpty?: FC }) {
70
- const children = useMemo(() => {
69
+export function CustomCode({ name, children, ...props }: { name: string, children?: ReactNode } & any) {
70
+ const result = useMemo(() => {
71
+ props.def = children // not using 'default' because user can have unexpected error destructuring object
72
const ret = onlyTruthy(hfsEvent(name, props)
73
.map((x, key) => isValidElement(x) ? h(Fragment, { key }, x)
74
: x === 0 || x && isPrimitive(x) ? h(Html, { key, code: String(x) })
74
- : null))
75
+ : _.isArray(x) ? h(Fragment, { key }, ...x)
76
+ : null))
77
const html = getHFS().customHtml?.[name]
78
if (html?.trim?.())
79
ret.push(h(Html, { key: 'x', code: html }))
80
return ret
79
- }, [name, ...props ? Object.values(props) : []])
80
- return children.length || !ifEmpty ? h(Fragment, {}, children) : h(ifEmpty)
81
+ }, [name, children, ...props ? Object.values(props) : []])
82
+ return result.length || !children ? h(Fragment, {}, result) : children
83
}
84
85
interface IconBtnOptions extends ButtonHTMLAttributes<any> { style?: any, title?: string }