plugins: support url icons
Massimo Melina committed
Mar 26, 2023 at 11:55 UTC
77ed5b04e9c596e19e07c8fe7bf2bb4450f29392
4 files changed
+30
-8
dev-plugins.md
+4
-2
@@ -194,9 +194,11 @@ This is a list of available frontend-events, with respective object parameter an
194
- output `FileMenuEntry | FileMenuEntry[]`
195
```typescript
196
interface FileMenuEntry {
197
- label: ReactNode, icon?: string, href?: string,
197
+ label: ReactNode,
198
+ href?: string, // use this if you want your entry to be a link
199
+ icon?: string, // supports: emoji, name from a limited set
200
onClick?: () => (Promisable<boolean>) // return false to not close menu dialog
199
- //...rest is transfered to <a> element
201
+ //...rest is transfered to <a> element, for example 'target', or 'title'
202
}
203
```
204
## Publish your plug-in
frontend/src/BrowseFiles.ts
+4
-2
@@ -11,7 +11,7 @@ import {
11
useRef,
12
useState
13
} from 'react'
14
-import { domOn, formatBytes, ErrorMsg, hIcon, isMobile, newDialog, hfsEvent, getHFS } from './misc'
14
+import { domOn, formatBytes, ErrorMsg, hIcon, isMobile, newDialog, hfsEvent, getHFS, wantArray } from './misc'
15
import { Checkbox, CustomCode, Spinner } from './components'
16
import { Head } from './Head'
17
import { state, useSnapState } from './state'
@@ -227,7 +227,9 @@ const Entry = memo((entry: DirEntry & { midnight: Date, separator?: string }) =>
227
{ label: "Download", href: href + '?dl', icon: 'download' },
228
can_delete && { label: "Delete", icon: 'trash', onClick: () => deleteFiles([href], base) }
229
]
230
- hfsEvent('fileMenu', { entry, menu })
230
+ const res = hfsEvent('fileMenu', { entry })
231
+ if (res)
232
+ menu.push(...res.flat())
233
const close = newDialog({
234
title: "File menu",
235
icon: () => ico,
frontend/src/icons.ts
+9
-4
@@ -35,14 +35,19 @@ document.fonts.ready.then(async ()=> {
35
state.iconsClass = ' ' // with fontello we don't need an additional class (unlike google material icons), but the empty space will cause reload
36
})
37
38
-export const Icon = memo(({ name, alt, className='', ...props }: { name:string, className?:string, alt?:string, style?:any }) => {
38
+interface IconProps { name:string, className?:string, alt?:string, [rest:string]: any }
39
+export const Icon = memo(({ name, alt, className='', ...props }: IconProps) => {
40
const [emoji,clazz] = ((SYS_ICONS as any)[name] || name).split(':')
41
const { iconsClass } = useSnapState()
41
- className += ' icon ' + (iconsClass ? 'fa-'+(clazz||name) : 'emoji')
42
+ className += ' icon'
43
+ const nameIsEmoji = name.length <= 2
44
+ const nameIsFile = name.includes('.')
45
+ className += nameIsEmoji ? ' emoji-icon' : nameIsFile ? ' file-icon' : iconsClass ? ' fa-'+(clazz||name) : ' emoji'
46
return h('span',{
43
- ...props,
47
'aria-label': alt,
48
role: 'img',
49
+ ...props,
50
+ ...nameIsFile ? { style: { backgroundImage: `url(${JSON.stringify(name)})`, ...props?.style } } : undefined,
51
className,
47
- }, iconsClass ? null : (emoji||'#'))
52
+ }, nameIsEmoji ? name : iconsClass ? null : (emoji||'#'))
53
})
frontend/src/index.scss
+13
@@ -75,6 +75,19 @@ select { text-align: center; } /* it is surely cooler on the options dialog */
75
.icon {
76
font-size: 1.2em;
77
}
78
+.emoji-icon {
79
+ display: inline-block;
80
+ width: 1.4em;
81
+ text-align: center;
82
+}
83
+.file-icon {
84
+ @extend .emoji-icon;
85
+ height: 1em;
86
+ background-size: contain;
87
+ background-repeat: no-repeat;
88
+ background-position: center;
89
+ vertical-align: text-bottom;
90
+}
91
92
.icon.mirror:before { transform: scaleX(-1); }
93
a {