Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ yarn-error.log*

# tarballs produced during local tests
*.tgz

t.*

# keep these files
!next-env.d.ts
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"typecheck": "tsc --noEmit --project tsconfig.base.json",
"size": "npx esbuild ./packages/core/dist/index.js --bundle --minify --format=esm --external:react --define:process.env.NODE_ENV='\"production\"' | gzip -c | wc -c"
"size": "npx esbuild ./packages/core/dist/index.js --bundle --minify --format=esm --external:react --define:process.env.NODE_ENV='\"production\"' | gzip -c | wc -c",
"build-output": "npx esbuild ./packages/core/dist/index.js --bundle --minify --format=esm --external:react --define:process.env.NODE_ENV='\"production\"'"
},
"devDependencies": {
"@eslint/js": "^9.30.1",
Expand All @@ -48,4 +49,4 @@
"tsx": "^4.20.3",
"typescript": "^5.8.3"
}
}
}
3 changes: 2 additions & 1 deletion packages/core/__tests__/e2e/next.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ test.describe('Zero-UI Next.js Integration Tests', () => {

// Click scope toggle
await page.getByTestId('scope-toggle').click();
await expect(body).toHaveAttribute('data-scope', 'off');
const scope = page.getByTestId('scope-container');
await expect(scope).toHaveAttribute('data-scope', 'on');

// Verify other states are preserved
await expect(body).toHaveAttribute('data-theme', 'dark');
Expand Down
3 changes: 2 additions & 1 deletion packages/core/__tests__/e2e/vite.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ test.describe('Zero-UI Next.js Integration Tests', () => {

// Click scope toggle
await page.getByTestId('scope-toggle').click();
await expect(body).toHaveAttribute('data-scope', 'off');
const scope = page.getByTestId('scope-container');
await expect(scope).toHaveAttribute('data-scope', 'on');

// Verify other states are preserved
await expect(body).toHaveAttribute('data-theme', 'dark');
Expand Down
3 changes: 0 additions & 3 deletions packages/core/__tests__/fixtures/next/.zero-ui/attributes.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
/* AUTO-GENERATED - DO NOT EDIT */
export const bodyAttributes = {
"data-child": "closed",
"data-faq": "closed",
"data-mobile": "false",
"data-number": "1",
"data-scope": "off",
"data-theme": "light",
"data-theme-2": "light",
"data-theme-three": "light",
Expand Down
7 changes: 4 additions & 3 deletions packages/core/__tests__/fixtures/next/app/FAQ.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useUI } from '@react-zero-ui/core';
import { useScopedUI } from '@react-zero-ui/core';

function FAQ({ question, answer, index }) {
const [, setOpen] = useUI<'open' | 'closed'>('faq', 'closed'); // Same key everywhere!
const [open, setOpen] = useScopedUI<'open' | 'closed'>('faq', 'closed'); // Same key everywhere!

return (
<div
ref={setOpen.ref}
data-index={index}>
data-index={index}
data-faq={open}>
<button
data-testid={`faq-${index}-toggle`}
className="bg-blue-500 text-white p-2 rounded-md m-5"
Expand Down
20 changes: 9 additions & 11 deletions packages/core/__tests__/fixtures/next/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { bodyAttributes } from '@zero-ui/attributes';
import './globals.css';
import { bodyAttributes } from "@zero-ui/attributes";import './globals.css';

export default function RootLayout({ children }) {
return (
<html lang="en">
<body
{...bodyAttributes}
className="bg-red test-ww this is to test the body tag"
id="88">
return (
<html lang="en">
<body {...bodyAttributes}
className="bg-red test-ww this is to test the body tag"
id="88">
{children}
</body>
</html>
);
}
</html>);

}
17 changes: 11 additions & 6 deletions packages/core/__tests__/fixtures/next/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
'use client';
import { useUI } from '@react-zero-ui/core';
import { useScopedUI, useUI } from '@react-zero-ui/core';
import UseEffectComponent from './UseEffectComponent';
import FAQ from './FAQ';
import { ChildComponent } from './ChildComponent';
import { ChildWithoutSetter } from './ChildWithoutSetter';

export default function Page() {
const [scope, setScope] = useScopedUI<'off' | 'on'>('scope', 'off');

const [, setTheme] = useUI<'light' | 'dark'>('theme', 'light');
const [, setTheme2] = useUI<'light' | 'dark'>('theme-2', 'light');
const [, setThemeThree] = useUI<'light' | 'dark'>('theme-three', 'light');
const [, setToggle] = useUI<'true' | 'false'>('toggle-boolean', 'true');
const [, setNumber] = useUI<'1' | '2'>('number', '1');
const [, setOpen] = useUI<'open' | 'closed'>('faq', 'closed'); // Same key everywhere!
const [, setScope] = useUI<'off' | 'on'>('scope', 'off');
const [, setMobile] = useUI<'true' | 'false'>('mobile', 'false');
const [open, setOpen] = useScopedUI<'open' | 'closed'>('faq', 'closed');
const [mobile, setMobile] = useScopedUI<'true' | 'false'>('mobile', 'false');
const [, setChildOpen] = useUI<'open' | 'closed'>('child', 'closed');

const [, setToggleFunction] = useUI<'white' | 'black'>('toggle-function', 'white');
Expand Down Expand Up @@ -145,6 +146,7 @@ export default function Page() {
className="scope-off:bg-blue-100 scope-on:bg-blue-900 scope-on:text-white"
data-testid="scope-container"
//this ref tells the hook to flip the data key here
data-scope={scope}
ref={setScope.ref}>
<button
type="button"
Expand All @@ -166,7 +168,8 @@ export default function Page() {
className="mobile-false:bg-blue-100 mobile-true:bg-blue-900 mobile-true:text-white"
data-testid="mobile-container"
//this ref tells the hook to flip the data key here
ref={setMobile.ref}>
ref={setMobile.ref}
data-mobile={mobile}>
<button
type="button"
onClick={() => {
Expand Down Expand Up @@ -194,7 +197,9 @@ export default function Page() {

<hr />

<div ref={setOpen.ref}>
<div
ref={setOpen.ref}
data-faq={open}>
<button
className="bg-blue-500 text-white p-2 rounded-md m-5"
onClick={() => setOpen((prev) => (prev === 'open' ? 'closed' : 'open'))}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* AUTO-GENERATED - DO NOT EDIT */
export declare const bodyAttributes: {
"data-child": "closed" | "open";
"data-faq": "closed" | "open";
"data-mobile": "false" | "true";
"data-number": "1" | "2";
Expand Down
4 changes: 1 addition & 3 deletions packages/core/__tests__/fixtures/vite/.zero-ui/attributes.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* AUTO-GENERATED - DO NOT EDIT */
export const bodyAttributes = {
"data-faq": "closed",
"data-mobile": "false",
"data-child": "closed",
"data-number": "1",
"data-scope": "off",
"data-theme": "light",
"data-theme-2": "light",
"data-theme-three": "light",
Expand Down
4 changes: 0 additions & 4 deletions packages/core/__tests__/fixtures/vite/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,3 @@
--color-primary: #000000;
--color-secondary: #ffffff;
}

body {
@apply bg-primary text-secondary;
}
30 changes: 23 additions & 7 deletions packages/core/__tests__/fixtures/vite/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
'use client';
import { useUI } from '@react-zero-ui/core';
import { useUI, useScopedUI } from '@react-zero-ui/core';
import UseEffectComponent from './UseEffectComponent';
import FAQ from './FAQ';
import './App.css';
import { ChildComponent } from './ChildComponent';
import { ChildWithoutSetter } from './ChildWithoutSetter';

export default function App() {
const [scope, setScope] = useScopedUI<'off' | 'on'>('scope', 'off');

const [, setTheme] = useUI<'light' | 'dark'>('theme', 'light');
const [, setTheme2] = useUI<'light' | 'dark'>('theme-2', 'light');
const [, setThemeThree] = useUI<'light' | 'dark'>('theme-three', 'light');
const [, setToggle] = useUI<'true' | 'false'>('toggle-boolean', 'true');
const [, setNumber] = useUI<'1' | '2'>('number', '1');
const [, setOpen] = useUI<'open' | 'closed'>('faq', 'closed'); // Same key everywhere!
const [, setScope] = useUI<'off' | 'on'>('scope', 'off');
const [, setMobile] = useUI<'true' | 'false'>('mobile', 'false');
const [open, setOpen] = useScopedUI<'open' | 'closed'>('faq', 'closed'); // Same key everywhere!
const [mobile, setMobile] = useScopedUI<'true' | 'false'>('mobile', 'false');
const [, setChildOpen] = useUI<'open' | 'closed'>('child', 'closed');

const [, setToggleFunction] = useUI<'white' | 'black'>('toggle-function', 'white');

Expand All @@ -22,7 +26,7 @@ export default function App() {

return (
<div
className="p-8 theme-light:bg-white theme-dark:bg-white bg-black text-black"
className="p-8 theme-light:bg-white theme-dark:bg-white bg-black"
data-testid="page-container">
<h1 className="text-2xl font-bold py-5">Global State</h1>
<hr />
Expand Down Expand Up @@ -129,16 +133,21 @@ export default function App() {
Function: <span className="toggle-function-white:block hidden">White</span> <span className="toggle-function-black:block hidden">Black</span>
</div>
</div>
<hr />
<ChildComponent setIsOpen={setChildOpen} />
</div>
<hr />

<h1 className="text-2xl font-bold py-5">Scoped Style Tests</h1>

<hr />

<div className="border-2 border-blue-500">
<div
className="scope-off:bg-blue-100 scope-on:bg-blue-900 scope-on:text-white"
data-testid="scope-container"
//this ref tells the hook to flip the data key here
data-scope={scope}
ref={setScope.ref}>
<button
type="button"
Expand All @@ -147,18 +156,21 @@ export default function App() {
data-testid="scope-toggle">
Toggle Scope
</button>
<ChildWithoutSetter />
<div className="scope-on:bg-blue-900 scope-off:bg-blue-100 ">
Scope: <span className="scope-off:block scope-on:hidden">False</span>
<span className="scope-on:block scope-off:hidden">True</span>
</div>
</div>

<hr />

<div
className="mobile-false:bg-blue-100 mobile-true:bg-blue-900 mobile-true:text-white"
data-testid="mobile-container"
//this ref tells the hook to flip the data key here
ref={setMobile.ref}>
ref={setMobile.ref}
data-mobile={mobile}>
<button
type="button"
onClick={() => {
Expand All @@ -184,7 +196,11 @@ export default function App() {
</div>
</div>

<div ref={setOpen.ref}>
<hr />

<div
ref={setOpen.ref}
data-faq={open}>
<button
className="bg-blue-500 text-white p-2 rounded-md m-5"
onClick={() => setOpen((prev) => (prev === 'open' ? 'closed' : 'open'))}>
Expand Down
20 changes: 20 additions & 0 deletions packages/core/__tests__/fixtures/vite/src/ChildComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { type UISetterFn } from '@react-zero-ui/core';

export function ChildComponent({ setIsOpen }: { setIsOpen: UISetterFn<'open' | 'closed'> }) {
return (
<div
className="child-closed:bg-gray-100 child-open:bg-gray-900 child-open:text-white"
data-testid="child-container">
<button
type="button"
onClick={() => setIsOpen((prev) => (prev === 'closed' ? 'open' : 'closed'))}
className="border-2 border-red-500"
data-testid="child-toggle">
Toggle Child
</button>
<div className="child-closed:bg-gray-100 child-open:bg-gray-900 flex">
Child: <span className="child-open:block child-closed:hidden">Open</span> <span className="child-closed:block child-open:hidden">Closed</span>
</div>
</div>
);
}
14 changes: 14 additions & 0 deletions packages/core/__tests__/fixtures/vite/src/ChildWithoutSetter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export function ChildWithoutSetter() {
return (
<div className="border-2 border-blue-500">
<div
className="scope-on:bg-blue-900 scope-off:bg-blue-100 scope-on:text-white bg-red-500"
data-testid="child-scope-container">
<div className="scope-off:bg-blue-100 scope-on:bg-blue-900 ">
Child Scope: <span className="scope-off:block scope-on:hidden">False</span>
<span className="scope-on:block scope-off:hidden">True</span>
</div>
</div>
</div>
);
}
7 changes: 4 additions & 3 deletions packages/core/__tests__/fixtures/vite/src/FAQ.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useUI } from '@react-zero-ui/core';
import { useScopedUI } from '@react-zero-ui/core';

function FAQ({ question, answer, index }: { question: string; answer: string; index: number }) {
const [, setOpen] = useUI<'open' | 'closed'>('faq', 'closed'); // Same key everywhere!
const [open, setOpen] = useScopedUI<'open' | 'closed'>('faq', 'closed'); // Same key everywhere!

return (
<div
ref={setOpen.ref}
data-index={index}>
data-index={index}
data-faq={open}>
<button
data-testid={`faq-${index}-toggle`}
className="bg-blue-500 text-white p-2 rounded-md m-5"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@react-zero-ui/core",
"version": "0.2.7",
"version": "0.2.8",
"description": "Zero re-render, global UI state management for React",
"private": false,
"type": "module",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/cli/postInstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export async function runZeroUiInit() {
}

// Process all variants using the shared helper
const { finalVariants, initialValues, sourceFiles } = await processVariants();
const { finalVariants, initialGlobalValues, sourceFiles } = await processVariants();

// Generate attribute files using the shared helper
await generateAttributesFile(finalVariants, initialValues);
await generateAttributesFile(finalVariants, initialGlobalValues);

console.log(`[Zero-UI] βœ… Initialized with ${finalVariants.length} variants from ${sourceFiles.length} files`);

Expand Down
Loading