@zerodep-dev/vue-tsx-pro-ui 0.3.3 → 0.4.1
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/dist/charts/AreaChart.d.ts +1 -1
- package/dist/charts/AreaChart.js +6 -48
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.js +6 -49
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/ChordChart.d.ts +7 -2
- package/dist/charts/ChordChart.js +151 -60
- package/dist/charts/ChordChart.js.map +1 -1
- package/dist/charts/ComboChart.d.ts +2 -1
- package/dist/charts/ComboChart.js +17 -93
- package/dist/charts/ComboChart.js.map +1 -1
- package/dist/charts/DonutChart.d.ts +6 -1
- package/dist/charts/DonutChart.js +134 -51
- package/dist/charts/DonutChart.js.map +1 -1
- package/dist/charts/FunnelChart.js.map +1 -1
- package/dist/charts/GraphChart.d.ts +22 -10
- package/dist/charts/GraphChart.js +415 -175
- package/dist/charts/GraphChart.js.map +1 -1
- package/dist/charts/LineChart.d.ts +1 -29
- package/dist/charts/LineChart.js +6 -54
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/MapChart.d.ts +21 -48
- package/dist/charts/MapChart.js +426 -353
- package/dist/charts/MapChart.js.map +1 -1
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/SankeyChart.d.ts +7 -2
- package/dist/charts/SankeyChart.js +164 -76
- package/dist/charts/SankeyChart.js.map +1 -1
- package/dist/charts/ScatterChart.d.ts +7 -1
- package/dist/charts/ScatterChart.js +45 -30
- package/dist/charts/ScatterChart.js.map +1 -1
- package/dist/charts/TimelineChart.d.ts +8 -1
- package/dist/charts/TimelineChart.js +141 -89
- package/dist/charts/TimelineChart.js.map +1 -1
- package/dist/charts/TreemapChart.d.ts +6 -0
- package/dist/charts/TreemapChart.js +174 -58
- package/dist/charts/TreemapChart.js.map +1 -1
- package/dist/charts/WaterfallChart.d.ts +6 -7
- package/dist/charts/WaterfallChart.js +46 -51
- package/dist/charts/WaterfallChart.js.map +1 -1
- package/dist/charts/XYChart.d.ts +44 -35
- package/dist/charts/XYChart.js +493 -281
- package/dist/charts/XYChart.js.map +1 -1
- package/dist/charts/axis-ticks.d.ts +1 -1
- package/dist/charts/axis-ticks.js +4 -5
- package/dist/charts/axis-ticks.js.map +1 -1
- package/dist/charts/flow.d.ts +4 -11
- package/dist/charts/flow.js +4 -4
- package/dist/charts/flow.js.map +1 -1
- package/dist/charts/frame.d.ts +6 -30
- package/dist/charts/frame.js +6 -99
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/geo/projection.d.ts +45 -0
- package/dist/charts/geo/projection.js +201 -0
- package/dist/charts/geo/projection.js.map +1 -0
- package/dist/charts/geo/topojson.d.ts +50 -0
- package/dist/charts/geo/topojson.js +119 -0
- package/dist/charts/geo/topojson.js.map +1 -0
- package/dist/charts/index.d.ts +21 -9
- package/dist/charts/index.js +19 -8
- package/dist/charts/label-layout.js.map +1 -1
- package/dist/charts/layout/chord.d.ts +44 -0
- package/dist/charts/layout/chord.js +45 -0
- package/dist/charts/layout/chord.js.map +1 -0
- package/dist/charts/layout/graph.d.ts +49 -0
- package/dist/charts/layout/graph.js +268 -0
- package/dist/charts/layout/graph.js.map +1 -0
- package/dist/charts/layout/sankey.d.ts +79 -0
- package/dist/charts/layout/sankey.js +200 -0
- package/dist/charts/layout/sankey.js.map +1 -0
- package/dist/charts/layout/treemap.d.ts +46 -0
- package/dist/charts/layout/treemap.js +152 -0
- package/dist/charts/layout/treemap.js.map +1 -0
- package/dist/charts/marks.d.ts +79 -0
- package/dist/charts/marks.js +231 -0
- package/dist/charts/marks.js.map +1 -0
- package/dist/charts/plot.d.ts +107 -0
- package/dist/charts/plot.js +231 -0
- package/dist/charts/plot.js.map +1 -0
- package/dist/charts/scale.d.ts +29 -0
- package/dist/charts/scale.js +244 -0
- package/dist/charts/scale.js.map +1 -0
- package/dist/charts/shape.d.ts +46 -0
- package/dist/charts/shape.js +131 -0
- package/dist/charts/shape.js.map +1 -0
- package/dist/charts/stack.d.ts +17 -0
- package/dist/charts/stack.js +35 -0
- package/dist/charts/stack.js.map +1 -0
- package/dist/charts/text-size.d.ts +12 -0
- package/dist/charts/text-size.js +32 -0
- package/dist/charts/text-size.js.map +1 -0
- package/dist/config/createUi.d.ts +21 -2
- package/dist/config/createUi.js +18 -1
- package/dist/config/createUi.js.map +1 -1
- package/dist/config/theme/preference.js +2 -1
- package/dist/config/theme/preference.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/maps/china-provinces.js +35680 -0
- package/dist/maps/china-provinces.js.map +1 -0
- package/dist/maps/china.d.ts +13 -0
- package/dist/maps/china.js +8 -0
- package/dist/maps/china.js.map +1 -0
- package/dist/maps/index.d.ts +9 -0
- package/dist/maps/index.js +3 -0
- package/dist/maps/world-countries.js +22273 -0
- package/dist/maps/world-countries.js.map +1 -0
- package/dist/maps/world.d.ts +13 -0
- package/dist/maps/world.js +8 -0
- package/dist/maps/world.js.map +1 -0
- package/dist/overlay/detached.js.map +1 -1
- package/dist/utils/request/use-request.d.ts +25 -1
- package/dist/utils/request/use-request.js +35 -3
- package/dist/utils/request/use-request.js.map +1 -1
- package/package.json +7 -8
- package/dist/charts/single.d.ts +0 -41
- package/dist/charts/single.js +0 -66
- package/dist/charts/single.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detached.js","names":["_vtp_readonlyProp","_createVNode"],"sources":["../../src/overlay/detached.tsx"],"sourcesContent":["/**\n * 渲染到组件树之外:沿用调用处的配置(主题、语言、默认值,经 ConfigScope)与应用上下文(全局组件),\n * 生命周期跟着调用的组件——它卸载时一起收掉,不留孤儿。\n *\n * const renderDetached = useDetachedRender(); // setup
|
|
1
|
+
{"version":3,"file":"detached.js","names":["_vtp_readonlyProp","_createVNode"],"sources":["../../src/overlay/detached.tsx"],"sourcesContent":["/**\n * 渲染到组件树之外:沿用调用处的配置(主题、语言、默认值,经 ConfigScope)与应用上下文(全局组件),\n * 生命周期跟着调用的组件——它卸载时一起收掉,不留孤儿。\n *\n * const renderDetached = useDetachedRender(); // setup 里;第三方库管的元素(如别的图表库的提示框)\n * renderDetached(<Tip />, element); // 再调一次换内容,传 null 卸载;组件卸载时自动卸载\n *\n * const mount = useDetachedDialog(); // setup 里;命令式的对话框(useConfirm、useFormDialog)\n * mount((host) => <Dialog _open={host.open} onClose={host.closed}>…</Dialog>);\n *\n * 对话框渲染到 body 末尾,关上后等离场过渡播完再卸载;调用的组件卸载时(例如切换了路由)把开着的对话框关上,\n * 照常走关闭的流程,等着结果的 `await confirm()` 也拿到结果。\n * useDetachedOverlay 在 useDetachedDialog 上面加一层句柄(close、update、closed),useDialog、useDrawer 用它。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport {\n getCurrentInstance,\n getCurrentScope,\n h,\n onScopeDispose,\n render,\n shallowRef,\n type AppContext,\n type VNodeChild,\n} from 'vue';\nimport { ConfigContext, useConfig, type UiConfig } from '../config/context';\nimport { ConfigScope } from '../config/Provider';\nimport type { OpenChangeProps, OpenChangeReasons } from '../utils/dom/open-change';\nimport { afterTransitions } from '../utils/style/transitions';\n\n/** 交给对话框内容的控制:开着没有、关上了 */\nexport interface DetachedDialogHost {\n /** 对话框开着:对话框写 _open={host.open},改成 false 就关上 */\n open: boolean;\n /** 对话框关上了(原生 close 事件里调用):等离场过渡播完卸载 */\n closed: () => void;\n}\n\n/** 组件树之外的根数到第几个了:给每个根一个自己的 useId 命名段 */\nlet detachedRoots = 0;\n\n/**\n * 组件树之外的根:提供调用处的配置,内容包在 ConfigScope 里(主题变量、语言)。\n * render() 出来的根,useId 从头计数、前缀又和应用相同(共用 appContext),生成的 id 会和页面上已有的撞上\n * (表单对话框的 label 关联到页面里另一个控件)。照 Vue 处理异步边界的做法(markAsyncBoundary)换一个命名段:\n * 子组件沿用父组件的 ids,所以在根上换一次就够了。\n */\nconst DetachedRoot = _component(\n ({ config, content }: { config: UiConfig; content: () => VNodeChild }) => {\n const instance = getCurrentInstance() as { ids?: [string, number, number] } | null;\n if (instance?.ids) instance.ids = [`d${++detachedRoots}-`, 0, 0];\n ConfigContext.provide(config);\n return () => <ConfigScope>{content()}</ConfigScope>;\n },\n);\n\n/** 调用处的配置与应用上下文(在 setup 里取),交给组件树之外的根 */\nfunction captureContext() {\n return { config: useConfig(), appContext: getCurrentInstance()?.appContext ?? null };\n}\n\nfunction renderInto(\n container: Element,\n { config, appContext }: { config: UiConfig; appContext: AppContext | null },\n content: () => VNodeChild,\n) {\n const vnode = h(DetachedRoot, { config, content });\n vnode.appContext = appContext;\n render(vnode, container);\n}\n\n/** 在 setup 里调用,返回 renderDetached(content, container):渲染到组件树之外的元素里,调用的组件卸载时一起卸载 */\nexport function useDetachedRender(): (content: VNodeChild | null, container: Element) => void {\n const context = captureContext();\n const containers = new Set<Element>();\n if (getCurrentScope()) {\n onScopeDispose(() => {\n for (const container of containers) render(null, container);\n containers.clear();\n });\n }\n return (content, container) => {\n if (content == null) {\n containers.delete(container);\n render(null, container);\n return;\n }\n containers.add(container);\n renderInto(container, context, () => content);\n };\n}\n\n/** 在 setup 里调用,返回 mount(content):把对话框渲染到组件树之外 */\nexport function useDetachedDialog(): (content: (host: DetachedDialogHost) => VNodeChild) => void {\n const context = captureContext();\n /** 开着的对话框:调用的组件卸载时关上 */\n const live = new Set<DetachedDialogHost>();\n if (getCurrentScope()) {\n onScopeDispose(() => {\n for (const host of live) host.open = false;\n });\n }\n\n return (content) => {\n const container = document.createElement('div');\n document.body.append(container);\n const open = shallowRef(true);\n const host: DetachedDialogHost = {\n get open() {\n return open.value;\n },\n set open(value) {\n open.value = value;\n },\n closed() {\n live.delete(host);\n const dialog = container.querySelector('dialog');\n void (dialog ? afterTransitions(dialog) : Promise.resolve()).then(() => {\n render(null, container);\n container.remove();\n });\n },\n };\n live.add(host);\n renderInto(container, context, () => content(host));\n };\n}\n\n/** 命令式浮层(useDialog、useDrawer)的选项:浮层的属性(_open 由句柄管理),content 是正文 */\nexport type DetachedOverlayOptions<P> = Omit<P, '_open' | 'children'> & {\n /** 正文;给函数时拿到句柄,正文里的按钮可以自己关上、改标题 */\n content?: VNodeChild | ((handle: DetachedOverlayHandle<P>) => VNodeChild);\n};\n\n/** useDialog().open、useDrawer().open 返回的句柄;方法不依赖 this,可以直接传出去(onSaved={handle.close}) */\nexport interface DetachedOverlayHandle<P> {\n /** 关上(onOpenChange 的原因是 imperative);已经关上时什么也不做 */\n readonly close: () => void;\n /** 改属性(标题、宽度、确认按钮的文字、正文……),没写的保持不变 */\n readonly update: (patch: Partial<DetachedOverlayOptions<P>>) => void;\n /** 关上之后 resolve,得到关上的原因:确认是 confirm,Esc、遮罩、关闭按钮、close() 各是 escape、outside、close、imperative */\n readonly closed: Promise<keyof OpenChangeReasons>;\n}\n\n/** 原生 dialog 的 close 事件 */\ntype DialogCloseEvent = Event & { currentTarget: HTMLDialogElement };\n\n/** 浮层要支持的:onOpenChange 报告原因,onClose 落到原生 dialog 上 */\ninterface DetachedOverlayProps extends OpenChangeProps {\n onClose?: (event: DialogCloseEvent) => void;\n}\n\n/**\n * 命令式浮层:在 setup 里调用,返回 open(options)。renderOverlay 把属性、开关与正文交给具体的浮层(Dialog、Drawer):\n *\n * useDetachedOverlay<DialogProps>((props, host, children) => <Dialog {...props} _open={host.open}>{children}</Dialog>);\n */\nexport function useDetachedOverlay<P extends DetachedOverlayProps>(\n renderOverlay: (\n props: Omit<P, '_open' | 'children'>,\n host: DetachedDialogHost,\n children: VNodeChild,\n ) => VNodeChild,\n): (options: DetachedOverlayOptions<P>) => DetachedOverlayHandle<P> {\n const mount = useDetachedDialog();\n return (options) => {\n const state = shallowRef(options);\n let host: DetachedDialogHost | undefined;\n let reason: keyof OpenChangeReasons = 'imperative';\n let resolveClosed!: (reason: keyof OpenChangeReasons) => void;\n const handle: DetachedOverlayHandle<P> = {\n close: () => {\n if (!host?.open) return;\n reason = 'imperative';\n host.open = false;\n state.value.onOpenChange?.(false, { reason });\n },\n update: (patch) => {\n state.value = { ...state.value, ...patch };\n },\n closed: new Promise((resolve) => (resolveClosed = resolve)),\n };\n mount((current) => {\n host = current;\n const { content, onOpenChange, onClose, ...props } = state.value;\n const overlayProps = {\n ...props,\n onOpenChange: (open, details) => {\n if (!open) reason = details.reason;\n onOpenChange?.(open, details);\n },\n onClose: (event: DialogCloseEvent) => {\n onClose?.(event);\n // 原生 close 事件先到这里、后到浮层自己(它在那里报告原因):等它报告完再 resolve\n queueMicrotask(() => resolveClosed(reason));\n current.closed();\n },\n } as Omit<P, '_open' | 'children'> & DetachedOverlayProps;\n return renderOverlay(\n overlayProps,\n current,\n typeof content === 'function' ? content(handle) : content,\n );\n });\n options.onOpenChange?.(true, { reason: 'imperative' });\n return handle;\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/** 请求结果的缓存:@zerodep-dev/storage 的 createCache 满足这个形状(按形状对接,组件库不依赖它) */
|
|
2
|
+
export interface RequestCache {
|
|
3
|
+
/** 缓存的值;fresh 为 false 是过期但还能先显示的 */
|
|
4
|
+
read(key: string): Promise<{
|
|
5
|
+
value: unknown;
|
|
6
|
+
fresh: boolean;
|
|
7
|
+
} | undefined>;
|
|
8
|
+
/** 写回请求的结果;tags 是这条依赖的东西(后端的表名),按它失效 */
|
|
9
|
+
set(key: string, value: unknown, options?: {
|
|
10
|
+
tags?: readonly string[];
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
/** 缓存的键失效或被别处改了时回调(keys 为 undefined 表示全清了);返回取消收听 */
|
|
13
|
+
watch?(listener: (keys: readonly string[] | undefined) => void): () => void;
|
|
14
|
+
}
|
|
1
15
|
/** useRequest 的选项 */
|
|
2
16
|
export interface RequestOptions<P, T> {
|
|
3
17
|
/** 请求参数:读到的响应式值变了、且内容确实不同时重新请求;不写时只在开始与 refresh 时请求 */
|
|
@@ -13,6 +27,16 @@ export interface RequestOptions<P, T> {
|
|
|
13
27
|
* 变回 true 时不自动补一次,需要时调用 refresh
|
|
14
28
|
*/
|
|
15
29
|
enabled?: () => boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 结果的缓存(先显示旧值、后台刷新):有缓存先显示;新鲜的不再请求(refresh 照样请求),过期的接着请求、到了替换并写回。
|
|
32
|
+
* key 由参数算出,不同参数各存各的;tags 是写回时标上的标签(依赖的表)。缓存读写出错时当作没有缓存,命中新鲜缓存时不调 onSuccess。
|
|
33
|
+
* 当前参数的键失效了(本页或别的标签页)自动再请求一次;别的标签页写进了新值时直接用它,不再请求
|
|
34
|
+
*/
|
|
35
|
+
cache?: {
|
|
36
|
+
store: RequestCache;
|
|
37
|
+
key: (params: P) => string;
|
|
38
|
+
tags?: (params: P) => readonly string[];
|
|
39
|
+
};
|
|
16
40
|
/** 成功后调用 */
|
|
17
41
|
onSuccess?: (data: T, params: P) => void;
|
|
18
42
|
/** 失败后调用(取消的不算) */
|
|
@@ -26,7 +50,7 @@ export interface RequestState<T> {
|
|
|
26
50
|
readonly error: unknown;
|
|
27
51
|
/** 正在请求(防抖等待时还不算) */
|
|
28
52
|
readonly loading: boolean;
|
|
29
|
-
/**
|
|
53
|
+
/** 按当前参数立即请求(缓存新鲜也请求),取消进行中的;返回结果,失败或被后来的请求顶掉时为 undefined */
|
|
30
54
|
refresh(): Promise<T | undefined>;
|
|
31
55
|
/** 取消进行中的请求与等着的防抖 */
|
|
32
56
|
cancel(): void;
|
|
@@ -13,12 +13,21 @@ import { onScopeDispose, shallowRef, watch } from "vue";
|
|
|
13
13
|
* );
|
|
14
14
|
* users.data?.rows; users.loading; users.error; users.refresh();
|
|
15
15
|
*
|
|
16
|
+
* 结果可以经 cache 选项存进 IndexedDB、localStorage(@zerodep-dev/storage 的 createCache):
|
|
17
|
+
*
|
|
18
|
+
* const cache = createCache(indexedDbDriver({ name: 'app' }), { ttl: 60_000, stale: 86_400_000 });
|
|
19
|
+
* useRequest(fetchCities, {
|
|
20
|
+
* params: () => province,
|
|
21
|
+
* cache: { store: cache, key: (p) => `cities:${p}`, tags: () => ['t_city'] }, // 标上依赖的表
|
|
22
|
+
* });
|
|
23
|
+
* // t_city 改了(kit 的失效推送、或自己保存后):await cache.invalidateTag('t_city'),正显示着的这个请求自动重新请求
|
|
24
|
+
*
|
|
16
25
|
* 只在浏览器里自动请求:服务端渲染时 data 留空,等页面在浏览器里接管后再请求。
|
|
17
26
|
* 一开始就请求时 loading 从 true 开始,服务端也按“加载中”渲染:水合时两边一致,浏览器接着发请求。
|
|
18
27
|
*/
|
|
19
28
|
/** 请求的状态(在 setup 里调用;见文件开头) */
|
|
20
29
|
function useRequest(fetcher, options = {}) {
|
|
21
|
-
const { params, debounce = 0, immediate = true, keepPrevious = true } = options;
|
|
30
|
+
const { params, cache, debounce = 0, immediate = true, keepPrevious = true } = options;
|
|
22
31
|
const data = shallowRef();
|
|
23
32
|
const error = shallowRef();
|
|
24
33
|
const starts = immediate && options.enabled?.() !== false;
|
|
@@ -35,7 +44,11 @@ function useRequest(fetcher, options = {}) {
|
|
|
35
44
|
controller = void 0;
|
|
36
45
|
loading.value = false;
|
|
37
46
|
}
|
|
38
|
-
|
|
47
|
+
/** 读缓存;没配缓存时 undefined(不 await,没缓存的请求照旧同步发出) */
|
|
48
|
+
function readCache(current) {
|
|
49
|
+
return cache?.store.read(cache.key(current)).catch(() => void 0);
|
|
50
|
+
}
|
|
51
|
+
async function run(current, force = false) {
|
|
39
52
|
cancel();
|
|
40
53
|
last = current;
|
|
41
54
|
const id = latest;
|
|
@@ -43,10 +56,21 @@ function useRequest(fetcher, options = {}) {
|
|
|
43
56
|
loading.value = true;
|
|
44
57
|
if (!keepPrevious) data.value = void 0;
|
|
45
58
|
try {
|
|
59
|
+
const reading = readCache(current);
|
|
60
|
+
const cached = reading && await reading;
|
|
61
|
+
if (id !== latest) return void 0;
|
|
62
|
+
if (cached) {
|
|
63
|
+
data.value = cached.value;
|
|
64
|
+
if (cached.fresh && !force) return cached.value;
|
|
65
|
+
}
|
|
46
66
|
const result = await fetcher(current, own.signal);
|
|
47
67
|
if (id !== latest) return void 0;
|
|
48
68
|
data.value = result;
|
|
49
69
|
error.value = void 0;
|
|
70
|
+
if (cache) {
|
|
71
|
+
const tags = cache.tags?.(current);
|
|
72
|
+
cache.store.set(cache.key(current), result, tags ? { tags } : void 0).catch(() => void 0);
|
|
73
|
+
}
|
|
50
74
|
options.onSuccess?.(result, current);
|
|
51
75
|
return result;
|
|
52
76
|
} catch (reason) {
|
|
@@ -70,6 +94,14 @@ function useRequest(fetcher, options = {}) {
|
|
|
70
94
|
else run(next);
|
|
71
95
|
});
|
|
72
96
|
if (starts && typeof window !== "undefined") run(readParams());
|
|
97
|
+
if (cache?.store.watch && typeof window !== "undefined") {
|
|
98
|
+
const stop = cache.store.watch((keys) => {
|
|
99
|
+
if (latest === 0 || options.enabled?.() === false) return;
|
|
100
|
+
const current = readParams();
|
|
101
|
+
if (!keys || keys.includes(cache.key(current))) run(current);
|
|
102
|
+
});
|
|
103
|
+
onScopeDispose(stop);
|
|
104
|
+
}
|
|
73
105
|
onScopeDispose(cancel);
|
|
74
106
|
return {
|
|
75
107
|
get data() {
|
|
@@ -81,7 +113,7 @@ function useRequest(fetcher, options = {}) {
|
|
|
81
113
|
get loading() {
|
|
82
114
|
return loading.value;
|
|
83
115
|
},
|
|
84
|
-
refresh: () => run(readParams()),
|
|
116
|
+
refresh: () => run(readParams(), true),
|
|
85
117
|
cancel,
|
|
86
118
|
mutate: (value) => data.value = value
|
|
87
119
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-request.js","names":[],"sources":["../../../src/utils/request/use-request.ts"],"sourcesContent":["/**\n * 一个请求的状态(参考 TanStack Query、alova 的 useWatcher):只采用最后一次的结果,发新请求时取消上一次,\n * 参数变化防抖,重新请求时保留旧数据(表格不闪成空的)。\n *\n * const http = useHttp();\n * const users = useRequest(\n * ({ keyword, page }, signal) => http.request<Page<User>>({ url: '/users', query: { keyword, page }, signal }),\n * { params: () => ({ keyword, page }), debounce: 300 },\n * );\n * users.data?.rows; users.loading; users.error; users.refresh();\n *\n * 只在浏览器里自动请求:服务端渲染时 data 留空,等页面在浏览器里接管后再请求。\n * 一开始就请求时 loading 从 true 开始,服务端也按“加载中”渲染:水合时两边一致,浏览器接着发请求。\n */\nimport { onScopeDispose, shallowRef, watch } from 'vue';\nimport { sameValue } from '../data/equality';\nimport { isAbortError } from './http';\n\n/** useRequest 的选项 */\nexport interface RequestOptions<P, T> {\n /** 请求参数:读到的响应式值变了、且内容确实不同时重新请求;不写时只在开始与 refresh 时请求 */\n params?: () => P;\n /** 参数变了以后等多久再请求(毫秒),默认 0;refresh 不等 */\n debounce?: number;\n /** 一开始就请求,默认 true;false 时等调用 refresh(或参数变化) */\n immediate?: boolean;\n /** 重新请求时保留上一次的数据,默认 true;false 时请求一开始就清空 */\n keepPrevious?: boolean;\n /**\n * 自动请求的开关(弹层打开时才请求这类):为 false 时 immediate 与参数变化都不请求,refresh 照常。\n * 变回 true 时不自动补一次,需要时调用 refresh\n */\n enabled?: () => boolean;\n /** 成功后调用 */\n onSuccess?: (data: T, params: P) => void;\n /** 失败后调用(取消的不算) */\n onError?: (error: unknown, params: P) => void;\n}\n\n/** useRequest 的返回值 */\nexport interface RequestState<T> {\n /** 最后一次成功的结果 */\n readonly data: T | undefined;\n /** 最后一次失败的原因;成功后清空 */\n readonly error: unknown;\n /** 正在请求(防抖等待时还不算) */\n readonly loading: boolean;\n /**
|
|
1
|
+
{"version":3,"file":"use-request.js","names":[],"sources":["../../../src/utils/request/use-request.ts"],"sourcesContent":["/**\n * 一个请求的状态(参考 TanStack Query、alova 的 useWatcher):只采用最后一次的结果,发新请求时取消上一次,\n * 参数变化防抖,重新请求时保留旧数据(表格不闪成空的)。\n *\n * const http = useHttp();\n * const users = useRequest(\n * ({ keyword, page }, signal) => http.request<Page<User>>({ url: '/users', query: { keyword, page }, signal }),\n * { params: () => ({ keyword, page }), debounce: 300 },\n * );\n * users.data?.rows; users.loading; users.error; users.refresh();\n *\n * 结果可以经 cache 选项存进 IndexedDB、localStorage(@zerodep-dev/storage 的 createCache):\n *\n * const cache = createCache(indexedDbDriver({ name: 'app' }), { ttl: 60_000, stale: 86_400_000 });\n * useRequest(fetchCities, {\n * params: () => province,\n * cache: { store: cache, key: (p) => `cities:${p}`, tags: () => ['t_city'] }, // 标上依赖的表\n * });\n * // t_city 改了(kit 的失效推送、或自己保存后):await cache.invalidateTag('t_city'),正显示着的这个请求自动重新请求\n *\n * 只在浏览器里自动请求:服务端渲染时 data 留空,等页面在浏览器里接管后再请求。\n * 一开始就请求时 loading 从 true 开始,服务端也按“加载中”渲染:水合时两边一致,浏览器接着发请求。\n */\nimport { onScopeDispose, shallowRef, watch } from 'vue';\nimport { sameValue } from '../data/equality';\nimport { isAbortError } from './http';\n\n/** 请求结果的缓存:@zerodep-dev/storage 的 createCache 满足这个形状(按形状对接,组件库不依赖它) */\nexport interface RequestCache {\n /** 缓存的值;fresh 为 false 是过期但还能先显示的 */\n read(key: string): Promise<{ value: unknown; fresh: boolean } | undefined>;\n /** 写回请求的结果;tags 是这条依赖的东西(后端的表名),按它失效 */\n set(key: string, value: unknown, options?: { tags?: readonly string[] }): Promise<void>;\n /** 缓存的键失效或被别处改了时回调(keys 为 undefined 表示全清了);返回取消收听 */\n watch?(listener: (keys: readonly string[] | undefined) => void): () => void;\n}\n\n/** useRequest 的选项 */\nexport interface RequestOptions<P, T> {\n /** 请求参数:读到的响应式值变了、且内容确实不同时重新请求;不写时只在开始与 refresh 时请求 */\n params?: () => P;\n /** 参数变了以后等多久再请求(毫秒),默认 0;refresh 不等 */\n debounce?: number;\n /** 一开始就请求,默认 true;false 时等调用 refresh(或参数变化) */\n immediate?: boolean;\n /** 重新请求时保留上一次的数据,默认 true;false 时请求一开始就清空 */\n keepPrevious?: boolean;\n /**\n * 自动请求的开关(弹层打开时才请求这类):为 false 时 immediate 与参数变化都不请求,refresh 照常。\n * 变回 true 时不自动补一次,需要时调用 refresh\n */\n enabled?: () => boolean;\n /**\n * 结果的缓存(先显示旧值、后台刷新):有缓存先显示;新鲜的不再请求(refresh 照样请求),过期的接着请求、到了替换并写回。\n * key 由参数算出,不同参数各存各的;tags 是写回时标上的标签(依赖的表)。缓存读写出错时当作没有缓存,命中新鲜缓存时不调 onSuccess。\n * 当前参数的键失效了(本页或别的标签页)自动再请求一次;别的标签页写进了新值时直接用它,不再请求\n */\n cache?: {\n store: RequestCache;\n key: (params: P) => string;\n tags?: (params: P) => readonly string[];\n };\n /** 成功后调用 */\n onSuccess?: (data: T, params: P) => void;\n /** 失败后调用(取消的不算) */\n onError?: (error: unknown, params: P) => void;\n}\n\n/** useRequest 的返回值 */\nexport interface RequestState<T> {\n /** 最后一次成功的结果 */\n readonly data: T | undefined;\n /** 最后一次失败的原因;成功后清空 */\n readonly error: unknown;\n /** 正在请求(防抖等待时还不算) */\n readonly loading: boolean;\n /** 按当前参数立即请求(缓存新鲜也请求),取消进行中的;返回结果,失败或被后来的请求顶掉时为 undefined */\n refresh(): Promise<T | undefined>;\n /** 取消进行中的请求与等着的防抖 */\n cancel(): void;\n /** 直接改数据(乐观更新、本地删掉一行),不发请求 */\n mutate(data: T | undefined): void;\n}\n\n/** 请求的状态(在 setup 里调用;见文件开头) */\nexport function useRequest<T, P = undefined>(\n fetcher: (params: P, signal: AbortSignal) => Promise<T>,\n options: RequestOptions<P, T> = {},\n): RequestState<T> {\n const { params, cache, debounce = 0, immediate = true, keepPrevious = true } = options;\n const data = shallowRef<T>();\n const error = shallowRef<unknown>();\n const starts = immediate && options.enabled?.() !== false;\n const loading = shallowRef(starts);\n let controller: AbortController | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n // 每次请求一个编号:结果回来时不是最后一次的就丢掉(fetcher 没理会 signal 时也不会覆盖)\n let latest = 0;\n const readParams = () => params?.() as P;\n // 上一次请求(或开始时)的参数:参数变了但与它相同时不再请求(refresh 之后侦听器又看到同一组参数)\n let last = params ? readParams() : undefined;\n\n function cancel() {\n clearTimeout(timer);\n latest++;\n controller?.abort();\n controller = undefined;\n loading.value = false;\n }\n\n /** 读缓存;没配缓存时 undefined(不 await,没缓存的请求照旧同步发出) */\n function readCache(current: P) {\n return cache?.store.read(cache.key(current)).catch(() => undefined);\n }\n\n async function run(current: P, force = false): Promise<T | undefined> {\n cancel();\n last = current;\n const id = latest;\n const own = (controller = new AbortController());\n loading.value = true;\n if (!keepPrevious) data.value = undefined;\n try {\n const reading = readCache(current);\n const cached = reading && (await reading);\n if (id !== latest) return undefined;\n if (cached) {\n data.value = cached.value as T;\n if (cached.fresh && !force) return cached.value as T;\n }\n const result = await fetcher(current, own.signal);\n if (id !== latest) return undefined;\n data.value = result;\n error.value = undefined;\n if (cache) {\n const tags = cache.tags?.(current);\n void cache.store\n .set(cache.key(current), result, tags ? { tags } : undefined)\n .catch(() => undefined);\n }\n options.onSuccess?.(result, current);\n return result;\n } catch (reason) {\n if (id !== latest || isAbortError(reason)) return undefined;\n error.value = reason;\n options.onError?.(reason, current);\n return undefined;\n } finally {\n if (id === latest) {\n loading.value = false;\n controller = undefined;\n }\n }\n }\n\n if (params) {\n watch(readParams, (next) => {\n if (options.enabled?.() === false) return;\n if (sameValue(next, last)) return;\n last = next;\n clearTimeout(timer);\n if (debounce > 0) timer = setTimeout(() => void run(next), debounce);\n else void run(next);\n });\n }\n if (starts && typeof window !== 'undefined') {\n void run(readParams());\n }\n if (cache?.store.watch && typeof window !== 'undefined') {\n // 不强制:失效了缓存里没有、照常请求;别的标签页刚写进新值时直接读到它。还没请求过(immediate: false)的不管\n const stop = cache.store.watch((keys) => {\n if (latest === 0 || options.enabled?.() === false) return;\n const current = readParams();\n if (!keys || keys.includes(cache.key(current))) void run(current);\n });\n onScopeDispose(stop);\n }\n onScopeDispose(cancel);\n\n return {\n get data() {\n return data.value;\n },\n get error() {\n return error.value;\n },\n get loading() {\n return loading.value;\n },\n refresh: () => run(readParams(), true),\n cancel,\n mutate: (value) => (data.value = value),\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerodep-dev/vue-tsx-pro-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "vue-tsx-pro 组件库",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,6 +48,11 @@
|
|
|
48
48
|
"types": "./dist/charts/index.d.ts",
|
|
49
49
|
"default": "./dist/charts/index.js"
|
|
50
50
|
},
|
|
51
|
+
"./maps": {
|
|
52
|
+
"@vue-tsx-pro/source": "./src/maps/index.ts",
|
|
53
|
+
"types": "./dist/maps/index.d.ts",
|
|
54
|
+
"default": "./dist/maps/index.js"
|
|
55
|
+
},
|
|
51
56
|
"./docs-kit": {
|
|
52
57
|
"@vue-tsx-pro/source": "./src/docs-kit/index.ts",
|
|
53
58
|
"types": "./dist/docs-kit/index.d.ts",
|
|
@@ -112,7 +117,7 @@
|
|
|
112
117
|
"@floating-ui/dom": "1.8.0",
|
|
113
118
|
"@internationalized/date": "3.12.4",
|
|
114
119
|
"@radix-ui/colors": "3.0.0",
|
|
115
|
-
"@zerodep-dev/vue-tsx-pro-tsx": "^0.
|
|
120
|
+
"@zerodep-dev/vue-tsx-pro-tsx": "^0.4.1",
|
|
116
121
|
"animejs": "4.5.0",
|
|
117
122
|
"culori": "4.0.2",
|
|
118
123
|
"decimal.js": "10.6.0",
|
|
@@ -124,7 +129,6 @@
|
|
|
124
129
|
"pinia": "^4.0.0",
|
|
125
130
|
"shiki": "^4.0.0",
|
|
126
131
|
"three": ">=0.170.0",
|
|
127
|
-
"@unovis/ts": "^1.7.0",
|
|
128
132
|
"vue": "^3.5.0",
|
|
129
133
|
"vue-router": "^5.0.0",
|
|
130
134
|
"zod": "^4.6.0",
|
|
@@ -149,9 +153,6 @@
|
|
|
149
153
|
"three": {
|
|
150
154
|
"optional": true
|
|
151
155
|
},
|
|
152
|
-
"@unovis/ts": {
|
|
153
|
-
"optional": true
|
|
154
|
-
},
|
|
155
156
|
"vue-router": {
|
|
156
157
|
"optional": true
|
|
157
158
|
},
|
|
@@ -201,7 +202,6 @@
|
|
|
201
202
|
"@lezer/highlight",
|
|
202
203
|
"@lucide/icons",
|
|
203
204
|
"@radix-ui/colors",
|
|
204
|
-
"@unovis/ts",
|
|
205
205
|
"animejs",
|
|
206
206
|
"culori/fn",
|
|
207
207
|
"exceljs",
|
|
@@ -231,7 +231,6 @@
|
|
|
231
231
|
"three": "0.186.1",
|
|
232
232
|
"@types/three": "0.186.0",
|
|
233
233
|
"typescript": "7.1.0-beta",
|
|
234
|
-
"@unovis/ts": "1.7.1",
|
|
235
234
|
"vite": "8.3.4",
|
|
236
235
|
"vitest": "5.0.5",
|
|
237
236
|
"vue": "3.5.44",
|
package/dist/charts/single.d.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 单容器图表(环形图、桑基图、弦图)共用的绘制过程:Unovis 的 SingleContainer 只建一次,数据与配置变化时 update,
|
|
3
|
-
* 卸载时 destroy;绘图区换了元素(加载、空状态之后重新出现)时容器连同组件、提示框一起重建。
|
|
4
|
-
* 提示框按选择器触发,内容用 Vue 渲染进同一个元素交给 Unovis 定位。在组件 setup 里调用。
|
|
5
|
-
*
|
|
6
|
-
* const chart = useSingleChart(() => ({
|
|
7
|
-
* element: plot, ready: !loading && rows.length > 0, tooltip,
|
|
8
|
-
* build: (instance) => ({ component: …, data: rows, triggers: { [Donut.selectors.segment]: (d) => chart.tooltip(…) } }),
|
|
9
|
-
* }));
|
|
10
|
-
*/
|
|
11
|
-
import { Tooltip, type ComponentCore } from '@unovis/ts';
|
|
12
|
-
import { type VNode } from 'vue';
|
|
13
|
-
/** 一次绘制要的东西 */
|
|
14
|
-
export interface SingleChartBuild<D> {
|
|
15
|
-
/** 配置好的组件 */
|
|
16
|
-
component: ComponentCore<D>;
|
|
17
|
-
/** 交给组件的数据 */
|
|
18
|
-
data: D;
|
|
19
|
-
/** 提示框:选择器 → 悬停的数据 → 内容(Unovis 的 Tooltip triggers) */
|
|
20
|
-
triggers?: NonNullable<Tooltip['config']['triggers']>;
|
|
21
|
-
/** 每次画完之后(容器的 onRenderComplete):组件要在画好之后调整的(地图的标签避让) */
|
|
22
|
-
onRender?: () => void;
|
|
23
|
-
}
|
|
24
|
-
/** useSingleChart 每次读取的状态 */
|
|
25
|
-
export interface SingleChartState<D> {
|
|
26
|
-
/** 绘图区元素 */
|
|
27
|
-
element: HTMLElement | null;
|
|
28
|
-
/** 可以画了:不在加载中、有数据 */
|
|
29
|
-
ready: boolean;
|
|
30
|
-
/** 显示提示框 */
|
|
31
|
-
tooltip: boolean;
|
|
32
|
-
/** 配置组件:instance 取跨渲染保留的实例(第一次调用时创建) */
|
|
33
|
-
build: (instance: <C>(key: string, create: () => C) => C) => SingleChartBuild<D>;
|
|
34
|
-
}
|
|
35
|
-
/** useSingleChart 的返回值 */
|
|
36
|
-
export interface SingleChart {
|
|
37
|
-
/** 提示框的内容渲染进同一个元素,交给 Unovis 定位 */
|
|
38
|
-
tooltip(content: VNode): HTMLElement;
|
|
39
|
-
}
|
|
40
|
-
/** 单容器图表的绘制 */
|
|
41
|
-
export declare function useSingleChart<D>(read: () => SingleChartState<D>): SingleChart;
|
package/dist/charts/single.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { useDetachedRender } from "../overlay/detached.js";
|
|
2
|
-
import { chartDuration, createChartInstances, uniqueHighlightFilter, usePlotSeen } from "./frame.js";
|
|
3
|
-
import { useId } from "vue";
|
|
4
|
-
import { effect } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
5
|
-
import { SingleContainer, Tooltip } from "@unovis/ts";
|
|
6
|
-
//#region src/charts/single.tsx
|
|
7
|
-
/**
|
|
8
|
-
* 单容器图表(环形图、桑基图、弦图)共用的绘制过程:Unovis 的 SingleContainer 只建一次,数据与配置变化时 update,
|
|
9
|
-
* 卸载时 destroy;绘图区换了元素(加载、空状态之后重新出现)时容器连同组件、提示框一起重建。
|
|
10
|
-
* 提示框按选择器触发,内容用 Vue 渲染进同一个元素交给 Unovis 定位。在组件 setup 里调用。
|
|
11
|
-
*
|
|
12
|
-
* const chart = useSingleChart(() => ({
|
|
13
|
-
* element: plot, ready: !loading && rows.length > 0, tooltip,
|
|
14
|
-
* build: (instance) => ({ component: …, data: rows, triggers: { [Donut.selectors.segment]: (d) => chart.tooltip(…) } }),
|
|
15
|
-
* }));
|
|
16
|
-
*/
|
|
17
|
-
/** 一次绘制要的东西 */
|
|
18
|
-
/** useSingleChart 每次读取的状态 */
|
|
19
|
-
/** useSingleChart 的返回值 */
|
|
20
|
-
/** 单容器图表的绘制 */
|
|
21
|
-
function useSingleChart(read) {
|
|
22
|
-
const id = useId();
|
|
23
|
-
const instances = createChartInstances();
|
|
24
|
-
let tooltipElement;
|
|
25
|
-
const renderDetached = useDetachedRender();
|
|
26
|
-
let container;
|
|
27
|
-
let containerElement;
|
|
28
|
-
const seen = usePlotSeen(() => read().element);
|
|
29
|
-
effect(() => () => {
|
|
30
|
-
container?.destroy();
|
|
31
|
-
});
|
|
32
|
-
effect(() => {
|
|
33
|
-
const { element, ready, tooltip, build } = read();
|
|
34
|
-
if (!element || !ready || !seen()) return;
|
|
35
|
-
if (container && containerElement !== element) {
|
|
36
|
-
container.destroy();
|
|
37
|
-
container = void 0;
|
|
38
|
-
instances.reset();
|
|
39
|
-
}
|
|
40
|
-
const { component, data, triggers, onRender } = build(instances.get);
|
|
41
|
-
const tooltipBox = instances.get("tooltip", () => new Tooltip());
|
|
42
|
-
tooltipBox.setConfig({ triggers: triggers ?? {} });
|
|
43
|
-
const config = {
|
|
44
|
-
component,
|
|
45
|
-
tooltip: tooltip ? tooltipBox : void 0,
|
|
46
|
-
ariaLabel: null,
|
|
47
|
-
duration: chartDuration(element),
|
|
48
|
-
onRenderComplete: onRender
|
|
49
|
-
};
|
|
50
|
-
if (container) container.update(config, void 0, data);
|
|
51
|
-
else {
|
|
52
|
-
container = new SingleContainer(element, config, data);
|
|
53
|
-
containerElement = element;
|
|
54
|
-
uniqueHighlightFilter(element, `${id}-highlight`);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
return { tooltip(content) {
|
|
58
|
-
tooltipElement ??= document.createElement("div");
|
|
59
|
-
renderDetached(content, tooltipElement);
|
|
60
|
-
return tooltipElement;
|
|
61
|
-
} };
|
|
62
|
-
}
|
|
63
|
-
//#endregion
|
|
64
|
-
export { useSingleChart };
|
|
65
|
-
|
|
66
|
-
//# sourceMappingURL=single.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"single.js","names":[],"sources":["../../src/charts/single.tsx"],"sourcesContent":["/**\n * 单容器图表(环形图、桑基图、弦图)共用的绘制过程:Unovis 的 SingleContainer 只建一次,数据与配置变化时 update,\n * 卸载时 destroy;绘图区换了元素(加载、空状态之后重新出现)时容器连同组件、提示框一起重建。\n * 提示框按选择器触发,内容用 Vue 渲染进同一个元素交给 Unovis 定位。在组件 setup 里调用。\n *\n * const chart = useSingleChart(() => ({\n * element: plot, ready: !loading && rows.length > 0, tooltip,\n * build: (instance) => ({ component: …, data: rows, triggers: { [Donut.selectors.segment]: (d) => chart.tooltip(…) } }),\n * }));\n */\nimport { SingleContainer, Tooltip, type ComponentCore } from '@unovis/ts';\nimport { useId, type VNode } from 'vue';\nimport { useDetachedRender } from '../overlay/detached';\nimport { chartDuration, createChartInstances, uniqueHighlightFilter, usePlotSeen } from './frame';\n\n/** 一次绘制要的东西 */\nexport interface SingleChartBuild<D> {\n /** 配置好的组件 */\n component: ComponentCore<D>;\n /** 交给组件的数据 */\n data: D;\n /** 提示框:选择器 → 悬停的数据 → 内容(Unovis 的 Tooltip triggers) */\n triggers?: NonNullable<Tooltip['config']['triggers']>;\n /** 每次画完之后(容器的 onRenderComplete):组件要在画好之后调整的(地图的标签避让) */\n onRender?: () => void;\n}\n\n/** useSingleChart 每次读取的状态 */\nexport interface SingleChartState<D> {\n /** 绘图区元素 */\n element: HTMLElement | null;\n /** 可以画了:不在加载中、有数据 */\n ready: boolean;\n /** 显示提示框 */\n tooltip: boolean;\n /** 配置组件:instance 取跨渲染保留的实例(第一次调用时创建) */\n build: (instance: <C>(key: string, create: () => C) => C) => SingleChartBuild<D>;\n}\n\n/** useSingleChart 的返回值 */\nexport interface SingleChart {\n /** 提示框的内容渲染进同一个元素,交给 Unovis 定位 */\n tooltip(content: VNode): HTMLElement;\n}\n\n/** 单容器图表的绘制 */\nexport function useSingleChart<D>(read: () => SingleChartState<D>): SingleChart {\n const id = useId();\n const instances = createChartInstances();\n let tooltipElement: HTMLElement | undefined;\n // 提示框的内容渲染进 Unovis 管的元素:带上调用处的主题与语言,组件卸载时一起卸载\n const renderDetached = useDetachedRender();\n let container: SingleContainer<D> | undefined;\n let containerElement: HTMLElement | undefined;\n const seen = usePlotSeen(() => read().element);\n\n _effect(() => () => {\n container?.destroy();\n });\n _effect(() => {\n const { element, ready, tooltip, build } = read();\n if (!element || !ready || !seen()) return;\n // 绘图区换了元素:旧容器连同组件、提示框一起销毁,实例表清空后重新创建\n if (container && containerElement !== element) {\n container.destroy();\n container = undefined;\n instances.reset();\n }\n const { component, data, triggers, onRender } = build(instances.get);\n const tooltipBox = instances.get('tooltip', () => new Tooltip());\n tooltipBox.setConfig({ triggers: triggers ?? {} });\n const config = {\n component,\n tooltip: tooltip ? tooltipBox : undefined,\n ariaLabel: null,\n duration: chartDuration(element),\n onRenderComplete: onRender,\n };\n if (container) container.update(config, undefined, data);\n else {\n container = new SingleContainer<D>(element, config, data);\n containerElement = element;\n uniqueHighlightFilter(element, `${id}-highlight`);\n }\n });\n\n return {\n tooltip(content) {\n tooltipElement ??= document.createElement('div');\n renderDetached(content, tooltipElement);\n return tooltipElement;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA8CA,SAAgB,eAAkB,MAA8C;CAC9E,MAAM,KAAK,MAAM;CACjB,MAAM,YAAY,qBAAqB;CACvC,IAAI;CAEJ,MAAM,iBAAiB,kBAAkB;CACzC,IAAI;CACJ,IAAI;CACJ,MAAM,OAAO,kBAAkB,KAAK,CAAC,CAAC,OAAO;CAE7C,mBAAoB;EAClB,WAAW,QAAQ;CACrB,CAAC;CACD,aAAc;EACZ,MAAM,EAAE,SAAS,OAAO,SAAS,UAAU,KAAK;EAChD,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,GAAG;EAEnC,IAAI,aAAa,qBAAqB,SAAS;GAC7C,UAAU,QAAQ;GAClB,YAAY,KAAA;GACZ,UAAU,MAAM;EAClB;EACA,MAAM,EAAE,WAAW,MAAM,UAAU,aAAa,MAAM,UAAU,GAAG;EACnE,MAAM,aAAa,UAAU,IAAI,iBAAiB,IAAI,QAAQ,CAAC;EAC/D,WAAW,UAAU,EAAE,UAAU,YAAY,CAAC,EAAE,CAAC;EACjD,MAAM,SAAS;GACb;GACA,SAAS,UAAU,aAAa,KAAA;GAChC,WAAW;GACX,UAAU,cAAc,OAAO;GAC/B,kBAAkB;EACpB;EACA,IAAI,WAAW,UAAU,OAAO,QAAQ,KAAA,GAAW,IAAI;OAClD;GACH,YAAY,IAAI,gBAAmB,SAAS,QAAQ,IAAI;GACxD,mBAAmB;GACnB,sBAAsB,SAAS,GAAG,GAAE,WAAY;EAClD;CACF,CAAC;CAED,OAAO,EACL,QAAQ,SAAS;EACf,mBAAmB,SAAS,cAAc,KAAK;EAC/C,eAAe,SAAS,cAAc;EACtC,OAAO;CACT,EACF;AACF"}
|