mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
ui-next: add component registry
This commit is contained in:
8 files changed
+120
-8
No files matched your search
@@ -0,0 +1,2 @@
|
||||
export type { Context } from './src/context';
|
||||
export type { ComponentName } from './src/registry';
|
||||
@@ -29,7 +29,7 @@ function hydroPlugins(): Plugin {
|
||||
if (fs.existsSync(uiEntry)) entries.push(uiEntry);
|
||||
}
|
||||
if (!entries.length) return 'export default [];';
|
||||
const imports = entries.map((e, i) => `import plugin${i} from '${e}';`).join('\n');
|
||||
const imports = entries.map((e, i) => `import * as plugin${i} from '${e}';`).join('\n');
|
||||
const exports = `export default [${entries.map((_, i) => `plugin${i}`).join(', ')}];`;
|
||||
return `${imports}\n${exports}`;
|
||||
}
|
||||
@@ -39,6 +39,7 @@ function hydroPlugins(): Plugin {
|
||||
|
||||
export async function apply(ctx: Context) {
|
||||
if (process.env.HYDRO_CLI) return;
|
||||
// 现在只是开发环境的实现,生产环境的实现还未完成
|
||||
const vite = await createServer({
|
||||
server: {
|
||||
middlewareMode: true,
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
"name": "@hydrooj/ui-next",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "index.ts",
|
||||
"types": "api.ts",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
function App() {
|
||||
return <div></div>;
|
||||
}
|
||||
import { Component } from './registry';
|
||||
|
||||
export default App;
|
||||
const AppInner = Component('page:app', () => <div>app</div>);
|
||||
|
||||
export default AppInner;
|
||||
@@ -0,0 +1,16 @@
|
||||
import {
|
||||
after, before, Component, patch,
|
||||
} from './registry';
|
||||
|
||||
export interface Context {
|
||||
before: typeof before;
|
||||
after: typeof after;
|
||||
patch: typeof patch;
|
||||
register: typeof Component;
|
||||
}
|
||||
|
||||
export function createContext(): Context {
|
||||
return {
|
||||
before, after, patch, register: Component,
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,16 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import plugins from 'virtual:hydro-plugins';
|
||||
import App from './App';
|
||||
import { createContext } from './context';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(<StrictMode>
|
||||
<App />
|
||||
</StrictMode>);
|
||||
const ctx = createContext();
|
||||
for (const plugin of plugins) {
|
||||
plugin.apply(ctx);
|
||||
}
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
);
|
||||
@@ -0,0 +1,76 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type Scope = 'page' | 'component';
|
||||
export type ComponentName = `${Scope}:${string}`;
|
||||
|
||||
interface BeforeAfterEntry {
|
||||
id: string;
|
||||
Comp: React.FC<any>;
|
||||
}
|
||||
|
||||
interface PatchEntry {
|
||||
id: string;
|
||||
cb: (props: any) => any;
|
||||
}
|
||||
|
||||
const registry = {
|
||||
components: {} as Record<string, React.FC<any>>,
|
||||
before: {} as Record<string, BeforeAfterEntry[]>,
|
||||
after: {} as Record<string, BeforeAfterEntry[]>,
|
||||
patch: {} as Record<string, PatchEntry[]>,
|
||||
};
|
||||
|
||||
let idCounter = 0;
|
||||
function genId() { return `__auto_${++idCounter}`; }
|
||||
|
||||
function upsert<T extends { id: string }>(list: T[], entry: T) {
|
||||
const idx = list.findIndex((e) => e.id === entry.id);
|
||||
if (idx !== -1) list[idx] = entry;
|
||||
else list.push(entry);
|
||||
}
|
||||
|
||||
export function Component<P extends Record<string, any>>(
|
||||
name: ComponentName,
|
||||
Inner: React.FC<P>,
|
||||
): React.FC<P> {
|
||||
if (registry.components[name]) {
|
||||
console.warn(`[Registry] Component "${name}" is being re-registered.`);
|
||||
}
|
||||
const Wrapped: React.FC<P> = (props) => {
|
||||
let patched = props;
|
||||
for (const { cb } of registry.patch[name] ?? []) {
|
||||
try {
|
||||
patched = cb(patched);
|
||||
} catch (e) {
|
||||
console.error(`[Registry] patch error in "${name}":`, e);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{(registry.before[name] ?? []).map(({ id, Comp }) => <Comp key={id} {...patched} />)}
|
||||
<Inner {...patched} />
|
||||
{(registry.after[name] ?? []).map(({ id, Comp }) => <Comp key={id} {...patched} />)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
Wrapped.displayName = `Registry(${name})`;
|
||||
registry.components[name] = Wrapped;
|
||||
return Wrapped;
|
||||
}
|
||||
|
||||
export function before(name: ComponentName, Comp: React.FC<any>, id?: string) {
|
||||
const list = registry.before[name] ??= [];
|
||||
upsert(list, { id: id ?? genId(), Comp });
|
||||
}
|
||||
|
||||
export function after(name: ComponentName, Comp: React.FC<any>, id?: string) {
|
||||
const list = registry.after[name] ??= [];
|
||||
upsert(list, { id: id ?? genId(), Comp });
|
||||
}
|
||||
|
||||
export function patch(name: ComponentName, cb: (props: any) => any, id?: string) {
|
||||
const list = registry.patch[name] ??= [];
|
||||
upsert(list, { id: id ?? genId(), cb });
|
||||
}
|
||||
Vendored
+6
@@ -1 +1,7 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module 'virtual:hydro-plugins' {
|
||||
import type { RegistryContext } from './registry';
|
||||
const plugins: Array<{ apply: (ctx: RegistryContext) => void }>;
|
||||
export default plugins;
|
||||
}
|
||||
Reference in new issue
Block a user