mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +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 (fs.existsSync(uiEntry)) entries.push(uiEntry);
|
||||||
}
|
}
|
||||||
if (!entries.length) return 'export default [];';
|
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(', ')}];`;
|
const exports = `export default [${entries.map((_, i) => `plugin${i}`).join(', ')}];`;
|
||||||
return `${imports}\n${exports}`;
|
return `${imports}\n${exports}`;
|
||||||
}
|
}
|
||||||
@@ -39,6 +39,7 @@ function hydroPlugins(): Plugin {
|
|||||||
|
|
||||||
export async function apply(ctx: Context) {
|
export async function apply(ctx: Context) {
|
||||||
if (process.env.HYDRO_CLI) return;
|
if (process.env.HYDRO_CLI) return;
|
||||||
|
// 现在只是开发环境的实现,生产环境的实现还未完成
|
||||||
const vite = await createServer({
|
const vite = await createServer({
|
||||||
server: {
|
server: {
|
||||||
middlewareMode: true,
|
middlewareMode: true,
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
"name": "@hydrooj/ui-next",
|
"name": "@hydrooj/ui-next",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "index.ts",
|
||||||
|
"types": "api.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
function App() {
|
import { Component } from './registry';
|
||||||
return <div></div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 { StrictMode } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import plugins from 'virtual:hydro-plugins';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
import { createContext } from './context';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(<StrictMode>
|
const ctx = createContext();
|
||||||
<App />
|
for (const plugin of plugins) {
|
||||||
</StrictMode>);
|
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" />
|
/// <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