vitral 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +250 -217
- package/dist/angular.d.ts +15 -4
- package/dist/angular.js +25 -5
- package/dist/auto.d.ts +2 -0
- package/dist/auto.js +12 -0
- package/dist/chunk-2XF5BKV3.js +190 -0
- package/dist/chunk-6Y3VMWHH.js +181 -0
- package/dist/chunk-DXORSF4X.js +42 -0
- package/dist/chunk-GNPFWISJ.js +80 -0
- package/dist/chunk-HP6QWU6K.js +51 -0
- package/dist/chunk-NCONW6WS.js +606 -0
- package/dist/chunk-PX7PBCCT.js +89 -0
- package/dist/chunk-S5MZT5IT.js +20 -0
- package/dist/chunk-SFL6LTP3.js +56 -0
- package/dist/chunk-UTYAX6MU.js +152 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +88 -0
- package/dist/dedupe-IWFIXRWG.js +81 -0
- package/dist/devtools.d.ts +12 -0
- package/dist/devtools.js +86 -0
- package/dist/element.d.ts +60 -2
- package/dist/element.js +9 -2
- package/dist/errors-KKB8FGCz.d.ts +11 -0
- package/dist/events-DUdjhfps.d.ts +6 -0
- package/dist/expose-53BEJB34.js +14 -0
- package/dist/host-slot-Cz6EiGsB.d.ts +6 -0
- package/dist/iframe.d.ts +28 -0
- package/dist/iframe.js +236 -0
- package/dist/index.d.ts +142 -147
- package/dist/index.js +39 -10
- package/dist/navigate-BRG1WJUM.d.ts +9 -0
- package/dist/overrides-NZAIUGK5.js +15 -0
- package/dist/preact.d.ts +30 -7
- package/dist/preact.js +37 -9
- package/dist/react.d.ts +38 -7
- package/dist/react.js +92 -42
- package/dist/routes.d.ts +56 -0
- package/dist/routes.js +143 -0
- package/dist/shadow-MYRJQLLY.js +72 -0
- package/dist/slot-pB276oPt.d.ts +36 -0
- package/dist/solid.d.ts +29 -6
- package/dist/solid.js +31 -8
- package/dist/sri-BVKZNX5Q.js +114 -0
- package/dist/state-BDdCwc21.d.ts +4 -0
- package/dist/svelte.d.ts +19 -3
- package/dist/svelte.js +10 -4
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +8 -3
- package/dist/types-BV-hUrms.d.ts +265 -0
- package/dist/vite.d.ts +58 -4
- package/dist/vite.js +714 -24
- package/dist/vue.d.ts +79 -7
- package/dist/vue.js +69 -46
- package/package.json +144 -8
- package/dist/chunk-IZK6ITIJ.js +0 -96
- package/dist/chunk-TTL52BF2.js +0 -547
package/dist/preact.d.ts
CHANGED
|
@@ -1,22 +1,45 @@
|
|
|
1
1
|
import * as preact from 'preact';
|
|
2
2
|
import { VNode, ComponentType } from 'preact';
|
|
3
|
-
import { Props, Isolation,
|
|
4
|
-
export {
|
|
3
|
+
import { P as Props, I as Isolation, C as CheckedProps, a as RemoteModule, S as Shared } from './types-BV-hUrms.js';
|
|
4
|
+
export { R as RemoteContext } from './types-BV-hUrms.js';
|
|
5
|
+
import { R as RemoteError } from './errors-KKB8FGCz.js';
|
|
6
|
+
import { S as SlotStatus } from './slot-pB276oPt.js';
|
|
7
|
+
export { e as events } from './events-DUdjhfps.js';
|
|
8
|
+
export { n as navigate } from './navigate-BRG1WJUM.js';
|
|
9
|
+
import { L as Loading, P as Prefetch } from './host-slot-Cz6EiGsB.js';
|
|
5
10
|
|
|
6
11
|
/** Transforma um componente Preact em um remote. O contexto chega na prop `vitral`. */
|
|
7
12
|
declare function preactRemote<P extends Props>(Component: ComponentType<any>): RemoteModule<P>;
|
|
8
13
|
interface RemoteProps<P extends Props = Props> {
|
|
9
|
-
name
|
|
14
|
+
name?: string;
|
|
15
|
+
/** URL do `vitral.json`. Dispensa `configure()`. */
|
|
16
|
+
src?: string;
|
|
10
17
|
props?: P;
|
|
11
18
|
isolate?: Isolation;
|
|
12
19
|
fallback?: VNode | string | null;
|
|
13
20
|
error?: VNode | string | ((error: RemoteError, retry: () => void) => VNode | string | null) | null;
|
|
14
21
|
class?: string;
|
|
22
|
+
/** `'lazy'`: monta perto de aparecer na tela; `'idle'`: com o navegador ocioso. */
|
|
23
|
+
loading?: Loading;
|
|
24
|
+
/** Baixa antes de montar: `'hover'`, `'visible'` ou `'idle'`. */
|
|
25
|
+
prefetch?: Prefetch;
|
|
26
|
+
/** `'high'` para o remote que aparece logo ao abrir a página. */
|
|
27
|
+
priority?: RequestPriority;
|
|
28
|
+
/** Ao sair da página, guarda o remote montado (LRU, padrão 5) e na volta só atualiza as props. */
|
|
29
|
+
keepAlive?: boolean | number;
|
|
15
30
|
}
|
|
16
|
-
declare function Remote<P extends Props = Props>({ name, props, isolate, fallback, error, ...rest }: RemoteProps<P>): VNode<preact.ClassAttributes<HTMLElement> & {
|
|
17
|
-
'data-vitral': string;
|
|
31
|
+
declare function Remote<P extends Props = Props, N extends string = string>({ name, src, props, isolate, fallback, error, loading, prefetch, priority, keepAlive, ...rest }: RemoteProps<P> & CheckedProps<N>): VNode<preact.ClassAttributes<HTMLElement> & {
|
|
32
|
+
'data-vitral': string | undefined;
|
|
18
33
|
'data-vitral-status': SlotStatus;
|
|
19
|
-
class:
|
|
34
|
+
class: (RemoteProps<P> & CheckedProps<N>)["class"] | undefined;
|
|
20
35
|
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Lê uma chave compartilhada (`provide`/`shared`) e re-renderiza só quando
|
|
38
|
+
* ela muda. Com seletor, só quando o resultado do seletor muda (`Object.is`).
|
|
39
|
+
*
|
|
40
|
+
* const nome = useShared('usuario', (u) => u?.nome)
|
|
41
|
+
*/
|
|
42
|
+
declare function useShared<K extends keyof Shared, S = Shared[K] | undefined>(key: K, select?: (value: Shared[K] | undefined) => S): S;
|
|
43
|
+
declare function useShared<T = unknown, S = T | undefined>(key: string, select?: (value: T | undefined) => S): S;
|
|
21
44
|
|
|
22
|
-
export { Remote, type RemoteProps, preactRemote };
|
|
45
|
+
export { Remote, type RemoteProps, preactRemote, useShared };
|
package/dist/preact.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
hostSlot
|
|
3
|
+
} from "./chunk-UTYAX6MU.js";
|
|
4
|
+
import {
|
|
3
5
|
defineRemote,
|
|
4
6
|
events,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
memoSelect,
|
|
8
|
+
navigate,
|
|
9
|
+
same,
|
|
10
|
+
shared
|
|
11
|
+
} from "./chunk-NCONW6WS.js";
|
|
12
|
+
import "./chunk-2XF5BKV3.js";
|
|
13
|
+
import "./chunk-SFL6LTP3.js";
|
|
14
|
+
import "./chunk-PX7PBCCT.js";
|
|
15
|
+
import "./chunk-GNPFWISJ.js";
|
|
7
16
|
|
|
8
17
|
// src/preact.ts
|
|
9
18
|
import { h, render } from "preact";
|
|
10
|
-
import { useEffect, useRef, useState } from "preact/hooks";
|
|
19
|
+
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
11
20
|
function preactRemote(Component) {
|
|
12
21
|
return defineRemote({
|
|
13
22
|
mount(el, props, ctx) {
|
|
@@ -17,27 +26,46 @@ function preactRemote(Component) {
|
|
|
17
26
|
}
|
|
18
27
|
});
|
|
19
28
|
}
|
|
20
|
-
function Remote({ name, props, isolate, fallback = null, error = null, ...rest }) {
|
|
29
|
+
function Remote({ name, src, props, isolate, fallback = null, error = null, loading, prefetch, priority, keepAlive, ...rest }) {
|
|
21
30
|
const ref = useRef(null);
|
|
22
31
|
const slot = useRef(null);
|
|
23
32
|
const [state, setState] = useState({ status: "loading", error: null });
|
|
24
33
|
useEffect(() => {
|
|
25
|
-
slot.current =
|
|
34
|
+
slot.current = hostSlot(ref.current, { name, src, props: props ?? {}, isolate, loading, prefetch, priority, keepAlive, onStatus: (status, error2) => setState({ status, error: error2 }) });
|
|
26
35
|
return () => slot.current?.destroy();
|
|
27
36
|
}, []);
|
|
28
|
-
useEffect(() => slot.current?.set({ name, isolate }), [name, isolate]);
|
|
37
|
+
useEffect(() => slot.current?.set({ name, src, isolate }), [name, src, isolate]);
|
|
29
38
|
useEffect(() => slot.current?.update(props ?? {}), [props]);
|
|
30
39
|
const retry = () => slot.current?.retry();
|
|
31
|
-
return h("div", { "data-vitral": name, "data-vitral-status": state.status, class: rest.class }, [
|
|
40
|
+
return h("div", { "data-vitral": name ?? src, "data-vitral-status": state.status, class: rest.class }, [
|
|
32
41
|
state.status === "loading" ? fallback : null,
|
|
33
42
|
state.status === "error" && state.error ? typeof error === "function" ? error(state.error, retry) : error : null,
|
|
34
43
|
// O Preact nunca coloca filhos neste div: ele pertence ao remote.
|
|
35
44
|
h("div", { ref })
|
|
36
45
|
]);
|
|
37
46
|
}
|
|
47
|
+
function useShared(key, select = same) {
|
|
48
|
+
const handle = useMemo(() => shared(key), [key]);
|
|
49
|
+
const [pick] = useState(memoSelect);
|
|
50
|
+
const [, rerender] = useState(0);
|
|
51
|
+
const value = pick(handle.get(), select);
|
|
52
|
+
const latest = useRef({ value, select });
|
|
53
|
+
latest.current = { value, select };
|
|
54
|
+
useEffect(
|
|
55
|
+
() => (
|
|
56
|
+
// subscribe chama na hora: pega mudanças entre o render e o efeito.
|
|
57
|
+
handle.subscribe((v) => {
|
|
58
|
+
if (!Object.is(pick(v, latest.current.select), latest.current.value)) rerender((n) => n + 1);
|
|
59
|
+
})
|
|
60
|
+
),
|
|
61
|
+
[handle]
|
|
62
|
+
);
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
38
65
|
export {
|
|
39
66
|
Remote,
|
|
40
67
|
events,
|
|
41
68
|
navigate,
|
|
42
|
-
preactRemote
|
|
69
|
+
preactRemote,
|
|
70
|
+
useShared
|
|
43
71
|
};
|
package/dist/react.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
1
|
import { ReactNode, ComponentType } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { L as Loading, P as Prefetch } from './host-slot-Cz6EiGsB.js';
|
|
3
|
+
import * as JSXRuntime from 'react/jsx-runtime';
|
|
4
|
+
import { P as Props, I as Isolation, C as CheckedProps, a as RemoteModule, S as Shared, R as RemoteContext } from './types-BV-hUrms.js';
|
|
5
|
+
export { E as EventBus } from './types-BV-hUrms.js';
|
|
6
|
+
export { e as events } from './events-DUdjhfps.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adaptador React (funciona em Vite, CRA e Next.js App/Pages Router).
|
|
10
|
+
*
|
|
11
|
+
* Host: <Remote name="carrinho" props={{ userId }} />
|
|
12
|
+
* <Remote src="https://cdn.exemplo.com/carrinho/vitral.json" props={{ userId }} />
|
|
13
|
+
* Remote: export default reactRemote(App)
|
|
14
|
+
*/
|
|
5
15
|
|
|
6
16
|
/** Transforma um componente React em um remote. */
|
|
7
17
|
declare function reactRemote<P extends Props>(Component: ComponentType<P>): RemoteModule<P>;
|
|
@@ -11,9 +21,21 @@ declare function useVitral(): RemoteContext | null;
|
|
|
11
21
|
declare function useEvent<T = unknown>(type: string, handler: (payload: T) => void): void;
|
|
12
22
|
/** Estado derivado do último valor emitido em `type`. */
|
|
13
23
|
declare function useEventValue<T = unknown>(type: string): T | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Lê uma chave compartilhada (`provide`/`shared`) e re-renderiza só quando
|
|
26
|
+
* ela muda. Com seletor, só quando o resultado do seletor muda (`Object.is`).
|
|
27
|
+
* No servidor e na hidratação o valor é `undefined`.
|
|
28
|
+
*
|
|
29
|
+
* const nome = useShared('usuario', (u) => u?.nome)
|
|
30
|
+
*/
|
|
31
|
+
declare function useShared<K extends keyof Shared, S = Shared[K] | undefined>(key: K, select?: (value: Shared[K] | undefined) => S): S;
|
|
32
|
+
declare function useShared<T = unknown, S = T | undefined>(key: string, select?: (value: T | undefined) => S): S;
|
|
14
33
|
|
|
15
34
|
interface RemoteProps<P extends Props = Props> {
|
|
16
|
-
|
|
35
|
+
/** Nome registrado com `configure()`. Com `src`, o nome sob o qual a URL é registrada. */
|
|
36
|
+
name?: string;
|
|
37
|
+
/** URL do `vitral.json`. Dispensa `configure()`. */
|
|
38
|
+
src?: string;
|
|
17
39
|
props?: P;
|
|
18
40
|
isolate?: Isolation;
|
|
19
41
|
/** Mostrado enquanto o remote baixa. */
|
|
@@ -22,11 +44,20 @@ interface RemoteProps<P extends Props = Props> {
|
|
|
22
44
|
error?: ReactNode | ((error: Error, retry: () => void) => ReactNode);
|
|
23
45
|
onError?: (error: Error) => void;
|
|
24
46
|
className?: string;
|
|
47
|
+
/** `'lazy'`: monta perto de aparecer na tela; `'idle'`: com o navegador ocioso. */
|
|
48
|
+
loading?: Loading;
|
|
49
|
+
/** Baixa antes de montar: `'hover'`, `'visible'` ou `'idle'`. */
|
|
50
|
+
prefetch?: Prefetch;
|
|
51
|
+
/** `'high'` para o remote que aparece logo ao abrir a página. */
|
|
52
|
+
priority?: RequestPriority;
|
|
53
|
+
/** Ao sair da página, guarda o remote montado (LRU, padrão 5) e na volta só atualiza as props. */
|
|
54
|
+
keepAlive?: boolean | number;
|
|
25
55
|
}
|
|
26
56
|
/**
|
|
27
57
|
* Renderiza um remote. No servidor (Next SSR) renderiza só o fallback; o
|
|
28
|
-
* remote monta no cliente, então não há erro de hidratação.
|
|
58
|
+
* remote monta no cliente, então não há erro de hidratação. Com o nome em
|
|
59
|
+
* `Remotes` (`npx vitral types`), as props são checadas.
|
|
29
60
|
*/
|
|
30
|
-
declare function Remote<P extends Props = Props>({ name, props, isolate, fallback, error, onError, className, }: RemoteProps<P>):
|
|
61
|
+
declare function Remote<P extends Props = Props, N extends string = string>({ name, src, props, isolate, fallback, error, onError, className, loading, prefetch, priority, keepAlive, }: RemoteProps<P> & CheckedProps<N>): JSXRuntime.JSX.Element;
|
|
31
62
|
|
|
32
|
-
export { Remote, type RemoteProps, reactRemote, useEvent, useEventValue, useVitral };
|
|
63
|
+
export { Remote, type RemoteProps, reactRemote, useEvent, useEventValue, useShared, useVitral };
|
package/dist/react.js
CHANGED
|
@@ -1,13 +1,38 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
hostSlot
|
|
4
|
+
} from "./chunk-UTYAX6MU.js";
|
|
2
5
|
import {
|
|
3
6
|
defineRemote,
|
|
4
7
|
events,
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
memoSelect,
|
|
9
|
+
same,
|
|
10
|
+
shared
|
|
11
|
+
} from "./chunk-NCONW6WS.js";
|
|
12
|
+
import "./chunk-2XF5BKV3.js";
|
|
13
|
+
import "./chunk-SFL6LTP3.js";
|
|
14
|
+
import "./chunk-PX7PBCCT.js";
|
|
15
|
+
import {
|
|
16
|
+
protocol
|
|
17
|
+
} from "./chunk-GNPFWISJ.js";
|
|
7
18
|
|
|
8
19
|
// src/react.tsx
|
|
9
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
Component as ReactComponent,
|
|
22
|
+
createContext,
|
|
23
|
+
useContext,
|
|
24
|
+
useEffect,
|
|
25
|
+
useMemo,
|
|
26
|
+
useRef,
|
|
27
|
+
useState,
|
|
28
|
+
useSyncExternalStore
|
|
29
|
+
} from "react";
|
|
10
30
|
import { createRoot } from "react-dom/client";
|
|
31
|
+
import * as ReactAll from "react";
|
|
32
|
+
import * as ReactDOM from "react-dom";
|
|
33
|
+
import * as ReactDOMClient from "react-dom/client";
|
|
34
|
+
import * as JSXRuntime from "react/jsx-runtime";
|
|
35
|
+
import * as JSXDevRuntime from "react/jsx-dev-runtime";
|
|
11
36
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
37
|
var VitralContext = createContext(null);
|
|
13
38
|
var Boundary = class extends ReactComponent {
|
|
@@ -50,61 +75,85 @@ function useEventValue(type) {
|
|
|
50
75
|
useEvent(type, setValue);
|
|
51
76
|
return value;
|
|
52
77
|
}
|
|
78
|
+
function useShared(key, select = same) {
|
|
79
|
+
const handle = useMemo(() => shared(key), [key]);
|
|
80
|
+
const [pick] = useState(memoSelect);
|
|
81
|
+
return useSyncExternalStore(
|
|
82
|
+
handle.subscribe,
|
|
83
|
+
() => pick(handle.get(), select),
|
|
84
|
+
() => pick(void 0, select)
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
var offeredTo;
|
|
88
|
+
function offerReact() {
|
|
89
|
+
const p = protocol();
|
|
90
|
+
if (offeredTo === p) return;
|
|
91
|
+
offeredTo = p;
|
|
92
|
+
const compiler = ReactAll.__COMPILER_RUNTIME;
|
|
93
|
+
const modules = {
|
|
94
|
+
react: ReactAll,
|
|
95
|
+
"react-dom": ReactDOM,
|
|
96
|
+
"react-dom/client": ReactDOMClient,
|
|
97
|
+
"react/jsx-runtime": JSXRuntime,
|
|
98
|
+
"react/jsx-dev-runtime": JSXDevRuntime,
|
|
99
|
+
// O React 19 expõe o react/compiler-runtime pelo próprio react.
|
|
100
|
+
...compiler && { "react/compiler-runtime": compiler }
|
|
101
|
+
};
|
|
102
|
+
p.pending(import("./dedupe-IWFIXRWG.js").then((m) => m.offer(p, { react: { version: ReactAll.version, modules } })).catch(() => {
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
53
105
|
function Remote({
|
|
54
106
|
name,
|
|
107
|
+
src,
|
|
55
108
|
props,
|
|
56
109
|
isolate,
|
|
57
110
|
fallback = null,
|
|
58
111
|
error,
|
|
59
112
|
onError,
|
|
60
|
-
className
|
|
113
|
+
className,
|
|
114
|
+
loading,
|
|
115
|
+
prefetch,
|
|
116
|
+
priority,
|
|
117
|
+
keepAlive
|
|
61
118
|
}) {
|
|
119
|
+
const target = useRef(null);
|
|
62
120
|
const slot = useRef(null);
|
|
63
|
-
const
|
|
64
|
-
const latestProps = useRef(props ?? {});
|
|
65
|
-
latestProps.current = props ?? {};
|
|
66
|
-
const [status, setStatus] = useState("loading");
|
|
67
|
-
const [err, setErr] = useState(null);
|
|
68
|
-
const [attempt, setAttempt] = useState(0);
|
|
121
|
+
const [state, setState] = useState({ status: "loading", error: null });
|
|
69
122
|
const onErrorRef = useRef(onError);
|
|
70
123
|
onErrorRef.current = onError;
|
|
71
124
|
useEffect(() => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
fail
|
|
92
|
-
);
|
|
125
|
+
offerReact();
|
|
126
|
+
const s = hostSlot(target.current, {
|
|
127
|
+
name,
|
|
128
|
+
src,
|
|
129
|
+
props: props ?? {},
|
|
130
|
+
isolate,
|
|
131
|
+
loading,
|
|
132
|
+
prefetch,
|
|
133
|
+
priority,
|
|
134
|
+
keepAlive,
|
|
135
|
+
onStatus(status2, e) {
|
|
136
|
+
setState({ status: status2, error: e });
|
|
137
|
+
if (e) {
|
|
138
|
+
if (onErrorRef.current) onErrorRef.current(e);
|
|
139
|
+
else console.error(e);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
slot.current = s;
|
|
93
144
|
return () => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
handle.current = null;
|
|
97
|
-
if (h) queueMicrotask(() => h.unmount());
|
|
145
|
+
slot.current = null;
|
|
146
|
+
queueMicrotask(() => s.destroy());
|
|
98
147
|
};
|
|
99
|
-
}, [
|
|
100
|
-
useEffect(() => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
return /* @__PURE__ */ jsxs("div", { className, "data-vitral": name, "data-vitral-status": status, children: [
|
|
148
|
+
}, []);
|
|
149
|
+
useEffect(() => slot.current?.set({ name, src, isolate }), [name, src, isolate]);
|
|
150
|
+
useEffect(() => slot.current?.update(props ?? {}), [props]);
|
|
151
|
+
const retry = () => slot.current?.retry();
|
|
152
|
+
const { status, error: err } = state;
|
|
153
|
+
return /* @__PURE__ */ jsxs("div", { className, "data-vitral": name ?? src, "data-vitral-status": status, children: [
|
|
105
154
|
status === "loading" && fallback,
|
|
106
155
|
status === "error" && err && (typeof error === "function" ? error(err, retry) : error ?? null),
|
|
107
|
-
/* @__PURE__ */ jsx("div", { ref:
|
|
156
|
+
/* @__PURE__ */ jsx("div", { ref: target })
|
|
108
157
|
] });
|
|
109
158
|
}
|
|
110
159
|
export {
|
|
@@ -113,5 +162,6 @@ export {
|
|
|
113
162
|
reactRemote,
|
|
114
163
|
useEvent,
|
|
115
164
|
useEventValue,
|
|
165
|
+
useShared,
|
|
116
166
|
useVitral
|
|
117
167
|
};
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { P as Props } from './types-BV-hUrms.js';
|
|
2
|
+
import { S as SlotStatus } from './slot-pB276oPt.js';
|
|
3
|
+
import './errors-KKB8FGCz.js';
|
|
4
|
+
|
|
5
|
+
/** Padrão de URL → nome do remote. A primeira rota que bate vence. */
|
|
6
|
+
type Routes = Record<string, string>;
|
|
7
|
+
interface RoutesOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Links `<a href>` da mesma origem que batem com uma rota navegam sem
|
|
10
|
+
* recarregar a página (via `navigate()`). Padrão: true.
|
|
11
|
+
*/
|
|
12
|
+
links?: boolean;
|
|
13
|
+
}
|
|
14
|
+
interface RouteMatch {
|
|
15
|
+
/** Nome do remote. */
|
|
16
|
+
name: string;
|
|
17
|
+
/** Padrão que bateu. */
|
|
18
|
+
pattern: string;
|
|
19
|
+
/** Segmentos `:nome` do padrão. */
|
|
20
|
+
params: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Remote da rota que bate com `path`, ou `null`. Padrões: `/conta` (exato),
|
|
24
|
+
* `/checkout/*` (o prefixo e tudo abaixo dele), `/produto/:id` e `*` (qualquer).
|
|
25
|
+
*/
|
|
26
|
+
declare function matchRoute(path: string, routes?: Routes): RouteMatch | null;
|
|
27
|
+
/**
|
|
28
|
+
* Define as rotas, registra `<vitral-outlet>` e, com `links` (padrão), faz os
|
|
29
|
+
* links internos navegarem sem recarregar. Devolve a função que desfaz tudo.
|
|
30
|
+
*/
|
|
31
|
+
declare function routes(map: Routes, options?: RoutesOptions): () => void;
|
|
32
|
+
declare const Base: typeof HTMLElement;
|
|
33
|
+
declare class VitralOutletElement extends Base {
|
|
34
|
+
#private;
|
|
35
|
+
static observedAttributes: string[];
|
|
36
|
+
get props(): Props;
|
|
37
|
+
set props(value: Props);
|
|
38
|
+
/** Remote montado agora, ou `null`. */
|
|
39
|
+
get route(): string | null;
|
|
40
|
+
get status(): SlotStatus | "empty";
|
|
41
|
+
retry(): void;
|
|
42
|
+
connectedCallback(): void;
|
|
43
|
+
disconnectedCallback(): void;
|
|
44
|
+
attributeChangedCallback(attr: string, _old: string | null, value: string | null): void;
|
|
45
|
+
/** Confere a URL atual e troca de remote se a rota mudou. */
|
|
46
|
+
sync(): void;
|
|
47
|
+
}
|
|
48
|
+
/** Registra `<vitral-outlet>` (ou outro nome). `routes()` já chama. */
|
|
49
|
+
declare function defineVitralOutlet(tag?: string): void;
|
|
50
|
+
declare global {
|
|
51
|
+
interface HTMLElementTagNameMap {
|
|
52
|
+
'vitral-outlet': VitralOutletElement;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { type RouteMatch, type Routes, type RoutesOptions, VitralOutletElement, defineVitralOutlet, matchRoute, routes };
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSlot,
|
|
3
|
+
navigate
|
|
4
|
+
} from "./chunk-NCONW6WS.js";
|
|
5
|
+
import "./chunk-2XF5BKV3.js";
|
|
6
|
+
import "./chunk-SFL6LTP3.js";
|
|
7
|
+
import "./chunk-PX7PBCCT.js";
|
|
8
|
+
import "./chunk-GNPFWISJ.js";
|
|
9
|
+
|
|
10
|
+
// src/routes.ts
|
|
11
|
+
var table = {};
|
|
12
|
+
var outlets = /* @__PURE__ */ new Set();
|
|
13
|
+
var compiled = /* @__PURE__ */ new Map();
|
|
14
|
+
function compile(pattern) {
|
|
15
|
+
let re = compiled.get(pattern);
|
|
16
|
+
if (!re) {
|
|
17
|
+
const body = pattern.replace(/\/\*$|^\*$/, "\0").replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/:(\w+)/g, "(?<$1>[^/]+)").replace(/\/$/, "").replace("\0", "(?:/.*)?");
|
|
18
|
+
compiled.set(pattern, re = new RegExp(`^${body}/?$`));
|
|
19
|
+
}
|
|
20
|
+
return re;
|
|
21
|
+
}
|
|
22
|
+
function matchRoute(path, routes2 = table) {
|
|
23
|
+
for (const [pattern, name] of Object.entries(routes2)) {
|
|
24
|
+
const m = compile(pattern).exec(path);
|
|
25
|
+
if (m) return { name, pattern, params: { ...m.groups } };
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function routes(map, options = {}) {
|
|
30
|
+
table = map;
|
|
31
|
+
defineVitralOutlet();
|
|
32
|
+
for (const o of outlets) o.sync();
|
|
33
|
+
const intercept = options.links !== false && typeof document !== "undefined";
|
|
34
|
+
if (intercept) document.addEventListener("click", onClick);
|
|
35
|
+
return () => {
|
|
36
|
+
if (intercept) document.removeEventListener("click", onClick);
|
|
37
|
+
if (table === map) table = {};
|
|
38
|
+
for (const o of outlets) o.sync();
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function onClick(e) {
|
|
42
|
+
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
43
|
+
const a = e.composedPath().find((n) => n.localName === "a" && !!n.href);
|
|
44
|
+
if (!a || a.target && a.target !== "_self" || a.hasAttribute("download")) return;
|
|
45
|
+
const url = new URL(a.href);
|
|
46
|
+
if (url.origin !== location.origin || !matchRoute(url.pathname)) return;
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
navigate(url.href);
|
|
49
|
+
}
|
|
50
|
+
var Base = typeof HTMLElement === "undefined" ? class {
|
|
51
|
+
} : HTMLElement;
|
|
52
|
+
var VitralOutletElement = class extends Base {
|
|
53
|
+
static {
|
|
54
|
+
this.observedAttributes = ["props", "isolate"];
|
|
55
|
+
}
|
|
56
|
+
#slot = null;
|
|
57
|
+
#props = {};
|
|
58
|
+
// `undefined` até a primeira checagem, para o estado `empty` aparecer já de início.
|
|
59
|
+
#route;
|
|
60
|
+
get props() {
|
|
61
|
+
return this.#props;
|
|
62
|
+
}
|
|
63
|
+
set props(value) {
|
|
64
|
+
this.#props = value ?? {};
|
|
65
|
+
this.#slot?.update(this.#props);
|
|
66
|
+
}
|
|
67
|
+
/** Remote montado agora, ou `null`. */
|
|
68
|
+
get route() {
|
|
69
|
+
return this.#route ?? null;
|
|
70
|
+
}
|
|
71
|
+
get status() {
|
|
72
|
+
return this.#slot?.status ?? (this.#route ? "loading" : "empty");
|
|
73
|
+
}
|
|
74
|
+
retry() {
|
|
75
|
+
this.#slot?.retry();
|
|
76
|
+
}
|
|
77
|
+
#onNavigate = () => this.sync();
|
|
78
|
+
connectedCallback() {
|
|
79
|
+
outlets.add(this);
|
|
80
|
+
addEventListener("popstate", this.#onNavigate);
|
|
81
|
+
globalThis.navigation?.addEventListener("navigatesuccess", this.#onNavigate);
|
|
82
|
+
this.sync();
|
|
83
|
+
}
|
|
84
|
+
disconnectedCallback() {
|
|
85
|
+
outlets.delete(this);
|
|
86
|
+
removeEventListener("popstate", this.#onNavigate);
|
|
87
|
+
globalThis.navigation?.removeEventListener("navigatesuccess", this.#onNavigate);
|
|
88
|
+
queueMicrotask(() => !this.isConnected && this.#show(null));
|
|
89
|
+
}
|
|
90
|
+
attributeChangedCallback(attr, _old, value) {
|
|
91
|
+
if (attr === "props") {
|
|
92
|
+
try {
|
|
93
|
+
this.props = value ? JSON.parse(value) : {};
|
|
94
|
+
} catch {
|
|
95
|
+
console.error("[vitral] atributo props n\xE3o \xE9 JSON v\xE1lido:", value);
|
|
96
|
+
}
|
|
97
|
+
} else this.#slot?.set({ isolate: value || void 0 });
|
|
98
|
+
}
|
|
99
|
+
/** Confere a URL atual e troca de remote se a rota mudou. */
|
|
100
|
+
sync() {
|
|
101
|
+
if (this.isConnected) this.#show(matchRoute(location.pathname)?.name ?? null);
|
|
102
|
+
}
|
|
103
|
+
#show(name) {
|
|
104
|
+
if (name === this.#route) return;
|
|
105
|
+
this.#route = name;
|
|
106
|
+
if (name) this.setAttribute("data-vitral-route", name);
|
|
107
|
+
else this.removeAttribute("data-vitral-route");
|
|
108
|
+
if (!name) {
|
|
109
|
+
this.#slot?.destroy();
|
|
110
|
+
this.#slot = null;
|
|
111
|
+
this.#status("empty", null);
|
|
112
|
+
} else if (this.#slot) {
|
|
113
|
+
this.#slot.set({ name });
|
|
114
|
+
} else {
|
|
115
|
+
this.#slot = createSlot(this, {
|
|
116
|
+
name,
|
|
117
|
+
props: this.#props,
|
|
118
|
+
isolate: this.getAttribute("isolate") || void 0,
|
|
119
|
+
onStatus: (s, e) => this.#status(s, e)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
#status(status, error) {
|
|
124
|
+
this.setAttribute("data-vitral-status", status);
|
|
125
|
+
for (const [slot, on] of [["fallback", "loading"], ["error", "error"], ["empty", "empty"]]) {
|
|
126
|
+
for (const el of this.querySelectorAll(`:scope > [slot="${slot}"]`)) el.hidden = status !== on;
|
|
127
|
+
}
|
|
128
|
+
if (status === "ready") this.dispatchEvent(new CustomEvent("vitral-ready", { bubbles: true, detail: { route: this.#route } }));
|
|
129
|
+
if (status === "error") this.dispatchEvent(new CustomEvent("vitral-error", { bubbles: true, detail: { error } }));
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
function defineVitralOutlet(tag = "vitral-outlet") {
|
|
133
|
+
if (typeof customElements !== "undefined" && !customElements.get(tag)) {
|
|
134
|
+
customElements.define(tag, class extends VitralOutletElement {
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
VitralOutletElement,
|
|
140
|
+
defineVitralOutlet,
|
|
141
|
+
matchRoute,
|
|
142
|
+
routes
|
|
143
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fetchCssText,
|
|
3
|
+
injectCss
|
|
4
|
+
} from "./chunk-SFL6LTP3.js";
|
|
5
|
+
import "./chunk-PX7PBCCT.js";
|
|
6
|
+
import "./chunk-GNPFWISJ.js";
|
|
7
|
+
|
|
8
|
+
// src/core/shadow.ts
|
|
9
|
+
var sheets = /* @__PURE__ */ new Map();
|
|
10
|
+
var hoisted = /* @__PURE__ */ new Set();
|
|
11
|
+
var RELATIVE_URL = /url\(\s*(['"]?)(?![a-z]+:|\/\/|#)([^'")]+)\1\s*\)/gi;
|
|
12
|
+
var GLOBAL_RULES = /@property\s+(--[\w-]+)\s*\{[^}]*\}|@font-face\s*\{[^}]*\}|@import\s+(?:url\(\s*)?(?:(['"])(.*?)\2|([^'"\s);]+))\s*\)?[^;]*;/gi;
|
|
13
|
+
async function attachStyles(root, el, css, global = {}, local = {}, sri = {}) {
|
|
14
|
+
const urls = [...global.inherit ?? [], ...local.inherit ?? []].map((u) => new URL(u, document.baseURI).href).concat(css);
|
|
15
|
+
if (typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
|
|
16
|
+
const loaded = await Promise.all(urls.map((u) => loadSheet(u, [], sri[u])));
|
|
17
|
+
root.adoptedStyleSheets = [.../* @__PURE__ */ new Set([...root.adoptedStyleSheets, ...loaded.flat()])];
|
|
18
|
+
} else {
|
|
19
|
+
await injectCss(urls, root, sri);
|
|
20
|
+
}
|
|
21
|
+
return (local.mirrorTheme ?? global.mirrorTheme) === false ? () => {
|
|
22
|
+
} : mirrorTheme(el);
|
|
23
|
+
}
|
|
24
|
+
function loadSheet(href, chain, integrity) {
|
|
25
|
+
let p = sheets.get(href);
|
|
26
|
+
if (!p) {
|
|
27
|
+
p = fetchCssText(href, integrity).then((text) => {
|
|
28
|
+
const up = [];
|
|
29
|
+
const imports = [];
|
|
30
|
+
const rest = text.replace(RELATIVE_URL, (_, q, u) => `url(${q}${new URL(u, href).href}${q})`).replace(GLOBAL_RULES, (rule, prop, _q, quoted, bare) => {
|
|
31
|
+
const url = quoted ?? bare;
|
|
32
|
+
if (url) imports.push(new URL(url, href).href);
|
|
33
|
+
else if (!hoisted.has(prop ?? rule)) {
|
|
34
|
+
hoisted.add(prop ?? rule);
|
|
35
|
+
up.push(rule);
|
|
36
|
+
}
|
|
37
|
+
return "";
|
|
38
|
+
});
|
|
39
|
+
if (up.length) {
|
|
40
|
+
const style = document.createElement("style");
|
|
41
|
+
style.setAttribute("data-vitral-properties", "");
|
|
42
|
+
style.textContent = up.join("\n");
|
|
43
|
+
document.head.appendChild(style);
|
|
44
|
+
}
|
|
45
|
+
const sheet = new CSSStyleSheet();
|
|
46
|
+
sheet.replaceSync(rest);
|
|
47
|
+
const next = [...chain, href];
|
|
48
|
+
return Promise.all(imports.filter((u) => !next.includes(u)).map((u) => loadSheet(u, next))).then((found) => [...found.flat(), sheet]);
|
|
49
|
+
}).catch(() => []);
|
|
50
|
+
sheets.set(href, p);
|
|
51
|
+
}
|
|
52
|
+
return p;
|
|
53
|
+
}
|
|
54
|
+
function mirrorTheme(el) {
|
|
55
|
+
const html = document.documentElement;
|
|
56
|
+
let added = [];
|
|
57
|
+
const sync = () => {
|
|
58
|
+
el.classList.remove(...added);
|
|
59
|
+
added = [...html.classList].filter((c) => !el.classList.contains(c));
|
|
60
|
+
el.classList.add(...added);
|
|
61
|
+
const theme = html.getAttribute("data-theme");
|
|
62
|
+
if (theme === null) el.removeAttribute("data-theme");
|
|
63
|
+
else el.setAttribute("data-theme", theme);
|
|
64
|
+
};
|
|
65
|
+
sync();
|
|
66
|
+
const observer = new MutationObserver(sync);
|
|
67
|
+
observer.observe(html, { attributeFilter: ["class", "data-theme"] });
|
|
68
|
+
return () => observer.disconnect();
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
attachStyles
|
|
72
|
+
};
|