@chiselandco/nexus 2.6.0 → 3.0.0

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Self-contained project portfolio components for Next.js App Router. Pass a `clientSlug`, `apiBase`, and `apiKey` — each component fetches, caches, and renders everything it needs with no client-side waterfall requests.
4
4
 
5
- **Version:** 2.6.0
5
+ **Version:** 3.0.0
6
6
 
7
7
  ---
8
8
 
@@ -150,7 +150,7 @@ export default async function ProjectsPage({
150
150
  | `font` | `string` | No | System font | Font family string |
151
151
  | `noCache` | `boolean` | No | `false` | Sets `cache: "no-store"` — useful during development |
152
152
  | `revalidate` | `number` | No | `86400` | Cache revalidation period in seconds |
153
- | `side` | `"architectural" \| "speedrail"` | No | — | Filter projects by website side. When omitted all projects are shown. See [Filtering by side](#filtering-by-side). |
153
+ | `filterBy` | `{ field: string; value: string }` | No | — | Pre-filter projects by any custom field value. See [Filtering by field](#filtering-by-field). |
154
154
 
155
155
  #### URL param format
156
156
 
@@ -403,7 +403,7 @@ Use `variant="card"` to render baseball-card style instead of the default list s
403
403
  | `font` | `string` | No | System font stack | Font family string |
404
404
  | `revalidate` | `number` | No | `86400` | Cache revalidation period in seconds |
405
405
  | `noCache` | `boolean` | No | `false` | Sets `cache: "no-store"` — useful during development |
406
- | `side` | `"architectural" \| "speedrail"` | No | — | Filter projects by website side. When omitted all projects are shown. |
406
+ | `filterBy` | `{ field: string; value: string }` | No | — | Pre-filter projects by any custom field value. See [Filtering by field](#filtering-by-field). |
407
407
 
408
408
  ---
409
409
 
@@ -568,7 +568,7 @@ export default async function ProjectsPage({
568
568
  | `searchParams` | `Record<string, string \| string[] \| undefined>` | No | `{}` | Filter params — pass Next.js `searchParams` directly |
569
569
  | `revalidate` | `number` | No | `86400` | Cache revalidation period in seconds |
570
570
  | `noCache` | `boolean` | No | `false` | Sets `cache: "no-store"` — useful during development |
571
- | `side` | `"architectural" \| "speedrail"` | No | — | Filter projects by website side. When omitted all projects are shown. |
571
+ | `filterBy` | `{ field: string; value: string }` | No | — | Pre-filter projects by any custom field value. See [Filtering by field](#filtering-by-field). |
572
572
 
573
573
  ---
574
574
 
@@ -611,39 +611,55 @@ export default function ProjectsPage() {
611
611
  | `filters` | `Record<string, string>` | No | `{}` | Active filters — filtering is instant, no API call on change |
612
612
  | `columns` | `2 \| 3` | No | `3` | Number of grid columns |
613
613
  | `font` | `string` | No | System font stack | Font family string |
614
- | `side` | `"architectural" \| "speedrail"` | No | — | Filter projects by website side. When omitted all projects are shown. |
614
+ | `filterBy` | `{ field: string; value: string }` | No | — | Pre-filter projects by any custom field value. See [Filtering by field](#filtering-by-field). |
615
615
 
616
616
  ---
617
617
 
618
- ## Filtering by side
618
+ ## Filtering by field
619
619
 
620
- Every project returned by the API includes a `side` field: `"architectural"`, `"speedrail"`, or `"both"`. This reflects which section of the client's website a project belongs to.
621
-
622
- The following components accept an optional `side` prop: `FilteredPortfolio`, `ProjectPortfolio`, `ProjectPortfolioClient`, and `SimilarProjects`.
620
+ `FilteredPortfolio`, `ProjectPortfolio`, `ProjectPortfolioClient`, and `SimilarProjects` all accept an optional `filterBy` prop. It pre-filters the project list by any custom field value before any user-driven filters are applied.
623
621
 
624
622
  ```tsx
625
- // Architectural side of the site only shows architectural + both projects
623
+ // Only show projects where custom field "side" equals "architectural" or "both"
626
624
  <FilteredPortfolio
627
625
  clientSlug="hollaender"
628
626
  apiBase="https://your-api.com"
629
627
  apiKey={process.env.HOLLAENDER_API_KEY!}
630
628
  basePath="/architectural/projects"
631
629
  searchParams={searchParams}
632
- side="architectural"
630
+ filterBy={{ field: "side", value: "architectural" }}
633
631
  />
634
632
 
635
- // Speed-Rail side of the site only shows speedrail + both projects
633
+ // Only show projects where custom field "side" equals "speedrail" or "both"
636
634
  <FilteredPortfolio
637
635
  clientSlug="hollaender"
638
636
  apiBase="https://your-api.com"
639
637
  apiKey={process.env.HOLLAENDER_API_KEY!}
640
638
  basePath="/speedrail/projects"
641
639
  searchParams={searchParams}
642
- side="speedrail"
640
+ filterBy={{ field: "side", value: "speedrail" }}
641
+ />
642
+
643
+ // Works with any field — not just "side"
644
+ <ProjectPortfolio
645
+ clientSlug="acme"
646
+ apiBase="https://your-api.com"
647
+ apiKey={process.env.ACME_API_KEY!}
648
+ filterBy={{ field: "region", value: "northeast" }}
643
649
  />
644
650
  ```
645
651
 
646
- When `side` is omitted the component behaves exactly as before all projects are shown. The client controls which value to pass based on the current route or context. No extra API calls are made — the filtering happens in memory after the standard fetch.
652
+ The `"both"` fallback is built in if a project's field value is `"both"` it matches any `filterBy.value`. When `filterBy` is omitted all projects are shown. No extra API calls are made — filtering happens in memory after the standard fetch.
653
+
654
+ ### Migration from v2 `side` prop
655
+
656
+ ```tsx
657
+ // v2
658
+ <FilteredPortfolio side="architectural" />
659
+
660
+ // v3
661
+ <FilteredPortfolio filterBy={{ field: "side", value: "architectural" }} />
662
+ ```
647
663
 
648
664
  ---
649
665
 
@@ -16,5 +16,5 @@ export interface FilterSidebarProps {
16
16
  /** Label for the trigger link. Defaults to "Advanced Filters" */
17
17
  triggerLabel?: string;
18
18
  }
19
- export declare function FilterSidebar({ schema, filterKeys, font, triggerLabel, }: FilterSidebarProps): import("react/jsx-runtime").JSX.Element | null;
19
+ export declare function FilterSidebar({ schema, filterKeys, font, triggerLabel, }: FilterSidebarProps): import("react").JSX.Element | null;
20
20
  //# sourceMappingURL=FilterSidebar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FilterSidebar.d.ts","sourceRoot":"","sources":["../src/FilterSidebar.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAEhD,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,UAAU,EACV,IAAI,EACJ,YAAiC,GAClC,EAAE,kBAAkB,kDAuXpB"}
1
+ {"version":3,"file":"FilterSidebar.d.ts","sourceRoot":"","sources":["../src/FilterSidebar.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAEhD,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,UAAU,EACV,IAAI,EACJ,YAAiC,GAClC,EAAE,kBAAkB,sCAuXpB"}
@@ -20,12 +20,16 @@ export interface FilteredPortfolioProps {
20
20
  noCache?: boolean;
21
21
  font?: string;
22
22
  /**
23
- * Filter projects by website side.
24
- * - "architectural" shows projects with side "architectural" or "both"
25
- * - "speedrail" — shows projects with side "speedrail" or "both"
26
- * - omit — shows all projects regardless of side
23
+ * Pre-filter projects by any custom field value before rendering.
24
+ * Useful for splitting a portfolio by a routing field (e.g. site section).
25
+ * e.g. filterBy={{ field: "side", value: "architectural" }}
26
+ * Projects where the field value equals the given value OR "both" are shown.
27
+ * When omitted all projects are shown.
27
28
  */
28
- side?: "architectural" | "speedrail";
29
+ filterBy?: {
30
+ field: string;
31
+ value: string;
32
+ };
29
33
  }
30
34
  /**
31
35
  * FilteredPortfolio — server component.
@@ -48,5 +52,5 @@ export interface FilteredPortfolioProps {
48
52
  * )
49
53
  * }
50
54
  */
51
- export declare function FilteredPortfolio({ clientSlug, apiBase, apiKey, searchParams, filterKeys, basePath, revalidate, noCache, font, side, }: FilteredPortfolioProps): Promise<import("react/jsx-runtime").JSX.Element>;
55
+ export declare function FilteredPortfolio({ clientSlug, apiBase, apiKey, searchParams, filterKeys, basePath, revalidate, noCache, font, filterBy, }: FilteredPortfolioProps): Promise<import("react").JSX.Element>;
52
56
  //# sourceMappingURL=FilteredPortfolio.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FilteredPortfolio.d.ts","sourceRoot":"","sources":["../src/FilteredPortfolio.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IAC5D;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,GAAG,WAAW,CAAA;CACrC;AA8ED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,UAAU,EACV,OAAO,EACP,MAAM,EACN,YAAiB,EACjB,UAAU,EACV,QAAsB,EACtB,UAAe,EACf,OAAe,EACf,IAAI,EACJ,IAAI,GACL,EAAE,sBAAsB,oDAkGxB"}
1
+ {"version":3,"file":"FilteredPortfolio.d.ts","sourceRoot":"","sources":["../src/FilteredPortfolio.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IAC5D;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5C;AA8ED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,UAAU,EACV,OAAO,EACP,MAAM,EACN,YAAiB,EACjB,UAAU,EACV,QAAsB,EACtB,UAAe,EACf,OAAe,EACf,IAAI,EACJ,QAAQ,GACT,EAAE,sBAAsB,wCAqGxB"}
@@ -74,7 +74,7 @@ async function fetchSchema(apiBase, clientSlug, apiKey, fetchOpts) {
74
74
  * )
75
75
  * }
76
76
  */
77
- export async function FilteredPortfolio({ clientSlug, apiBase, apiKey, searchParams = {}, filterKeys, basePath = "/projects", revalidate = 60, noCache = false, font, side, }) {
77
+ export async function FilteredPortfolio({ clientSlug, apiBase, apiKey, searchParams = {}, filterKeys, basePath = "/projects", revalidate = 60, noCache = false, font, filterBy, }) {
78
78
  const fetchOpts = noCache
79
79
  ? { cache: "no-store" }
80
80
  : { next: { revalidate } };
@@ -95,8 +95,12 @@ export async function FilteredPortfolio({ clientSlug, apiBase, apiKey, searchPar
95
95
  fetchSchema(apiBase, clientSlug, apiKey, fetchOpts),
96
96
  ]);
97
97
  const activeCount = Object.values(activeFilters).reduce((n, ids) => n + ids.length, 0);
98
- const visible = side
99
- ? projects.filter((p) => p.side === side || p.side === "both")
98
+ const visible = filterBy
99
+ ? projects.filter((p) => {
100
+ var _a;
101
+ const v = (_a = p.custom_field_values) === null || _a === void 0 ? void 0 : _a[filterBy.field];
102
+ return v === filterBy.value || v === "both";
103
+ })
100
104
  : projects;
101
105
  return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
102
106
  .nxs-fp-wrap {
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import type { Media, CustomFieldSchema } from "./types";
2
3
  export interface GalleryCarouselProps {
3
4
  images: Media[];
@@ -10,5 +11,5 @@ export interface GalleryCarouselProps {
10
11
  */
11
12
  schema?: CustomFieldSchema[];
12
13
  }
13
- export declare function GalleryCarousel({ images, projectTitle, schema }: GalleryCarouselProps): import("react/jsx-runtime").JSX.Element | null;
14
+ export declare function GalleryCarousel({ images, projectTitle, schema }: GalleryCarouselProps): React.JSX.Element | null;
14
15
  //# sourceMappingURL=GalleryCarousel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GalleryCarousel.d.ts","sourceRoot":"","sources":["../src/GalleryCarousel.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAe,MAAM,SAAS,CAAA;AASpE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;CAC7B;AA+ND,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAW,EAAE,EAAE,oBAAoB,kDA2V1F"}
1
+ {"version":3,"file":"GalleryCarousel.d.ts","sourceRoot":"","sources":["../src/GalleryCarousel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAEhF,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAe,MAAM,SAAS,CAAA;AASpE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;CAC7B;AAgOD,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAW,EAAE,EAAE,oBAAoB,4BA2V1F"}
@@ -23,6 +23,8 @@ function getTagsForImage(image, schema) {
23
23
  return [];
24
24
  const tags = [];
25
25
  for (const field of schema) {
26
+ if (field.display_position === "hidden")
27
+ continue;
26
28
  const raw = cfv[field.key];
27
29
  if (raw === null || raw === undefined)
28
30
  continue;
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import type { Project, CustomFieldSchema } from "./types";
2
3
  export type CardVariant = "card" | "compact";
3
4
  interface ProjectCardProps {
@@ -10,6 +11,6 @@ interface ProjectCardProps {
10
11
  /** Base path for project detail links. Defaults to "/projects" */
11
12
  basePath?: string;
12
13
  }
13
- export declare function ProjectCard({ project, schema, fieldOptionsMap, priority, variant, basePath, }: ProjectCardProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function ProjectCard({ project, schema, fieldOptionsMap, priority, variant, basePath, }: ProjectCardProps): React.JSX.Element;
14
15
  export {};
15
16
  //# sourceMappingURL=ProjectCard.d.ts.map
@@ -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,2FAA2F;IAC3F,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,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,eAAoB,EACpB,QAAQ,EACR,OAAgB,EAChB,QAAsB,GACvB,EAAE,gBAAgB,2CA2QlB"}
1
+ {"version":3,"file":"ProjectCard.d.ts","sourceRoot":"","sources":["../src/ProjectCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,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,2FAA2F;IAC3F,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,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,eAAoB,EACpB,QAAQ,EACR,OAAgB,EAChB,QAAsB,GACvB,EAAE,gBAAgB,qBA2QlB"}
@@ -25,5 +25,5 @@ export interface ProjectDetailProps {
25
25
  */
26
26
  noCache?: boolean;
27
27
  }
28
- export declare function ProjectDetail({ slug: slugProp, projectSlug, clientSlug, apiBase, apiKey, backPath, backLabel, revalidate, noCache, }: ProjectDetailProps): Promise<import("react/jsx-runtime").JSX.Element>;
28
+ export declare function ProjectDetail({ slug: slugProp, projectSlug, clientSlug, apiBase, apiKey, backPath, backLabel, revalidate, noCache, }: ProjectDetailProps): Promise<import("react").JSX.Element>;
29
29
  //# sourceMappingURL=ProjectDetail.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectDetail.d.ts","sourceRoot":"","sources":["../src/ProjectDetail.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAsFD,wBAAsB,aAAa,CAAC,EAClC,IAAI,EAAE,QAAQ,EACd,WAAW,EACX,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,SAA0B,EAC1B,UAAe,EACf,OAAe,GAChB,EAAE,kBAAkB,oDAiRpB"}
1
+ {"version":3,"file":"ProjectDetail.d.ts","sourceRoot":"","sources":["../src/ProjectDetail.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAsFD,wBAAsB,aAAa,CAAC,EAClC,IAAI,EAAE,QAAQ,EACd,WAAW,EACX,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,SAA0B,EAC1B,UAAe,EACf,OAAe,GAChB,EAAE,kBAAkB,wCAiRpB"}
@@ -80,5 +80,5 @@ export declare function fetchProjectMenuData({ apiBase, clientSlug, apiKey, menu
80
80
  filterFieldName: string;
81
81
  fieldOptionsMap: Record<string, Record<string, string>>;
82
82
  }>;
83
- export declare function ProjectMenu({ clientSlug, apiBase, apiKey, menuId, basePath, viewAllPath, subtitle, font, maxProjects, revalidate, noCache, }: ProjectMenuProps): Promise<import("react/jsx-runtime").JSX.Element>;
83
+ export declare function ProjectMenu({ clientSlug, apiBase, apiKey, menuId, basePath, viewAllPath, subtitle, font, maxProjects, revalidate, noCache, }: ProjectMenuProps): Promise<import("react").JSX.Element>;
84
84
  //# sourceMappingURL=ProjectMenu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectMenu.d.ts","sourceRoot":"","sources":["../src/ProjectMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAGzD,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,UAAU,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,UAAkB,GACnB,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,aACoC,OAAO,uBAmE3C;AAED,wBAAsB,oBAAoB,CAAC,EACzC,OAAO,EACP,UAAU,EACV,MAAM,EACN,MAAM,EACN,UAAkB,EAClB,OAAe,GAChB,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,GAAG,OAAO,CAAC;IACV,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,aAAa,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC9C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACxD,CAAC,CAqBD;AAwDD,wBAAsB,WAAW,CAAC,EAChC,UAAU,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,QAAsB,EACtB,WAAW,EACX,QAAQ,EACR,IAA0E,EAC1E,WAAe,EACf,UAAkB,EAClB,OAAe,GAChB,EAAE,gBAAgB,oDAiClB"}
1
+ {"version":3,"file":"ProjectMenu.d.ts","sourceRoot":"","sources":["../src/ProjectMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAGzD,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,UAAU,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,UAAkB,GACnB,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,IAC2B,SAAS,OAAO,uBAmE3C;AAED,wBAAsB,oBAAoB,CAAC,EACzC,OAAO,EACP,UAAU,EACV,MAAM,EACN,MAAM,EACN,UAAkB,EAClB,OAAe,GAChB,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,GAAG,OAAO,CAAC;IACV,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,aAAa,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC9C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACxD,CAAC,CAqBD;AAwDD,wBAAsB,WAAW,CAAC,EAChC,UAAU,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,QAAsB,EACtB,WAAW,EACX,QAAQ,EACR,IAA0E,EAC1E,WAAe,EACf,UAAkB,EAClB,OAAe,GAChB,EAAE,gBAAgB,wCAiClB"}
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import type { Project, CustomFieldSchema } from "./types";
2
3
  export interface ProjectMenuClientProps {
3
4
  /**
@@ -42,5 +43,5 @@ export interface ProjectMenuClientProps {
42
43
  font?: string;
43
44
  maxProjects?: number;
44
45
  }
45
- export declare function ProjectMenuClient({ dataUrl, clientSlug, apiBase, apiKey, menuId, noCache, projects: projectsProp, schema: schemaProp, filterOptions: filterOptionsProp, filterFieldKey: filterFieldKeyProp, filterFieldName: filterFieldNameProp, fieldOptionsMap: fieldOptionsMapProp, subtitle, basePath, viewAllPath, font, maxProjects, }: ProjectMenuClientProps): import("react/jsx-runtime").JSX.Element;
46
+ export declare function ProjectMenuClient({ dataUrl, clientSlug, apiBase, apiKey, menuId, noCache, projects: projectsProp, schema: schemaProp, filterOptions: filterOptionsProp, filterFieldKey: filterFieldKeyProp, filterFieldName: filterFieldNameProp, fieldOptionsMap: fieldOptionsMapProp, subtitle, basePath, viewAllPath, font, maxProjects, }: ProjectMenuClientProps): React.JSX.Element;
46
47
  //# sourceMappingURL=ProjectMenuClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectMenuClient.d.ts","sourceRoot":"","sources":["../src/ProjectMenuClient.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAA;AAa3E,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC5B,aAAa,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/C,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAkBD,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,UAAU,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,OAAe,EACf,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,iBAAiB,EAChC,cAAc,EAAE,kBAAkB,EAClC,eAAe,EAAE,mBAAoC,EACrD,eAAe,EAAE,mBAAwB,EACzC,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,IAAmB,EACnB,WAAe,GAChB,EAAE,sBAAsB,2CAqfxB"}
1
+ {"version":3,"file":"ProjectMenuClient.d.ts","sourceRoot":"","sources":["../src/ProjectMenuClient.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAA;AAa3E,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC5B,aAAa,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/C,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAkBD,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,UAAU,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,OAAe,EACf,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,iBAAiB,EAChC,cAAc,EAAE,kBAAkB,EAClC,eAAe,EAAE,mBAAoC,EACrD,eAAe,EAAE,mBAAwB,EACzC,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,IAAmB,EACnB,WAAe,GAChB,EAAE,sBAAsB,qBAqfxB"}
@@ -25,12 +25,15 @@ export interface ProjectPortfolioProps {
25
25
  */
26
26
  noCache?: boolean;
27
27
  /**
28
- * Filter projects by website side.
29
- * - "architectural" shows projects with side "architectural" or "both"
30
- * - "speedrail" — shows projects with side "speedrail" or "both"
31
- * - omit — shows all projects regardless of side
28
+ * Pre-filter projects by any custom field value before rendering.
29
+ * e.g. filterBy={{ field: "side", value: "architectural" }}
30
+ * Projects where the field value equals the given value OR "both" are shown.
31
+ * When omitted all projects are shown.
32
32
  */
33
- side?: "architectural" | "speedrail";
33
+ filterBy?: {
34
+ field: string;
35
+ value: string;
36
+ };
34
37
  }
35
38
  /**
36
39
  * ProjectPortfolio — pure self-fetching card grid.
@@ -47,5 +50,5 @@ export interface ProjectPortfolioProps {
47
50
  * apiBase="https://your-api.com"
48
51
  * />
49
52
  */
50
- export declare function ProjectPortfolio({ clientSlug, apiBase, apiKey, basePath, searchParams, revalidate, noCache, side, }: ProjectPortfolioProps): Promise<import("react/jsx-runtime").JSX.Element>;
53
+ export declare function ProjectPortfolio({ clientSlug, apiBase, apiKey, basePath, searchParams, revalidate, noCache, filterBy, }: ProjectPortfolioProps): Promise<import("react").JSX.Element>;
51
54
  //# sourceMappingURL=ProjectPortfolio.d.ts.map
@@ -1 +1 @@
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,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IAC5D;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,GAAG,WAAW,CAAA;CACrC;AA0ED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,YAAiB,EACjB,UAAe,EACf,OAAe,EACf,IAAI,GACL,EAAE,qBAAqB,oDAmIvB"}
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,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IAC5D;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5C;AA0ED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,YAAiB,EACjB,UAAe,EACf,OAAe,EACf,QAAQ,GACT,EAAE,qBAAqB,wCAsIvB"}
@@ -65,7 +65,7 @@ const fetchPortfolioData = cache(async (apiBase, clientSlug, apiKey, revalidate,
65
65
  * apiBase="https://your-api.com"
66
66
  * />
67
67
  */
68
- export async function ProjectPortfolio({ clientSlug, apiBase, apiKey, basePath = "/projects", searchParams = {}, revalidate = 60, noCache = false, side, }) {
68
+ export async function ProjectPortfolio({ clientSlug, apiBase, apiKey, basePath = "/projects", searchParams = {}, revalidate = 60, noCache = false, filterBy, }) {
69
69
  // Parse filter[key]=value from searchParams into { key: value }
70
70
  const filters = {};
71
71
  Object.entries(searchParams).forEach(([key, val]) => {
@@ -77,8 +77,12 @@ export async function ProjectPortfolio({ clientSlug, apiBase, apiKey, basePath =
77
77
  });
78
78
  const filtersKey = JSON.stringify(filters);
79
79
  const { projects, schema, fieldOptionsMap } = await fetchPortfolioData(apiBase, clientSlug, apiKey, revalidate, filtersKey, noCache);
80
- const visible = side
81
- ? projects.filter((p) => p.side === side || p.side === "both")
80
+ const visible = filterBy
81
+ ? projects.filter((p) => {
82
+ var _a;
83
+ const v = (_a = p.custom_field_values) === null || _a === void 0 ? void 0 : _a[filterBy.field];
84
+ return v === filterBy.value || v === "both";
85
+ })
82
86
  : projects;
83
87
  const hasFilters = Object.keys(filters).length > 0;
84
88
  const activeFilterLabels = Object.entries(filters)
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  export interface ProjectPortfolioClientProps {
2
3
  /** Client slug identifying which client's projects to load */
3
4
  clientSlug: string;
@@ -19,12 +20,15 @@ export interface ProjectPortfolioClientProps {
19
20
  /** Max columns in the grid. 2 or 3. Defaults to 3 */
20
21
  columns?: 2 | 3;
21
22
  /**
22
- * Filter projects by website side.
23
- * - "architectural" shows projects with side "architectural" or "both"
24
- * - "speedrail" — shows projects with side "speedrail" or "both"
25
- * - omit — shows all projects regardless of side
23
+ * Pre-filter projects by any custom field value before rendering.
24
+ * e.g. filterBy={{ field: "side", value: "architectural" }}
25
+ * Projects where the field value equals the given value OR "both" are shown.
26
+ * When omitted all projects are shown.
26
27
  */
27
- side?: "architectural" | "speedrail";
28
+ filterBy?: {
29
+ field: string;
30
+ value: string;
31
+ };
28
32
  }
29
- export declare function ProjectPortfolioClient({ clientSlug, apiBase, apiKey, basePath, filters, font, columns, side, }: ProjectPortfolioClientProps): import("react/jsx-runtime").JSX.Element;
33
+ export declare function ProjectPortfolioClient({ clientSlug, apiBase, apiKey, basePath, filters, font, columns, filterBy, }: ProjectPortfolioClientProps): React.JSX.Element;
30
34
  //# sourceMappingURL=ProjectPortfolioClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectPortfolioClient.d.ts","sourceRoot":"","sources":["../src/ProjectPortfolioClient.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,2BAA2B;IAC1C,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IACf;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,GAAG,WAAW,CAAA;CACrC;AAyDD,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,OAAY,EACZ,IAAmB,EACnB,OAAW,EACX,IAAI,GACL,EAAE,2BAA2B,2CAsJ7B"}
1
+ {"version":3,"file":"ProjectPortfolioClient.d.ts","sourceRoot":"","sources":["../src/ProjectPortfolioClient.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAuC,MAAM,OAAO,CAAA;AAM3D,MAAM,WAAW,2BAA2B;IAC1C,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IACf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5C;AAyDD,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,OAAY,EACZ,IAAmB,EACnB,OAAW,EACX,QAAQ,GACT,EAAE,2BAA2B,qBAyJ7B"}
@@ -39,7 +39,7 @@ function matchesFilters(project, filters, schema, fieldOptionsMap) {
39
39
  });
40
40
  }
41
41
  // ─── Component ───────────────────────────────────────────────────────────────
42
- export function ProjectPortfolioClient({ clientSlug, apiBase, apiKey, basePath = "/projects", filters = {}, font = DEFAULT_FONT, columns = 3, side, }) {
42
+ export function ProjectPortfolioClient({ clientSlug, apiBase, apiKey, basePath = "/projects", filters = {}, font = DEFAULT_FONT, columns = 3, filterBy, }) {
43
43
  const [data, setData] = useState(null);
44
44
  useEffect(() => {
45
45
  const cacheKey = `${clientSlug}:${apiBase}:${apiKey}`;
@@ -90,14 +90,18 @@ export function ProjectPortfolioClient({ clientSlug, apiBase, apiKey, basePath =
90
90
  const filteredProjects = useMemo(() => {
91
91
  if (!data)
92
92
  return [];
93
- const bySide = side
94
- ? data.projects.filter((p) => p.side === side || p.side === "both")
93
+ const byField = filterBy
94
+ ? data.projects.filter((p) => {
95
+ var _a;
96
+ const v = (_a = p.custom_field_values) === null || _a === void 0 ? void 0 : _a[filterBy.field];
97
+ return v === filterBy.value || v === "both";
98
+ })
95
99
  : data.projects;
96
100
  const hasActiveFilters = Object.values(filters).some(Boolean);
97
101
  if (!hasActiveFilters)
98
- return bySide;
99
- return bySide.filter((p) => matchesFilters(p, filters, data.schema, data.fieldOptionsMap));
100
- }, [data, filters, side]);
102
+ return byField;
103
+ return byField.filter((p) => matchesFilters(p, filters, data.schema, data.fieldOptionsMap));
104
+ }, [data, filters, filterBy]);
101
105
  const gridCols = columns === 2
102
106
  ? "repeat(2, 1fr)"
103
107
  : "repeat(3, 1fr)";
@@ -44,12 +44,15 @@ export interface SimilarProjectsProps {
44
44
  /** Small label above the heading. Defaults to "More Work" */
45
45
  subtitle?: string;
46
46
  /**
47
- * Filter projects by website side.
48
- * - "architectural" shows projects with side "architectural" or "both"
49
- * - "speedrail" — shows projects with side "speedrail" or "both"
50
- * - omit — shows all projects regardless of side
47
+ * Pre-filter projects by any custom field value before rendering.
48
+ * e.g. filterBy={{ field: "side", value: "architectural" }}
49
+ * Projects where the field value equals the given value OR "both" are shown.
50
+ * When omitted all projects are shown.
51
51
  */
52
- side?: "architectural" | "speedrail";
52
+ filterBy?: {
53
+ field: string;
54
+ value: string;
55
+ };
53
56
  }
54
- export declare function SimilarProjects({ filters, excludeSlug, clientSlug, apiBase, apiKey, basePath, maxItems, revalidate, noCache, variant, projectSlugs, title, subtitle, side, }: SimilarProjectsProps): Promise<import("react/jsx-runtime").JSX.Element | null>;
57
+ export declare function SimilarProjects({ filters, excludeSlug, clientSlug, apiBase, apiKey, basePath, maxItems, revalidate, noCache, variant, projectSlugs, title, subtitle, filterBy, }: SimilarProjectsProps): Promise<import("react").JSX.Element | null>;
55
58
  //# sourceMappingURL=SimilarProjects.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SimilarProjects.d.ts","sourceRoot":"","sources":["../src/SimilarProjects.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,GAAG,WAAW,CAAA;CACrC;AA6DD,wBAAsB,eAAe,CAAC,EACpC,OAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,QAAY,EACZ,UAAe,EACf,OAAe,EACf,OAAgB,EAChB,YAAY,EACZ,KAA0B,EAC1B,QAAsB,EACtB,IAAI,GACL,EAAE,oBAAoB,2DAyKtB"}
1
+ {"version":3,"file":"SimilarProjects.d.ts","sourceRoot":"","sources":["../src/SimilarProjects.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5C;AA6DD,wBAAsB,eAAe,CAAC,EACpC,OAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,QAAY,EACZ,UAAe,EACf,OAAe,EACf,OAAgB,EAChB,YAAY,EACZ,KAA0B,EAC1B,QAAsB,EACtB,QAAQ,GACT,EAAE,oBAAoB,+CA4KtB"}
@@ -49,11 +49,15 @@ async function fetchSimilarData(apiBase, clientSlug, apiKey, revalidate, noCache
49
49
  }
50
50
  }
51
51
  // ─── Component ────────────────────────────────────────────────────────────────
52
- export async function SimilarProjects({ filters = {}, excludeSlug, clientSlug, apiBase, apiKey, basePath = "/projects", maxItems = 3, revalidate = 60, noCache = false, variant = "list", projectSlugs, title = "Similar Projects", subtitle = "More Work", side, }) {
52
+ export async function SimilarProjects({ filters = {}, excludeSlug, clientSlug, apiBase, apiKey, basePath = "/projects", maxItems = 3, revalidate = 60, noCache = false, variant = "list", projectSlugs, title = "Similar Projects", subtitle = "More Work", filterBy, }) {
53
53
  const { allProjects: allProjectsRaw, schema, fieldOptionsMap } = await fetchSimilarData(apiBase, clientSlug, apiKey, revalidate, noCache);
54
- // Apply side filter before any other filtering
55
- const allProjects = side
56
- ? allProjectsRaw.filter((p) => p.side === side || p.side === "both")
54
+ // Apply filterBy before any other filtering
55
+ const allProjects = filterBy
56
+ ? allProjectsRaw.filter((p) => {
57
+ var _a;
58
+ const v = (_a = p.custom_field_values) === null || _a === void 0 ? void 0 : _a[filterBy.field];
59
+ return v === filterBy.value || v === "both";
60
+ })
57
61
  : allProjectsRaw;
58
62
  const badgeField = schema.find((f) => f.display_position === "badge_overlay");
59
63
  const locationField = schema.find((f) => f.type === "location");
package/dist/types.d.ts CHANGED
@@ -39,8 +39,6 @@ export interface Project {
39
39
  image_url: string | null;
40
40
  is_featured: boolean;
41
41
  is_published?: boolean;
42
- /** Which website side this project belongs to. Omitted = show on all sides. */
43
- side?: "architectural" | "speedrail" | "both";
44
42
  custom_field_values: Record<string, CustomFieldValue>;
45
43
  created_at: string;
46
44
  updated_at: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,UAAU,CAAA;IAChE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,eAAe,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;CACpE;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,+FAA+F;IAC/F,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;CAC/D;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,aAAa,GAAG,IAAI,CAAA;AAEhF,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,eAAe,GAAG,WAAW,GAAG,MAAM,CAAA;IAC7C,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,KAAK,EAAE,CAAA;CACf"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,UAAU,CAAA;IAChE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,eAAe,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;CACpE;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,+FAA+F;IAC/F,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;CAC/D;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,aAAa,GAAG,IAAI,CAAA;AAEhF,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,KAAK,EAAE,CAAA;CACf"}
package/package.json CHANGED
@@ -1,8 +1,18 @@
1
1
  {
2
2
  "name": "@chiselandco/nexus",
3
- "version": "2.6.0",
3
+ "version": "3.0.0",
4
4
  "description": "Self-contained project portfolio components for Next.js App Router. Includes ProjectPortfolio, ProjectPortfolioClient, ProjectDetail, SimilarProjects, ProjectMenu, ProjectMenuClient, GalleryCarousel, FilterSidebar, and FilteredPortfolio. Pass a clientSlug and apiBase — done.",
5
- "keywords": ["nextjs", "react", "portfolio", "projects", "megamenu", "gallery", "filtering", "chiselandco", "nexus"],
5
+ "keywords": [
6
+ "nextjs",
7
+ "react",
8
+ "portfolio",
9
+ "projects",
10
+ "megamenu",
11
+ "gallery",
12
+ "filtering",
13
+ "chiselandco",
14
+ "nexus"
15
+ ],
6
16
  "license": "MIT",
7
17
  "type": "module",
8
18
  "main": "./dist/index.js",
@@ -14,7 +24,9 @@
14
24
  "types": "./dist/index.d.ts"
15
25
  }
16
26
  },
17
- "files": ["dist"],
27
+ "files": [
28
+ "dist"
29
+ ],
18
30
  "scripts": {
19
31
  "build": "tsc",
20
32
  "prepublishOnly": "npm run build"
@@ -25,6 +37,7 @@
25
37
  "react-dom": ">=18.0.0"
26
38
  },
27
39
  "devDependencies": {
40
+ "@types/node": "^26.0.0",
28
41
  "@types/react": "^18.0.0",
29
42
  "@types/react-dom": "^18.0.0",
30
43
  "next": "^15.0.0",