denwa-web-shared 1.0.51 → 1.0.53
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/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# denwa-web-shared
|
|
2
|
+
|
|
3
|
+
A shared library for Next.js App Router projects containing reusable components, hooks, schemas, and utilities.
|
|
4
|
+
|
|
5
|
+
## AI Agent Integration
|
|
6
|
+
|
|
7
|
+
This library supports `@tanstack/intent` for automated developer assistance.
|
|
8
|
+
|
|
9
|
+
If you use an AI agent (Claude Code, Cursor, Copilot, etc.), run the following command in your project to install this library's skills:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx @tanstack/intent@latest install
|
|
13
|
+
```
|
package/package.json
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "denwa-web-shared",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.53",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Denwa",
|
|
7
7
|
"main": "dist/denwa-web-shared.cjs.js",
|
|
8
8
|
"module": "dist/denwa-web-shared.es.js",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/Denwa799/web-shared.git"
|
|
12
|
+
},
|
|
9
13
|
"files": [
|
|
10
|
-
"dist"
|
|
14
|
+
"dist",
|
|
15
|
+
"skills",
|
|
16
|
+
"!skills/_artifacts"
|
|
11
17
|
],
|
|
12
18
|
"types": "dist/index.d.ts",
|
|
13
19
|
"exports": {
|
|
@@ -52,6 +58,7 @@
|
|
|
52
58
|
"devDependencies": {
|
|
53
59
|
"@eslint/eslintrc": "^3.3.1",
|
|
54
60
|
"@eslint/js": "^9.28.0",
|
|
61
|
+
"@tanstack/intent": "^0.0.36",
|
|
55
62
|
"@types/react": "^19.1.6",
|
|
56
63
|
"@types/react-dom": "^19.1.6",
|
|
57
64
|
"@vitejs/plugin-react-swc": "^3.10.1",
|
|
@@ -75,5 +82,8 @@
|
|
|
75
82
|
"typescript-eslint": "^8.33.1",
|
|
76
83
|
"vite": "^6.3.5",
|
|
77
84
|
"vite-plugin-dts": "^4.5.4"
|
|
78
|
-
}
|
|
85
|
+
},
|
|
86
|
+
"keywords": [
|
|
87
|
+
"tanstack-intent"
|
|
88
|
+
]
|
|
79
89
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: infinity-list
|
|
3
|
+
description: >
|
|
4
|
+
Implement a continuous feed of items using InfinityList component.
|
|
5
|
+
Load when implementing infinite scroll, lazy loading, or pagination lists.
|
|
6
|
+
type: core
|
|
7
|
+
library: denwa-web-shared
|
|
8
|
+
library_version: "1.0.51"
|
|
9
|
+
sources:
|
|
10
|
+
- "src/client/ui/infinity-list.tsx"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# denwa-web-shared — Implement infinite lists
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { InfinityList } from 'denwa-web-shared/client';
|
|
19
|
+
|
|
20
|
+
<InfinityList
|
|
21
|
+
isNext={hasNextPage}
|
|
22
|
+
onIntersection={fetchNextPage}
|
|
23
|
+
intersectionElement={() => <div>Loading...</div>}
|
|
24
|
+
>
|
|
25
|
+
{items.map(item => <Item key={item.id} {...item} />)}
|
|
26
|
+
</InfinityList>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Core Patterns
|
|
30
|
+
|
|
31
|
+
### Infinite scroll list
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { InfinityList } from 'denwa-web-shared/client';
|
|
35
|
+
import { Spinner } from '@/components/spinner';
|
|
36
|
+
|
|
37
|
+
export const Feed = ({ items, hasNext, loadMore }) => {
|
|
38
|
+
return (
|
|
39
|
+
<InfinityList
|
|
40
|
+
isNext={hasNext}
|
|
41
|
+
onIntersection={loadMore}
|
|
42
|
+
intersectionElement={() => <Spinner />}
|
|
43
|
+
intersectionElementClassName="py-4"
|
|
44
|
+
>
|
|
45
|
+
{items.map(item => (
|
|
46
|
+
<FeedItem key={item.id} data={item} />
|
|
47
|
+
))}
|
|
48
|
+
</InfinityList>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Common Mistakes
|
|
54
|
+
|
|
55
|
+
### MEDIUM Incorrect handling of intersection triggers
|
|
56
|
+
|
|
57
|
+
Wrong:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
// Manual intersection observer implementation
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Correct:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { InfinityList } from 'denwa-web-shared/client';
|
|
67
|
+
|
|
68
|
+
<InfinityList isNext={hasNextPage} onIntersection={fetchNextPage} intersectionElement={() => <Spinner />}>
|
|
69
|
+
{items}
|
|
70
|
+
</InfinityList>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Agents might manually write intersection observers instead of using InfinityList.
|
|
74
|
+
|
|
75
|
+
Source: maintainer interview
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: render-picture
|
|
3
|
+
description: >
|
|
4
|
+
Use the BasePicture component to render images instead of Next.js Image.
|
|
5
|
+
Load when generating images, using getImageData, IPreparedServerImage, or IPreparedServerImageWithAlt.
|
|
6
|
+
type: core
|
|
7
|
+
library: denwa-web-shared
|
|
8
|
+
library_version: "1.0.51"
|
|
9
|
+
sources:
|
|
10
|
+
- "src/server/ui/image.tsx"
|
|
11
|
+
- "src/server/lib/utils.ts"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# denwa-web-shared — Render responsive images
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { BasePicture } from 'denwa-web-shared/server';
|
|
20
|
+
import { getImageData } from 'denwa-web-shared/server';
|
|
21
|
+
|
|
22
|
+
// Assuming serverImage is of type IPreparedServerImage
|
|
23
|
+
const pictureData = getImageData(serverImage);
|
|
24
|
+
|
|
25
|
+
<BasePicture data={pictureData} alt="alt text" type={serverImage.type} />
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Core Patterns
|
|
29
|
+
|
|
30
|
+
### Rendering a picture using server data
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { BasePicture, getImageData } from 'denwa-web-shared/server';
|
|
34
|
+
import type { IPreparedServerImage } from 'denwa-web-shared/server';
|
|
35
|
+
|
|
36
|
+
export const MyComponent = ({ image }: { image: IPreparedServerImage }) => {
|
|
37
|
+
const data = getImageData(image);
|
|
38
|
+
|
|
39
|
+
return <BasePicture data={data} alt="Responsive image" type={image.type} />;
|
|
40
|
+
};
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Common Mistakes
|
|
44
|
+
|
|
45
|
+
### CRITICAL Using next/image instead of Picture
|
|
46
|
+
|
|
47
|
+
Wrong:
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import Image from 'next/image';
|
|
51
|
+
|
|
52
|
+
<Image src={src} alt={alt} />
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Correct:
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { BasePicture, getImageData } from 'denwa-web-shared/server';
|
|
59
|
+
|
|
60
|
+
const pictureData = getImageData(serverImage);
|
|
61
|
+
<BasePicture data={pictureData} alt="alt text" type={serverImage.type} />
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Agents default to next/image instead of using the custom BasePicture component.
|
|
65
|
+
|
|
66
|
+
Source: maintainer interview
|
|
67
|
+
|
|
68
|
+
### HIGH Not using getImageData for Picture path
|
|
69
|
+
|
|
70
|
+
Wrong:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { BasePicture } from 'denwa-web-shared/server';
|
|
74
|
+
|
|
75
|
+
<BasePicture data={{ image1x: item.path }} alt="alt text" type="image/jpeg" />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Correct:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { BasePicture, getImageData } from 'denwa-web-shared/server';
|
|
82
|
+
|
|
83
|
+
const pictureData = getImageData(item);
|
|
84
|
+
<BasePicture data={pictureData} alt="alt text" type={item.type} />
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Agents try to construct paths manually instead of using the getImageData helper to generate PictureData.
|
|
88
|
+
|
|
89
|
+
Source: maintainer interview
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: shared-constants
|
|
3
|
+
description: >
|
|
4
|
+
Use shared library constants like TIME and THEME.
|
|
5
|
+
Load when hardcoding time durations, milliseconds, breakpoints, or offsets.
|
|
6
|
+
type: core
|
|
7
|
+
library: denwa-web-shared
|
|
8
|
+
library_version: "1.0.51"
|
|
9
|
+
sources:
|
|
10
|
+
- "src/server/constants/index.ts"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# denwa-web-shared — Use library constants
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { TIME, THEME } from 'denwa-web-shared/server';
|
|
19
|
+
|
|
20
|
+
const timeout = TIME.milliseconds.seconds5;
|
|
21
|
+
const offset = THEME.OFFSET[4];
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Core Patterns
|
|
25
|
+
|
|
26
|
+
### Using TIME constants for durations
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { TIME } from 'denwa-web-shared/server';
|
|
30
|
+
|
|
31
|
+
export const fetchWithTimeout = async (url: string) => {
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
setTimeout(() => controller.abort(), TIME.milliseconds.seconds5);
|
|
34
|
+
|
|
35
|
+
return fetch(url, { signal: controller.signal });
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Common Mistakes
|
|
40
|
+
|
|
41
|
+
### MEDIUM Hardcoding time values
|
|
42
|
+
|
|
43
|
+
Wrong:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
setTimeout(fn, 1000);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Correct:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { TIME } from 'denwa-web-shared/server';
|
|
53
|
+
|
|
54
|
+
setTimeout(fn, TIME.milliseconds.seconds1);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Agents hardcode milliseconds values instead of using the TIME constant.
|
|
58
|
+
|
|
59
|
+
Source: maintainer interview
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: viewport-hooks
|
|
3
|
+
description: >
|
|
4
|
+
Use the shared viewport hooks rather than react-use.
|
|
5
|
+
Load when writing responsive behavior, using useMedia, useLaptopBigViewPort, useMobileViewPort.
|
|
6
|
+
type: core
|
|
7
|
+
library: denwa-web-shared
|
|
8
|
+
library_version: "1.0.51"
|
|
9
|
+
sources:
|
|
10
|
+
- "src/client/hooks/use-view-port.ts"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# denwa-web-shared — Use custom viewport hooks
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { useMobileViewPort } from 'denwa-web-shared/client';
|
|
19
|
+
|
|
20
|
+
const { isMobileMaxWidth, isMobileMinWidth } = useMobileViewPort();
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Core Patterns
|
|
24
|
+
|
|
25
|
+
### Conditional rendering based on viewport
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { useMobileViewPort, useLaptopViewPort } from 'denwa-web-shared/client';
|
|
29
|
+
|
|
30
|
+
export const ResponsiveLayout = () => {
|
|
31
|
+
const { isMobileMaxWidth } = useMobileViewPort();
|
|
32
|
+
const { isLaptopMinWidth } = useLaptopViewPort();
|
|
33
|
+
|
|
34
|
+
if (isMobileMaxWidth) return <MobileMenu />;
|
|
35
|
+
if (isLaptopMinWidth) return <DesktopMenu />;
|
|
36
|
+
|
|
37
|
+
return <DefaultMenu />;
|
|
38
|
+
};
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Common Mistakes
|
|
42
|
+
|
|
43
|
+
### HIGH Importing useMedia directly
|
|
44
|
+
|
|
45
|
+
Wrong:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { useMedia } from 'react-use';
|
|
49
|
+
|
|
50
|
+
const isMobile = useMedia('(max-width: 450px)');
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Correct:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { useMobileViewPort } from 'denwa-web-shared/client';
|
|
57
|
+
|
|
58
|
+
const { isMobileMaxWidth } = useMobileViewPort();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Agents import useMedia from react-use and manually provide viewport breakpoints instead of using the custom hooks.
|
|
62
|
+
|
|
63
|
+
Source: maintainer interview
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yandex-feed
|
|
3
|
+
description: >
|
|
4
|
+
Generate XML feeds for Yandex using generateYandexFeedXML.
|
|
5
|
+
Load when creating RSS feeds or Yandex XML exports.
|
|
6
|
+
type: core
|
|
7
|
+
library: denwa-web-shared
|
|
8
|
+
library_version: "1.0.51"
|
|
9
|
+
sources:
|
|
10
|
+
- "src/server/lib/utils.ts"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# denwa-web-shared — Generate Yandex Feed XML
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { generateYandexFeedXML } from 'denwa-web-shared/server';
|
|
19
|
+
|
|
20
|
+
const feedXml = generateYandexFeedXML({
|
|
21
|
+
// feed configuration
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Core Patterns
|
|
26
|
+
|
|
27
|
+
### Generating feed XML
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { generateYandexFeedXML } from 'denwa-web-shared/server';
|
|
31
|
+
|
|
32
|
+
export async function GET() {
|
|
33
|
+
const data = await getFeedData();
|
|
34
|
+
const xml = generateYandexFeedXML(data);
|
|
35
|
+
|
|
36
|
+
return new Response(xml, {
|
|
37
|
+
headers: { 'Content-Type': 'application/xml' }
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Common Mistakes
|
|
43
|
+
|
|
44
|
+
### HIGH Manual XML generation for Yandex feeds
|
|
45
|
+
|
|
46
|
+
Wrong:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
const xml = `<yml_catalog><shop>...</shop></yml_catalog>`;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Correct:
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { generateYandexFeedXML } from 'denwa-web-shared/server';
|
|
56
|
+
|
|
57
|
+
const xml = generateYandexFeedXML(feedData);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Agents write their own XML builders instead of using the library helper.
|
|
61
|
+
|
|
62
|
+
Source: maintainer interview
|