Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

State managment reworked #83

Merged
merged 20 commits into from
Jul 24, 2024
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
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@
"@code-hike/mdx": "0.9.0",
"@marigold/components": "8.0.2",
"@marigold/icons": "1.2.54",
"@marigold/system": "^8.0.0",
"@marigold/system": "^8.0.2",
"@marigold/theme-core": "26.1.14",
"@marigold/theme-docs": "^2.0.2",
"@marigold/theme-docs": "^2.0.4",
"@mdx-js/rollup": "3.0.1",
"@tailwindcss/typography": "^0.5.13",
"@tanstack/react-query": "5.48.0",
"@tanstack/react-query-devtools": "5.48.0",
"@tanstack/react-router": "1.40.0",
"@tanstack/react-query": "5.51.1",
"@tanstack/react-query-devtools": "5.51.1",
"@tanstack/react-router": "1.45.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"tailwind-merge": "2.3.0"
"tailwind-merge": "2.4.0"
},
"devDependencies": {
"@marigold/eslint-config": "0.4.16",
Expand Down
1,037 changes: 643 additions & 394 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

Binary file added public/component-tree-context-api.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/component-tree-prop-drill.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/floating-cogs.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/goku.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/server-cache-dia.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/routes/_components/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const About = () => (
Your feedback, ideas, and suggestions for improvement are always
welcome. Feel free to reach out to us through any of{' '}
<Link href="https://www.marigold-ui.io/introduction/get-in-touch">
our communication channels
<u>our communication channels</u>
</Link>
.
</p>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/_components/Tutorials.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const Tutorials = () => (
title: 'State Management',
href: '/state-management',
caption:
'Learn how compound components can enhance parent-child communication beyond prop drilling.',
'Learn to structure and maintain your state effectively.',
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down
17 changes: 7 additions & 10 deletions src/routes/compound-component/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Columns, SectionMessage } from '@marigold/components';
import { SectionMessage } from '@marigold/components';

import { Block, Content } from '@/components/Container';
import DemoLink from '@/components/DemoLink';
import { Content } from '@/components/Container';
import Preview from '@/components/Preview';

import SelectExample from './_components/SelectExample';
Expand Down Expand Up @@ -64,14 +63,12 @@ options and tracking user-selected items, eliminating the need for you to handle
tasks manually.

<SectionMessage variant="info">
<SectionMessage.Title>
Single Import
</SectionMessage.Title>
<SectionMessage.Title>Single Import</SectionMessage.Title>
<SectionMessage.Content>
Note how compound components usually export their related components using dot
notation (`Select.Option`). This technique allows you to bundle the parent and
child components together in a single export, making it easy to access all
related components.
Note how compound components usually export their related components using
dot notation (`Select.Option`). This technique allows you to bundle the
parent and child components together in a single export, making it easy to
access all related components.
</SectionMessage.Content>
</SectionMessage>

Expand Down
76 changes: 76 additions & 0 deletions src/routes/state-management/_components/ContextApiExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { createContext, useContext } from 'react';

interface Product {
id: number;
name: string;
category: string;
subcategory: string;
price: number;
}

const CategoryContext = createContext<string>('all');

const ProductPage: React.FC = () => {
return (
<div style={{ display: 'flex' }}>
<CategoryContext.Provider value={'electronics'}>
<MainContent />
</CategoryContext.Provider>
</div>
);
};

const MainContent: React.FC = () => {
return <ProductList />;
};

const products: Product[] = [
{
id: 1,
name: 'Laptop',
category: 'electronics',
subcategory: 'Laptops',
price: 1000,
},
{
id: 2,
name: 'Shirt',
category: 'fashion',
subcategory: 'Shirts',
price: 50,
},
{ id: 3, name: 'Book', category: 'books', subcategory: 'Fiction', price: 20 },
];

const ProductList: React.FC = () => {
const category = useContext(CategoryContext);

const filteredProducts = products.filter(
product => category === 'all' || product.category === category
);

return (
<div>
{filteredProducts.map(product => (
<ProductItem key={product.id} product={product} />
))}
</div>
);
};

interface ProductItemProps {
product: Product;
}

const ProductItem: React.FC<ProductItemProps> = ({ product }) => {
return (
<div>
<h2>{product.name}</h2>
<p>Category: {product.category}</p>
<p>Subcategory: {product.subcategory}</p>
<p>Price: ${product.price}</p>
</div>
);
};

export default ProductPage;
25 changes: 25 additions & 0 deletions src/routes/state-management/_components/CounterExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState } from 'react';
import { Button, MarigoldProvider } from '@marigold/components';
import theme from '@marigold/theme-core';

const MyButton = () => {
const [count, setCount] = useState(0);

return (
<Button onPress={() => setCount(count + 1)} size="small">
Clicked {count} times
</Button>
);
};

const Counter = () => {
return (
<MarigoldProvider theme={theme}>
<div>
<MyButton />
</div>
</MarigoldProvider>
);
};

export default Counter;
66 changes: 66 additions & 0 deletions src/routes/state-management/_components/SearchFilterComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useEffect, useState } from 'react';
import {
apiUrl,
type IMovie,
} from '@/routes/state-management/_components/globals';
import { MarigoldProvider, SearchField, Stack } from '@marigold/components';
import theme from '@marigold/theme-core';
import useFetch from './useFetch';

const SearchFilterComponent = () => {
const [searchQuery, setSearchQuery] = useState('');
const [filteredList, setFilteredList] = useState<Array<IMovie>>([]);

const {
data: movies,
isError,
isLoading,
error,
} = useFetch<Array<IMovie>>(apiUrl, ['movies']);

useEffect(() => {
if (movies) {
setFilteredList(movies);
}
}, [movies]);

const handleSearchChange = (value: string) => {
setSearchQuery(value);

const filtered = movies!.filter(item =>
item.title.toLowerCase().includes(value.toLowerCase())
);
setFilteredList(filtered);
};

if (isLoading) {
return <div>Loading...</div>;
}

if (isError) {
return <div>Error: {error.message}</div>;
}

return (
<MarigoldProvider theme={theme}>
<Stack space={2}>
<SearchField
value={searchQuery}
onChange={value => handleSearchChange(value)}
label="Search"
/>
{filteredList.length > 0 ? (
<ul>
{filteredList.map((item, index) => (
<li key={index}>{item.title}</li>
))}
</ul>
) : (
<span>No movies found!</span>
)}
</Stack>
</MarigoldProvider>
);
};

export default SearchFilterComponent;
67 changes: 29 additions & 38 deletions src/routes/state-management/_components/ServerStateExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,74 +2,65 @@ import {
Card,
Image,
Inline,
MarigoldProvider,
SearchField,
Select,
Stack,
Text,
} from '@marigold/components';
import {
apiUrl,
type IMovie,
} from '@/routes/state-management/_components/globals';
import theme from '@marigold/theme-core';
import { Route } from '@/routes/state-management/preview.lazy';
import { useRouterState } from '@tanstack/react-router';
import { useQuery } from '@tanstack/react-query';
import { useState } from 'react';

interface IMovie {
href: string;
year: number;
title: string;
category: string;
thumbnail: string;
thumbnail_width: number;
thumbnail_height: number;
extract: string;
}
function ServerStateExample() {
const [filters, setFilters] = useState<{ title: string; category: string }>({
title: '',
category: '',
});

const fetchData = async (
url: string,
queryParams: Record<string, string>
) => {
const queryString = new URLSearchParams(queryParams).toString();
const apiURL = `${url}${queryString ? `?${queryString}` : ''}`;
const data = await fetch(apiURL);
return await data.json();
};
const filters: { title: string; category: string } = Route.useSearch();
const navigate = Route.useNavigate();
const router = useRouterState();

const {
data: movies,
isError,
isLoading,
isPending,
error,
} = useQuery<Array<IMovie>>({
queryKey: ['users', filters],
queryFn: async () =>
await fetchData(
'https://6630d183c92f351c03db2e12.mockapi.io/movies',
filters
),
queryFn: async () => {
const data = await fetch(`${apiUrl}${router.location.searchStr}`);
return await data.json();
},
});

if (isError) {
return <span>Error: {error.message}</span>;
}

return (
<>
<MarigoldProvider theme={theme}>
<Stack space={4}>
<Inline space={4}>
<SearchField
value={filters?.title}
onChange={value => setFilters(prev => ({ ...prev, title: value }))}
label="search"
value={filters.title}
onChange={value =>
navigate({
search: prev => ({ ...prev, title: value }),
})
}
label="Search"
width={'1/2'}
/>
<Select
label="Category"
placeholder="Select your character"
width={'1/5'}
onChange={key => {
setFilters(prev => ({ ...prev, category: key as string }));
navigate({
search: prev => ({ ...prev, category: key }),
});
}}
selectedKey={filters.category}
>
Expand All @@ -83,7 +74,7 @@ function ServerStateExample() {
</Select>
</Inline>

{isLoading && <span>Loading...</span>}
{isPending && <span>Loading...</span>}
<Inline space={4}>
{
// check if search returns nothing(movies='not found')
Expand All @@ -109,7 +100,7 @@ function ServerStateExample() {
}
</Inline>
</Stack>
</>
</MarigoldProvider>
);
}

Expand Down
Loading
Loading