diff --git a/bun.lockb b/bun.lockb
index 01bc7b98..203c8bb4 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/nodedevpkg/vue-vite-presets/src/chunkCleanup.ts b/nodedevpkg/vue-vite-presets/src/chunkCleanup.ts
index 8ef3d717..89f999a4 100644
--- a/nodedevpkg/vue-vite-presets/src/chunkCleanup.ts
+++ b/nodedevpkg/vue-vite-presets/src/chunkCleanup.ts
@@ -13,14 +13,14 @@ export const chunkCleanup = (
exclude?: string[];
include?: string[];
};
- } = {}
+ } = {},
): Plugin => {
const isJSOrLike = createFilter([
/\.vue$/,
/\.mdx$/,
/\.tsx?$/,
/\.mjs$/,
- /\.jsx?$/
+ /\.jsx?$/,
]);
return {
@@ -50,13 +50,13 @@ export const chunkCleanup = (
const result = await transform(code, {
filename: id,
env: opt.env ?? { targets: "defaults" },
- minify: false
+ minify: false,
});
return (
result.code && {
code: result.code,
- map: result.map || null
+ map: result.map || null,
}
);
},
@@ -65,11 +65,9 @@ export const chunkCleanup = (
return (
await transform(code, {
minify: opt.minify ?? false,
- plugins: [
- usePlugin({})
- ]
+ plugins: [usePlugin({})],
})
).code;
- }
+ },
};
};
diff --git a/nodedevpkg/vue-vite-presets/src/mdx/index.tsx b/nodedevpkg/vue-vite-presets/src/mdx/index.tsx
index 87d6e08b..fd03c213 100644
--- a/nodedevpkg/vue-vite-presets/src/mdx/index.tsx
+++ b/nodedevpkg/vue-vite-presets/src/mdx/index.tsx
@@ -43,31 +43,31 @@ export const mdx = (): PluginOption => {
const rawCode = children[0].value;
const exportName = `CodeBlock${getHash(
- `${metadata["filename"] ?? pos}`
+ `${metadata["filename"] ?? pos}`,
)}`;
const id = vc.store(`${mdxFile}~${exportName}.tsx`, rawCode);
additionalImports.set(mdxFile, {
...(additionalImports.get(mdxFile) ?? {}),
- [id]: exportName
+ [id]: exportName,
});
tree.children[pos] = h(
"div",
{
- "data-example": ""
+ "data-example": "",
},
[
h(
"div",
{
- "data-example-container": ""
+ "data-example-container": "",
},
- h(exportName)
+ h(exportName),
),
- pre
- ]
+ pre,
+ ],
);
}
}
@@ -80,7 +80,7 @@ export const mdx = (): PluginOption => {
include: [/\.mdx?$/],
jsxRuntime: "automatic",
jsxImportSource: "@innoai-tech/vuekit",
- rehypePlugins: [rehypeRenderCodeBlock, rehypePrism]
+ rehypePlugins: [rehypeRenderCodeBlock, rehypePrism],
});
return {
@@ -114,36 +114,36 @@ export const mdx = (): PluginOption => {
...ret,
code: `
${Object.keys(codeBlockImports)
- .map(
- (importPath) => `
-import ${codeBlockImports[importPath]} from ${JSON.stringify(importPath)}`
- )
- .join(";\n")}
+ .map(
+ (importPath) => `
+import ${codeBlockImports[importPath]} from ${JSON.stringify(importPath)}`,
+ )
+ .join(";\n")}
import { defineComponent, h } from "vue"
${ret.code.replace(
- "export default function MDXContent(",
- "function MDXContent("
- )}
+ "export default function MDXContent(",
+ "function MDXContent(",
+)}
export default defineComponent(() => {
return () => h(MDXContent, {
components: {
${Object.keys(codeBlockImports)
- .map(
- (importPath) =>
- `${codeBlockImports[importPath]?.toLowerCase()}: ${
- codeBlockImports[importPath]
- }`
- )
- .join(",\n")}
+ .map(
+ (importPath) =>
+ `${codeBlockImports[importPath]?.toLowerCase()}: ${
+ codeBlockImports[importPath]
+ }`,
+ )
+ .join(",\n")}
}
})
})
-`
+`,
};
}
- }
+ },
};
};
diff --git a/nodedevpkg/vue-vite-presets/src/viteVue.ts b/nodedevpkg/vue-vite-presets/src/viteVue.ts
index b7c216b3..55d038cb 100644
--- a/nodedevpkg/vue-vite-presets/src/viteVue.ts
+++ b/nodedevpkg/vue-vite-presets/src/viteVue.ts
@@ -2,10 +2,14 @@ import vue from "@vitejs/plugin-vue";
import type { PluginOption } from "vite";
import vitePages, {
type PageResolver,
- type PageOptions
+ type PageOptions,
} from "vite-plugin-pages";
import { mdx } from "./mdx";
-import { createPageMetaResolver, viteVueComponentCompleter, viteVueComponentHMR } from "./vue";
+import {
+ createPageMetaResolver,
+ viteVueComponentCompleter,
+ viteVueComponentHMR,
+} from "./vue";
export interface ViteReactOptions {
pagesDirs?: string | (string | PageOptions)[];
@@ -27,8 +31,8 @@ export const viteVue = (options: ViteReactOptions = {}): PluginOption[] => {
onRoutesGenerated: r.onRoutesGenerated,
resolver: {
...r.pagesResolver,
- ...options.pagesResolver
- }
- }) as PluginOption
+ ...options.pagesResolver,
+ },
+ }) as PluginOption,
];
};
diff --git a/nodedevpkg/vue-vite-presets/src/vue/componentCompleter.ts b/nodedevpkg/vue-vite-presets/src/vue/componentCompleter.ts
index 0bd46d69..b9e1217f 100644
--- a/nodedevpkg/vue-vite-presets/src/vue/componentCompleter.ts
+++ b/nodedevpkg/vue-vite-presets/src/vue/componentCompleter.ts
@@ -2,18 +2,17 @@ import { type Plugin, createFilter } from "vite";
import { usePlugin } from "@innoai-tech/vuecomponentcompleter";
import { transform } from "@swc/core";
-
export interface ComponentCompleterOptions {
include?: string[];
exclude?: string[];
}
export const viteVueComponentCompleter = (
- options: ComponentCompleterOptions = {}
+ options: ComponentCompleterOptions = {},
): Plugin => {
const filter = createFilter(
options.include || [/\.tsx$/, /\.mdx?$/],
- options.exclude
+ options.exclude,
);
return {
@@ -34,24 +33,24 @@ export const viteVueComponentCompleter = (
externalHelpers: false,
parser: {
syntax: "typescript",
- tsx: true
+ tsx: true,
},
experimental: {
disableBuiltinTransformsForInternalTesting: true,
- plugins: [usePlugin({})]
- }
- }
+ plugins: [usePlugin({})],
+ },
+ },
});
return (
result.code && {
code: result.code,
- map: result.map || null
+ map: result.map || null,
}
);
}
return null;
- }
+ },
};
};
diff --git a/nodedevpkg/vue-vite-presets/src/vue/componentHMR.ts b/nodedevpkg/vue-vite-presets/src/vue/componentHMR.ts
index 1772fce9..e9a5d0c0 100644
--- a/nodedevpkg/vue-vite-presets/src/vue/componentHMR.ts
+++ b/nodedevpkg/vue-vite-presets/src/vue/componentHMR.ts
@@ -18,12 +18,10 @@ export interface Module {
exports: Map;
}
-export const viteVueComponentHMR = (
- options: VueJsxHmrOptions = {}
-): Plugin => {
+export const viteVueComponentHMR = (options: VueJsxHmrOptions = {}): Plugin => {
const filter = createFilter(
options.include || [/\.tsx$/, /\.mdx?$/],
- options.exclude
+ options.exclude,
);
let hmrEnabled = false;
@@ -50,7 +48,7 @@ export const viteVueComponentHMR = (
}
return null;
- }
+ },
};
};
@@ -80,7 +78,6 @@ ${callbackBlock}
return code;
}
-
export const exportScanner = (id: string, filename = id) => {
const re =
/export (const (?\w+) =|default) (?(styled|component\$?)\()/;
@@ -89,7 +86,7 @@ export const exportScanner = (id: string, filename = id) => {
scan(code: string): Module {
const ret = {
code: "",
- exports: new Map()
+ exports: new Map(),
};
let src = code;
let m: RegExpMatchArray | null = null;
@@ -108,11 +105,13 @@ export const exportScanner = (id: string, filename = id) => {
const local =
exported !== "default"
? exported
- : upperFirst(camelCase(`${basename(filename, extname(filename))}Default`));
+ : upperFirst(
+ camelCase(`${basename(filename, extname(filename))}Default`),
+ );
const range = {
start: m.index ?? 0,
- length: m[0].length
+ length: m[0].length,
};
ret.exports.set(local, { exported, id: getHash(`${id}#${exported}`) });
@@ -143,6 +142,6 @@ export { ${nonDefaultExports.join(", ")} }
}
return ret;
- }
+ },
};
};
diff --git a/nodepkg/csstype/package.json b/nodepkg/csstype/package.json
index e0ae17b3..d755e4e3 100644
--- a/nodepkg/csstype/package.json
+++ b/nodepkg/csstype/package.json
@@ -19,4 +19,4 @@
"sideEffects": false,
"type": "module",
"types": "index.d.ts"
-}
+}
\ No newline at end of file
diff --git a/nodepkg/gents/src/__tests__/client/example.ts b/nodepkg/gents/src/__tests__/client/example.ts
index a0c540e5..07be5296 100644
--- a/nodepkg/gents/src/__tests__/client/example.ts
+++ b/nodepkg/gents/src/__tests__/client/example.ts
@@ -204,8 +204,8 @@ manifest = "manifest"
export const displayKubepkgV1Alpha1DigestMetaType = (v: KubepkgV1Alpha1DigestMetaType) => {
return ({
-blob: "Blob",
-manifest: "Manifest"
+"blob": "Blob",
+"manifest": "Manifest"
})[v] ?? v
}
diff --git a/nodepkg/jsoneditor/package.json b/nodepkg/jsoneditor/package.json
new file mode 100644
index 00000000..3101ec15
--- /dev/null
+++ b/nodepkg/jsoneditor/package.json
@@ -0,0 +1,51 @@
+{
+ "name": "@innoai-tech/jsoneditor",
+ "version": "0.1.4",
+ "monobundle": {
+ "build": {
+ "clean": true
+ },
+ "exports": {
+ ".": "./src/index.ts"
+ }
+ },
+ "dependencies": {
+ "@innoai-tech/vuekit": "workspace:^",
+ "@innoai-tech/vuemarkdown": "workspace:^",
+ "@innoai-tech/vuematerial": "workspace:^",
+ "@innoai-tech/vueuikit": "workspace:^",
+ "@mdi/js": "^7.4.47",
+ "copy-to-clipboard": "^3.3.3"
+ },
+ "peerDependencies": {},
+ "exports": {
+ ".": {
+ "bun": "./src/index.ts",
+ "import": {
+ "types": "./src/index.ts",
+ "default": "./dist/index.mjs"
+ }
+ }
+ },
+ "files": [
+ "dist/*",
+ "src/*",
+ "!/**/__tests__"
+ ],
+ "license": "MIT",
+ "publishConfig": {
+ "registry": "https://npm.pkg.github.com",
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "ssh://git@github.com:innoai-tech/vuekit.git",
+ "directory": "nodepkg/jsoneditor"
+ },
+ "scripts": {
+ "lint": "bunx --bun prettier --write . ",
+ "build": "bunx --bun monobundle",
+ "prepublishOnly": "bun run build"
+ },
+ "type": "module"
+}
diff --git a/nodepkg/jsoneditor/src/JSONEditorView.tsx b/nodepkg/jsoneditor/src/JSONEditorView.tsx
new file mode 100644
index 00000000..d7bf3bc9
--- /dev/null
+++ b/nodepkg/jsoneditor/src/JSONEditorView.tsx
@@ -0,0 +1,102 @@
+import {
+ type AnyType,
+ component$,
+ type Context,
+ EmptyContext,
+ rx,
+} from "@innoai-tech/vuekit";
+import { JSONEditorProvider, JSONEditorSlotsProvider } from "./models";
+import {
+ ObjectInput,
+ ArrayInput,
+ RecordInput,
+ EnumInput,
+ NumberInput,
+ BooleanInput,
+ StringInput,
+ LayoutContextProvider,
+ Line,
+} from "./views";
+import { styled } from "@innoai-tech/vueuikit";
+import { ref } from "vue";
+
+export const JSONEditorView = component$(({}, { render }) => {
+ const editor$ = JSONEditorProvider.use();
+
+ const renderValues = (typedef: AnyType, value: any, ctx: Context) => {
+ if (
+ typedef.type == "object" ||
+ typedef.type == "intersection" ||
+ typedef.type == "union"
+ ) {
+ return ;
+ }
+
+ if (typedef.type == "record") {
+ return ;
+ }
+
+ if (typedef.type == "array") {
+ return ;
+ }
+
+ if (typedef.type == "enums") {
+ return ;
+ }
+
+ if (typedef.type == "string") {
+ return ;
+ }
+
+ if (typedef.type == "number" || typedef.type == "integer") {
+ return ;
+ }
+
+ if (typedef.type == "boolean") {
+ return ;
+ }
+
+ return null;
+ };
+
+ const $container = ref(null);
+
+ return rx(
+ editor$,
+ render((root: any) => {
+ return (
+
+
+
+ {$container.value && (
+
+ {renderValues(editor$.typedef, root, EmptyContext)}
+
+ )}
+
+
+ );
+ }),
+ );
+});
+
+const JSONEditorContainer = styled("div")({
+ width: "100%",
+ height: "100%",
+ overflow: "auto",
+
+ section: {
+ display: "flex",
+ flexDirection: "column",
+ minWidth: "max-content",
+ },
+});
diff --git a/nodepkg/jsoneditor/src/JSONPointer.ts b/nodepkg/jsoneditor/src/JSONPointer.ts
new file mode 100644
index 00000000..2e7d0d57
--- /dev/null
+++ b/nodepkg/jsoneditor/src/JSONPointer.ts
@@ -0,0 +1,26 @@
+export class JSONPointer {
+ static parse(pointer: string): any[] {
+ if (pointer === "") {
+ return [];
+ }
+ if (pointer.charAt(0) !== "/") {
+ throw new Error("Invalid JSON pointer: " + pointer);
+ }
+ return pointer.substring(1).split(/\//).map(JSONPointer.unescape);
+ }
+
+ static compile(keyPath: any[]) {
+ if (keyPath.length === 0) {
+ return "";
+ }
+ return "/" + keyPath.map(JSONPointer.escape).join("/");
+ }
+
+ static unescape(str: string) {
+ return str.replace(/~1/g, "/").replace(/~0/g, "~");
+ }
+
+ static escape(str: string) {
+ return str.toString().replace(/~/g, "~0").replace(/\//g, "~1");
+ }
+}
diff --git a/nodepkg/jsoneditor/src/index.ts b/nodepkg/jsoneditor/src/index.ts
new file mode 100644
index 00000000..0aad583e
--- /dev/null
+++ b/nodepkg/jsoneditor/src/index.ts
@@ -0,0 +1,3 @@
+export * from "./models";
+export * from "./JSONEditorView.tsx";
+export * from "./JSONPointer.ts";
diff --git a/nodepkg/jsoneditor/src/models/JSONEditor.tsx b/nodepkg/jsoneditor/src/models/JSONEditor.tsx
new file mode 100644
index 00000000..512d6048
--- /dev/null
+++ b/nodepkg/jsoneditor/src/models/JSONEditor.tsx
@@ -0,0 +1,46 @@
+import {
+ type AnyType,
+ type Context,
+ createProvider,
+ ImmerBehaviorSubject,
+ type Infer,
+ t,
+ Type,
+} from "@innoai-tech/vuekit";
+import { get, isPlainObject, isUndefined } from "@innoai-tech/lodash";
+
+export class JSONEditor extends ImmerBehaviorSubject> {
+ static of(typedef: T, initials?: Partial>) {
+ return new JSONEditor(
+ typedef,
+ initials ?? (typedef.type == "array" ? [] : {}),
+ );
+ }
+
+ constructor(
+ public typedef: T,
+ protected initials: Infer,
+ ) {
+ super(initials);
+ }
+
+ isDirty(value: any, path: any[]) {
+ if (!isPlainObject(value)) {
+ const v = get(this.initials, path);
+
+ return isUndefined(v) || v !== value;
+ }
+ return false;
+ }
+}
+
+export const JSONEditorProvider = createProvider(
+ () => new JSONEditor(t.object(), {}),
+);
+
+export const JSONEditorSlotsProvider = createProvider(() => {
+ return {
+ render: (_t: AnyType, _value: any, _ctx: Context): JSX.Element | null =>
+ null,
+ };
+});
diff --git a/nodepkg/jsoneditor/src/models/index.ts b/nodepkg/jsoneditor/src/models/index.ts
new file mode 100644
index 00000000..8fbfc9bd
--- /dev/null
+++ b/nodepkg/jsoneditor/src/models/index.ts
@@ -0,0 +1 @@
+export * from "./JSONEditor.tsx";
diff --git a/nodepkg/jsoneditor/src/views/Actions.tsx b/nodepkg/jsoneditor/src/views/Actions.tsx
new file mode 100644
index 00000000..60d8ae19
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/Actions.tsx
@@ -0,0 +1,56 @@
+import { styled } from "@innoai-tech/vueuikit";
+import type { VNodeChild } from "@innoai-tech/vuekit";
+
+const ActionToolbar = styled("span")({
+ pos: "relative",
+ px: 8,
+ display: "flex",
+ alignItems: "center",
+ gap: 8
+});
+
+export const Actions = styled<
+ {
+ $default?: VNodeChild;
+ },
+ "span"
+>("span", ({}, { slots }) => {
+ return (Root) => (
+
+ {slots.default?.()}
+
+ );
+})({
+ flex: 1,
+ lineHeight: 18,
+
+ wordBreak: "keep-all",
+ whiteSpace: "nowrap",
+ display: "inline-flex",
+ alignItems: "center",
+
+ [`& ${ActionToolbar}`]: {
+ visibility: "hidden"
+ },
+
+ _hover: {
+ [`& ${ActionToolbar}`]: {
+ visibility: "visible"
+ }
+ }
+});
+
+export const ActionBtn = styled("span")({
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ height: 16,
+ width: 16,
+ opacity: 0.5,
+ cursor: "pointer",
+ textStyle: "sys.label-small",
+
+ _hover: {
+ opacity: 0.8
+ }
+});
diff --git a/nodepkg/jsoneditor/src/views/ArrayInput.tsx b/nodepkg/jsoneditor/src/views/ArrayInput.tsx
new file mode 100644
index 00000000..f20859f5
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/ArrayInput.tsx
@@ -0,0 +1,126 @@
+import {
+ type AnyType,
+ component$,
+ type Context,
+ rx,
+ type VNodeChild
+} from "@innoai-tech/vuekit";
+import { get, set } from "@innoai-tech/lodash";
+import { Icon } from "@innoai-tech/vuematerial";
+import { mdiMinusBoxOutline, mdiPlusBoxOutline } from "@mdi/js";
+import { Block, Line, PropName } from "./TokenView.tsx";
+import { JSONEditorProvider, JSONEditorSlotsProvider } from "../models";
+import { ActionBtn, Actions } from "./Actions.tsx";
+import { CopyAsJSONIconBtn, InputFromJSONRawIconBtn } from "./JSONRaw.tsx";
+import { Tooltip } from "./Tooltip.tsx";
+
+
+export const ArrayInput = component$<{
+ ctx: Context;
+ value: [];
+ typedef: AnyType;
+}>((props, { render }) => {
+ const editor$ = JSONEditorProvider.use();
+ const slots = JSONEditorSlotsProvider.use();
+
+ return rx(
+ props.value$,
+ render((obj) => {
+ return (
+
+ {
+ editor$.next((values: any) => {
+ const arr = get(values, props.ctx.path, []);
+
+ set(values, props.ctx.path, [...arr, undefined]);
+ });
+ }}
+ />
+
+ {
+ editor$.next((values: any) => {
+ if (props.ctx.path.length) {
+ set(values, props.ctx.path, updated);
+ } else {
+ Object.assign(values, updated);
+ }
+ });
+ }}
+ />
+
+ }
+ >
+ {[...props.typedef.entries(obj, props.ctx)].map(
+ ([idx, itemValue, propSchema]) => {
+ const path = [...props.ctx.path, idx];
+
+ return (
+
+ {
+ editor$.next((values: any) => {
+ let arr = get(values, props.ctx.path, [] as any[]);
+
+ set(
+ values,
+ props.ctx.path,
+ arr.filter((_: any, i: number) => i !== idx)
+ );
+ });
+ }}
+ />
+ }
+ >
+ {String(idx)}
+
+ {slots.render(propSchema, itemValue, {
+ ...props.ctx,
+ path: path,
+ branch: [...props.ctx.branch, itemValue]
+ })}
+
+ );
+ }
+ )}
+
+ );
+ })
+ );
+});
+
+const AddItemIconBtn = component$<{
+ $default?: VNodeChild;
+ onAdd?: () => void;
+}>(({}, { emit }) => {
+ return () => (
+
+ emit("add")}>
+
+
+
+ );
+});
+
+const RemoteItemIconBtn = component$<{
+ $default?: VNodeChild;
+ onRemove?: () => void;
+}>(({}, { emit }) => {
+ return () => (
+
+ emit("remove")}>
+
+
+
+ );
+});
diff --git a/nodepkg/jsoneditor/src/views/BooleanInput.tsx b/nodepkg/jsoneditor/src/views/BooleanInput.tsx
new file mode 100644
index 00000000..f333225a
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/BooleanInput.tsx
@@ -0,0 +1,48 @@
+import {
+ type AnyType,
+ component$,
+ type Context,
+ rx,
+} from "@innoai-tech/vuekit";
+import { JSONEditorProvider } from "../models";
+import { set } from "@innoai-tech/lodash";
+import { Menu, MenuItem, PopupStatus } from "./Menu.tsx";
+import { ValueView } from "./TokenView.tsx";
+
+export const BooleanInput = component$<{
+ ctx: Context;
+ value: any;
+ typedef: AnyType;
+}>((props, { render }) => {
+ const editor$ = JSONEditorProvider.use();
+ const open$ = new PopupStatus(false);
+
+ return rx(
+ props.value$,
+ render((value) => {
+ const enumValues = [false, true];
+
+ return (
+
+ );
+ }),
+ );
+});
diff --git a/nodepkg/jsoneditor/src/views/EnumInput.tsx b/nodepkg/jsoneditor/src/views/EnumInput.tsx
new file mode 100644
index 00000000..a087dc20
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/EnumInput.tsx
@@ -0,0 +1,50 @@
+import {
+ type AnyType,
+ component$,
+ type Context,
+ rx,
+} from "@innoai-tech/vuekit";
+import { JSONEditorProvider } from "../models";
+import { set } from "@innoai-tech/lodash";
+import { Menu, MenuItem, PopupStatus } from "./Menu.tsx";
+import { ValueView } from "./TokenView.tsx";
+
+export const EnumInput = component$<{
+ ctx: Context;
+ value: any;
+ typedef: AnyType;
+}>((props, { render }) => {
+ const editor$ = JSONEditorProvider.use();
+ const open$ = new PopupStatus(false);
+
+ return rx(
+ props.value$,
+ render((value) => {
+ const enumValues = props.typedef.getSchema("enum") ?? [];
+ const enumLabels = props.typedef.getMeta("enumLabels") ?? [];
+
+ return (
+
+ );
+ }),
+ );
+});
diff --git a/nodepkg/jsoneditor/src/views/Form.tsx b/nodepkg/jsoneditor/src/views/Form.tsx
new file mode 100644
index 00000000..7772c542
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/Form.tsx
@@ -0,0 +1,45 @@
+import { styled } from "@innoai-tech/vueuikit";
+
+export const FormControls = styled("form")({
+ display: "flex",
+ pos: "absolute",
+ right: 0,
+ bottom: 0,
+ px: 8,
+});
+
+export const FormContainer = styled("div")({
+ display: "block",
+ pos: "relative",
+
+ textarea: {
+ minW: "20vw",
+ outline: "none",
+ border: "none",
+ bg: "none",
+ py: 8,
+ px: 12,
+ },
+});
+
+export const FormContainerAsRow = styled("div")({
+ display: "flex",
+ alignItems: "center",
+ gap: 8,
+ pos: "relative",
+ px: 8,
+
+ input: {
+ outline: "none",
+ border: "none",
+ bg: "none",
+ minWidth: "10vw",
+ py: 8,
+ px: 12,
+ },
+
+ [`& ${FormControls}`]: {
+ pos: "relative",
+ px: 0,
+ },
+});
diff --git a/nodepkg/jsoneditor/src/views/JSONRaw.tsx b/nodepkg/jsoneditor/src/views/JSONRaw.tsx
new file mode 100644
index 00000000..6757a9a1
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/JSONRaw.tsx
@@ -0,0 +1,116 @@
+import { component$, ImmerBehaviorSubject, rx } from "@innoai-tech/vuekit";
+import { Icon, IconButton } from "@innoai-tech/vuematerial";
+import { ActionBtn } from "./Actions.tsx";
+import copyToClipboard from "copy-to-clipboard";
+import { mdiCancel, mdiCheck, mdiCodeJson, mdiContentCopy } from "@mdi/js";
+import { Popover, PopupStatus } from "./Menu.tsx";
+import { onMounted, ref } from "vue";
+import { FormContainer, FormControls } from "./Form.tsx";
+import { Tooltip } from "./Tooltip.tsx";
+
+export const CopyAsJSONIconBtn = component$<{
+ value?: any;
+}>((props, {}) => {
+ return () => (
+
+ copyToClipboard(JSON.stringify(props.value, null, 2))}>
+
+
+
+ );
+});
+
+export const InputFromJSONRawIconBtn = component$<{
+ onInput?: (prop: string) => void;
+}>(({}, { emit }) => {
+ const open$ = new PopupStatus(false);
+
+ return (
+ {
+ if (v) {
+ emit("input", v);
+ }
+ open$.hide();
+ }}
+ />
+ }
+ >
+
+ open$.show()}>
+
+
+
+
+ );
+});
+
+const JSONRawForm = component$<{
+ onSubmit: (value?: any) => void;
+}>(({}, { emit, render }) => {
+ const input$ = new ImmerBehaviorSubject("");
+ const $input = ref(null);
+
+ const cancel = () => {
+ emit("submit", undefined);
+ };
+
+ const submit = () => {
+ try {
+ emit("submit", JSON.parse(input$.value!));
+ } catch (e) {
+ }
+ };
+
+ const handleUserKeyPress = (e: KeyboardEvent) => {
+ if (e.key === "Enter" && !e.shiftKey) {
+ e.preventDefault();
+ submit();
+ }
+ };
+
+ onMounted(() => {
+ $input.value?.focus();
+ });
+
+ return rx(
+ input$,
+ render((input) => {
+ return (
+ {
+ evt.preventDefault();
+ submit();
+ }}
+ >
+
+ );
+ })
+ );
+});
diff --git a/nodepkg/jsoneditor/src/views/Menu.tsx b/nodepkg/jsoneditor/src/views/Menu.tsx
new file mode 100644
index 00000000..1c178abf
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/Menu.tsx
@@ -0,0 +1,147 @@
+import {
+ component$,
+ ImmerBehaviorSubject,
+ rx,
+ type VNodeChild
+} from "@innoai-tech/vuekit";
+import {
+ defineTransition,
+ Popper,
+ styled,
+ transition
+} from "@innoai-tech/vueuikit";
+import { isUndefined } from "@innoai-tech/lodash";
+
+export const Menu = component$<{
+ open$?: PopupStatus;
+ onSelected?: (prop: any) => void;
+ $content?: VNodeChild;
+ $default?: VNodeChild;
+}>((props, { emit, slots, render }) => {
+ const open$ = props.open$ ?? new PopupStatus(false);
+
+ const handleSelected = (e: Event) => {
+ const $el = [e.target, ...e.composedPath()].find((t) => {
+ return (t as HTMLElement)?.hasAttribute?.("data-value");
+ }) as HTMLElement | undefined;
+
+ if ($el) {
+ const selected = $el.getAttribute("data-value");
+
+ if (!isUndefined(selected)) {
+ emit("selected", selected);
+ open$.hide();
+ }
+ }
+ };
+
+ return rx(
+ open$,
+ render((isOpen) => {
+ return (
+ open$.hide()}
+ $transition={(ctx: any) => (
+ {ctx.content}
+ )}
+ placement={"bottom-start"}
+ $content={
+
+ {slots.content?.()}
+
+ }
+ >
+ {slots.default?.()?.[0] ?? null}
+
+ );
+ })
+ );
+});
+
+export const Popover = component$<{
+ open$?: PopupStatus;
+ $content?: VNodeChild;
+ $default?: VNodeChild;
+}>((props, { slots, render }) => {
+ const open$ = props.open$ ?? new PopupStatus(false);
+
+ return rx(
+ open$,
+ render((isOpen) => {
+ return (
+ open$.hide()}
+ $transition={(ctx: any) => (
+ {ctx.content}
+ )}
+ placement={"bottom-start"}
+ $content={{slots.content?.()}}
+ >
+ {slots.default?.()?.[0] ?? null}
+
+ );
+ })
+ );
+});
+
+export class PopupStatus extends ImmerBehaviorSubject {
+ show() {
+ this.next(true);
+ }
+
+ hide() {
+ this.next(false);
+ }
+}
+
+export const PopoverContainer = styled("div")({
+ py: 4,
+ rounded: "sm",
+ shadow: "1",
+ containerStyle: "sys.surface",
+ textStyle: "sys.body-small",
+ pos: "relative",
+ maxW: "30vw"
+});
+
+export const MenuItem = styled("div")({
+ px: 12,
+ py: 8,
+ gap: 23,
+ display: "flex",
+ alignItems: "start",
+ justifyContent: "space-between",
+ textAlign: "right",
+ textStyle: "sys.body-small",
+
+ cursor: "pointer",
+
+ _hover: {
+ containerStyle: "sys.surface-container"
+ }
+});
+
+export const FadeInOutTransition = defineTransition(
+ {
+ from: {
+ opacity: 0
+ },
+ to: {
+ opacity: 1
+ },
+ duration: transition.duration.md1,
+ easing: transition.easing.standard.accelerate
+ },
+ {
+ from: {
+ opacity: 1
+ },
+ to: {
+ opacity: 0
+ },
+ duration: transition.duration.sm4,
+ easing: transition.easing.standard.decelerate
+ }
+);
diff --git a/nodepkg/jsoneditor/src/views/NumberInput.tsx b/nodepkg/jsoneditor/src/views/NumberInput.tsx
new file mode 100644
index 00000000..6b0b14f7
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/NumberInput.tsx
@@ -0,0 +1,122 @@
+import {
+ type AnyType,
+ component$,
+ type Context,
+ ImmerBehaviorSubject,
+ rx,
+} from "@innoai-tech/vuekit";
+import { ValueView } from "./TokenView.tsx";
+import { Popover, PopupStatus } from "./Menu.tsx";
+import { set } from "@innoai-tech/lodash";
+import { JSONEditorProvider } from "../models";
+import { Icon, IconButton } from "@innoai-tech/vuematerial";
+import { mdiCancel, mdiCheck } from "@mdi/js";
+import { onMounted, ref } from "vue";
+import { FormContainerAsRow, FormControls } from "./Form.tsx";
+
+export const NumberInput = component$<{
+ ctx: Context;
+ value: number | undefined;
+ typedef: AnyType;
+}>((props, { render }) => {
+ const open$ = new PopupStatus(false);
+ const editor$ = JSONEditorProvider.use();
+
+ return rx(
+ props.value$,
+ render((value) => {
+ return (
+ {
+ editor$.next((values: any) => {
+ set(values, props.ctx.path, v);
+ });
+ open$.hide();
+ }}
+ />
+ }
+ >
+ {
+ open$.show();
+ }}
+ />
+
+ );
+ }),
+ );
+});
+
+const NumberForm = component$<{
+ value?: number;
+ onSubmit: (value?: number) => void;
+}>((props, { emit, render }) => {
+ const input$ = new ImmerBehaviorSubject(props.value);
+ const $input = ref(null);
+
+ const cancel = () => {
+ emit("submit", undefined);
+ };
+
+ const submit = () => {
+ try {
+ emit("submit", input$.value);
+ } catch (e) {}
+ };
+
+ const handleUserKeyPress = (e: KeyboardEvent) => {
+ if (e.key === "Enter" && !e.shiftKey) {
+ e.preventDefault();
+ submit();
+ }
+ };
+
+ onMounted(() => {
+ $input.value?.focus();
+ });
+
+ return rx(
+ input$,
+ render((input) => {
+ return (
+ {
+ evt.preventDefault();
+ submit();
+ }}
+ >
+ {
+ try {
+ input$.next(parseFloat((evt.target as HTMLInputElement).value));
+ } catch (evt) {}
+ }}
+ onKeypress={handleUserKeyPress}
+ />
+
+ {
+ cancel();
+ }}
+ >
+
+
+
+
+
+
+
+ );
+ }),
+ );
+});
diff --git a/nodepkg/jsoneditor/src/views/ObjectInput.tsx b/nodepkg/jsoneditor/src/views/ObjectInput.tsx
new file mode 100644
index 00000000..5eabae08
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/ObjectInput.tsx
@@ -0,0 +1,185 @@
+import {
+ type AnyType,
+ component,
+ component$,
+ type Context,
+ EmptyContext,
+ rx,
+ type VNodeChild
+} from "@innoai-tech/vuekit";
+import { get, set } from "@innoai-tech/lodash";
+import { styled } from "@innoai-tech/vueuikit";
+import { Icon } from "@innoai-tech/vuematerial";
+import { mdiMinusBoxOutline, mdiPlusBoxOutline } from "@mdi/js";
+import { Description, Block, Line, PropName, Token } from "./TokenView.tsx";
+import { JSONEditorProvider, JSONEditorSlotsProvider } from "../models";
+import { Menu, MenuItem, PopupStatus } from "./Menu.tsx";
+import { ActionBtn, Actions } from "./Actions.tsx";
+import { CopyAsJSONIconBtn, InputFromJSONRawIconBtn } from "./JSONRaw.tsx";
+import { Tooltip } from "./Tooltip.tsx";
+
+export const ObjectInput = component$<{
+ ctx: Context;
+ value: {};
+ typedef: AnyType;
+}>((props, { render }) => {
+ const editor$ = JSONEditorProvider.use();
+ const slots = JSONEditorSlotsProvider.use();
+
+ return rx(
+ props.value$,
+ render((obj) => {
+ return (
+
+ {
+ editor$.next((values: any) => {
+ const p = [...props.ctx.path, propName];
+ set(values, p, get(values, p));
+ });
+ }}
+ >
+ {[...props.typedef.entries(obj, EmptyContext)].map(
+ ([propName, _propValue, propSchema]) => {
+ if (!Object.hasOwn(obj, propName)) {
+ return (
+
+ );
+ }
+ return null;
+ }
+ )}
+
+
+ {
+ editor$.next((values: any) => {
+ if (props.ctx.path.length) {
+ set(values, props.ctx.path, updated);
+ } else {
+ Object.assign(values, updated);
+ }
+ });
+ }}
+ />
+
+ }
+ >
+ {[...props.typedef.entries(obj, props.ctx)].map(
+ ([propName, propValue, propSchema]) => {
+ if (!Object.hasOwn(obj, propName)) {
+ return null;
+ }
+
+ const path = [...props.ctx.path, propName];
+
+ return (
+
+ {
+ editor$.next((values: any) => {
+ let v = values;
+ for (const k of props.ctx.path) {
+ v = v[k];
+ }
+ delete v[propName];
+ });
+ }}
+ />
+ }
+ >
+ {String(propName)}
+
+ {":"}
+ {slots.render(propSchema, propValue, {
+ ...props.ctx,
+ path: path,
+ branch: [...props.ctx.branch, propValue]
+ })}
+
+ );
+ }
+ )}
+
+ );
+ })
+ );
+});
+
+const RemotePropIconBtn = component$<{
+ $default?: VNodeChild;
+ onRemove?: () => void;
+}>(({}, { emit }) => {
+ return () => (
+
+ emit("remove")}>
+
+
+
+ );
+});
+
+const AddPropIconBtn = component$<{
+ $default?: VNodeChild;
+ onAddProp?: (prop: string) => void;
+}>(({}, { slots, emit }) => {
+ const open$ = new PopupStatus(false);
+
+ return () => {
+ const children = slots.default?.().filter(n => n.type == AddPropMenuItem) ?? [];
+
+ return children?.length > 0 ? (
+
+ ) : null;
+ };
+});
+
+const AddPropMenuItem = component<{
+ propName: string;
+ typedef: AnyType;
+}>((props) => {
+ return () => {
+ return (
+
+
+ {props.propName}
+
+
+ {props.typedef.getMeta("description")?.split("\n")?.[0] ?? ""}
+
+
+ );
+ };
+});
+
+const AddPropMenuItemContainer = styled(MenuItem)({
+ width: 400,
+
+ [`& ${PropName}`]: {
+ width: "30%",
+ textAlign: "left"
+ }
+});
diff --git a/nodepkg/jsoneditor/src/views/RecordInput.tsx b/nodepkg/jsoneditor/src/views/RecordInput.tsx
new file mode 100644
index 00000000..f5eccc75
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/RecordInput.tsx
@@ -0,0 +1,224 @@
+import {
+ type AnyType,
+ component$,
+ type Context,
+ ImmerBehaviorSubject,
+ rx,
+ type VNodeChild
+} from "@innoai-tech/vuekit";
+import { get, set } from "@innoai-tech/lodash";
+import { Icon, IconButton } from "@innoai-tech/vuematerial";
+import {
+ mdiCancel,
+ mdiCheck,
+ mdiMinusBoxOutline,
+ mdiPlusBoxOutline
+} from "@mdi/js";
+import { Block, Line, PropName, Token } from "./TokenView.tsx";
+import { JSONEditorProvider, JSONEditorSlotsProvider } from "../models";
+import { Popover, PopupStatus } from "./Menu.tsx";
+import { ActionBtn, Actions } from "./Actions.tsx";
+import { CopyAsJSONIconBtn, InputFromJSONRawIconBtn } from "./JSONRaw.tsx";
+import { onMounted, ref } from "vue";
+import { FormContainerAsRow, FormControls } from "./Form.tsx";
+import { Tooltip } from "./Tooltip.tsx";
+
+export const RecordInput = component$<{
+ ctx: Context;
+ value: {};
+ typedef: AnyType;
+}>((props, { render }) => {
+ const editor$ = JSONEditorProvider.use();
+ const slots = JSONEditorSlotsProvider.use();
+
+ return rx(
+ props.value$,
+ render((obj) => {
+ return (
+
+ {
+ editor$.next((values: any) => {
+ const p = [...props.ctx.path, propName];
+ set(values, p, get(values, p));
+ });
+ }}
+ />
+
+ {
+ editor$.next((values: any) => {
+ if (props.ctx.path.length) {
+ set(values, props.ctx.path, updated);
+ } else {
+ Object.assign(values, updated);
+ }
+ });
+ }}
+ />
+
+ }
+ >
+ {[...props.typedef.entries(obj, props.ctx)].map(
+ ([propName, propValue, propSchema]) => {
+ if (!Object.hasOwn(obj, propName)) {
+ return null;
+ }
+
+ const path = [...props.ctx.path, propName];
+
+ return (
+
+ {
+ editor$.next((values: any) => {
+ let v = values;
+ for (const k of props.ctx.path) {
+ v = v[k];
+ }
+ delete v[propName];
+ });
+ }}
+ />
+ }
+ >
+ {String(propName)}
+
+ {":"}
+ {slots.render(propSchema, propValue, {
+ ...props.ctx,
+ path: path,
+ branch: [...props.ctx.branch, propValue]
+ })}
+
+ );
+ }
+ )}
+
+ );
+ })
+ );
+});
+
+const RemotePropIconBtn = component$<{
+ $default?: VNodeChild;
+ onRemove?: () => void;
+}>(({}, { emit }) => {
+ return () => (
+
+ emit("remove")}>
+
+
+
+ );
+});
+
+const AddKeyIconBtn = component$<{
+ $default?: VNodeChild;
+ onAddKey?: (prop: string) => void;
+}>(({}, { emit }) => {
+ const open$ = new PopupStatus(false);
+
+ return () => (
+ {
+ if (v) {
+ emit("add-key", v);
+ }
+ open$.hide();
+ }}
+ />
+ }
+ >
+
+ {
+ open$.show();
+ }}
+ >
+
+
+
+
+ );
+});
+
+const TextForm = component$<{
+ value?: string;
+ onSubmit: (value?: string) => void;
+}>((props, { emit, render }) => {
+ const input$ = new ImmerBehaviorSubject(props.value);
+ const $input = ref(null);
+
+ const cancel = () => {
+ emit("submit", undefined);
+ };
+
+ const submit = () => {
+ try {
+ emit("submit", input$.value);
+ } catch (e) {
+ }
+ };
+
+ const handleUserKeyPress = (e: KeyboardEvent) => {
+ if (e.key === "Enter" && !e.shiftKey) {
+ e.preventDefault();
+ submit();
+ }
+ };
+
+ onMounted(() => {
+ $input.value?.focus();
+ });
+
+ return rx(
+ input$,
+ render((input) => {
+ return (
+ {
+ evt.preventDefault();
+ submit();
+ }}
+ >
+ {
+ try {
+ input$.next((evt.target as HTMLInputElement).value?.trim());
+ } catch (evt) {
+ }
+ }}
+ onKeypress={handleUserKeyPress}
+ />
+
+ {
+ cancel();
+ }}
+ >
+
+
+
+
+
+
+
+ );
+ })
+ );
+});
diff --git a/nodepkg/jsoneditor/src/views/StringInput.tsx b/nodepkg/jsoneditor/src/views/StringInput.tsx
new file mode 100644
index 00000000..fc0c91eb
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/StringInput.tsx
@@ -0,0 +1,111 @@
+import {
+ type AnyType,
+ component$,
+ type Context,
+ ImmerBehaviorSubject,
+ rx,
+} from "@innoai-tech/vuekit";
+import { ValueView } from "./TokenView.tsx";
+import { Popover, PopupStatus } from "./Menu.tsx";
+import { set } from "@innoai-tech/lodash";
+import { JSONEditorProvider } from "../models";
+import { Icon, IconButton } from "@innoai-tech/vuematerial";
+import { mdiCancel, mdiCheck } from "@mdi/js";
+import { onMounted, ref } from "vue";
+import { FormContainer, FormControls } from "./Form.tsx";
+
+export const StringInput = component$<{
+ ctx: Context;
+ value: string | undefined;
+ typedef: AnyType;
+}>((props, { render }) => {
+ const open$ = new PopupStatus(false);
+ const editor$ = JSONEditorProvider.use();
+
+ return rx(
+ props.value$,
+ render((value) => {
+ return (
+ {
+ editor$.next((values: any) => {
+ set(values, props.ctx.path, v);
+ });
+ open$.hide();
+ }}
+ />
+ }
+ >
+ {
+ open$.show();
+ }}
+ />
+
+ );
+ }),
+ );
+});
+
+const TextareaForm = component$<{
+ value?: string;
+ onSubmit: (value?: string) => void;
+}>((props, { emit, render }) => {
+ const input$ = new ImmerBehaviorSubject(props.value);
+ const $input = ref(null);
+
+ const handleUserKeyPress = (e: KeyboardEvent) => {
+ if (e.key === "Enter" && !e.shiftKey) {
+ e.preventDefault();
+
+ emit("submit", input$.value);
+ }
+ };
+
+ onMounted(() => {
+ $input.value?.focus();
+ });
+
+ return rx(
+ input$,
+ render((input) => {
+ return (
+ {
+ evt.preventDefault();
+ emit("submit", input$.value);
+ }}
+ >
+
+ );
+ }),
+ );
+});
diff --git a/nodepkg/jsoneditor/src/views/TokenView.tsx b/nodepkg/jsoneditor/src/views/TokenView.tsx
new file mode 100644
index 00000000..a0080aba
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/TokenView.tsx
@@ -0,0 +1,269 @@
+import { alpha, styled, variant } from "@innoai-tech/vueuikit";
+import {
+ component,
+ createProvider,
+ type VNodeChild
+} from "@innoai-tech/vuekit";
+import { isBoolean, isNull, isString, isUndefined } from "@innoai-tech/lodash";
+import { ref, Teleport } from "vue";
+import { Tooltip } from "./Tooltip.tsx";
+import { Markdown } from "@innoai-tech/vuemarkdown";
+
+export const Token = styled("span")({
+ textStyle: "sys.label-small",
+ fontWeight: "bold",
+ fontFamily: "inherit",
+ fontSize: 11,
+ lineHeight: 18,
+ wordBreak: "keep-all",
+ whiteSpace: "nowrap",
+ font: "code",
+ opacity: 0.3
+});
+
+const PropLeading = styled("span")({
+ display: "inline-table",
+ lineHeight: 18,
+ ml: -18
+});
+
+export const PropName = styled<
+ {
+ deprecated?: boolean;
+ optional?: boolean;
+ nullable?: boolean;
+ description?: string;
+ $leading?: VNodeChild;
+ $default?: VNodeChild;
+ },
+ "span"
+>("span", (props, { slots }) => {
+ return (Root) => {
+ const $el = (
+
+ {slots.leading && {slots.leading?.()}}
+ {slots.default?.()}
+
+ );
+
+ if (props.description) {
+ return (
+
+
+
+ )}
+ >
+ {$el}
+
+ );
+ }
+
+ return (
+ $el
+ );
+ };
+})({
+ display: "inline-flex",
+ alignItems: "center",
+ textStyle: "sys.label-small",
+ fontWeight: "bold",
+ fontFamily: "inherit",
+ fontSize: 11,
+ lineHeight: 18,
+
+ _deprecated: {
+ textDecoration: "line-through"
+ },
+
+ _nullable: {
+ "&:after": { content: `"??"`, color: "sys.error" }
+ },
+
+ _optional: {
+ "&:after": { content: `"?"`, color: "sys.secondary" }
+ },
+
+ [`& ${PropLeading}`]: {
+ visibility: "hidden"
+ },
+
+ _hover: {
+ [`& ${PropLeading}`]: {
+ visibility: "visible"
+ }
+ }
+});
+
+export const LayoutContextProvider = createProvider(
+ () => {
+ return {
+ indent: 0,
+ $container: ref(null)
+ };
+ },
+ {
+ name: "IntentContext"
+ }
+);
+
+export const Block = component<{
+ openToken: string;
+ closeToken: string;
+ $leading?: VNodeChild;
+ $default?: VNodeChild;
+}>((props, { slots }) => {
+ const ctx = LayoutContextProvider.use();
+ const $container = ref(null);
+
+ return () => (
+ <>
+ {props.openToken}
+ {slots.leading?.()}
+
+
+
+
+ {props.closeToken}
+
+
+ {$container.value && (
+
+ {slots.default?.()}
+
+ )}
+ >
+ );
+});
+
+export const Line = component<{
+ dirty?: boolean;
+ $default?: VNodeChild;
+}>((props, { slots }) => {
+ const ctx = LayoutContextProvider.use();
+
+ return () => {
+ return (
+
+
+ {slots.default?.()}
+
+
+ );
+ };
+});
+
+export const LineRow = styled("div")({
+ display: "flex",
+ alignItems: "end",
+
+ _hover: {
+ containerStyle: "sys.surface-container"
+ },
+
+ _dirty: {
+ bgColor: variant("sys.warning-container", alpha(0.38))
+ }
+});
+
+export const Description = styled("span")({
+ display: "inline-block",
+ maxWidth: "20vw",
+
+ "& p": {
+ my: "0.5em"
+ }
+});
+
+export const ValueView = component<{
+ value: any;
+ onClick?: () => void;
+}>((props, { emit }) => {
+ return () => {
+ if (isUndefined(props.value) || isNull(props.value)) {
+ return (
+ emit("click")}>
+ {"undefined"}
+
+ );
+ }
+
+ if (isString(props.value)) {
+ return (
+ emit("click")}>
+ {JSON.stringify(props.value)}
+
+ );
+ }
+
+ if (isBoolean(props.value)) {
+ return (
+ emit("click")}>
+ {String(props.value)}
+
+ );
+ }
+
+ return (
+ emit("click")}>
+ {String(props.value)}
+
+ );
+ };
+});
+
+const StringValue = styled(Token)({
+ display: "inline-block",
+ color: "sys.primary",
+ font: "code",
+
+ opacity: 1,
+ cursor: "pointer"
+});
+
+const BooleanValue = styled(Token)({
+ display: "inline-block",
+ color: "sys.warning",
+ font: "code",
+
+ opacity: 1,
+ cursor: "pointer"
+});
+
+const NumberValue = styled(Token)({
+ display: "inline-block",
+ color: "sys.success",
+ font: "code",
+
+ opacity: 1,
+ cursor: "pointer"
+});
+
+const UndefinedValue = styled(Token)({
+ display: "inline-block",
+ color: "sys.error",
+ font: "code",
+
+ opacity: 1,
+ cursor: "pointer"
+});
diff --git a/nodepkg/jsoneditor/src/views/Tooltip.tsx b/nodepkg/jsoneditor/src/views/Tooltip.tsx
new file mode 100644
index 00000000..0d5178bf
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/Tooltip.tsx
@@ -0,0 +1,71 @@
+import { component$, rx, type VNodeChild } from "@innoai-tech/vuekit";
+import {
+ Popper,
+ styled
+} from "@innoai-tech/vueuikit";
+import { cloneVNode } from "vue";
+import { FadeInOutTransition, PopupStatus } from "./Menu.tsx";
+import type { Placement } from "@popperjs/core";
+
+export const Tooltip = component$<{
+ $title: VNodeChild
+ placement?: Placement
+ $default?: VNodeChild
+}>((props, { slots, render }) => {
+ const open$ = new PopupStatus(false);
+
+ return rx(
+ open$,
+ render((isOpen) => {
+ const child = slots.default?.()[0];
+
+ return (
+
+
+ {slots.title()}
+
+
+ )}
+ $transition={(ctx) => (
+
+ {ctx.content}
+
+ )}
+ >
+ {child
+ ? cloneVNode(child, {
+ onMouseover: () => {
+ open$.show();
+ },
+ onMouseout: () => {
+ open$.hide();
+ }
+ })
+ : null}
+
+ );
+ })
+ );
+ }
+);
+
+
+const TooltipContainer = styled("div")({
+ py: 4,
+ px: 12,
+ rounded: "xs",
+ shadow: "3",
+ textStyle: "sys.body-small",
+ containerStyle: "sys.on-surface",
+ pos: "relative",
+ zIndex: 100
+});
+
+const TooltipWrapper = styled("div")({
+ px: 8
+});
+
diff --git a/nodepkg/jsoneditor/src/views/index.ts b/nodepkg/jsoneditor/src/views/index.ts
new file mode 100644
index 00000000..12945a07
--- /dev/null
+++ b/nodepkg/jsoneditor/src/views/index.ts
@@ -0,0 +1,10 @@
+export * from "./TokenView.tsx";
+export * from "./Menu.tsx";
+
+export * from "./ObjectInput.tsx";
+export * from "./ArrayInput.tsx";
+export * from "./RecordInput.tsx";
+export * from "./EnumInput.tsx";
+export * from "./StringInput.tsx";
+export * from "./BooleanInput.tsx";
+export * from "./NumberInput.tsx";
diff --git a/nodepkg/jsoneditor/tsconfig.monobundle.json b/nodepkg/jsoneditor/tsconfig.monobundle.json
new file mode 100644
index 00000000..3170e721
--- /dev/null
+++ b/nodepkg/jsoneditor/tsconfig.monobundle.json
@@ -0,0 +1,6 @@
+{
+ "extends": "@innoai-tech/vuedevconfig/tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "./src"
+ }
+}
diff --git a/nodepkg/typedef/package.json b/nodepkg/typedef/package.json
index aa5c5f1d..34fef3a9 100644
--- a/nodepkg/typedef/package.json
+++ b/nodepkg/typedef/package.json
@@ -1,6 +1,6 @@
{
"name": "@innoai-tech/typedef",
- "version": "0.2.32",
+ "version": "0.2.33",
"monobundle": {
"exports": {
".": "./src/index.ts"
diff --git a/nodepkg/typedef/src/core/TypeBoolean.ts b/nodepkg/typedef/src/core/TypeBoolean.ts
index 8edcbaf0..153cfd7e 100644
--- a/nodepkg/typedef/src/core/TypeBoolean.ts
+++ b/nodepkg/typedef/src/core/TypeBoolean.ts
@@ -15,7 +15,7 @@ export class TypeBoolean extends Type {
override coercer(value: unknown, _: Context) {
try {
- return (value != undefined) ? String(value) === "true" : undefined;
+ return value != undefined ? String(value) === "true" : undefined;
} catch (err) {
return undefined;
}
diff --git a/nodepkg/typedef/src/core/TypeInteger.ts b/nodepkg/typedef/src/core/TypeInteger.ts
index a9de6902..bd6e8365 100644
--- a/nodepkg/typedef/src/core/TypeInteger.ts
+++ b/nodepkg/typedef/src/core/TypeInteger.ts
@@ -19,7 +19,7 @@ export class TypeInteger extends Type {
override coercer(value: unknown, _: Context) {
try {
- return (value != undefined) ? parseInt(String(value)) : undefined;
+ return value != undefined ? parseInt(String(value)) : undefined;
} catch (err) {
return undefined;
}
diff --git a/nodepkg/typedef/src/core/TypeNumber.ts b/nodepkg/typedef/src/core/TypeNumber.ts
index 84e371f4..8a9a7334 100644
--- a/nodepkg/typedef/src/core/TypeNumber.ts
+++ b/nodepkg/typedef/src/core/TypeNumber.ts
@@ -15,7 +15,7 @@ export class TypeNumber extends Type {
override coercer(value: unknown, _: Context) {
try {
- return (value != undefined) ? parseFloat(String(value)) : undefined;
+ return value != undefined ? parseFloat(String(value)) : undefined;
} catch (err) {
return undefined;
}
diff --git a/nodepkg/typedef/src/encoding/TypeScriptEncoder.ts b/nodepkg/typedef/src/encoding/TypeScriptEncoder.ts
index c38f23b2..8d3c796c 100644
--- a/nodepkg/typedef/src/encoding/TypeScriptEncoder.ts
+++ b/nodepkg/typedef/src/encoding/TypeScriptEncoder.ts
@@ -75,7 +75,6 @@ export ${t} ${name}${t === "enum" ? " " : " = "}${decl}`;
return JSON.stringify(type.schema.enum[0]);
}
-
case "enums": {
if (declName) {
const isPrefixDigit = (v: any) => {
@@ -93,7 +92,7 @@ export ${t} ${name}${t === "enum" ? " " : " = "}${decl}`;
"enum",
`{
${type.schema.enum.map((v: any) => `${isPrefixDigit(v) ? `_${v}` : v} = ${JSON.stringify(v)}`).join(",\n")}
-}`
+}`,
]);
const enumLabels = rawType.getMeta("enumLabels") as any[];
@@ -104,10 +103,13 @@ ${type.schema.enum.map((v: any) => `${isPrefixDigit(v) ? `_${v}` : v} = ${JSON.s
`(v: ${declName}) => {
return ({
${type.schema.enum
- .map((v: any, i: number) => `${v}: ${JSON.stringify(enumLabels[i])}`)
- .join(",\n")}
+ .map(
+ (v: any, i: number) =>
+ `${JSON.stringify(v)}: ${JSON.stringify(enumLabels[i])}`,
+ )
+ .join(",\n")}
})[v] ?? v
-}`
+}`,
]);
}
diff --git a/nodepkg/typedef/src/encoding/__tests__/__snapshots__/index.spec.ts.snap b/nodepkg/typedef/src/encoding/__tests__/__snapshots__/index.spec.ts.snap
index 9c5c0d2e..61a3cbb0 100644
--- a/nodepkg/typedef/src/encoding/__tests__/__snapshots__/index.spec.ts.snap
+++ b/nodepkg/typedef/src/encoding/__tests__/__snapshots__/index.spec.ts.snap
@@ -55,9 +55,9 @@ select = "select"
export const displayInputType = (v: InputType) => {
return ({
-text: "文本",
-number: "数字",
-select: "选项"
+"text": "文本",
+"number": "数字",
+"select": "选项"
})[v] ?? v
}"
`;
diff --git a/nodepkg/vuekit/package.json b/nodepkg/vuekit/package.json
index 10125dce..1d45f36f 100644
--- a/nodepkg/vuekit/package.json
+++ b/nodepkg/vuekit/package.json
@@ -1,6 +1,6 @@
{
"name": "@innoai-tech/vuekit",
- "version": "0.8.2",
+ "version": "0.8.3",
"monobundle": {
"exports": {
".": "./src/index.ts",
diff --git a/nodepkg/vuekit/src/Provider.tsx b/nodepkg/vuekit/src/Provider.tsx
index 0114b0f8..a60e5969 100644
--- a/nodepkg/vuekit/src/Provider.tsx
+++ b/nodepkg/vuekit/src/Provider.tsx
@@ -1,4 +1,4 @@
-import { isEmpty, isFunction, isPlainObject } from "@innoai-tech/lodash";
+import { isEmpty, isFunction, isUndefined } from "@innoai-tech/lodash";
import { type AnyType, t } from "@innoai-tech/typedef";
import { type ComponentOptions, component } from "./component";
import { ext } from "./ext";
@@ -8,64 +8,63 @@ import {
type PublicPropsOf,
type VNodeChild,
inject,
- provide,
+ provide
} from "./vue.ts";
export type ProviderComponent = Component<
P & {
- $default?: VNodeChild;
- }
-> & { use: () => Context };
+ $default?: VNodeChild;
+}> & { use: () => Context };
-export type CreateFunction<
- Context extends object,
- PropTypes extends Record,
-> = (props: InternalPropsOf) => Context;
+export type CreateFunction = (props: InternalPropsOf) => Context;
export function createProvider(
create: CreateFunction,
- options?: ComponentOptions,
+ options?: ComponentOptions
): ProviderComponent;
-export function createProvider<
- Context extends object,
- PropTypes extends Record,
->(
+export function createProvider(
+ create: CreateFunction,
+ options?: ComponentOptions
+): ProviderComponent;
+export function createProvider>(
propTypes: PropTypes,
- create: CreateFunction,
- options?: ComponentOptions,
+ create: CreateFunction>,
+ options?: ComponentOptions
): ProviderComponent>;
export function createProvider<
Context extends object,
PropTypes extends Record,
->(
- propTypesOrCreate: PropTypes | CreateFunction,
- createOrOptions?: CreateFunction | ComponentOptions,
- options?: ComponentOptions,
-): ProviderComponent> {
- const finalPropTypes = (
- isPlainObject(propTypesOrCreate) ? propTypesOrCreate : {}
- ) as Record;
- const finalCreate = (
- isFunction(propTypesOrCreate) ? propTypesOrCreate : createOrOptions
- ) as CreateFunction;
- const finalOptions = (options ?? createOrOptions ?? {}) as ComponentOptions;
-
- const key = Symbol(finalOptions.name ?? "");
-
- if (isEmpty(finalPropTypes)) {
+>(...args: any[]): ProviderComponent> {
+ let finalPropTypes: Record = {};
+ let finalCreate: any = undefined;
+ let finalOptions: Record = {};
+
+ for (const arg of args) {
+ if (isFunction(arg)) {
+ finalCreate = arg;
+ continue;
+ }
+
+ if (isUndefined(finalCreate)) {
+ finalPropTypes = arg;
+ } else {
+ finalOptions = arg;
+ }
+ }
+
+ const key = Symbol(finalOptions?.["name"] ?? "");
+
+ if (isEmpty(finalPropTypes) && isEmpty(finalOptions["props"])) {
let _default: any;
const getDefaults = () => {
- if (typeof _default === "undefined") {
- _default = finalCreate({});
- }
- return _default;
+ return _default ??= finalCreate({});
};
const Provider = component(
{
value: t.custom().optional(),
- $default: t.custom().optional(),
+ $default: t.custom().optional()
},
(props, { slots }) => {
provide(key, props.value ?? getDefaults());
@@ -76,14 +75,14 @@ export function createProvider<
},
{
...finalOptions,
- name: `Provide(${finalOptions.name ?? ""})`,
- },
+ name: `Provide(${finalOptions?.["name"] ?? ""})`
+ }
);
return ext(Provider as any, {
use: () => {
return inject(key, getDefaults, true) as Context;
- },
+ }
});
}
@@ -92,20 +91,13 @@ export function createProvider<
let _default: any;
- const getDefaults = () => {
- if (typeof _default === "undefined") {
- _default = finalCreate(getDefaultProps() as any);
- }
- return _default;
- };
-
const Provider = component(
{
...finalPropTypes,
- $default: t.custom().optional(),
+ $default: t.custom().optional()
},
(props, { slots }) => {
- provide(key, finalCreate(props));
+ provide(key, _default = finalCreate(props));
return () => {
return slots.default?.();
@@ -113,13 +105,15 @@ export function createProvider<
},
{
...finalOptions,
- name: `Provide(${finalOptions.name ?? ""})`,
- },
+ name: `Provide(${finalOptions?.["name"] ?? ""})`
+ }
);
return ext(Provider as any, {
use: () => {
- return inject(key, getDefaults, true) as Context;
- },
+ return inject(key, () => {
+ return _default ??= finalCreate(getDefaultProps());
+ }, true) as Context;
+ }
});
}
diff --git a/nodepkg/vuekit/src/__tests__/Provider.spec.tsx b/nodepkg/vuekit/src/__tests__/Provider.spec.tsx
index cabd8a3d..0a185342 100644
--- a/nodepkg/vuekit/src/__tests__/Provider.spec.tsx
+++ b/nodepkg/vuekit/src/__tests__/Provider.spec.tsx
@@ -48,18 +48,16 @@ describe("Provider", () => {
});
test("could inject custom value", async () => {
- const With = component(
- {
- context: t.string().optional().default("injected"),
- },
- (props, _) => {
- const r = ref({ context: props.context });
+ const With = component<{
+ context?: string,
+ }>((props, _) => {
+ const r = ref({ context: props.context ?? "injected" });
watch(
() => props.context,
(context) => {
- r.value = { context: context };
- },
+ r.value = { context: context ?? "injected" };
+ }
);
return () => (
@@ -68,12 +66,15 @@ describe("Provider", () => {
);
},
+ {
+ props: ["context"]
+ }
);
const wrapper = mount(With, {});
expect(wrapper.text()).toContain("injected");
await wrapper.setProps({
- context: "injected2",
+ context: "injected2"
});
expect(wrapper.text()).toContain("injected2");
@@ -84,9 +85,11 @@ describe("Provider", () => {
describe("factory", () => {
const P = createProvider(
{
- input: t.string().default("default"),
+ input: t.string().default("default")
},
- (props) => ({ context: `${props.input}` }),
+ (props) => ({ context: `${props.input}` }), {
+ name: "Factory"
+ }
);
const C = component((_, _1) => {
@@ -117,26 +120,21 @@ describe("Provider", () => {
});
describe("reactive factory", () => {
- const P = createProvider(
- {
- input: t.string().default("default"),
- },
- (props) => {
- const r = ref(props.input);
-
- watch(
- () => props.input,
- (input) => {
- r.value = input;
- },
- );
+ const P = createProvider<{ value: string }, { input: string }>((props) => {
+ const r = ref(props.input ?? "default");
- return r;
- },
- );
+ watch(() => props.input, (input) => {
+ r.value = input;
+ });
+
+ return r;
+ }, {
+ props: ["input"]
+ });
const C = component((_, _1) => {
const p = P.use();
+
return () => {p.value}
;
});
@@ -147,26 +145,27 @@ describe("Provider", () => {
});
test("could inject custom value", async () => {
- const With = component(
- {
- input: t.string().optional().default("injected"),
- },
- (props, _) => {
- return () => (
-
-
-
- );
- },
- );
+ const With = component<{
+ input?: string,
+ }>((props, _) => {
+
+ return () => (
+
+
+
+ );
+ }, {
+ props: ["input"]
+ });
const wrapper = mount(With, {});
expect(wrapper.text()).toContain("injected");
for (let i = 0; i < 2; i++) {
await wrapper.setProps({
- input: "injected2",
+ input: "injected2"
});
+
expect(wrapper.text()).toContain("injected2");
}
diff --git a/nodepkg/vuekit/src/__tests__/Type.spec.tsx b/nodepkg/vuekit/src/__tests__/Type.spec.tsx
index 91ad4b51..f2a161e0 100644
--- a/nodepkg/vuekit/src/__tests__/Type.spec.tsx
+++ b/nodepkg/vuekit/src/__tests__/Type.spec.tsx
@@ -15,7 +15,7 @@ describe("Type", () => {
type: t.nativeEnum(InputType),
inputWithDefault: t.number().optional().default(1),
onDidSetup: t.custom<() => void>(),
- onDidSetupWith: t.custom<(v: string) => void>(),
+ onDidSetupWith: t.custom<(v: string) => void>()
};
const C = component(propTypes, (props, { emit }) => {
@@ -26,15 +26,15 @@ describe("Type", () => {
input: {props.input ?? 1}
type: {props.type === InputType.select}
- inputWithDefault: {props.inputWithDefault * 2}
+ inputWithDefault: {(props.inputWithDefault ?? 1) * 2}
);
});
const wrapper = mount(C, {
props: {
- type: InputType.text,
- },
+ type: InputType.text
+ }
});
expect(wrapper.text()).toContain("2");
@@ -45,7 +45,7 @@ describe("Type", () => {
test("render with slots", () => {
const propTypes = {
$default: t.custom<(v: number) => VNodeChild>(),
- $optional: t.custom().optional(),
+ $optional: t.custom().optional()
};
const C = component(propTypes, (_, { slots }) => {
@@ -59,8 +59,8 @@ describe("Type", () => {
const wrapper = mount(C, {
slots: {
- default: () => "1",
- },
+ default: () => "1"
+ }
});
expect(wrapper.text()).toContain("1");
diff --git a/nodepkg/vuekit/src/component.ts b/nodepkg/vuekit/src/component.ts
index 66b06822..944f8d92 100644
--- a/nodepkg/vuekit/src/component.ts
+++ b/nodepkg/vuekit/src/component.ts
@@ -1,4 +1,10 @@
-import { isFunction, isPlainObject, isUndefined, kebabCase, partition } from "@innoai-tech/lodash";
+import {
+ isFunction,
+ isPlainObject,
+ isUndefined,
+ kebabCase,
+ partition
+} from "@innoai-tech/lodash";
import { Fragment as OriginFragment } from "vue";
import type {
Component,
@@ -79,10 +85,11 @@ export function component(...args: any[]): Component {
return {
...ret,
[prop]: {
- default: () => {
+ default() {
try {
return d.create(undefined);
} catch (e) {
+ console.log(e);
}
return;
},
@@ -96,7 +103,7 @@ export function component(...args: any[]): Component {
return {
...ret,
[prop]: {
- default: () => {
+ default() {
return undefined;
}
}
@@ -104,10 +111,16 @@ export function component(...args: any[]): Component {
}, {})
};
+ if (emitsAndProps.props.input) {
+ console.log(finalOptions["name"], emitsAndProps.props.input?.default());
+ }
+
return {
...finalOptions,
get name() {
- return this.displayName ?? finalOptions["displayName"] ?? finalOptions["name"];
+ return (
+ this.displayName ?? finalOptions["displayName"] ?? finalOptions["name"]
+ );
},
set name(n: string) {
finalOptions["name"] = n;
@@ -116,7 +129,6 @@ export function component(...args: any[]): Component {
emits: emitsAndProps.emits,
props: emitsAndProps.props,
inheritAttrs: finalOptions["inheritAttrs"],
- propTypes: finalPropTypes,
__component
} as any;
}
diff --git a/nodepkg/vuekit/src/reactive/RxSlot.tsx b/nodepkg/vuekit/src/reactive/RxSlot.tsx
index fbc4a5d1..2c9ee9d2 100644
--- a/nodepkg/vuekit/src/reactive/RxSlot.tsx
+++ b/nodepkg/vuekit/src/reactive/RxSlot.tsx
@@ -25,7 +25,7 @@ const RxSlot = component(
{
elem$: t.custom>(),
- $default: t.custom<{}>()
+ $default: t.custom<{}>(),
},
(props, _) => {
const r = shallowRef(null);
@@ -35,7 +35,7 @@ const RxSlot = component(
tap((renderFunc) => {
r.value = renderFunc;
}),
- subscribeUntilUnmount()
+ subscribeUntilUnmount(),
);
return () => {
@@ -43,6 +43,6 @@ const RxSlot = component(
};
},
{
- name: "RxSlot"
- }
+ name: "RxSlot",
+ },
);
diff --git a/nodepkg/vuekit/src/reactive/component$.tsx b/nodepkg/vuekit/src/reactive/component$.tsx
index 7ab4ef24..6960e05e 100644
--- a/nodepkg/vuekit/src/reactive/component$.tsx
+++ b/nodepkg/vuekit/src/reactive/component$.tsx
@@ -8,7 +8,7 @@ import {
type InternalPropsOf,
type InternalSlotsOf,
type PublicPropsOf,
- type SetupContext
+ type SetupContext,
} from "../vue";
import { render } from "./RxSlot";
import { type Observables, toObservables } from "./toObservable";
@@ -16,31 +16,31 @@ import { type Observables, toObservables } from "./toObservable";
export { render };
export type ObservablesAndProps> =
- Observables
- & Omit>;
+ Observables & Omit>;
-export type ObservableSetupFunction> =
- (
- props: ObservablesAndProps>,
- ctx: SetupContext, InternalSlotsOf> & {
- render: typeof render
- }
- ) => RenderFunction | JSX.Element | null;
+export type ObservableSetupFunction> = (
+ props: ObservablesAndProps>,
+ ctx: SetupContext, InternalSlotsOf> & {
+ render: typeof render;
+ },
+) => RenderFunction | JSX.Element | null;
export function component$(
setup: ObservableSetupFunction<{}>,
- options?: ComponentOptions
+ options?: ComponentOptions,
): Component<{}>;
export function component$>(
setup: ObservableSetupFunction,
- options?: ComponentOptions
+ options?: ComponentOptions,
): Component;
export function component$>(
propTypes: PropTypes,
setup: ObservableSetupFunction>,
- options?: ComponentOptions
+ options?: ComponentOptions,
): Component>;
-export function component$>(...args: any[]): Component {
+export function component$>(
+ ...args: any[]
+): Component {
let finalPropTypes: Record = {};
let finalSetup: any = undefined;
let finalOptions: Record = {};
@@ -68,8 +68,8 @@ export function component$>(...args: any[]): C
{
get(_, key: string) {
return (props as any)[key] ?? (props$ as any)[key];
- }
- }
+ },
+ },
) as any;
const c = new Proxy(
@@ -80,8 +80,8 @@ export function component$>(...args: any[]): C
return render;
}
return (ctx as any)[key];
- }
- }
+ },
+ },
) as any;
const renderFuncOrVNode = finalSetup(p, c);
@@ -92,6 +92,6 @@ export function component$>(...args: any[]): C
return () => renderFuncOrVNode;
},
- finalOptions
+ finalOptions,
) as any;
}
diff --git a/nodepkg/vuekit/src/vue.ts b/nodepkg/vuekit/src/vue.ts
index 80b2c601..4cd3132b 100644
--- a/nodepkg/vuekit/src/vue.ts
+++ b/nodepkg/vuekit/src/vue.ts
@@ -2,7 +2,7 @@ import {
type AnyType,
type Infer,
type Simplify,
- t
+ t,
} from "@innoai-tech/typedef";
import {
type ObjectEmitsOptions,
@@ -11,7 +11,7 @@ import {
type VNode,
type Ref,
type UnwrapRef,
- customRef
+ customRef,
} from "vue";
export type VElementType = string | Component;
@@ -57,14 +57,14 @@ type EmitFn<
> = UnionToIntersection<
{
[Key in Event]: E[Key] extends (...args: infer Args) => any
- ? (event: Key, ...args: Args) => void
- : (event: Key) => void;
+ ? (event: Key, ...args: Args) => void
+ : (event: Key) => void;
}[Event]
>;
type UnionToIntersection = (
U extends any ? (arg: U) => any : never
- ) extends (arg: infer I) => void
+) extends (arg: infer I) => void
? I
: never;
@@ -75,15 +75,19 @@ export type PublicPropsOf<
export type SetupFunction> = (
props: InternalPropsOf,
- ctx: SetupContext, InternalSlotsOf>
+ ctx: SetupContext, InternalSlotsOf>,
) => RenderFunction;
+export type InternalPropsOf> = Simplify<
+ PickProps
+>;
-export type InternalPropsOf> = Simplify>;
-
-export type InternalSlotsOf> = Simplify>>;
+export type InternalSlotsOf> = Simplify<
+ ToInternalSlots>
+>;
-export type InternalEmitsOf> = ToInternalEmits>>;
+export type InternalEmitsOf> =
+ ToInternalEmits>>;
export type PickProps> = {
[K in keyof O as K extends string ? NormalProp : never]: O[K];
@@ -173,7 +177,7 @@ export function ref(): Ref {
currentValue = newValue;
trigger();
}
- }
+ },
};
});
}
diff --git a/nodepkg/vuematerial/src/Buttons/ElevatedButton.tsx b/nodepkg/vuematerial/src/Buttons/ElevatedButton.tsx
index d2794e11..4ae4e52e 100644
--- a/nodepkg/vuematerial/src/Buttons/ElevatedButton.tsx
+++ b/nodepkg/vuematerial/src/Buttons/ElevatedButton.tsx
@@ -5,35 +5,35 @@ export const ElevatedButton = styled(ButtonBase)({
color: "sys.primary",
shadow: "1",
_$before: {
- bgColor: "sys.surface-container-low"
+ bgColor: "sys.surface-container-low",
},
_hover: {
shadow: "2",
_$before: {
- bgColor: variant("sys.primary" as const, alpha(0.08))
- }
+ bgColor: variant("sys.primary" as const, alpha(0.08)),
+ },
},
_focus: {
shadow: "2",
_$before: {
- bgColor: variant("sys.primary" as const, alpha(0.12))
- }
+ bgColor: variant("sys.primary" as const, alpha(0.12)),
+ },
},
_active: {
shadow: "2",
_$before: {
- bgColor: variant("sys.primary" as const, alpha(0.12))
- }
+ bgColor: variant("sys.primary" as const, alpha(0.12)),
+ },
},
_disabled: {
shadow: "0",
color: variant("sys.on-surface", alpha(0.38)),
_$before: {
- bgColor: variant("sys.on-surface" as const, alpha(0.12))
- }
- }
-});
\ No newline at end of file
+ bgColor: variant("sys.on-surface" as const, alpha(0.12)),
+ },
+ },
+});
diff --git a/nodepkg/vuematerial/src/Buttons/FilledButton.tsx b/nodepkg/vuematerial/src/Buttons/FilledButton.tsx
index 30a3f194..2fab0a89 100644
--- a/nodepkg/vuematerial/src/Buttons/FilledButton.tsx
+++ b/nodepkg/vuematerial/src/Buttons/FilledButton.tsx
@@ -9,22 +9,22 @@ export const FilledButton = styled(ButtonBase)({
_hover: {
shadow: "2",
_$before: {
- bgColor: variant("white", alpha(0.08))
- }
+ bgColor: variant("white", alpha(0.08)),
+ },
},
_focus: {
shadow: "2",
_$before: {
- bgColor: variant("white", alpha(0.12))
- }
+ bgColor: variant("white", alpha(0.12)),
+ },
},
_active: {
shadow: "2",
_$before: {
- bgColor: variant("white", alpha(0.12))
- }
+ bgColor: variant("white", alpha(0.12)),
+ },
},
_disabled: {
@@ -33,7 +33,7 @@ export const FilledButton = styled(ButtonBase)({
bgColor: "rgba(0,0,0,0)",
_$before: {
- bgColor: variant("sys.on-surface", alpha(0.12))
- }
- }
+ bgColor: variant("sys.on-surface", alpha(0.12)),
+ },
+ },
});
diff --git a/nodepkg/vuematerial/src/Buttons/IconButton.tsx b/nodepkg/vuematerial/src/Buttons/IconButton.tsx
index 32325794..3324c3ce 100644
--- a/nodepkg/vuematerial/src/Buttons/IconButton.tsx
+++ b/nodepkg/vuematerial/src/Buttons/IconButton.tsx
@@ -3,5 +3,5 @@ import { TextButton } from "./TextButton";
export const IconButton = styled(TextButton)({
boxSize: 40,
- p: 0
+ p: 0,
});
diff --git a/nodepkg/vuematerial/src/Icons/Icon.tsx b/nodepkg/vuematerial/src/Icons/Icon.tsx
index a7ba6142..514274c5 100644
--- a/nodepkg/vuematerial/src/Icons/Icon.tsx
+++ b/nodepkg/vuematerial/src/Icons/Icon.tsx
@@ -1,9 +1,12 @@
import { styled } from "@innoai-tech/vueuikit";
-export const Icon = styled<{
- path: string,
- placement?: "start" | "end",
-}, "span">("span", (props, _) => {
+export const Icon = styled<
+ {
+ path: string;
+ placement?: "start" | "end";
+ },
+ "span"
+>("span", (props, _) => {
return (Wrapper) => (
);
})({
- boxSize: "1.2em"
+ boxSize: "1.2em",
});
-
diff --git a/nodepkg/vuematerial/src/Overlays/Dialog.tsx b/nodepkg/vuematerial/src/Overlays/Dialog.tsx
index 9fde41ef..85d9ce53 100644
--- a/nodepkg/vuematerial/src/Overlays/Dialog.tsx
+++ b/nodepkg/vuematerial/src/Overlays/Dialog.tsx
@@ -5,7 +5,7 @@ import {
defineTransition,
styled,
transition,
- variant
+ variant,
} from "@innoai-tech/vueuikit";
import { ref, watch } from "vue";
@@ -18,7 +18,7 @@ const Container = styled("div")({
zIndex: 100,
display: "flex",
alignItems: "center",
- justifyContent: "center"
+ justifyContent: "center",
});
export const DialogBackdrop = styled("div")({
@@ -29,7 +29,7 @@ export const DialogBackdrop = styled("div")({
h: "100vh",
w: "100vw",
zIndex: -1,
- bgColor: variant("sys.scrim", alpha(0.38))
+ bgColor: variant("sys.scrim", alpha(0.38)),
});
export const DialogContainer = styled("div")({
@@ -37,37 +37,37 @@ export const DialogContainer = styled("div")({
rounded: "sm",
shadow: "3",
minW: "50vw",
- containerStyle: "sys.surface-container-high"
+ containerStyle: "sys.surface-container-high",
});
const FadeInOutTransition = defineTransition(
{
from: {
- opacity: 0
+ opacity: 0,
},
to: {
- opacity: 1
+ opacity: 1,
},
duration: transition.duration.md1,
- easing: transition.easing.standard.accelerate
+ easing: transition.easing.standard.accelerate,
},
{
from: {
- opacity: 1
+ opacity: 1,
},
to: {
- opacity: 0
+ opacity: 0,
},
duration: transition.duration.sm4,
- easing: transition.easing.standard.accelerate
- }
+ easing: transition.easing.standard.accelerate,
+ },
);
export const Dialog = component(
{
isOpen: t.boolean().optional(),
onClose: t.custom<() => void>(),
- $default: t.custom().optional()
+ $default: t.custom().optional(),
},
(props, { slots, emit }) => {
const mount = ref(props.isOpen ?? false);
@@ -83,7 +83,7 @@ export const Dialog = component(
// animate leave first,then unmount
animateEnterOrLeave.value = false;
}
- }
+ },
);
return () => {
@@ -119,5 +119,5 @@ export const Dialog = component(
);
};
- }
+ },
);
diff --git a/nodepkg/vueuikit/example/color-palette.tsx b/nodepkg/vueuikit/example/color-palette.tsx
index 9b10fd4e..4e960834 100644
--- a/nodepkg/vueuikit/example/color-palette.tsx
+++ b/nodepkg/vueuikit/example/color-palette.tsx
@@ -24,7 +24,7 @@ export default component(() => {
flex: 1,
display: "flex",
alignItems: "center",
- gap: 16
+ gap: 16,
}}
>
{name}
@@ -57,8 +57,8 @@ export default component(() => {
"& input": {
width: "40%",
border: "1px solid",
- borderColor: "sys.outline"
- }
+ borderColor: "sys.outline",
+ },
}}
>
{
min={0}
style={{
backgroundColor: Palette.toHEX(
- pp.seeds[base].tone(tone)
+ pp.seeds[base].tone(tone),
),
- color: tone > 50 ? "black" : "white"
+ color: tone > 50 ? "black" : "white",
}}
data-color={Palette.toHEX(pp.seeds[base].tone(tone))}
data-theme={"dark"}
onChange={(evt) => {
try {
const v = parseInt(
- (evt.target as HTMLInputElement).value
+ (evt.target as HTMLInputElement).value,
);
p.next((x) => {
(x.rules as any)[role] = [base, v, tone];
});
- } catch (_) {
- }
+ } catch (_) {}
}}
/>
diff --git a/nodepkg/vueuikit/package.json b/nodepkg/vueuikit/package.json
index 9d5714d6..c2374051 100644
--- a/nodepkg/vueuikit/package.json
+++ b/nodepkg/vueuikit/package.json
@@ -1,6 +1,6 @@
{
"name": "@innoai-tech/vueuikit",
- "version": "0.13.2",
+ "version": "0.13.3",
"monobundle": {
"exports": {
".": "./src/index.ts"
diff --git a/nodepkg/vueuikit/src/GlobalStyle.tsx b/nodepkg/vueuikit/src/GlobalStyle.tsx
index b6058b81..58e224a2 100644
--- a/nodepkg/vueuikit/src/GlobalStyle.tsx
+++ b/nodepkg/vueuikit/src/GlobalStyle.tsx
@@ -1,31 +1,30 @@
import { isString } from "@innoai-tech/lodash";
-import { component, t } from "@innoai-tech/vuekit";
+import { component } from "@innoai-tech/vuekit";
import { onBeforeMount } from "vue";
import { CacheProvider } from "./CacheProvider";
import { ThemeProvider } from "./ThemeProvider";
import { type SystemStyleObject } from "./theming";
import { useInsertStyles } from "./useInsertStyles";
-export const GlobalStyle = component(
- { styles: t.custom() },
- ({ styles }) => {
- const theme = ThemeProvider.use();
- const cache = CacheProvider.use();
+export const GlobalStyle = component<{
+ styles: SystemStyleObject | string
+}>(({ styles }) => {
+ const theme = ThemeProvider.use();
+ const cache = CacheProvider.use();
- const insert = useInsertStyles(cache);
+ const insert = useInsertStyles(cache);
- const serialized = theme.unstable_css(
- cache,
- isString(styles) ? ({ "&": styles } as any) : styles,
- );
+ const serialized = theme.unstable_css(
+ cache,
+ isString(styles) ? ({ "&": styles } as any) : styles
+ );
- onBeforeMount(() => {
- insert({
- serialized,
- withoutScoping: true,
- });
+ onBeforeMount(() => {
+ insert({
+ serialized,
+ withoutScoping: true
});
+ });
- return () => null;
- },
-);
+ return () => null;
+});
diff --git a/nodepkg/vueuikit/src/Overlay.tsx b/nodepkg/vueuikit/src/Overlay.tsx
index c52e5429..540d78c3 100644
--- a/nodepkg/vueuikit/src/Overlay.tsx
+++ b/nodepkg/vueuikit/src/Overlay.tsx
@@ -5,9 +5,8 @@ import {
createProvider,
rx,
subscribeUntilUnmount,
- t,
tapEffect,
- toObservable,
+ toObservable
} from "@innoai-tech/vuekit";
import {
type CSSProperties,
@@ -17,18 +16,18 @@ import {
cloneVNode,
onBeforeUnmount,
ref,
- unref,
+ unref
} from "vue";
export const OverlaySettingProvider = createProvider(
() => {
return {
- mountPoint: () => document.body,
+ mountPoint: () => document.body
};
},
{
- name: "OverlaySetting",
- },
+ name: "OverlaySetting"
+ }
);
const OverlayProvider = createProvider(
@@ -36,8 +35,8 @@ const OverlayProvider = createProvider(
return new OverlayContext(ref(null), ref(null), () => false);
},
{
- name: "Overlay",
- },
+ name: "Overlay"
+ }
);
class OverlayContext {
@@ -46,8 +45,9 @@ class OverlayContext {
constructor(
private triggerRef: Ref,
private contentRef: Ref,
- private isOpen: () => boolean,
- ) {}
+ private isOpen: () => boolean
+ ) {
+ }
add = (p: OverlayContext) => {
this.children = [...this.children, p];
@@ -82,28 +82,26 @@ class OverlayContext {
}
}
-export const Overlay = component(
- {
- isOpen: t.boolean().optional(),
- style: t.custom().optional(),
- contentRef: t.custom[>().optional(),
- triggerRef: t.custom][>().optional(),
-
- onClickOutside: t.custom<(e: Event) => void>(),
- onEscKeydown: t.custom<(e: Event) => void>(),
- onContentBeforeMount: t.custom<() => void>(),
- $transition: t
- .custom<(ctx: { content: JSX.Element | null }) => VNodeChild>()
- .optional(),
- $default: t.custom().optional(),
- },
+export const Overlay = component<{
+ isOpen?: boolean,
+ style?: CSSProperties,
+ contentRef?: Ref,
+ triggerRef?: Ref,
+
+ onClickOutside?: (e: Event) => void,
+ onEscKeydown?: (e: Event) => void,
+ onContentBeforeMount?: () => void,
+
+ $transition?: (ctx: { content: JSX.Element | null }) => VNodeChild,
+ $default?: VNodeChild,
+}>(
(props, { slots, attrs, emit }) => {
const contentRef = props.contentRef || ref(null);
const popperContext = new OverlayContext(
props.triggerRef ?? ref(null),
contentRef,
- () => !!props.isOpen,
+ () => !!props.isOpen
);
const setting = OverlaySettingProvider.use();
@@ -148,24 +146,24 @@ export const Overlay = component(
window.removeEventListener("keydown", handleEscKeydown);
};
}),
- subscribeUntilUnmount(),
+ subscribeUntilUnmount()
);
}
return () => {
const content = props.isOpen
? cloneVNode(
- ]
-
- {slots.default?.()}
-
-
,
- {
- onVnodeBeforeMount: () => {
- emit("content-before-mount");
- },
- },
- )
+
+
+ {slots.default?.()}
+
+
,
+ {
+ onVnodeBeforeMount: () => {
+ emit("content-before-mount");
+ }
+ }
+ )
: null;
return (
@@ -176,7 +174,7 @@ export const Overlay = component(
};
},
{
- inheritAttrs: false,
name: "Overlay",
- },
+ inheritAttrs: false
+ }
);
diff --git a/nodepkg/vueuikit/src/Popper.tsx b/nodepkg/vueuikit/src/Popper.tsx
index 5dfc7a72..8d197076 100644
--- a/nodepkg/vueuikit/src/Popper.tsx
+++ b/nodepkg/vueuikit/src/Popper.tsx
@@ -1,38 +1,40 @@
-import { type VNode, type VNodeChild, component, t } from "@innoai-tech/vuekit";
+import { type VNode, type VNodeChild, component } from "@innoai-tech/vuekit";
import {
type Modifier,
type Placement,
createPopperLite,
- flip,
+ flip
} from "@popperjs/core";
import type { ModifierArguments, State } from "@popperjs/core";
-import { cloneVNode, ref, watch } from "vue";
+import { cloneVNode, type CSSProperties, type Ref, ref, watch } from "vue";
import { Overlay } from "./Overlay";
export function createPopperModifier>(
fn: (o: ModifierArguments) => State | undefined,
- options: Omit, "fn" | "enabled">,
+ options: Omit, "fn" | "enabled">
): Modifier {
return {
fn,
enabled: true,
- ...options,
+ ...options
};
}
-export const Popper = component(
- {
- isOpen: Overlay.propTypes!.isOpen,
- onClickOutside: Overlay.propTypes!.onClickOutside,
-
- placement: t.custom().optional(),
- modifiers: t.custom>>().optional(),
+export const Popper = component<{
+ isOpen?: boolean,
+ style?: CSSProperties,
+ contentRef?: Ref,
+ triggerRef?: Ref,
+ onClickOutside?: (e: Event) => void,
+ onEscKeydown?: (e: Event) => void,
+ onContentBeforeMount?: () => void,
+ $transition?: (ctx: { content: JSX.Element | null }) => VNodeChild,
+ $default?: JSX.Element | null,
+ $content?: VNodeChild,
- $transition: Overlay.propTypes!.$transition,
- $content: t.custom(),
- $default: t.custom(),
- },
- (props, { slots, emit, attrs }) => {
+ placement?: Placement,
+ modifiers?: Array>
+}>((props, { slots, emit, attrs }) => {
const triggerRef = ref(null);
const contentRef = ref(null);
@@ -42,10 +44,10 @@ export const Popper = component(
if (contentEl && triggerRef.value) {
createPopperLite(triggerRef.value, contentEl, {
placement: props.placement ?? "bottom",
- modifiers: [...(props.modifiers ?? []), flip],
+ modifiers: [...(props.modifiers ?? []), flip]
});
}
- },
+ }
);
return () => {
@@ -61,7 +63,7 @@ export const Popper = component(
...attrs,
onVnodeMounted: (n) => {
triggerRef.value = resolveElement(n.el);
- },
+ }
})}
(
+export function styled(
defaultComponent: DefaultComponent,
- setup?: StyledSetupFunction<{}, DefaultComponent>
+ setup?: StyledSetupFunction<{}, DefaultComponent>,
): (presetSx: SystemStyleObject) => OverridableComponent<{
props: Partial;
defaultComponent: DefaultComponent;
@@ -34,17 +32,17 @@ export function styled<
// https://github.com/microsoft/TypeScript/pull/26349
export function styled<
Props extends Record,
- _DefaultComponent extends VElementType
+ _DefaultComponent extends VElementType,
>(
defaultComponent: _DefaultComponent,
- setup?: StyledSetupFunction
+ setup?: StyledSetupFunction,
): (presetSx: SystemStyleObject) => OverridableComponent<{
props: Props & Partial;
defaultComponent: _DefaultComponent;
}>;
export function styled(
defaultComponent: DefaultComponent,
- setup?: StyledSetupFunction<{}, DefaultComponent>
+ setup?: StyledSetupFunction<{}, DefaultComponent>,
): (presetSx: SystemStyleObject) => OverridableComponent<{
props: Partial;
defaultComponent: DefaultComponent;
@@ -55,7 +53,7 @@ export function styled<
>(
defaultComponent: DefaultComponent,
propTypes: PropTypes,
- setup?: StyledSetupFunction, DefaultComponent>
+ setup?: StyledSetupFunction, DefaultComponent>,
): (presetSx: SystemStyleObject) => OverridableComponent<{
props: PublicPropsOf & Partial;
defaultComponent: DefaultComponent;
@@ -63,7 +61,9 @@ export function styled<
export function styled<
Props extends Record,
DefaultComponent extends VElementType,
->(...args: any[]): (presetSx: SystemStyleObject) => OverridableComponent<{
+>(
+ ...args: any[]
+): (presetSx: SystemStyleObject) => OverridableComponent<{
props: Props & Partial;
defaultComponent: DefaultComponent;
}> {
@@ -108,86 +108,89 @@ export function styled<
};
return (presetSx: SystemStyleObject): any => {
- const c = Object.assign(component(
- {
- ...finalPropTypes,
- sx: t.custom().optional(),
- component: t.custom().optional()
- },
- (props, ctx) => {
- const theme = ThemeProvider.use();
- const cache = CacheProvider.use();
- const insertCSS = useInsertStyles(cache);
+ const c = Object.assign(
+ component(
+ {
+ ...finalPropTypes,
+ sx: t.custom().optional(),
+ component: t.custom().optional(),
+ },
+ (props, ctx) => {
+ const theme = ThemeProvider.use();
+ const cache = CacheProvider.use();
+ const insertCSS = useInsertStyles(cache);
+
+ const sxClassName = ref("");
+
+ const presetSxSerialized = theme.unstable_css(cache, presetSx);
+
+ const className = (): string =>
+ (presetSxSerialized.name !== "0"
+ ? `${cache.key}-${presetSxSerialized.name}${sxClassName.value}`
+ : `${sxClassName.value}`) + (c.name ? ` ${c.name}` : "");
- const sxClassName = ref("");
-
- const presetSxSerialized = theme.unstable_css(cache, presetSx);
+ if ((defaultComponent as any).__styled) {
+ const serialized = theme.unstable_css(cache, props.sx ?? {});
- const className = (): string =>
- (presetSxSerialized.name !== "0"
- ? `${cache.key}-${presetSxSerialized.name}${sxClassName.value}`
- : `${sxClassName.value}`) + (c.name ? ` ${c.name}` : "");
+ if (serialized.name !== "0") {
+ sxClassName.value = ` ${cache.key}-${serialized.name}`;
+ }
- if ((defaultComponent as any).__styled) {
- const serialized = theme.unstable_css(cache, props.sx ?? {});
+ onMounted(() => {
+ insertCSS({
+ serialized: presetSxSerialized,
+ isStringTag: true,
+ });
- if (serialized.name !== "0") {
- sxClassName.value = ` ${cache.key}-${serialized.name}`;
+ insertCSS({
+ serialized,
+ isStringTag: true,
+ });
+ });
+ } else {
+ onBeforeMount(() => {
+ insertCSS({
+ serialized: presetSxSerialized,
+ isStringTag: true,
+ });
+ });
}
- onMounted(() => {
- insertCSS({
- serialized: presetSxSerialized,
- isStringTag: true
- });
+ const render = finalSetup(props as any, ctx as any);
- insertCSS({
- serialized,
- isStringTag: true
- });
- });
- } else {
- onBeforeMount(() => {
- insertCSS({
- serialized: presetSxSerialized,
- isStringTag: true
- });
- });
- }
+ return () => {
+ if ((defaultComponent as any).__styled) {
+ const ret = render(defaultComponent);
- const render = finalSetup(props as any, ctx as any);
+ if (ret) {
+ return cloneVNode(ret, {
+ component: (props as any).component,
+ class: className(),
+ });
+ }
- return () => {
- if ((defaultComponent as any).__styled) {
- const ret = render(defaultComponent);
+ return null;
+ }
+
+ const ret = render(Box as any);
if (ret) {
return cloneVNode(ret, {
- component: (props as any).component,
- class: className()
+ component: (props as any).component || defaultComponent,
+ sx: (props as any).sx,
+ class: className(),
});
}
return null;
- }
-
- const ret = render(Box as any);
-
- if (ret) {
- return cloneVNode(ret, {
- component: (props as any).component || defaultComponent,
- sx: (props as any).sx,
- class: className()
- });
- }
-
- return null;
- };
+ };
+ },
+ finalOptions,
+ ),
+ {
+ __styled: true,
},
- finalOptions
- ), {
- __styled: true
- }) as any;
+ ) as any;
c.toString = () => {
return `.${c.name}`;
@@ -197,11 +200,10 @@ export function styled<
};
}
-
export type StyledSetupFunction<
Props extends Record,
DefaultComponent extends VElementType,
> = (
props: InternalPropsOf,
- ctx: SetupContext, InternalSlotsOf>
+ ctx: SetupContext, InternalSlotsOf>,
) => (Wrap: DefaultComponent) => VNode | null;
diff --git a/nodepkg/vueuikit/src/theming/m3/index.ts b/nodepkg/vueuikit/src/theming/m3/index.ts
index 99b8f723..d01d8136 100644
--- a/nodepkg/vueuikit/src/theming/m3/index.ts
+++ b/nodepkg/vueuikit/src/theming/m3/index.ts
@@ -11,7 +11,7 @@ const seedColors = Palette.fromColors({
neutral: "#5e5e5e",
error: "#d93f23",
warning: "#e69c00",
- success: "#5ac220"
+ success: "#5ac220",
});
export const defaultTheme = {
@@ -19,7 +19,7 @@ export const defaultTheme = {
...motion,
...elevation,
rounded: rounded,
- ...seedColors.toDesignTokens({})
+ ...seedColors.toDesignTokens({}),
} as const;
export * from "./palette";
diff --git a/nodepkg/vueuikit/src/theming/m3/palette.ts b/nodepkg/vueuikit/src/theming/m3/palette.ts
index 0693e7ae..f8b6121a 100644
--- a/nodepkg/vueuikit/src/theming/m3/palette.ts
+++ b/nodepkg/vueuikit/src/theming/m3/palette.ts
@@ -2,13 +2,13 @@ import {
type Dictionary,
isNumber,
mapValues,
- padStart
+ padStart,
} from "@innoai-tech/lodash";
import {
CorePalette,
TonalPalette,
argbFromHex,
- rgbaFromArgb
+ rgbaFromArgb,
} from "@material/material-color-utilities";
import { DesignToken, type WithMixin } from "../token";
@@ -24,7 +24,7 @@ const tones = {
"80": true,
"90": true,
"95": true,
- "100": true
+ "100": true,
} as const;
export type ColorTonalPalette = {
@@ -93,16 +93,22 @@ export interface SeedColors {
}
export type RoleColorSource = {
- [K in keyof Seeds]: (tone: number) => [K, number]
-}
+ [K in keyof Seeds]: (tone: number) => [K, number];
+};
-export type RoleColorRuleBuilder> = {
- rule(role: K, rule: [string, number]): RoleColorRuleBuilder>
- build(): RoleColorRules
-}
+export type RoleColorRuleBuilder<
+ Seeds extends SeedColors,
+ RuleKeys extends string = keyof ColorPalettes,
+> = {
+ rule(
+ role: K,
+ rule: [string, number],
+ ): RoleColorRuleBuilder>;
+ build(): RoleColorRules;
+};
export type RoleColorRules = {
- [k in keyof ColorPalettes]: [keyof Seeds, number]
+ [k in keyof ColorPalettes]: [keyof Seeds, number];
};
const rgbFromArgb = (argb: number): [number, number, number] => {
@@ -117,7 +123,7 @@ const isKeyColor = (k: string) => {
tertiary: true,
error: true,
warning: true,
- success: true
+ success: true,
}[k];
};
@@ -128,36 +134,46 @@ export class Palette {
.join("")}`;
};
- static createRoleColorRuleBuilder(mode?: string): RoleColorRuleBuilder {
+ static createRoleColorRuleBuilder(
+ mode?: string,
+ ): RoleColorRuleBuilder {
const rules: any = {};
- const x = new Proxy({}, {
- get(_, prop: string) {
- if (prop == "build") {
- return () => rules;
- }
+ const x = new Proxy(
+ {},
+ {
+ get(_, prop: string) {
+ if (prop == "build") {
+ return () => rules;
+ }
- return (role: string, rule: [string: number]) => {
- rules[`${role}${mode ? `:${mode}` : ""}`] = rule;
+ return (role: string, rule: [string: number]) => {
+ rules[`${role}${mode ? `:${mode}` : ""}`] = rule;
- return x;
- };
- }
- });
+ return x;
+ };
+ },
+ },
+ );
return x as any;
}
- static createRoleColorSourcePicker(): RoleColorSource {
- return new Proxy({}, {
- get(_, prop: string) {
- return (tone: number) => [prop, tone];
- }
- }) as any;
+ static createRoleColorSourcePicker<
+ Colors extends SeedColors = SeedColors,
+ >(): RoleColorSource {
+ return new Proxy(
+ {},
+ {
+ get(_, prop: string) {
+ return (tone: number) => [prop, tone];
+ },
+ },
+ ) as any;
}
static fromColors = (
- colors: Partial & { primary: string }
+ colors: Partial & { primary: string },
) => {
const {
primary,
@@ -173,7 +189,7 @@ export class Palette {
primary: argbFromHex(primary),
secondary: secondary ? argbFromHex(secondary) : undefined,
tertiary: tertiary ? argbFromHex(tertiary) : undefined,
- error: error ? argbFromHex(error) : undefined
+ error: error ? argbFromHex(error) : undefined,
});
if (neutral) {
@@ -183,7 +199,7 @@ export class Palette {
if (neutralVariant) {
palette.n2 = TonalPalette.fromHueAndChroma(
argbFromHex(neutralVariant),
- 8
+ 8,
);
}
@@ -195,15 +211,16 @@ export class Palette {
neutralVariant: palette.n2,
error: palette.error,
...(mapValues(otherColors as Dictionary, (v) =>
- TonalPalette.fromInt(argbFromHex(v))
- ) as any)
+ TonalPalette.fromInt(argbFromHex(v)),
+ ) as any),
});
};
- constructor(public seeds: { [K in keyof Colors]: TonalPalette }) {
- }
+ constructor(public seeds: { [K in keyof Colors]: TonalPalette }) {}
- normalizeRoleRules(rules: Partial> = {}): RoleColorRules {
+ normalizeRoleRules(
+ rules: Partial> = {},
+ ): RoleColorRules {
const seed = Palette.createRoleColorSourcePicker();
let defaultRoleRules = Palette.createRoleColorRuleBuilder()
@@ -246,7 +263,6 @@ export class Palette {
.rule("inverse-on-surface", seed.neutral(20))
.rule("inverse-primary", seed.primary(40));
-
for (const name in this.seeds) {
if (name.startsWith("neutral")) {
continue;
@@ -259,7 +275,6 @@ export class Palette {
.rule(`${name}-container` as any, seed[name](90))
.rule(`on-${name}-container` as any, seed[name](10));
-
darkRoleRules = darkRoleRules
.rule(name as any, seed[name](80))
.rule(`on-${name}` as any, seed[name](20))
@@ -270,7 +285,7 @@ export class Palette {
return {
...defaultRoleRules.build(),
...darkRoleRules.build(),
- ...rules
+ ...rules,
} as RoleColorRules;
}
@@ -304,10 +319,13 @@ export class Palette {
const [themeColors, dartThemeColors] = this.getThemeRoleColors(finalRules);
const sysColors: Record = {};
- const containerStyles: Record = {};
+ const containerStyles: Record<
+ string,
+ {
+ color: string;
+ bgColor: string;
+ }
+ > = {};
for (const role in themeColors) {
sysColors[`${role}`] = {
@@ -316,18 +334,18 @@ export class Palette {
: (themeColors as any)[role],
_dark: isNumber((dartThemeColors as any)[role])
? rgbFromArgb((dartThemeColors as any)[role])
- : (dartThemeColors as any)[role]
+ : (dartThemeColors as any)[role],
};
if (isKeyColor(role)) {
containerStyles[`${role}`] = DesignToken.mixin({
bgColor: `sys.${role}`,
- color: `sys.on-${role}`
+ color: `sys.on-${role}`,
});
containerStyles[`${role}-container`] = DesignToken.mixin({
bgColor: `sys.${role}-container`,
- color: `sys.on-${role}-container`
+ color: `sys.on-${role}-container`,
});
}
@@ -335,19 +353,19 @@ export class Palette {
if (role.includes("container")) {
containerStyles[`${role}`] = DesignToken.mixin({
bgColor: `sys.${role}`,
- color: "sys.on-surface"
+ color: "sys.on-surface",
});
continue;
}
containerStyles[`${role}`] = DesignToken.mixin({
bgColor: `sys.${role}`,
- color: "sys.on-surface"
+ color: "sys.on-surface",
});
containerStyles[`on-${role}`] = DesignToken.mixin({
bgColor: `sys.on-${role}`,
- color: "sys.inverse-on-surface"
+ color: "sys.inverse-on-surface",
});
}
}
@@ -356,30 +374,30 @@ export class Palette {
return Object.keys(tones).reduce(
(ret, tone) =>
Object.assign(ret, {
- [tone]: rgbFromArgb(t.tone(parseInt(tone)))
+ [tone]: rgbFromArgb(t.tone(parseInt(tone))),
}),
- {}
+ {},
) as any;
};
const color = DesignToken.color({
...mapValues(this.seeds as { [K in keyof Colors]: TonalPalette }, (tp) =>
- toTonalPalette(tp)
+ toTonalPalette(tp),
),
white: [255, 255, 255],
black: [0, 0, 0],
- sys: sysColors as unknown as ColorPalettes
+ sys: sysColors as unknown as ColorPalettes,
});
const containerStyle = DesignToken.customMixin("containerStyle", {
sys: containerStyles as ContainerStyles<
Omit
- >
+ >,
});
return {
color,
- containerStyle
+ containerStyle,
};
}
}
diff --git a/nodepkg/vueuikit/src/theming/token/DesignToken.ts b/nodepkg/vueuikit/src/theming/token/DesignToken.ts
index eb640ddf..de67940d 100644
--- a/nodepkg/vueuikit/src/theming/token/DesignToken.ts
+++ b/nodepkg/vueuikit/src/theming/token/DesignToken.ts
@@ -35,7 +35,7 @@ export type MustDefined = T extends Primitive ? T : never;
export type DesignTokenTransform = (
i: I,
- cssVar: (token: string) => string
+ cssVar: (token: string) => string,
) => O | { default: O; [V: string]: O };
export interface DesignTokenOption<
@@ -53,9 +53,9 @@ export interface DesignTokenOption<
__Tokens: FlattenTokenNames;
__CSSTokens: {
[P in CSSPropNames]:
- | FlattenTokenNames
- | Globals
- | MustDefined;
+ | FlattenTokenNames
+ | Globals
+ | MustDefined;
};
__ValueType: ValueType;
}
@@ -88,13 +88,13 @@ export class DesignToken {
{
value,
on,
- transform
+ transform,
}: {
value: Tokens;
on: Array;
transform?: DesignTokenTransform;
fallback?: Fallback;
- }
+ },
): DesignTokenOption<
Tokens,
CSSPropNames,
@@ -110,12 +110,12 @@ export class DesignToken {
__Tokens: undefined as any,
__ValueType: undefined as any,
- __CSSTokens: undefined as any
+ __CSSTokens: undefined as any,
};
}
static color>(
- value: T
+ value: T,
) {
return DesignToken.create(DesignTokenType.var, {
value,
@@ -127,23 +127,23 @@ export class DesignToken {
CSSAllProperty.accentColor,
CSSAllProperty.fill,
- CSSAllProperty.stroke
+ CSSAllProperty.stroke,
),
transform: (
rgb: [number, number, number] | string,
- cssVar: (token: string) => string
+ cssVar: (token: string) => string,
) => {
return isString(rgb)
? {
- default: `var(${cssVar(rgb)})`,
- rgb: `var(${cssVar(`${rgb}/rgb`)})`
- }
+ default: `var(${cssVar(rgb)})`,
+ rgb: `var(${cssVar(`${rgb}/rgb`)})`,
+ }
: {
- default: `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`,
- rgb: `${rgb[0]} ${rgb[1]} ${rgb[2]}`
- };
+ default: `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`,
+ rgb: `${rgb[0]} ${rgb[1]} ${rgb[2]}`,
+ };
},
- fallback: "" as Color
+ fallback: "" as Color,
});
}
@@ -178,8 +178,8 @@ export class DesignToken {
CSSAllProperty.pb,
CSSAllProperty.pl,
CSSAllProperty.px,
- CSSAllProperty.py
- )
+ CSSAllProperty.py,
+ ),
});
}
@@ -193,9 +193,9 @@ export class DesignToken {
CSSAllProperty.h,
CSSAllProperty.minH,
CSSAllProperty.maxH,
- CSSAllProperty.boxSize
+ CSSAllProperty.boxSize,
),
- fallback: 0 as number
+ fallback: 0 as number,
});
}
@@ -203,7 +203,7 @@ export class DesignToken {
return DesignToken.create(DesignTokenType.var, {
value,
on: expandAliases(CSSAllProperty.fontSize),
- fallback: 0 as number
+ fallback: 0 as number,
});
}
@@ -211,7 +211,7 @@ export class DesignToken {
return DesignToken.create(DesignTokenType.var, {
value,
on: expandAliases(CSSAllProperty.lineHeight),
- fallback: 0 as number
+ fallback: 0 as number,
});
}
@@ -233,17 +233,17 @@ export class DesignToken {
CSSProperty.borderTopRightRadius,
CSSProperty.borderBottomRightRadius,
CSSProperty.borderTopLeftRadius,
- CSSProperty.borderBottomLeftRadius
- )
+ CSSProperty.borderBottomLeftRadius,
+ ),
});
}
static transitionTimingFunction>(
- value: T
+ value: T,
) {
return DesignToken.create(DesignTokenType.var, {
value,
- on: expandAliases(CSSAllProperty.transitionTimingFunction)
+ on: expandAliases(CSSAllProperty.transitionTimingFunction),
});
}
@@ -251,35 +251,35 @@ export class DesignToken {
return DesignToken.create(DesignTokenType.var, {
value,
on: expandAliases(CSSAllProperty.transitionDuration),
- transform: (v) => `${v}ms`
+ transform: (v) => `${v}ms`,
});
}
static font>(value: T) {
return DesignToken.create(DesignTokenType.var, {
value,
- on: expandAliases(CSSAllProperty.font)
+ on: expandAliases(CSSAllProperty.font),
});
}
static fontWeight>(value: T) {
return DesignToken.create(DesignTokenType.var, {
value,
- on: expandAliases(CSSAllProperty.fontWeight)
+ on: expandAliases(CSSAllProperty.fontWeight),
});
}
static letterSpacing>(value: T) {
return DesignToken.create(DesignTokenType.var, {
value,
- on: expandAliases(CSSAllProperty.letterSpacing)
+ on: expandAliases(CSSAllProperty.letterSpacing),
});
}
static shadow>(value: T) {
return DesignToken.create(DesignTokenType.var, {
value,
- on: expandAliases(CSSAllProperty.shadow)
+ on: expandAliases(CSSAllProperty.shadow),
});
}
@@ -289,7 +289,7 @@ export class DesignToken {
>(customProp: CustomProp, value: T) {
return DesignToken.create(DesignTokenType.mixin, {
value: value,
- on: [customProp]
+ on: [customProp],
});
}
diff --git a/package.json b/package.json
index 7fcd76c4..5aaf902d 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,8 @@
"vue": "^3.4.35"
},
"resolutions": {
+ "@codemirror/state": "^6.4.1",
+ "@codemirror/view": "^6.29.1",
"vite": "5.3.5",
"vue": "3.4.35"
}
diff --git a/webapp/openapi-playground/common/manifest/index.ts b/webapp/openapi-playground/common/manifest/index.ts
index e346cb01..a2db405e 100644
--- a/webapp/openapi-playground/common/manifest/index.ts
+++ b/webapp/openapi-playground/common/manifest/index.ts
@@ -7,9 +7,9 @@ export interface Index {
export const ManifestProvider = createProvider(
() => ({
- name: "undefined",
+ name: "undefined"
}),
{
- name: "Manifest",
- },
+ name: "Manifest"
+ }
);
diff --git a/webapp/openapi-playground/mod/openapi/RequestBuilder.tsx b/webapp/openapi-playground/mod/openapi/RequestBuilder.tsx
index 597d6dc3..ba526b10 100644
--- a/webapp/openapi-playground/mod/openapi/RequestBuilder.tsx
+++ b/webapp/openapi-playground/mod/openapi/RequestBuilder.tsx
@@ -18,7 +18,7 @@ import { FilledButton, Icon } from "@innoai-tech/vuematerial";
import { Box } from "@innoai-tech/vueuikit";
import { Description, Line, SchemaView } from "./SchemaView.tsx";
import { OpenAPIProvider } from "./OpenAPIProvider.tsx";
-import { JSONCueEditorInput } from "./components/JSONCueEditor.tsx";
+import { JSONEditorInput } from "./components/JSONEditor.tsx";
import { onUnmounted } from "vue";
import { ResponsePreview } from "./ResponsePreview.tsx";
import { HttpRequest } from "./HTTPViews.tsx";
@@ -45,7 +45,7 @@ export const RequestBuilder = component$({
}
if (["object", "array"].includes(p.schema.type ?? "")) {
- propSchemas[p.name] = x.use(f.inputBy(JSONCueEditorInput));
+ propSchemas[p.name] = x.use(f.inputBy(JSONEditorInput));
} else {
propSchemas[p.name] = x;
}
@@ -64,7 +64,7 @@ export const RequestBuilder = component$({
);
if (contentType.includes("json")) {
- propSchemas["body"] = x.use(f.inputBy(JSONCueEditorInput));
+ propSchemas["body"] = x.use(f.inputBy(JSONEditorInput));
} else if (contentType.includes("octet-stream")) {
propSchemas["body"] = x.use(f.inputBy(FileSelectInput));
} else {
diff --git a/webapp/openapi-playground/mod/openapi/SchemaView.tsx b/webapp/openapi-playground/mod/openapi/SchemaView.tsx
index 9ebf0b0a..9aaa10f6 100644
--- a/webapp/openapi-playground/mod/openapi/SchemaView.tsx
+++ b/webapp/openapi-playground/mod/openapi/SchemaView.tsx
@@ -2,8 +2,9 @@ import {
type AnyType,
component,
component$,
- createProvider, RouterLink,
+ createProvider,
t,
+ RouterLink,
type VNodeChild
} from "@innoai-tech/vuekit";
import { isUndefined } from "@innoai-tech/lodash";
@@ -19,11 +20,11 @@ export const Token = styled("div")({
lineHeight: 14
});
-export const PropName = styled("div", {
- deprecated: t.boolean().optional(),
- optional: t.boolean().optional(),
- nullable: t.boolean().optional()
-})({
+export const PropName = styled<{
+ deprecated?: boolean,
+ optional?: boolean,
+ nullable?: boolean,
+}, "div">("div")({
display: "inline-table",
textStyle: "sys.label-small",
fontWeight: "bold",
@@ -46,37 +47,30 @@ export const PropName = styled("div", {
}
});
-export const Line = styled(
- "div",
- {
- spacing: t.number().optional().default(0),
- $default: t.custom().optional()
- },
- (props, { slots }) => {
- const i = IntentContextProvider.use();
-
- return (Root) => (
-
- {slots.default?.()}
-
- );
- }
-)({
+export const Line = styled<{
+ spacing?: number,
+ $default?: VNodeChild
+}, "div">("div", (props, { slots }) => {
+ const i = IntentContextProvider.use();
+
+ return (Root) => (
+
+ {slots.default?.()}
+
+ );
+})({
position: "relative",
display: "block"
});
-export const Description = styled(
- "div",
- {
- schema: t.custom()
- },
- (props, {}) => {
+export const Description = styled<{
+ schema: AnyType
+}, "div">("div", (props, {}) => {
return (Root) => {
const description = props.schema.getMeta("description") ?? "";
@@ -107,6 +101,7 @@ export const Description = styled(
whiteSpace: "nowrap",
opacity: 0.7
},
+
"& code": {
wordBreak: "keep-all",
whiteSpace: "nowrap"
@@ -163,11 +158,9 @@ const IntentContextProvider = createProvider(
);
-export const Indent = component(
- {
- $default: t.custom().optional()
- },
- ({}, { slots }) => {
+export const Indent = component<{
+ $default?: VNodeChild,
+}>(({}, { slots }) => {
const i = IntentContextProvider.use();
return () => (
@@ -210,38 +203,35 @@ class Node {
const SchemaRenderProvider = createProvider(() => new Node(""));
-const SchemaViewLink = component$(
- {
- schema: t.custom()
- }, (props) => {
- const node = SchemaRenderProvider.use();
+const SchemaViewLink = component$<{
+ schema: AnyType,
+}>((props) => {
+ const node = SchemaRenderProvider.use();
- return () => {
- const id = props.schema.getSchema("$ref");
+ return () => {
+ const id = props.schema.getSchema("$ref");
- let schema = props.schema;
+ let schema = props.schema;
- while (schema.getSchema("$ref")) {
- schema = schema.unwrap;
- }
+ while (schema.getSchema("$ref")) {
+ schema = schema.unwrap;
+ }
- const needID = schema.type == "intersection" || schema.type == "object" || schema.type == "union" || schema.type == "record" || schema.type == "array" || schema.type == "union";
+ const needID = schema.type == "intersection" || schema.type == "object" || schema.type == "union" || schema.type == "record" || schema.type == "array" || schema.type == "union";
- return (
-
- {needID && {id} }
- {node.safe(id) && }
-
- );
- };
- });
+ return (
+
+ {needID && {id} }
+ {node.safe(id) && }
+
+ );
+ };
+});
-export const SchemaView = component$(
- {
- schema: t.custom()
- },
- (props) => {
+export const SchemaView = component$<{
+ schema: AnyType,
+}>((props) => {
const schema = props.schema;
if (schema.getSchema("$ref")) {
@@ -427,7 +417,6 @@ function hasValidate(schema: AnyType) {
] as Array).some((key) => schema.getSchema(key));
}
-
export interface ValidatedSchemaProps {
maximum?: number;
exclusiveMaximum?: boolean;
diff --git a/webapp/openapi-playground/mod/openapi/components/JSONCueEditor.tsx b/webapp/openapi-playground/mod/openapi/components/JSONCueEditor.tsx
deleted file mode 100644
index 65b59dac..00000000
--- a/webapp/openapi-playground/mod/openapi/components/JSONCueEditor.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-import {
- type AnyType,
- component$,
- rx,
- subscribeUntilUnmount,
- t,
- tapEffect
-} from "@innoai-tech/vuekit";
-import type { Field } from "@innoai-tech/vueformdata";
-import {
- startCompletion,
- EditorView,
- indentWithTab,
- forceLinting,
- diagnosticCount,
- autocompletion
-} from "@innoai-tech/codemirror";
-import {
- useExtension,
- EditorContainer,
- createEditorContext,
- EditorContextProvider
-} from "@innoai-tech/codemirror/view";
-import { jsoncue, jsoncueParserOrValidateLinter } from "@innoai-tech/jsoncue/codemirror";
-import { Box } from "@innoai-tech/vueuikit";
-import { JSONCue } from "@innoai-tech/jsoncue";
-import { keymap } from "@innoai-tech/codemirror";
-import type { Extension } from "@innoai-tech/codemirror";
-import { linter } from "@innoai-tech/codemirror";
-import { jsoncueCompletions } from "@innoai-tech/jsoncue/codemirror";
-import { isUndefined } from "@innoai-tech/lodash";
-
-export const JSONCueEditorInput = component$(
- {
- field$: t.custom>(),
- readOnly: t.boolean().optional()
- },
- (props, {}) => {
- const editorContext = createEditorContext(!isUndefined(props.field$.input) ? JSONCue.stringify(props.field$.input) : "");
-
- return () => (
-
-
-
-
-
- );
- }
-);
-
-const Editor = component$(
- {
- field$: t.custom>(),
- schema: t.custom()
- },
- (props) => {
- const ctx = EditorContextProvider.use();
-
- rx(
- props.schema$,
- tapEffect((schema) => ctx.use(() => createJSONCueExt(schema))),
- subscribeUntilUnmount()
- );
-
- useExtension(
- () => EditorView.updateListener.of((v) => {
- if (v.focusChanged) {
- forceLinting(v.view);
-
- props.field$.next((s) => {
- s.error = diagnosticCount(v.state) == 0 ? null : ["配置有误"];
- });
-
- return;
- }
-
- if (v.docChanged) {
- forceLinting(v.view);
-
- if (diagnosticCount(v.state) > 0) {
- props.field$.next((s) => {
- s.error = ["配置有误"];
- });
- } else {
- try {
- const text = v.state.doc.sliceString(0);
- const value = JSONCue.parse(text);
-
- props.field$.update(value);
- props.field$.next((s) => {
- s.error = null;
- });
-
- } catch (err) {
- props.field$.next((s) => {
- s.error = ["格式错误"];
- });
- }
- }
- }
- })
- );
-
- return () => ;
- }
-);
-
-export function createJSONCueExt(tpe: AnyType): Extension[] {
- return [
- jsoncue(),
- autocompletion({
- override: [
- jsoncueCompletions(tpe)
- ]
- }),
- keymap.of([
- {
- key: "Shift-Space",
- shift: startCompletion
- }
- ]),
- linter(jsoncueParserOrValidateLinter(tpe)),
- keymap.of([indentWithTab])
- ];
-}
diff --git a/webapp/openapi-playground/mod/openapi/components/JSONEditor.tsx b/webapp/openapi-playground/mod/openapi/components/JSONEditor.tsx
new file mode 100644
index 00000000..a344d04c
--- /dev/null
+++ b/webapp/openapi-playground/mod/openapi/components/JSONEditor.tsx
@@ -0,0 +1,45 @@
+import {
+ component$, rx, subscribeUntilUnmount
+} from "@innoai-tech/vuekit";
+import type { Field } from "@innoai-tech/vueformdata";
+import { styled } from "@innoai-tech/vueuikit";
+import { isUndefined } from "@innoai-tech/lodash";
+import { JSONEditor, JSONEditorView, JSONEditorProvider } from "@innoai-tech/jsoneditor";
+import { skip, tap } from "rxjs";
+
+export const JSONEditorInput = component$<{
+ field$: Field
+ readOnly?: boolean,
+}>((props, {}) => {
+ const editor$ = JSONEditor.of(
+ props.field$.typedef,
+ !isUndefined(props.field$.input) ? props.field$.input : undefined
+ );
+
+ rx(
+ editor$,
+ skip(1),
+ tap((values) => {
+ props.field$.update(values);
+ }),
+ subscribeUntilUnmount()
+ );
+
+ return () => (
+
+
+
+
+
+ );
+ }
+);
+
+const JSONInputContainer = styled("div")({
+ position: "relative",
+ width: "100%",
+ minHeight: "40em",
+ py: 8,
+ px: 12,
+ overflow: "hidden"
+});
\ No newline at end of file