project-portfolio 1.0.5 → 1.0.8
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/dist/ProjectCard.d.ts.map +1 -1
- package/dist/ProjectCard.js +9 -2
- package/dist/ProjectPortfolio.d.ts +11 -23
- package/dist/ProjectPortfolio.d.ts.map +1 -1
- package/dist/ProjectPortfolio.js +21 -28
- package/package.json +5 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectCard.d.ts","sourceRoot":"","sources":["../src/ProjectCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAmC,MAAM,SAAS,CAAA;AAE1F,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,CAAA;AAE5C,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAeD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,MAAM,EACN,QAAQ,EACR,OAAgB,EAChB,QAAsB,GACvB,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"ProjectCard.d.ts","sourceRoot":"","sources":["../src/ProjectCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAmC,MAAM,SAAS,CAAA;AAE1F,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,CAAA;AAE5C,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAeD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,MAAM,EACN,QAAQ,EACR,OAAgB,EAChB,QAAsB,GACvB,EAAE,gBAAgB,2CAyPlB"}
|
package/dist/ProjectCard.js
CHANGED
|
@@ -45,6 +45,7 @@ export function ProjectCard({ project, schema, priority, variant = "card", baseP
|
|
|
45
45
|
const href = `${basePath}/${project.slug}`;
|
|
46
46
|
if (variant === "compact") {
|
|
47
47
|
return (_jsxs("a", { href: href, style: {
|
|
48
|
+
all: "revert",
|
|
48
49
|
display: "flex",
|
|
49
50
|
alignItems: "center",
|
|
50
51
|
gap: "1rem",
|
|
@@ -54,6 +55,9 @@ export function ProjectCard({ project, schema, priority, variant = "card", baseP
|
|
|
54
55
|
borderRadius: "2px",
|
|
55
56
|
textDecoration: "none",
|
|
56
57
|
transition: "border-color 0.2s, box-shadow 0.2s",
|
|
58
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
59
|
+
boxSizing: "border-box",
|
|
60
|
+
color: "inherit",
|
|
57
61
|
}, children: [_jsx("div", { style: {
|
|
58
62
|
position: "relative",
|
|
59
63
|
width: "80px",
|
|
@@ -65,6 +69,7 @@ export function ProjectCard({ project, schema, priority, variant = "card", baseP
|
|
|
65
69
|
}, children: imageUrl ? (_jsx("img", { src: imageUrl, alt: project.title, style: { width: "100%", height: "100%", objectFit: "cover" } })) : (_jsx("div", { style: { width: "100%", height: "100%", backgroundColor: "#e4e4e7" } })) }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "4px", minWidth: 0 }, children: [_jsx("p", { style: { fontWeight: 700, color: "#18181b", fontSize: "14px", margin: 0, lineHeight: 1.4, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: project.title }), badgeValue && (_jsx("p", { style: { fontSize: "12px", color: "#71717a", margin: 0 }, children: badgeValue })), compactTags.length > 0 && (_jsx("p", { style: { fontSize: "12px", color: "#f18a00", margin: 0, lineHeight: 1.4 }, children: compactTags.join(" · ") }))] })] }));
|
|
66
70
|
}
|
|
67
71
|
return (_jsxs("article", { style: {
|
|
72
|
+
all: "revert",
|
|
68
73
|
backgroundColor: "#fff",
|
|
69
74
|
borderRadius: "2px",
|
|
70
75
|
boxShadow: "0 2px 8px 0 rgba(0,0,0,0.10), 0 0 0 1px rgba(0,0,0,0.07)",
|
|
@@ -72,6 +77,8 @@ export function ProjectCard({ project, schema, priority, variant = "card", baseP
|
|
|
72
77
|
display: "flex",
|
|
73
78
|
flexDirection: "column",
|
|
74
79
|
transition: "box-shadow 0.3s",
|
|
80
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
81
|
+
boxSizing: "border-box",
|
|
75
82
|
}, children: [_jsxs("div", { style: { position: "relative", aspectRatio: "16/10", overflow: "hidden", flexShrink: 0 }, children: [imageUrl ? (_jsx("img", { src: imageUrl, alt: project.title, loading: priority ? "eager" : "lazy", style: { width: "100%", height: "100%", objectFit: "cover" } })) : (_jsx("div", { style: { position: "absolute", inset: 0, backgroundColor: "#f4f4f5" } })), _jsx("div", { style: {
|
|
76
83
|
position: "absolute",
|
|
77
84
|
inset: 0,
|
|
@@ -106,7 +113,7 @@ export function ProjectCard({ project, schema, priority, variant = "card", baseP
|
|
|
106
113
|
fontSize: "11px",
|
|
107
114
|
fontWeight: 600,
|
|
108
115
|
padding: "3px 10px",
|
|
109
|
-
}, children: badgeValue })), _jsx("h3", { style: { color: "#fff", fontWeight: 700, fontSize: "20px", lineHeight: 1.3, margin: 0 }, children: project.title }), locationString && (_jsx("p", { style: { color: "rgba(255,255,255,0.8)", fontSize: "14px", margin: 0 }, children: locationString }))] })] }), _jsxs("div", { style: { display: "flex", flexDirection: "column", flex: 1, padding: "1.5rem" }, children: [(project.blurb || project.description) && (_jsx("p", { style: { fontSize: "14px", color: "#3f3f46", lineHeight: 1.6, margin: "0 0 16px 0" }, children: project.blurb || project.description })), tagFields.map((field) => {
|
|
116
|
+
}, children: badgeValue })), _jsx("h3", { style: { all: "revert", color: "#fff", fontWeight: 700, fontSize: "20px", lineHeight: 1.3, margin: 0, fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" }, children: project.title }), locationString && (_jsx("p", { style: { color: "rgba(255,255,255,0.8)", fontSize: "14px", margin: 0 }, children: locationString }))] })] }), _jsxs("div", { style: { display: "flex", flexDirection: "column", flex: 1, padding: "1.5rem" }, children: [(project.blurb || project.description) && (_jsx("p", { style: { fontSize: "14px", color: "#3f3f46", lineHeight: 1.6, margin: "0 0 16px 0" }, children: project.blurb || project.description })), tagFields.map((field) => {
|
|
110
117
|
const vals = parseMultiValue(project.custom_field_values[field.key]);
|
|
111
118
|
if (vals.length === 0)
|
|
112
119
|
return null;
|
|
@@ -124,5 +131,5 @@ export function ProjectCard({ project, schema, priority, variant = "card", baseP
|
|
|
124
131
|
};
|
|
125
132
|
return url ? (_jsx("a", { href: url, style: tagStyle, children: val }, `${project.id}-${field.key}-${i}`)) : (_jsx("span", { style: tagStyle, children: val }, `${project.id}-${field.key}-${i}`));
|
|
126
133
|
}) })] }, `${project.id}-${field.key}`));
|
|
127
|
-
}), _jsx("div", { style: { flex: 1 } }), _jsxs("div", { style: { marginTop: "16px", borderTop: "1px solid #e4e4e7", paddingTop: "12px", display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: "16px" }, children: [footerStat ? (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [_jsx("span", { style: { fontSize: "12px", color: "#a1a1aa" }, children: footerStat.label }), _jsx("span", { style: { color: "#18181b", fontWeight: 700, fontSize: "16px" }, children: footerStat.formatted })] })) : _jsx("div", {}), _jsx("a", { href: href, style: { color: "#f18a00", fontWeight: 600, fontSize: "14px", textDecoration: "none", flexShrink: 0 }, children: "Details \u2192" })] })] })] }));
|
|
134
|
+
}), _jsx("div", { style: { flex: 1 } }), _jsxs("div", { style: { marginTop: "16px", borderTop: "1px solid #e4e4e7", paddingTop: "12px", display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: "16px" }, children: [footerStat ? (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [_jsx("span", { style: { fontSize: "12px", color: "#a1a1aa" }, children: footerStat.label }), _jsx("span", { style: { color: "#18181b", fontWeight: 700, fontSize: "16px" }, children: footerStat.formatted })] })) : _jsx("div", {}), _jsx("a", { href: href, style: { all: "revert", color: "#f18a00", fontWeight: 600, fontSize: "14px", textDecoration: "none", flexShrink: 0, fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" }, children: "Details \u2192" })] })] })] }));
|
|
128
135
|
}
|
|
@@ -5,29 +5,19 @@ export interface ProjectPortfolioProps {
|
|
|
5
5
|
apiBase: string;
|
|
6
6
|
/** Base path for project detail links. Defaults to "/projects" */
|
|
7
7
|
basePath?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Seconds to cache via Next.js Data Cache on production deployments.
|
|
10
|
+
* React.cache() always deduplicates within a single render in all environments.
|
|
11
|
+
* Defaults to 60.
|
|
12
|
+
*/
|
|
13
|
+
revalidate?: number;
|
|
8
14
|
}
|
|
9
15
|
/**
|
|
10
|
-
* ProjectPortfolio — self-
|
|
16
|
+
* ProjectPortfolio — pure self-fetching card grid.
|
|
11
17
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* export default function Page() {
|
|
17
|
-
* return (
|
|
18
|
-
* <ProjectPortfolio
|
|
19
|
-
* clientSlug="my-client"
|
|
20
|
-
* apiBase="https://your-api.com"
|
|
21
|
-
* />
|
|
22
|
-
* )
|
|
23
|
-
* }
|
|
24
|
-
*
|
|
25
|
-
* Requirements: Next.js 13+ App Router, React 18+
|
|
26
|
-
*/
|
|
27
|
-
/**
|
|
28
|
-
* ProjectPortfolio — pure card grid, no chrome.
|
|
29
|
-
*
|
|
30
|
-
* Fetches client and project data directly from the API.
|
|
18
|
+
* Caching works in all environments:
|
|
19
|
+
* - Everywhere: React.cache() deduplicates fetches within a single render
|
|
20
|
+
* - Production: next.revalidate caches across requests for `revalidate` seconds
|
|
31
21
|
*
|
|
32
22
|
* Usage:
|
|
33
23
|
* import { ProjectPortfolio } from "chisel-project-portfolio"
|
|
@@ -36,8 +26,6 @@ export interface ProjectPortfolioProps {
|
|
|
36
26
|
* clientSlug="my-client"
|
|
37
27
|
* apiBase="https://your-api.com"
|
|
38
28
|
* />
|
|
39
|
-
*
|
|
40
|
-
* Requirements: Next.js 13+ App Router, React 18+
|
|
41
29
|
*/
|
|
42
|
-
export declare function ProjectPortfolio({ clientSlug, apiBase, basePath, }: ProjectPortfolioProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
30
|
+
export declare function ProjectPortfolio({ clientSlug, apiBase, basePath, revalidate, }: ProjectPortfolioProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
43
31
|
//# sourceMappingURL=ProjectPortfolio.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectPortfolio.d.ts","sourceRoot":"","sources":["../src/ProjectPortfolio.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProjectPortfolio.d.ts","sourceRoot":"","sources":["../src/ProjectPortfolio.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAuED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,QAAsB,EACtB,UAAe,GAChB,EAAE,qBAAqB,oDA8BvB"}
|
package/dist/ProjectPortfolio.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cache } from "react";
|
|
2
3
|
import { ProjectCard } from "./ProjectCard";
|
|
3
|
-
|
|
4
|
+
// Two-layer caching strategy:
|
|
5
|
+
// 1. React.cache() — deduplicates within a single render in ALL environments
|
|
6
|
+
// (preview, local, production). If this component appears twice on one page,
|
|
7
|
+
// the API is only hit once.
|
|
8
|
+
// 2. next: { revalidate } — Next.js Data Cache, caches across multiple requests
|
|
9
|
+
// on production deployments. Silently ignored in preview/local — no crash,
|
|
10
|
+
// just falls back to per-render dedup from React.cache().
|
|
11
|
+
const fetchPortfolioData = cache(async (apiBase, clientSlug, revalidate) => {
|
|
4
12
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
13
|
+
// next.revalidate is used where supported (production); silently ignored elsewhere.
|
|
14
|
+
// Either way React.cache() above ensures no duplicate fetches within one render.
|
|
15
|
+
const fetchOpts = { next: { revalidate } };
|
|
5
16
|
const [clientRes, projectsRes] = await Promise.all([
|
|
6
|
-
fetch(`${apiBase}/api/v1/clients/${clientSlug}
|
|
7
|
-
fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects
|
|
17
|
+
fetch(`${apiBase}/api/v1/clients/${clientSlug}`, fetchOpts),
|
|
18
|
+
fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects`, fetchOpts),
|
|
8
19
|
]);
|
|
9
20
|
const clientJson = clientRes.ok
|
|
10
21
|
? await clientRes.json()
|
|
@@ -26,29 +37,13 @@ const fetchPortfolioData = async (apiBase, clientSlug) => {
|
|
|
26
37
|
projects: (_j = projectsJson.data) !== null && _j !== void 0 ? _j : [],
|
|
27
38
|
schema,
|
|
28
39
|
};
|
|
29
|
-
};
|
|
40
|
+
});
|
|
30
41
|
/**
|
|
31
|
-
* ProjectPortfolio — self-
|
|
42
|
+
* ProjectPortfolio — pure self-fetching card grid.
|
|
32
43
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* export default function Page() {
|
|
38
|
-
* return (
|
|
39
|
-
* <ProjectPortfolio
|
|
40
|
-
* clientSlug="my-client"
|
|
41
|
-
* apiBase="https://your-api.com"
|
|
42
|
-
* />
|
|
43
|
-
* )
|
|
44
|
-
* }
|
|
45
|
-
*
|
|
46
|
-
* Requirements: Next.js 13+ App Router, React 18+
|
|
47
|
-
*/
|
|
48
|
-
/**
|
|
49
|
-
* ProjectPortfolio — pure card grid, no chrome.
|
|
50
|
-
*
|
|
51
|
-
* Fetches client and project data directly from the API.
|
|
44
|
+
* Caching works in all environments:
|
|
45
|
+
* - Everywhere: React.cache() deduplicates fetches within a single render
|
|
46
|
+
* - Production: next.revalidate caches across requests for `revalidate` seconds
|
|
52
47
|
*
|
|
53
48
|
* Usage:
|
|
54
49
|
* import { ProjectPortfolio } from "chisel-project-portfolio"
|
|
@@ -57,11 +52,9 @@ const fetchPortfolioData = async (apiBase, clientSlug) => {
|
|
|
57
52
|
* clientSlug="my-client"
|
|
58
53
|
* apiBase="https://your-api.com"
|
|
59
54
|
* />
|
|
60
|
-
*
|
|
61
|
-
* Requirements: Next.js 13+ App Router, React 18+
|
|
62
55
|
*/
|
|
63
|
-
export async function ProjectPortfolio({ clientSlug, apiBase, basePath = "/projects", }) {
|
|
64
|
-
const { projects, schema } = await fetchPortfolioData(apiBase, clientSlug);
|
|
56
|
+
export async function ProjectPortfolio({ clientSlug, apiBase, basePath = "/projects", revalidate = 60, }) {
|
|
57
|
+
const { projects, schema } = await fetchPortfolioData(apiBase, clientSlug, revalidate);
|
|
65
58
|
if (projects.length === 0) {
|
|
66
59
|
return (_jsx("div", { style: { textAlign: "center", padding: "4rem 0" }, children: _jsx("p", { style: { color: "#71717a" }, children: "No projects found." }) }));
|
|
67
60
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "project-portfolio",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Self-contained project portfolio component for Next.js App Router. Drop in one component, pass a clientSlug and apiBase, done.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"nextjs",
|
|
7
|
-
"react",
|
|
8
|
-
"portfolio",
|
|
9
|
-
"projects"
|
|
10
|
-
],
|
|
5
|
+
"keywords": ["nextjs", "react", "portfolio", "projects"],
|
|
11
6
|
"license": "MIT",
|
|
12
7
|
"type": "module",
|
|
13
8
|
"main": "./dist/index.js",
|
|
@@ -19,9 +14,7 @@
|
|
|
19
14
|
"types": "./dist/index.d.ts"
|
|
20
15
|
}
|
|
21
16
|
},
|
|
22
|
-
"files": [
|
|
23
|
-
"dist"
|
|
24
|
-
],
|
|
17
|
+
"files": ["dist"],
|
|
25
18
|
"scripts": {
|
|
26
19
|
"build": "tsc",
|
|
27
20
|
"prepublishOnly": "npm run build"
|
|
@@ -32,10 +25,8 @@
|
|
|
32
25
|
"react-dom": ">=18.0.0"
|
|
33
26
|
},
|
|
34
27
|
"devDependencies": {
|
|
35
|
-
"@types/react": "^18.
|
|
36
|
-
"@types/react-dom": "^18.
|
|
37
|
-
"react": "^19.2.4",
|
|
38
|
-
"react-dom": "^19.2.4",
|
|
28
|
+
"@types/react": "^18.0.0",
|
|
29
|
+
"@types/react-dom": "^18.0.0",
|
|
39
30
|
"typescript": "^5.0.0"
|
|
40
31
|
}
|
|
41
32
|
}
|