@airdraft/react-content 0.1.1 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.1.3](https://github.com/aevrHQ/airdraft/compare/v0.1.2...v0.1.3) (2026-05-23)
6
+
5
7
  ### 0.1.1 (2026-05-22)
6
8
 
7
9
 
@@ -0,0 +1,39 @@
1
+ import type { ReactNode } from 'react';
2
+ import { resolveImageDimensions } from '@airdraft/content';
3
+ import type { Entry } from '@airdraft/core';
4
+ export interface MediaFieldProps {
5
+ /** The field name in the entry's data (e.g. 'cover', 'attachment'). */
6
+ field: string;
7
+ /** The entry whose data is searched for the media value. */
8
+ entry: Entry;
9
+ alt?: string;
10
+ className?: string;
11
+ /** Class applied to the rendered `<img>` when no children render prop is given. */
12
+ imgClassName?: string;
13
+ /**
14
+ * Render prop — receives the resolved URL and MIME type hint (if known) and
15
+ * renders a custom element. Use this to plug in `next/image`, `<video>`, etc.
16
+ *
17
+ * @example
18
+ * <MediaField field="attachment" entry={entry}>
19
+ * {(url, mimeType) => <a href={url}>{mimeType ?? 'Download'}</a>}
20
+ * </MediaField>
21
+ */
22
+ children?: (url: string, mimeType: string | undefined) => ReactNode;
23
+ }
24
+ /**
25
+ * Renders a media asset resolved from an Airdraft entry field.
26
+ *
27
+ * - Images: `<img>`
28
+ * - Videos: `<video>`
29
+ * - Audio: `<audio>`
30
+ * - Other: `<a>` link
31
+ *
32
+ * Resolution chain: `{field}_media.url` → `{field}_url` → `/{field}` key.
33
+ * Returns `null` when no URL can be resolved.
34
+ *
35
+ * RSC-compatible — no `"use client"`.
36
+ */
37
+ export declare function MediaField({ field, entry, alt, className, imgClassName, children, }: MediaFieldProps): ReactNode;
38
+ export { resolveImageDimensions };
39
+ //# sourceMappingURL=MediaField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaField.d.ts","sourceRoot":"","sources":["../../src/fields/MediaField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAAmB,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAC3E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAE3C,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,KAAK,EAAE,KAAK,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,SAAS,CAAA;CACpE;AAkCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,GAAQ,EACR,SAAS,EACT,YAAY,EACZ,QAAQ,GACT,EAAE,eAAe,GAAG,SAAS,CAU7B;AAGD,OAAO,EAAE,sBAAsB,EAAE,CAAA"}
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { resolveMediaUrl, resolveImageDimensions } from '@airdraft/content';
3
+ function getMimeType(field, data) {
4
+ const mediaObj = data[`${field}_media`];
5
+ if (mediaObj && typeof mediaObj === 'object' && 'mimeType' in mediaObj) {
6
+ const m = mediaObj['mimeType'];
7
+ if (typeof m === 'string' && m)
8
+ return m;
9
+ }
10
+ return undefined;
11
+ }
12
+ function renderByMime(url, mimeType, alt, className) {
13
+ if (!mimeType || mimeType.startsWith('image/')) {
14
+ // eslint-disable-next-line @next/next/no-img-element
15
+ return _jsx("img", { src: url, alt: alt, className: className });
16
+ }
17
+ if (mimeType.startsWith('video/')) {
18
+ return (_jsx("video", { src: url, controls: true, className: className, children: _jsx("track", { kind: "captions" }) }));
19
+ }
20
+ if (mimeType.startsWith('audio/')) {
21
+ return _jsx("audio", { src: url, controls: true, className: className });
22
+ }
23
+ // Generic file — render a link
24
+ return (_jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: className, children: url.split('/').pop() ?? url }));
25
+ }
26
+ /**
27
+ * Renders a media asset resolved from an Airdraft entry field.
28
+ *
29
+ * - Images: `<img>`
30
+ * - Videos: `<video>`
31
+ * - Audio: `<audio>`
32
+ * - Other: `<a>` link
33
+ *
34
+ * Resolution chain: `{field}_media.url` → `{field}_url` → `/{field}` key.
35
+ * Returns `null` when no URL can be resolved.
36
+ *
37
+ * RSC-compatible — no `"use client"`.
38
+ */
39
+ export function MediaField({ field, entry, alt = '', className, imgClassName, children, }) {
40
+ const data = entry.data;
41
+ const url = resolveMediaUrl(field, data);
42
+ if (!url)
43
+ return null;
44
+ const mimeType = getMimeType(field, data);
45
+ if (children)
46
+ return children(url, mimeType);
47
+ return renderByMime(url, mimeType, alt, imgClassName ?? className);
48
+ }
49
+ // Re-export resolveImageDimensions for convenience when using MediaField with next/image
50
+ export { resolveImageDimensions };
51
+ //# sourceMappingURL=MediaField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaField.js","sourceRoot":"","sources":["../../src/fields/MediaField.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAwB3E,SAAS,WAAW,CAAC,KAAa,EAAE,IAA6B;IAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAA;IACvC,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,UAAU,IAAI,QAAQ,EAAE,CAAC;QACvE,MAAM,CAAC,GAAI,QAAoC,CAAC,UAAU,CAAC,CAAA;QAC3D,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC;YAAE,OAAO,CAAC,CAAA;IAC1C,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,YAAY,CAAC,GAAW,EAAE,QAA4B,EAAE,GAAW,EAAE,SAAkB;IAC9F,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/C,qDAAqD;QACrD,OAAO,cAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,GAAI,CAAA;IAC1D,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,CACL,gBAAO,GAAG,EAAE,GAAG,EAAE,QAAQ,QAAC,SAAS,EAAE,SAAS,YAC5C,gBAAO,IAAI,EAAC,UAAU,GAAG,GACnB,CACT,CAAA;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,gBAAO,GAAG,EAAE,GAAG,EAAE,QAAQ,QAAC,SAAS,EAAE,SAAS,GAAI,CAAA;IAC3D,CAAC;IACD,+BAA+B;IAC/B,OAAO,CACL,YAAG,IAAI,EAAE,GAAG,EAAE,MAAM,EAAC,QAAQ,EAAC,GAAG,EAAC,qBAAqB,EAAC,SAAS,EAAE,SAAS,YACzE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,GAAG,GAC1B,CACL,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,GAAG,GAAG,EAAE,EACR,SAAS,EACT,YAAY,EACZ,QAAQ,GACQ;IAChB,MAAM,IAAI,GAAG,KAAK,CAAC,IAA+B,CAAA;IAClD,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IACxC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IAErB,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAEzC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAE5C,OAAO,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,YAAY,IAAI,SAAS,CAAC,CAAA;AACpE,CAAC;AAED,yFAAyF;AACzF,OAAO,EAAE,sBAAsB,EAAE,CAAA"}
@@ -0,0 +1,33 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Entry } from '@airdraft/core';
3
+ export interface MediaListFieldProps {
4
+ /** The field name in the entry's data (e.g. 'gallery', 'attachments'). */
5
+ field: string;
6
+ /** The entry whose data is searched for the media array. */
7
+ entry: Entry;
8
+ className?: string;
9
+ /** Class applied to each rendered item's wrapper element. */
10
+ itemClassName?: string;
11
+ /**
12
+ * Render prop — receives the resolved URL, index, and MIME type hint for each item.
13
+ * Use this to render custom thumbnails, cards, links, etc.
14
+ *
15
+ * @example
16
+ * <MediaListField field="gallery" entry={entry}>
17
+ * {(url, i) => <img key={i} src={url} alt={`Gallery ${i + 1}`} />}
18
+ * </MediaListField>
19
+ */
20
+ children?: (url: string, index: number, mimeType: string | undefined) => ReactNode;
21
+ }
22
+ /**
23
+ * Renders a list of media assets from a `media` field with `multiple: true`.
24
+ *
25
+ * Each item is rendered using the `children` render prop when provided,
26
+ * otherwise falls back to an `<img>` tag (for images) or an `<a>` link.
27
+ *
28
+ * Returns `null` when no URLs can be resolved.
29
+ *
30
+ * RSC-compatible — no `"use client"`.
31
+ */
32
+ export declare function MediaListField({ field, entry, className, itemClassName, children, }: MediaListFieldProps): ReactNode;
33
+ //# sourceMappingURL=MediaListField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaListField.d.ts","sourceRoot":"","sources":["../../src/fields/MediaListField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAE3C,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,SAAS,CAAA;CACnF;AAmBD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,KAAK,EACL,SAAS,EACT,aAAa,EACb,QAAQ,GACT,EAAE,mBAAmB,GAAG,SAAS,CA+CjC"}
@@ -0,0 +1,61 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { resolveMediaUrls } from '@airdraft/content';
3
+ function getMediaItems(field, data) {
4
+ const mediasRaw = data[`${field}_medias`];
5
+ if (Array.isArray(mediasRaw)) {
6
+ return mediasRaw.flatMap((item) => {
7
+ if (item && typeof item === 'object' && 'url' in item) {
8
+ const obj = item;
9
+ const url = typeof obj['url'] === 'string' ? obj['url'] : undefined;
10
+ if (!url)
11
+ return [];
12
+ const mimeType = typeof obj['mimeType'] === 'string' ? obj['mimeType'] : undefined;
13
+ return [{ url, mimeType }];
14
+ }
15
+ return [];
16
+ });
17
+ }
18
+ return [];
19
+ }
20
+ /**
21
+ * Renders a list of media assets from a `media` field with `multiple: true`.
22
+ *
23
+ * Each item is rendered using the `children` render prop when provided,
24
+ * otherwise falls back to an `<img>` tag (for images) or an `<a>` link.
25
+ *
26
+ * Returns `null` when no URLs can be resolved.
27
+ *
28
+ * RSC-compatible — no `"use client"`.
29
+ */
30
+ export function MediaListField({ field, entry, className, itemClassName, children, }) {
31
+ const data = entry.data;
32
+ const items = getMediaItems(field, data);
33
+ // Fall back to URL-only resolution when medias sidecar is absent
34
+ const urls = items.length > 0 ? null : resolveMediaUrls(field, data);
35
+ if (items.length === 0 && (!urls || urls.length === 0))
36
+ return null;
37
+ if (children) {
38
+ if (items.length > 0) {
39
+ return (_jsx(_Fragment, { children: items.map(({ url, mimeType }, i) => children(url, i, mimeType)) }));
40
+ }
41
+ return _jsx(_Fragment, { children: (urls ?? []).map((url, i) => children(url, i, undefined)) });
42
+ }
43
+ // Default render — img for each item inside a wrapper
44
+ const resolvedItems = items.length > 0
45
+ ? items
46
+ : (urls ?? []).map((url) => ({ url, mimeType: undefined }));
47
+ return (_jsx("div", { className: className, children: resolvedItems.map(({ url, mimeType }, i) => {
48
+ if (!mimeType || mimeType.startsWith('image/')) {
49
+ // eslint-disable-next-line @next/next/no-img-element
50
+ return _jsx("img", { src: url, alt: `Item ${i + 1}`, className: itemClassName }, i);
51
+ }
52
+ if (mimeType.startsWith('video/')) {
53
+ return (_jsx("video", { src: url, controls: true, className: itemClassName, children: _jsx("track", { kind: "captions" }) }, i));
54
+ }
55
+ if (mimeType.startsWith('audio/')) {
56
+ return _jsx("audio", { src: url, controls: true, className: itemClassName }, i);
57
+ }
58
+ return (_jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: itemClassName, children: url.split('/').pop() ?? url }, i));
59
+ }) }));
60
+ }
61
+ //# sourceMappingURL=MediaListField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaListField.js","sourceRoot":"","sources":["../../src/fields/MediaListField.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,9 @@
1
1
  export { ImageField } from './ImageField.js';
2
2
  export type { ImageFieldProps } from './ImageField.js';
3
+ export { MediaField } from './MediaField.js';
4
+ export type { MediaFieldProps } from './MediaField.js';
5
+ export { MediaListField } from './MediaListField.js';
6
+ export type { MediaListFieldProps } from './MediaListField.js';
3
7
  export { BodyField } from './BodyField.js';
4
8
  export type { BodyFieldProps } from './BodyField.js';
5
9
  export { TagList } from './TagList.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAE1E,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAE9D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAE1E,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA"}
@@ -1,4 +1,6 @@
1
1
  export { ImageField } from './ImageField.js';
2
+ export { MediaField } from './MediaField.js';
3
+ export { MediaListField } from './MediaListField.js';
2
4
  export { BodyField } from './BodyField.js';
3
5
  export { TagList } from './TagList.js';
4
6
  export { AuthorByline } from './AuthorByline.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAG5C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAGtC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAG5C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAG5C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAGpD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAGtC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,35 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Entry } from '@airdraft/core';
3
+ export interface AirdraftMediaProps {
4
+ /** The field name in the entry's data (e.g. 'cover', 'attachment'). */
5
+ field: string;
6
+ /** The entry whose data is searched for the media value. */
7
+ entry: Entry;
8
+ alt?: string;
9
+ className?: string;
10
+ /** Passed through to next/image when rendering images. */
11
+ priority?: boolean;
12
+ /**
13
+ * Render prop — receives the resolved URL and MIME type hint.
14
+ * Use this to plug in custom rendering for non-image types.
15
+ *
16
+ * @example
17
+ * <AirdraftMedia field="attachment" entry={entry}>
18
+ * {(url, mimeType) => <a href={url}>{mimeType ?? 'Download'}</a>}
19
+ * </AirdraftMedia>
20
+ */
21
+ children?: (url: string, mimeType: string | undefined) => ReactNode;
22
+ }
23
+ /**
24
+ * Next.js-aware media component that handles all media types.
25
+ *
26
+ * - Images (image/* MIME or unknown type): uses `next/image` with dimensions when available
27
+ * - Videos: `<video controls>`
28
+ * - Audio: `<audio controls>`
29
+ * - Other: `<a>` download link
30
+ *
31
+ * Returns `null` when no URL can be resolved.
32
+ * RSC-compatible — no `"use client"`.
33
+ */
34
+ export declare function AirdraftMedia({ field, entry, alt, className, priority, children, }: AirdraftMediaProps): ReactNode;
35
+ //# sourceMappingURL=AirdraftMedia.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AirdraftMedia.d.ts","sourceRoot":"","sources":["../../src/next/AirdraftMedia.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAG3C,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,KAAK,EAAE,KAAK,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,SAAS,CAAA;CACpE;AAWD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,KAAK,EACL,GAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,GACT,EAAE,kBAAkB,GAAG,SAAS,CAiDhC"}
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Image from 'next/image';
3
+ import { resolveMediaUrl, resolveImageDimensions } from '@airdraft/content';
4
+ function getMimeType(field, data) {
5
+ const mediaObj = data[`${field}_media`];
6
+ if (mediaObj && typeof mediaObj === 'object' && 'mimeType' in mediaObj) {
7
+ const m = mediaObj['mimeType'];
8
+ if (typeof m === 'string' && m)
9
+ return m;
10
+ }
11
+ return undefined;
12
+ }
13
+ /**
14
+ * Next.js-aware media component that handles all media types.
15
+ *
16
+ * - Images (image/* MIME or unknown type): uses `next/image` with dimensions when available
17
+ * - Videos: `<video controls>`
18
+ * - Audio: `<audio controls>`
19
+ * - Other: `<a>` download link
20
+ *
21
+ * Returns `null` when no URL can be resolved.
22
+ * RSC-compatible — no `"use client"`.
23
+ */
24
+ export function AirdraftMedia({ field, entry, alt = '', className, priority, children, }) {
25
+ const data = entry.data;
26
+ const src = resolveMediaUrl(field, data);
27
+ if (!src)
28
+ return null;
29
+ const mimeType = getMimeType(field, data);
30
+ if (children)
31
+ return children(src, mimeType);
32
+ // Images (or unknown MIME) — use next/image
33
+ if (!mimeType || mimeType.startsWith('image/')) {
34
+ const dims = resolveImageDimensions(field, data);
35
+ if (dims) {
36
+ return (_jsx(Image, { src: src, alt: alt, width: dims.width, height: dims.height, className: className, priority: priority }));
37
+ }
38
+ return (_jsx("div", { className: className, style: { position: 'relative' }, children: _jsx(Image, { src: src, alt: alt, fill: true, priority: priority }) }));
39
+ }
40
+ if (mimeType.startsWith('video/')) {
41
+ return (_jsx("video", { src: src, controls: true, className: className, children: _jsx("track", { kind: "captions" }) }));
42
+ }
43
+ if (mimeType.startsWith('audio/')) {
44
+ return _jsx("audio", { src: src, controls: true, className: className });
45
+ }
46
+ // Generic file — render a link
47
+ return (_jsx("a", { href: src, target: "_blank", rel: "noopener noreferrer", className: className, children: src.split('/').pop() ?? src }));
48
+ }
49
+ //# sourceMappingURL=AirdraftMedia.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AirdraftMedia.js","sourceRoot":"","sources":["../../src/next/AirdraftMedia.tsx"],"names":[],"mappings":";AACA,OAAO,KAAK,MAAM,YAAY,CAAA;AAE9B,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAuB3E,SAAS,WAAW,CAAC,KAAa,EAAE,IAA6B;IAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAA;IACvC,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,UAAU,IAAI,QAAQ,EAAE,CAAC;QACvE,MAAM,CAAC,GAAI,QAAoC,CAAC,UAAU,CAAC,CAAA;QAC3D,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC;YAAE,OAAO,CAAC,CAAA;IAC1C,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,KAAK,EACL,KAAK,EACL,GAAG,GAAG,EAAE,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,GACW;IACnB,MAAM,IAAI,GAAG,KAAK,CAAC,IAA+B,CAAA;IAClD,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IACxC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IAErB,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAEzC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAE5C,4CAA4C;IAC5C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,sBAAsB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAChD,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CACL,KAAC,KAAK,IACJ,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,IAAI,CAAC,KAAK,EACjB,MAAM,EAAE,IAAI,CAAC,MAAM,EACnB,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,GAClB,CACH,CAAA;QACH,CAAC;QACD,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,YACxD,KAAC,KAAK,IAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,QAAC,QAAQ,EAAE,QAAQ,GAAI,GAClD,CACP,CAAA;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,CACL,gBAAO,GAAG,EAAE,GAAG,EAAE,QAAQ,QAAC,SAAS,EAAE,SAAS,YAC5C,gBAAO,IAAI,EAAC,UAAU,GAAG,GACnB,CACT,CAAA;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,gBAAO,GAAG,EAAE,GAAG,EAAE,QAAQ,QAAC,SAAS,EAAE,SAAS,GAAI,CAAA;IAC3D,CAAC;IAED,+BAA+B;IAC/B,OAAO,CACL,YAAG,IAAI,EAAE,GAAG,EAAE,MAAM,EAAC,QAAQ,EAAC,GAAG,EAAC,qBAAqB,EAAC,SAAS,EAAE,SAAS,YACzE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,GAAG,GAC1B,CACL,CAAA;AACH,CAAC"}
@@ -1,3 +1,5 @@
1
1
  export { AirdraftImage } from './AirdraftImage.js';
2
2
  export type { AirdraftImageProps } from './AirdraftImage.js';
3
+ export { AirdraftMedia } from './AirdraftMedia.js';
4
+ export type { AirdraftMediaProps } from './AirdraftMedia.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
@@ -1,2 +1,3 @@
1
1
  export { AirdraftImage } from './AirdraftImage.js';
2
+ export { AirdraftMedia } from './AirdraftMedia.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAGlD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@airdraft/react-content",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Airdraft React components for rendering CMS content — field atoms, entry cards, and blog recipes",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/react": "^19.0.0",
49
- "next": "^15.0.0",
49
+ "next": "^16.1.7",
50
50
  "react": "^18.0.0",
51
51
  "typescript": "^5.7.2"
52
52
  },