aidos-ui 4.0.1 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +2 -1
- package/dist/BaseButton.js +1 -1
- package/dist/BaseButton.js.map +1 -1
- package/dist/Card.js +1 -1
- package/dist/Card.js.map +1 -1
- package/dist/Dialog.js +7 -1
- package/dist/Dialog.js.map +1 -1
- package/dist/Dropdown.d.ts +1 -1
- package/dist/Dropdown.js +5 -4
- package/dist/Dropdown.js.map +1 -1
- package/dist/JSSComment.d.ts +10 -0
- package/dist/JSSComment.js +52 -0
- package/dist/JSSComment.js.map +1 -0
- package/dist/Popover.js +3 -1
- package/dist/Popover.js.map +1 -1
- package/dist/Storage.js +1 -3
- package/dist/Storage.js.map +1 -1
- package/dist/Tooltip.js +3 -2
- package/dist/Tooltip.js.map +1 -1
- package/dist/jss.d.ts +6 -1
- package/dist/jss.js +66 -11
- package/dist/jss.js.map +1 -1
- package/dist/jsx-dev-runtime.d.ts +2 -0
- package/dist/jsx-dev-runtime.js +48 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime.d.ts +1 -0
- package/dist/jsx-runtime.js +3 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/vite-plugin-jss-debug.d.ts +33 -0
- package/dist/vite-plugin-jss-debug.js +74 -0
- package/dist/vite-plugin-jss-debug.js.map +1 -0
- package/jss-debug-loader.mjs +44 -0
- package/next.config.js +12 -1
- package/package.json +19 -1
- package/src/BaseButton.tsx +1 -0
- package/src/Card.tsx +1 -1
- package/src/Dialog.tsx +7 -1
- package/src/Dropdown.tsx +5 -4
- package/src/JSSComment.tsx +56 -0
- package/src/Popover.tsx +3 -1
- package/src/Storage.tsx +1 -3
- package/src/Tooltip.tsx +3 -2
- package/src/jss.tsx +79 -14
- package/src/jsx-dev-runtime.ts +74 -0
- package/src/jsx-runtime.ts +2 -0
- package/src/vite-plugin-jss-debug.ts +75 -0
- package/tsconfig.json +6 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
/**
|
|
3
|
+
* Vite plugin that enables JSS debug tooling:
|
|
4
|
+
*
|
|
5
|
+
* 1. Injects component names into toClassnames() calls so they appear as CSS classes.
|
|
6
|
+
* toClassnames(jss) → toClassnames(jss, "Card")
|
|
7
|
+
*
|
|
8
|
+
* 2. Configures the custom JSX dev runtime that injects <!-- ComponentName -->
|
|
9
|
+
* HTML comments at component boundaries (same approach as the Next.js setup).
|
|
10
|
+
*
|
|
11
|
+
* Usage in vite.config.ts:
|
|
12
|
+
* import jssDebug from "aidos-ui/vite-plugin-jss-debug";
|
|
13
|
+
* export default defineConfig({ plugins: [jssDebug()] });
|
|
14
|
+
*/
|
|
15
|
+
export default function jssDebug() {
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
name: "jss-debug-jsx",
|
|
19
|
+
config() {
|
|
20
|
+
return {
|
|
21
|
+
esbuild: {
|
|
22
|
+
jsx: "automatic",
|
|
23
|
+
jsxDev: true,
|
|
24
|
+
jsxImportSource: "aidos-ui",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "jss-debug-classnames",
|
|
31
|
+
enforce: "pre",
|
|
32
|
+
transform(code, id) {
|
|
33
|
+
if (id.includes("node_modules"))
|
|
34
|
+
return;
|
|
35
|
+
if (!id.match(/\.[jt]sx?$/))
|
|
36
|
+
return;
|
|
37
|
+
if (!code.includes("toClassnames("))
|
|
38
|
+
return;
|
|
39
|
+
const filename = path.basename(id, path.extname(id));
|
|
40
|
+
const name = JSON.stringify(filename);
|
|
41
|
+
const needle = "toClassnames(";
|
|
42
|
+
let result = "";
|
|
43
|
+
let i = 0;
|
|
44
|
+
while (i < code.length) {
|
|
45
|
+
const idx = code.indexOf(needle, i);
|
|
46
|
+
if (idx === -1) {
|
|
47
|
+
result += code.slice(i);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
// Copy everything up to and including "toClassnames("
|
|
51
|
+
result += code.slice(i, idx + needle.length);
|
|
52
|
+
i = idx + needle.length;
|
|
53
|
+
// Track parens to find the matching closing )
|
|
54
|
+
let depth = 1;
|
|
55
|
+
let j = i;
|
|
56
|
+
while (j < code.length && depth > 0) {
|
|
57
|
+
const ch = code[j];
|
|
58
|
+
if (ch === "(")
|
|
59
|
+
depth++;
|
|
60
|
+
else if (ch === ")")
|
|
61
|
+
depth--;
|
|
62
|
+
if (depth > 0)
|
|
63
|
+
j++;
|
|
64
|
+
}
|
|
65
|
+
// Copy the original arguments, then inject the component name
|
|
66
|
+
result += code.slice(i, j) + ", " + name;
|
|
67
|
+
i = j;
|
|
68
|
+
}
|
|
69
|
+
return { code: result, map: null };
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=vite-plugin-jss-debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-plugin-jss-debug.js","sourceRoot":"","sources":["../src/vite-plugin-jss-debug.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ;IAC9B,OAAO;QACL;YACE,IAAI,EAAE,eAAe;YACrB,MAAM;gBACJ,OAAO;oBACL,OAAO,EAAE;wBACP,GAAG,EAAE,WAAW;wBAChB,MAAM,EAAE,IAAI;wBACZ,eAAe,EAAE,UAAU;qBAC5B;iBACF,CAAC;YACJ,CAAC;SACF;QACD;YACE,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,KAAc;YAEvB,SAAS,CAAC,IAAY,EAAE,EAAU;gBAChC,IAAI,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;oBAAE,OAAO;gBACxC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC;oBAAE,OAAO;gBACpC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;oBAAE,OAAO;gBAE5C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;gBACrD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;gBACtC,MAAM,MAAM,GAAG,eAAe,CAAC;gBAC/B,IAAI,MAAM,GAAG,EAAE,CAAC;gBAChB,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;oBACvB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;oBACpC,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;wBACf,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;wBACxB,MAAM;oBACR,CAAC;oBAED,sDAAsD;oBACtD,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;oBAC7C,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;oBAExB,8CAA8C;oBAC9C,IAAI,KAAK,GAAG,CAAC,CAAC;oBACd,IAAI,CAAC,GAAG,CAAC,CAAC;oBACV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;wBACpC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;wBACnB,IAAI,EAAE,KAAK,GAAG;4BAAE,KAAK,EAAE,CAAC;6BACnB,IAAI,EAAE,KAAK,GAAG;4BAAE,KAAK,EAAE,CAAC;wBAC7B,IAAI,KAAK,GAAG,CAAC;4BAAE,CAAC,EAAE,CAAC;oBACrB,CAAC;oBAED,8DAA8D;oBAC9D,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;oBACzC,CAAC,GAAG,CAAC,CAAC;gBACR,CAAC;gBAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;YACrC,CAAC;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Webpack/Turbopack loader that injects component names into toClassnames() calls.
|
|
5
|
+
*
|
|
6
|
+
* Rewrites:
|
|
7
|
+
* toClassnames(jss) → toClassnames(jss, "Card")
|
|
8
|
+
*
|
|
9
|
+
* The component name is inferred from the filename (Card.tsx → "Card").
|
|
10
|
+
*/
|
|
11
|
+
export default function jssDebugLoader(source) {
|
|
12
|
+
if (!source.includes("toClassnames(")) return source;
|
|
13
|
+
|
|
14
|
+
const filename = path.basename(this.resourcePath, path.extname(this.resourcePath));
|
|
15
|
+
const name = JSON.stringify(filename);
|
|
16
|
+
const needle = "toClassnames(";
|
|
17
|
+
let result = "";
|
|
18
|
+
let i = 0;
|
|
19
|
+
|
|
20
|
+
while (i < source.length) {
|
|
21
|
+
const idx = source.indexOf(needle, i);
|
|
22
|
+
if (idx === -1) {
|
|
23
|
+
result += source.slice(i);
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
result += source.slice(i, idx + needle.length);
|
|
28
|
+
i = idx + needle.length;
|
|
29
|
+
|
|
30
|
+
let depth = 1;
|
|
31
|
+
let j = i;
|
|
32
|
+
while (j < source.length && depth > 0) {
|
|
33
|
+
const ch = source[j];
|
|
34
|
+
if (ch === "(") depth++;
|
|
35
|
+
else if (ch === ")") depth--;
|
|
36
|
+
if (depth > 0) j++;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
result += source.slice(i, j) + ", " + name;
|
|
40
|
+
i = j;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return result;
|
|
44
|
+
}
|
package/next.config.js
CHANGED
|
@@ -15,7 +15,18 @@ const withMDX = mdx({
|
|
|
15
15
|
const nextConfig = {
|
|
16
16
|
pageExtensions: ["ts", "tsx", "js", "jsx", "md", "mdx"],
|
|
17
17
|
reactStrictMode: true,
|
|
18
|
-
turbopack: {
|
|
18
|
+
turbopack: {
|
|
19
|
+
rules: {
|
|
20
|
+
"*.tsx": {
|
|
21
|
+
loaders: [{ loader: "./jss-debug-loader.mjs" }],
|
|
22
|
+
as: "*.tsx",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
resolveAlias: {
|
|
26
|
+
"aidos-ui-jsx/jsx-dev-runtime": "./src/jsx-dev-runtime.ts",
|
|
27
|
+
"aidos-ui-jsx/jsx-runtime": "./src/jsx-runtime.ts",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
19
30
|
};
|
|
20
31
|
|
|
21
32
|
// Merge MDX config with Next.js config
|
package/package.json
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aidos-ui",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"description": "A simple UI framework",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./jsx-runtime": {
|
|
13
|
+
"types": "./dist/jsx-runtime.d.ts",
|
|
14
|
+
"default": "./dist/jsx-runtime.js"
|
|
15
|
+
},
|
|
16
|
+
"./jsx-dev-runtime": {
|
|
17
|
+
"types": "./dist/jsx-dev-runtime.d.ts",
|
|
18
|
+
"default": "./dist/jsx-dev-runtime.js"
|
|
19
|
+
},
|
|
20
|
+
"./vite-plugin-jss-debug": {
|
|
21
|
+
"types": "./dist/vite-plugin-jss-debug.d.ts",
|
|
22
|
+
"default": "./dist/vite-plugin-jss-debug.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
7
25
|
"scripts": {
|
|
8
26
|
"embeddings": "node ./scripts/embeddings.js",
|
|
9
27
|
"dev": "next dev",
|
package/src/BaseButton.tsx
CHANGED
package/src/Card.tsx
CHANGED
package/src/Dialog.tsx
CHANGED
|
@@ -11,8 +11,14 @@ import { toClassnames } from "./jss";
|
|
|
11
11
|
const jsss = {
|
|
12
12
|
dialog: {
|
|
13
13
|
border: "1px solid var(--divider)",
|
|
14
|
-
borderRadius: "var(--border-radius-
|
|
14
|
+
borderRadius: "var(--border-radius-l)",
|
|
15
15
|
padding: 0,
|
|
16
|
+
boxShadow: "var(--shadow-lg)",
|
|
17
|
+
animation: "dialogFadeIn 0.2s ease-out",
|
|
18
|
+
"::backdrop": {
|
|
19
|
+
backgroundColor: "rgba(0, 0, 0, 0.4)",
|
|
20
|
+
animation: "backdropFadeIn 0.2s ease-out",
|
|
21
|
+
},
|
|
16
22
|
},
|
|
17
23
|
root: {
|
|
18
24
|
display: "grid",
|
package/src/Dropdown.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from "react";
|
|
1
|
+
import React, { useEffect, useId, useRef, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { BaseView } from "./BaseView";
|
|
4
|
-
import { guid } from "./guid";
|
|
5
4
|
import { Button } from "./Button";
|
|
6
5
|
import { Column } from "./Column";
|
|
7
6
|
import { useNavigation } from "./useNavigation";
|
|
@@ -25,12 +24,12 @@ const jsss: { [key: string]: JSS } = {
|
|
|
25
24
|
left: 0,
|
|
26
25
|
right: 0,
|
|
27
26
|
borderRadius: "var(--border-radius-m)",
|
|
28
|
-
boxShadow: "
|
|
27
|
+
boxShadow: "var(--shadow-sm)",
|
|
29
28
|
zIndex: 1,
|
|
30
29
|
},
|
|
31
30
|
};
|
|
32
31
|
export function Dropdown<T extends string | number>({
|
|
33
|
-
id
|
|
32
|
+
id: propId,
|
|
34
33
|
label,
|
|
35
34
|
options,
|
|
36
35
|
selection,
|
|
@@ -54,6 +53,8 @@ export function Dropdown<T extends string | number>({
|
|
|
54
53
|
gap?: Gap;
|
|
55
54
|
size?: Size;
|
|
56
55
|
}) {
|
|
56
|
+
const hookId = useId();
|
|
57
|
+
const id = propId ?? hookId;
|
|
57
58
|
const triggerRef = useRef(null);
|
|
58
59
|
const [expanded, setExpanded] = useState(false);
|
|
59
60
|
const dropdownRef = useNavigation({ autofocus: true });
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Injects a real HTML comment node (<!-- ComponentName -->) into the DOM.
|
|
5
|
+
*
|
|
6
|
+
* Renders nothing (null). Uses the React fiber tree to locate the sibling
|
|
7
|
+
* component's first DOM node, then inserts a comment before it.
|
|
8
|
+
* Client-only — no SSR output, no hydration mismatch.
|
|
9
|
+
*/
|
|
10
|
+
export function JSSComment({ name }: { name: string }) {
|
|
11
|
+
const fiberRef = useRef<any>(null);
|
|
12
|
+
|
|
13
|
+
// Capture our fiber during render
|
|
14
|
+
try {
|
|
15
|
+
const internals = (React as any)
|
|
16
|
+
.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
17
|
+
fiberRef.current = internals?.ReactCurrentOwner?.current;
|
|
18
|
+
} catch {}
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const fiber = fiberRef.current;
|
|
22
|
+
if (!fiber) return;
|
|
23
|
+
|
|
24
|
+
// Our sibling in the Fragment is the actual component
|
|
25
|
+
const sibling = fiber.sibling;
|
|
26
|
+
if (!sibling) return;
|
|
27
|
+
|
|
28
|
+
// Walk down the sibling's fiber tree to find its first DOM node
|
|
29
|
+
const hostNode = findFirstDOMNode(sibling);
|
|
30
|
+
if (!hostNode?.parentNode) return;
|
|
31
|
+
|
|
32
|
+
const comment = document.createComment(` ${name} `);
|
|
33
|
+
hostNode.parentNode.insertBefore(comment, hostNode);
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
comment.remove();
|
|
37
|
+
};
|
|
38
|
+
}, [name]);
|
|
39
|
+
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function findFirstDOMNode(fiber: any): Node | null {
|
|
44
|
+
let current = fiber;
|
|
45
|
+
while (current) {
|
|
46
|
+
if (current.stateNode instanceof Node) {
|
|
47
|
+
return current.stateNode;
|
|
48
|
+
}
|
|
49
|
+
if (current.child) {
|
|
50
|
+
current = current.child;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
package/src/Popover.tsx
CHANGED
|
@@ -10,9 +10,11 @@ const jsss: { [key: string]: JSS } = {
|
|
|
10
10
|
padding: 0,
|
|
11
11
|
zIndex: 1,
|
|
12
12
|
backgroundColor: "var(--primary-background)",
|
|
13
|
-
borderRadius: "var(--border-radius-
|
|
13
|
+
borderRadius: "var(--border-radius-l)",
|
|
14
14
|
border: "1px solid var(--divider)",
|
|
15
15
|
overflow: "hidden",
|
|
16
|
+
boxShadow: "var(--shadow-lg)",
|
|
17
|
+
animation: "popoverFadeIn 0.15s ease-out",
|
|
16
18
|
},
|
|
17
19
|
};
|
|
18
20
|
|
package/src/Storage.tsx
CHANGED
|
@@ -99,9 +99,7 @@ export function createStorage<State>({
|
|
|
99
99
|
useEffect(() => {
|
|
100
100
|
return subscribe((newState) => {
|
|
101
101
|
const newLocalState = selector(newState);
|
|
102
|
-
|
|
103
|
-
setLocalState(selector(newState));
|
|
104
|
-
}
|
|
102
|
+
setLocalState((prev) => (newLocalState != prev ? newLocalState : prev));
|
|
105
103
|
});
|
|
106
104
|
}, []);
|
|
107
105
|
|
package/src/Tooltip.tsx
CHANGED
|
@@ -10,11 +10,12 @@ const jsss: { [key: string]: JSS } = {
|
|
|
10
10
|
top: "-100%",
|
|
11
11
|
transform: "translateY(50%)",
|
|
12
12
|
backgroundColor: cssVar("--primary-background"),
|
|
13
|
-
padding: cssVar("--spacing-s")
|
|
13
|
+
padding: `${cssVar("--spacing-s")} ${cssVar("--spacing-m")}`,
|
|
14
14
|
borderRadius: cssVar("--border-radius-m"),
|
|
15
15
|
border: `1px solid ${cssVar("--divider")}`,
|
|
16
|
-
boxShadow:
|
|
16
|
+
boxShadow: cssVar("--shadow-md"),
|
|
17
17
|
overflow: "hidden",
|
|
18
|
+
animation: "popoverFadeIn 0.15s ease-out",
|
|
18
19
|
},
|
|
19
20
|
};
|
|
20
21
|
|
package/src/jss.tsx
CHANGED
|
@@ -75,7 +75,7 @@ const aliases: {
|
|
|
75
75
|
},
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
const rawHashMap = new Map<string, string>();
|
|
78
|
+
const rawHashMap = new Map<string, { selector: string; key: string }>();
|
|
79
79
|
|
|
80
80
|
function createStyleNode(content: string) {
|
|
81
81
|
if (isServer()) {
|
|
@@ -111,6 +111,9 @@ const pixelStyles = new Set([
|
|
|
111
111
|
"left",
|
|
112
112
|
"right",
|
|
113
113
|
"outline-offset",
|
|
114
|
+
"gap",
|
|
115
|
+
"row-gap",
|
|
116
|
+
"column-gap",
|
|
114
117
|
]);
|
|
115
118
|
|
|
116
119
|
const toPixelValue = (key: string, value): string => {
|
|
@@ -141,20 +144,25 @@ const flattenJSS = (jss: JSS): Styles => {
|
|
|
141
144
|
if (!jss) {
|
|
142
145
|
return {};
|
|
143
146
|
} else if (Array.isArray(jss)) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
147
|
+
const result: Styles = {};
|
|
148
|
+
for (const item of jss) {
|
|
149
|
+
Object.assign(result, flattenJSS(item));
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
148
152
|
} else {
|
|
149
153
|
return jss;
|
|
150
154
|
}
|
|
151
155
|
};
|
|
152
156
|
|
|
153
|
-
export const toClassnames = (jss: JSS): string => {
|
|
157
|
+
export const toClassnames = (jss: JSS, componentName?: string): string => {
|
|
154
158
|
jss = flattenJSS(jss);
|
|
155
159
|
|
|
156
160
|
const stylesStack = Object.entries(jss);
|
|
157
|
-
const classNames = [];
|
|
161
|
+
const classNames: string[] = [];
|
|
162
|
+
|
|
163
|
+
if (componentName) {
|
|
164
|
+
classNames.push(componentName);
|
|
165
|
+
}
|
|
158
166
|
|
|
159
167
|
while (stylesStack.length) {
|
|
160
168
|
const [key, value] = stylesStack.pop();
|
|
@@ -164,17 +172,22 @@ export const toClassnames = (jss: JSS): string => {
|
|
|
164
172
|
continue;
|
|
165
173
|
}
|
|
166
174
|
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
const inputKey = key + JSON.stringify(value);
|
|
176
|
+
const rawHash = hash(inputKey).toString(32);
|
|
177
|
+
const cached = rawHashMap.get(rawHash);
|
|
178
|
+
|
|
179
|
+
if (cached != null) {
|
|
180
|
+
if (cached.key !== inputKey) {
|
|
181
|
+
console.warn(
|
|
182
|
+
`[jss] Hash collision detected: "${inputKey}" collides with "${cached.key}"`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
classNames.push(cached.selector);
|
|
172
186
|
continue;
|
|
173
187
|
}
|
|
174
188
|
|
|
175
|
-
// const selector = numberToBase(rawHashMap.size);
|
|
176
189
|
const selector = `x${rawHash}`;
|
|
177
|
-
rawHashMap.set(rawHash, selector);
|
|
190
|
+
rawHashMap.set(rawHash, { selector, key: inputKey });
|
|
178
191
|
classNames.push(selector);
|
|
179
192
|
|
|
180
193
|
if (typeof value === "number" || typeof value === "string") {
|
|
@@ -405,6 +418,13 @@ export type Theme = {
|
|
|
405
418
|
["--border-radius-m"]: string;
|
|
406
419
|
["--border-radius-l"]: string;
|
|
407
420
|
["--nav-bar-height"]: string;
|
|
421
|
+
/* Shadows */
|
|
422
|
+
["--shadow-sm"]: string;
|
|
423
|
+
["--shadow-md"]: string;
|
|
424
|
+
["--shadow-lg"]: string;
|
|
425
|
+
/* Transitions */
|
|
426
|
+
["--transition-fast"]: string;
|
|
427
|
+
["--transition-normal"]: string;
|
|
408
428
|
};
|
|
409
429
|
|
|
410
430
|
export const lightTheme: Theme = {
|
|
@@ -454,6 +474,13 @@ export const lightTheme: Theme = {
|
|
|
454
474
|
["--border-radius-m"]: "4px",
|
|
455
475
|
["--border-radius-l"]: "8px",
|
|
456
476
|
["--nav-bar-height"]: "50px",
|
|
477
|
+
/* Shadows */
|
|
478
|
+
["--shadow-sm"]: "0 1px 2px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.1)",
|
|
479
|
+
["--shadow-md"]: "0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.05)",
|
|
480
|
+
["--shadow-lg"]: "0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1)",
|
|
481
|
+
/* Transitions */
|
|
482
|
+
["--transition-fast"]: "0.15s ease",
|
|
483
|
+
["--transition-normal"]: "0.2s ease",
|
|
457
484
|
};
|
|
458
485
|
|
|
459
486
|
export const darkTheme: Theme = {
|
|
@@ -503,6 +530,13 @@ export const darkTheme: Theme = {
|
|
|
503
530
|
["--border-radius-m"]: "4px",
|
|
504
531
|
["--border-radius-l"]: "8px",
|
|
505
532
|
["--nav-bar-height"]: "50px",
|
|
533
|
+
/* Shadows - stronger for dark mode to be visible */
|
|
534
|
+
["--shadow-sm"]: "0 1px 2px rgba(0, 0, 0, 0.2), 0 1px 3px rgba(0, 0, 0, 0.3)",
|
|
535
|
+
["--shadow-md"]: "0 4px 6px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.2)",
|
|
536
|
+
["--shadow-lg"]: "0 10px 25px rgba(0, 0, 0, 0.35), 0 4px 10px rgba(0, 0, 0, 0.25)",
|
|
537
|
+
/* Transitions */
|
|
538
|
+
["--transition-fast"]: "0.15s ease",
|
|
539
|
+
["--transition-normal"]: "0.2s ease",
|
|
506
540
|
};
|
|
507
541
|
|
|
508
542
|
export const baseStyles = `
|
|
@@ -527,6 +561,37 @@ body {
|
|
|
527
561
|
background-color: ${cssVar("--primary-background")};
|
|
528
562
|
}
|
|
529
563
|
|
|
564
|
+
@keyframes dialogFadeIn {
|
|
565
|
+
from {
|
|
566
|
+
opacity: 0;
|
|
567
|
+
transform: scale(0.95) translateY(-10px);
|
|
568
|
+
}
|
|
569
|
+
to {
|
|
570
|
+
opacity: 1;
|
|
571
|
+
transform: scale(1) translateY(0);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
@keyframes backdropFadeIn {
|
|
576
|
+
from {
|
|
577
|
+
opacity: 0;
|
|
578
|
+
}
|
|
579
|
+
to {
|
|
580
|
+
opacity: 1;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
@keyframes popoverFadeIn {
|
|
585
|
+
from {
|
|
586
|
+
opacity: 0;
|
|
587
|
+
transform: scale(0.95);
|
|
588
|
+
}
|
|
589
|
+
to {
|
|
590
|
+
opacity: 1;
|
|
591
|
+
transform: scale(1);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
530
595
|
`;
|
|
531
596
|
|
|
532
597
|
const backgroundStyles = {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Custom JSX dev runtime that injects <!-- ComponentName --> comments
|
|
2
|
+
// into the DOM at component boundaries.
|
|
3
|
+
//
|
|
4
|
+
// Usage: in tsconfig.json, set "jsxImportSource": "aidos-ui/src"
|
|
5
|
+
// (or wherever this file is importable from)
|
|
6
|
+
|
|
7
|
+
export { Fragment } from "react/jsx-dev-runtime";
|
|
8
|
+
import { jsxDEV as _jsxDEV, Fragment } from "react/jsx-dev-runtime";
|
|
9
|
+
import { JSSComment } from "./JSSComment";
|
|
10
|
+
|
|
11
|
+
const wrapCache = new WeakMap<Function, Function>();
|
|
12
|
+
|
|
13
|
+
function getWrapped(type: Function): Function {
|
|
14
|
+
let wrapped = wrapCache.get(type);
|
|
15
|
+
if (wrapped) return wrapped;
|
|
16
|
+
|
|
17
|
+
const name = (type as any).displayName || type.name;
|
|
18
|
+
|
|
19
|
+
wrapped = function JSSBoundary(props: any) {
|
|
20
|
+
return _jsxDEV(
|
|
21
|
+
Fragment,
|
|
22
|
+
{
|
|
23
|
+
children: [
|
|
24
|
+
_jsxDEV(JSSComment, { name }, undefined, false, undefined, undefined),
|
|
25
|
+
_jsxDEV(type as any, props, undefined, false, undefined, undefined),
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
undefined,
|
|
29
|
+
true,
|
|
30
|
+
undefined,
|
|
31
|
+
undefined
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
(wrapped as any).displayName = name;
|
|
36
|
+
wrapCache.set(type, wrapped);
|
|
37
|
+
|
|
38
|
+
return wrapped;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Skip our own wrapper components by name
|
|
42
|
+
const SKIP_NAMES = new Set(["JSSComment", "JSSBoundary"]);
|
|
43
|
+
|
|
44
|
+
function isUserComponent(type: any, source: any): boolean {
|
|
45
|
+
if (typeof type !== "function") return false;
|
|
46
|
+
const name = type.displayName || type.name;
|
|
47
|
+
if (!name || !/^[A-Z]/.test(name)) return false;
|
|
48
|
+
if (SKIP_NAMES.has(name)) return false;
|
|
49
|
+
// Use the source fileName from the JSX transform to skip node_modules
|
|
50
|
+
if (source?.fileName?.includes("node_modules")) return false;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function jsxDEV(
|
|
55
|
+
type: any,
|
|
56
|
+
props: any,
|
|
57
|
+
key: any,
|
|
58
|
+
isStaticChildren: boolean,
|
|
59
|
+
source: any,
|
|
60
|
+
self: any
|
|
61
|
+
) {
|
|
62
|
+
if (isUserComponent(type, source)) {
|
|
63
|
+
return _jsxDEV(
|
|
64
|
+
getWrapped(type) as any,
|
|
65
|
+
props,
|
|
66
|
+
key,
|
|
67
|
+
isStaticChildren,
|
|
68
|
+
source,
|
|
69
|
+
self
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return _jsxDEV(type, props, key, isStaticChildren, source, self);
|
|
74
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vite plugin that enables JSS debug tooling:
|
|
5
|
+
*
|
|
6
|
+
* 1. Injects component names into toClassnames() calls so they appear as CSS classes.
|
|
7
|
+
* toClassnames(jss) → toClassnames(jss, "Card")
|
|
8
|
+
*
|
|
9
|
+
* 2. Configures the custom JSX dev runtime that injects <!-- ComponentName -->
|
|
10
|
+
* HTML comments at component boundaries (same approach as the Next.js setup).
|
|
11
|
+
*
|
|
12
|
+
* Usage in vite.config.ts:
|
|
13
|
+
* import jssDebug from "aidos-ui/vite-plugin-jss-debug";
|
|
14
|
+
* export default defineConfig({ plugins: [jssDebug()] });
|
|
15
|
+
*/
|
|
16
|
+
export default function jssDebug() {
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
name: "jss-debug-jsx",
|
|
20
|
+
config() {
|
|
21
|
+
return {
|
|
22
|
+
esbuild: {
|
|
23
|
+
jsx: "automatic",
|
|
24
|
+
jsxDev: true,
|
|
25
|
+
jsxImportSource: "aidos-ui",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "jss-debug-classnames",
|
|
32
|
+
enforce: "pre" as const,
|
|
33
|
+
|
|
34
|
+
transform(code: string, id: string) {
|
|
35
|
+
if (id.includes("node_modules")) return;
|
|
36
|
+
if (!id.match(/\.[jt]sx?$/)) return;
|
|
37
|
+
if (!code.includes("toClassnames(")) return;
|
|
38
|
+
|
|
39
|
+
const filename = path.basename(id, path.extname(id));
|
|
40
|
+
const name = JSON.stringify(filename);
|
|
41
|
+
const needle = "toClassnames(";
|
|
42
|
+
let result = "";
|
|
43
|
+
let i = 0;
|
|
44
|
+
|
|
45
|
+
while (i < code.length) {
|
|
46
|
+
const idx = code.indexOf(needle, i);
|
|
47
|
+
if (idx === -1) {
|
|
48
|
+
result += code.slice(i);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Copy everything up to and including "toClassnames("
|
|
53
|
+
result += code.slice(i, idx + needle.length);
|
|
54
|
+
i = idx + needle.length;
|
|
55
|
+
|
|
56
|
+
// Track parens to find the matching closing )
|
|
57
|
+
let depth = 1;
|
|
58
|
+
let j = i;
|
|
59
|
+
while (j < code.length && depth > 0) {
|
|
60
|
+
const ch = code[j];
|
|
61
|
+
if (ch === "(") depth++;
|
|
62
|
+
else if (ch === ")") depth--;
|
|
63
|
+
if (depth > 0) j++;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Copy the original arguments, then inject the component name
|
|
67
|
+
result += code.slice(i, j) + ", " + name;
|
|
68
|
+
i = j;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { code: result, map: null };
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
"sourceMap": true,
|
|
12
12
|
"outDir": "dist/",
|
|
13
13
|
"jsx": "react-jsx",
|
|
14
|
+
"jsxImportSource": "aidos-ui-jsx",
|
|
15
|
+
"baseUrl": ".",
|
|
16
|
+
"paths": {
|
|
17
|
+
"aidos-ui-jsx/jsx-dev-runtime": ["./src/jsx-dev-runtime"],
|
|
18
|
+
"aidos-ui-jsx/jsx-runtime": ["./src/jsx-runtime"]
|
|
19
|
+
},
|
|
14
20
|
"moduleResolution": "node",
|
|
15
21
|
"esModuleInterop": true,
|
|
16
22
|
"declaration": true,
|