plugins: support production of content on frontend with react
Massimo Melina committed
Feb 14, 2023 at 16:02 UTC
df030a7b38ad79ecf23cc84d92d72f0bc286c855
3 files changed
+37
-13
dev-plugins.md
+23
-6
@@ -141,12 +141,29 @@ The following information applies to the default front-end, and may not apply to
141
142
### Javascript
143
Once your script is loaded into the frontend (via `frontend_js`), you will have access to the `HFS` object in the global scope.
144
-There you'll find `HFS.onEvent` function that is the base of communication.
144
146
-`onEvent(eventName:string, callback: (object) => any)` your callback will be called on the specified event.
147
-Depending on the event you'll have an object with parameters in it, and may return some output. Refer to the specific event for further information.
145
+API at this level is done with frontend-events, that you can handle by calling
146
149
-This is a list of available frontend events, with respective object parameter and output.
147
+```typescript
148
+HFS.onEvent(eventName, callback)
149
+
150
+//type callback = (parameters: object, tools: object) => any
151
+```
152
+
153
+Parameters of your callback and meaning of returned value varies with the event name.
154
+Refer to the specific event for further information.
155
+Tools are extra data and functions to help you:
156
+- `React` whole React object, as for require('react')
157
+- `h` shortcut for React.createElement
158
+- `state` object with many values in it. [Refer here for details](https://github.com/rejetto/hfs/blob/main/frontend/src/state.ts).
159
+
160
+Some frontend-events can return Html, which can be expressed in several ways
161
+- as string, containing markup
162
+- as DOM Nodes, as for document.createElement()
163
+- as ReactElement
164
+- null, undefined, false and empty-string will just be discarded
165
+
166
+This is a list of available frontend-events, with respective object parameter and output.
167
168
- `additionalEntryProps`
169
- you receive each entry of the list, and optionally produce HTML code that will be added in the `entry-props` container.
@@ -158,11 +175,11 @@ This is a list of available frontend events, with respective object parameter an
175
- `t?: Date` generic timestamp, combination of creation-time and modified-time.
176
- `c?: Date` creation-time.
177
- `m?: Date` modified-time.
161
- - output `string | falsy`
178
+ - output `Html`
179
- `afterEntryName`
180
- you receive each entry of the list, and optionally produce HTML code that will be added after the name of the entry.
181
- parameter `{ entry: Entry }` (refer above for Entry object)
165
- - output `string | falsy`
182
+ - output `Html`
183
184
## Publish your plug-in
185
frontend/src/components.ts
+8
-3
@@ -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 { hfsEvent, hIcon } from './misc'
4
-import { createElement as h, HTMLAttributes, ReactNode, useMemo } from 'react'
4
+import { createElement as h, Fragment, HTMLAttributes, isValidElement, ReactNode, useMemo } from 'react'
5
6
export function Spinner() {
7
return hIcon('spinner', { className:'spinner' })
@@ -54,7 +54,12 @@ export function Html({ code, ...rest }:{ code:string } & HTMLAttributes<any>) {
54
}
55
56
export function useCustomCode(name: string, props={}) {
57
- const code = useMemo(()=> hfsEvent(name, props).filter(x => x === 0 || x).join(''),
57
+ const code = useMemo(()=>
58
+ hfsEvent(name, props)
59
+ .filter(x => x === 0 || x)
60
+ .map((x, key) => isValidElement(x) ? h(Fragment, { key }, x)
61
+ : typeof x === 'string' ? h(Html, { key, code: x })
62
+ : h('div', { key }, x)),
63
Object.values(props))
59
- return h(Html, { code, className: name })
64
+ return h(Fragment, { children: code })
65
}
frontend/src/misc.ts
+6
-4
@@ -1,10 +1,11 @@
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 { createElement as h } from 'react'
3
+import React, { createElement as h } from 'react'
4
import { Spinner } from './components'
5
import { newDialog } from './dialog'
6
import { Icon } from './icons'
7
import { Dict } from '@hfs/shared'
8
+import { state } from './state'
9
export * from '@hfs/shared'
10
11
export function hIcon(name: string, props?:any) {
@@ -38,16 +39,17 @@ export function working() {
39
40
export function hfsEvent(name: string, params?:Dict) {
41
const output: any[] = []
41
- document.dispatchEvent(new CustomEvent('hfs.'+name, { detail:{ params, output } }))
42
+ document.dispatchEvent(new CustomEvent('hfs.'+name, { detail: { params, output } }))
43
return output
44
}
45
46
const HFS: any = (window as any).HFS = {}
47
47
-HFS.onEvent = (name: string, cb: (params:any, output:any) => any) => {
48
+HFS.onEvent = (name: string, cb: (params:any, tools: any, output:any) => any) => {
49
+ const tools = { h, React, state }
50
document.addEventListener('hfs.' + name, ev => {
51
const { params, output } = (ev as CustomEvent).detail
50
- const res = cb(params, output)
52
+ const res = cb(params, tools, output)
53
if (res !== undefined && Array.isArray(output))
54
output.push(res)
55
})