react-datocms 8.0.0-0 → 8.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 +35 -15
- package/dist/cjs/ContentLink/index.js +104 -0
- package/dist/cjs/ContentLink/index.js.map +1 -0
- package/dist/cjs/Image/index.js +3 -3
- package/dist/cjs/Image/index.js.map +1 -1
- package/dist/cjs/Image/utils.js +3 -3
- package/dist/cjs/Image/utils.js.map +1 -1
- package/dist/cjs/SRCImage/index.js +3 -4
- package/dist/cjs/SRCImage/index.js.map +1 -1
- package/dist/cjs/SRCImage/utils.js +4 -4
- package/dist/cjs/SRCImage/utils.js.map +1 -1
- package/dist/cjs/Seo/nextUtils.js +1 -2
- package/dist/cjs/Seo/nextUtils.js.map +1 -1
- package/dist/cjs/Seo/remixUtils.js +2 -3
- package/dist/cjs/Seo/remixUtils.js.map +1 -1
- package/dist/cjs/Seo/renderMetaTags.js +1 -2
- package/dist/cjs/Seo/renderMetaTags.js.map +1 -1
- package/dist/cjs/Seo/renderMetaTagsToString.js +1 -2
- package/dist/cjs/Seo/renderMetaTagsToString.js.map +1 -1
- package/dist/cjs/StructuredText/index.js +34 -11
- package/dist/cjs/StructuredText/index.js.map +1 -1
- package/dist/cjs/VideoPlayer/index.js +45 -14
- package/dist/cjs/VideoPlayer/index.js.map +1 -1
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/useContentLink/index.js +111 -0
- package/dist/cjs/useContentLink/index.js.map +1 -0
- package/dist/cjs/useQuerySubscription/index.js +1 -2
- package/dist/cjs/useQuerySubscription/index.js.map +1 -1
- package/dist/cjs/useSiteSearch/index.js +3 -10
- package/dist/cjs/useSiteSearch/index.js.map +1 -1
- package/dist/esm/ContentLink/index.js +101 -0
- package/dist/esm/ContentLink/index.js.map +1 -0
- package/dist/esm/Image/index.js +3 -3
- package/dist/esm/Image/index.js.map +1 -1
- package/dist/esm/SRCImage/index.js +2 -2
- package/dist/esm/SRCImage/index.js.map +1 -1
- package/dist/esm/StructuredText/index.js +22 -4
- package/dist/esm/StructuredText/index.js.map +1 -1
- package/dist/esm/VideoPlayer/index.js +30 -9
- package/dist/esm/VideoPlayer/index.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/useContentLink/index.js +105 -0
- package/dist/esm/useContentLink/index.js.map +1 -0
- package/dist/esm/useSiteSearch/index.js +2 -8
- package/dist/esm/useSiteSearch/index.js.map +1 -1
- package/dist/types/ContentLink/index.d.ts +88 -0
- package/dist/types/Image/index.d.ts +8 -1
- package/dist/types/Image/utils.d.ts +0 -1
- package/dist/types/SRCImage/index.d.ts +10 -3
- package/dist/types/SRCImage/utils.d.ts +1 -2
- package/dist/types/Seo/renderMetaTags.d.ts +0 -1
- package/dist/types/StructuredText/index.d.ts +20 -9
- package/dist/types/VideoPlayer/index.d.ts +6 -6
- package/dist/types/index.d.ts +2 -0
- package/dist/types/useContentLink/index.d.ts +91 -0
- package/dist/types/useSiteSearch/index.d.ts +3 -1
- package/dist/types/useVideoPlayer/index.d.ts +2 -2
- package/package.json +32 -17
- package/src/ContentLink/index.tsx +134 -0
- package/src/Image/__tests__/__snapshots__/index.test.tsx.snap +20 -0
- package/src/Image/index.tsx +11 -1
- package/src/SRCImage/__tests__/__snapshots__/index.test.tsx.snap +7 -0
- package/src/SRCImage/index.tsx +11 -2
- package/src/SRCImage/utils.tsx +1 -1
- package/src/StructuredText/__tests__/__snapshots__/index.test.tsx.snap +17 -0
- package/src/StructuredText/__tests__/index.test.tsx +27 -1
- package/src/StructuredText/index.tsx +80 -15
- package/src/VideoPlayer/index.tsx +43 -17
- package/src/index.ts +3 -0
- package/src/useContentLink/index.ts +187 -0
- package/src/useSiteSearch/index.tsx +6 -5
- package/src/useVideoPlayer/index.ts +2 -2
- package/dist/cjs/VideoPlayer/lazy.js +0 -35
- package/dist/cjs/VideoPlayer/lazy.js.map +0 -1
- package/dist/esm/VideoPlayer/lazy.js +0 -9
- package/dist/esm/VideoPlayer/lazy.js.map +0 -1
- package/dist/types/VideoPlayer/lazy.d.ts +0 -6
- package/src/VideoPlayer/lazy.tsx +0 -26
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
renderNodeRule,
|
|
9
9
|
} from 'datocms-structured-text-generic-html-renderer';
|
|
10
10
|
import {
|
|
11
|
+
type CdaStructuredTextRecord,
|
|
12
|
+
type CdaStructuredTextValue,
|
|
11
13
|
type Node,
|
|
12
14
|
RenderError,
|
|
13
15
|
type RenderResult,
|
|
@@ -15,8 +17,10 @@ import {
|
|
|
15
17
|
type Document as StructuredTextDocument,
|
|
16
18
|
type StructuredText as StructuredTextGraphQlResponse,
|
|
17
19
|
type Record as StructuredTextGraphQlResponseRecord,
|
|
20
|
+
type TypesafeCdaStructuredTextValue,
|
|
18
21
|
type TypesafeStructuredText as TypesafeStructuredTextGraphQlResponse,
|
|
19
22
|
isBlock,
|
|
23
|
+
isInlineBlock,
|
|
20
24
|
isInlineItem,
|
|
21
25
|
isItemLink,
|
|
22
26
|
isStructuredText,
|
|
@@ -25,14 +29,11 @@ import React, { type ReactElement, cloneElement, isValidElement } from 'react';
|
|
|
25
29
|
|
|
26
30
|
export { renderNodeRule, renderMarkRule, RenderError };
|
|
27
31
|
|
|
28
|
-
// deprecated
|
|
29
|
-
export { renderNodeRule as renderRule };
|
|
30
|
-
|
|
31
32
|
export type {
|
|
32
|
-
StructuredTextGraphQlResponse,
|
|
33
|
-
TypesafeStructuredTextGraphQlResponse,
|
|
34
33
|
StructuredTextDocument,
|
|
35
|
-
|
|
34
|
+
CdaStructuredTextValue,
|
|
35
|
+
TypesafeCdaStructuredTextValue,
|
|
36
|
+
CdaStructuredTextRecord,
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
type AdapterReturn = ReactElement | string | null;
|
|
@@ -80,12 +81,16 @@ export type RenderBlockContext<R extends StructuredTextGraphQlResponseRecord> =
|
|
|
80
81
|
};
|
|
81
82
|
|
|
82
83
|
export type StructuredTextPropTypes<
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
BlockRecord extends
|
|
85
|
+
StructuredTextGraphQlResponseRecord = StructuredTextGraphQlResponseRecord,
|
|
86
|
+
LinkRecord extends
|
|
87
|
+
StructuredTextGraphQlResponseRecord = StructuredTextGraphQlResponseRecord,
|
|
88
|
+
InlineBlockRecord extends
|
|
89
|
+
StructuredTextGraphQlResponseRecord = StructuredTextGraphQlResponseRecord,
|
|
85
90
|
> = {
|
|
86
91
|
/** The actual field value you get from DatoCMS **/
|
|
87
92
|
data:
|
|
88
|
-
| StructuredTextGraphQlResponse<
|
|
93
|
+
| StructuredTextGraphQlResponse<BlockRecord, LinkRecord, InlineBlockRecord>
|
|
89
94
|
| StructuredTextDocument
|
|
90
95
|
| Node
|
|
91
96
|
| null
|
|
@@ -96,14 +101,20 @@ export type StructuredTextPropTypes<
|
|
|
96
101
|
customMarkRules?: RenderMarkRule<H, T, F>[];
|
|
97
102
|
/** Fuction that converts an 'inlineItem' node into React **/
|
|
98
103
|
renderInlineRecord?: (
|
|
99
|
-
context: RenderInlineRecordContext<
|
|
104
|
+
context: RenderInlineRecordContext<LinkRecord>,
|
|
100
105
|
) => ReactElement | null;
|
|
101
106
|
/** Fuction that converts an 'itemLink' node into React **/
|
|
102
107
|
renderLinkToRecord?: (
|
|
103
|
-
context: RenderRecordLinkContext<
|
|
108
|
+
context: RenderRecordLinkContext<LinkRecord>,
|
|
104
109
|
) => ReactElement | null;
|
|
105
110
|
/** Fuction that converts a 'block' node into React **/
|
|
106
|
-
renderBlock?: (
|
|
111
|
+
renderBlock?: (
|
|
112
|
+
context: RenderBlockContext<BlockRecord>,
|
|
113
|
+
) => ReactElement | null;
|
|
114
|
+
/** Fuction that converts an 'inlineBlock' node into React **/
|
|
115
|
+
renderInlineBlock?: (
|
|
116
|
+
context: RenderBlockContext<InlineBlockRecord>,
|
|
117
|
+
) => ReactElement | null;
|
|
107
118
|
/** Function that converts 'link' and 'itemLink' `meta` into HTML props */
|
|
108
119
|
metaTransformer?: TransformMetaFn;
|
|
109
120
|
/** Fuction that converts a simple string text into React **/
|
|
@@ -117,13 +128,18 @@ export type StructuredTextPropTypes<
|
|
|
117
128
|
};
|
|
118
129
|
|
|
119
130
|
export function StructuredText<
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
BlockRecord extends
|
|
132
|
+
StructuredTextGraphQlResponseRecord = StructuredTextGraphQlResponseRecord,
|
|
133
|
+
LinkRecord extends
|
|
134
|
+
StructuredTextGraphQlResponseRecord = StructuredTextGraphQlResponseRecord,
|
|
135
|
+
InlineBlockRecord extends
|
|
136
|
+
StructuredTextGraphQlResponseRecord = StructuredTextGraphQlResponseRecord,
|
|
122
137
|
>({
|
|
123
138
|
data,
|
|
124
139
|
renderInlineRecord,
|
|
125
140
|
renderLinkToRecord,
|
|
126
141
|
renderBlock,
|
|
142
|
+
renderInlineBlock,
|
|
127
143
|
renderText,
|
|
128
144
|
renderNode,
|
|
129
145
|
renderFragment,
|
|
@@ -131,7 +147,11 @@ export function StructuredText<
|
|
|
131
147
|
customRules,
|
|
132
148
|
customNodeRules,
|
|
133
149
|
metaTransformer,
|
|
134
|
-
}: StructuredTextPropTypes<
|
|
150
|
+
}: StructuredTextPropTypes<
|
|
151
|
+
BlockRecord,
|
|
152
|
+
LinkRecord,
|
|
153
|
+
InlineBlockRecord
|
|
154
|
+
>): ReactElement | null {
|
|
135
155
|
const result = render(data, {
|
|
136
156
|
adapter: {
|
|
137
157
|
renderText: renderText || defaultAdapter.renderText,
|
|
@@ -234,6 +254,35 @@ export function StructuredText<
|
|
|
234
254
|
|
|
235
255
|
return appendKeyToValidElement(renderBlock({ record: item }), key);
|
|
236
256
|
}),
|
|
257
|
+
renderNodeRule(isInlineBlock, ({ node, key }) => {
|
|
258
|
+
if (!renderInlineBlock) {
|
|
259
|
+
throw new RenderError(
|
|
260
|
+
`The Structured Text document contains an 'inlineBlock' node, but no 'renderInlineBlock' prop is specified!`,
|
|
261
|
+
node,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!(isStructuredText(data) && data.inlineBlocks)) {
|
|
266
|
+
throw new RenderError(
|
|
267
|
+
`The document contains an 'inlineBlock' node, but the passed data prop is not a Structured Text GraphQL response, or data.inlineBlocks is not present!`,
|
|
268
|
+
node,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const item = data.inlineBlocks.find((item) => item.id === node.item);
|
|
273
|
+
|
|
274
|
+
if (!item) {
|
|
275
|
+
throw new RenderError(
|
|
276
|
+
`The Structured Text document contains an 'inlineBlock' node, but cannot find a record with ID ${node.item} inside data.inlineBlocks!`,
|
|
277
|
+
node,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return appendKeyToValidElement(
|
|
282
|
+
renderInlineBlock({ record: item }),
|
|
283
|
+
key,
|
|
284
|
+
);
|
|
285
|
+
}),
|
|
237
286
|
...(customNodeRules || customRules || []),
|
|
238
287
|
],
|
|
239
288
|
});
|
|
@@ -244,3 +293,19 @@ export function StructuredText<
|
|
|
244
293
|
|
|
245
294
|
return result || null;
|
|
246
295
|
}
|
|
296
|
+
|
|
297
|
+
// ============================================================================
|
|
298
|
+
// DEPRECATED EXPORTS - kept for backward compatibility
|
|
299
|
+
// ============================================================================
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* @deprecated Use renderNodeRule instead
|
|
303
|
+
*/
|
|
304
|
+
export { renderNodeRule as renderRule };
|
|
305
|
+
|
|
306
|
+
/** @deprecated Use CdaStructuredTextValue */
|
|
307
|
+
export type { StructuredTextGraphQlResponse };
|
|
308
|
+
/** @deprecated Use TypesafeCdaStructuredTextValue */
|
|
309
|
+
export type { TypesafeStructuredTextGraphQlResponse };
|
|
310
|
+
/** @deprecated Use CdaStructuredTextRecord */
|
|
311
|
+
export type { StructuredTextGraphQlResponseRecord };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
|
|
3
3
|
// This file defines a React component that easily displays a video player using
|
|
4
4
|
// data stored on DatoCMS and retrieved via DatoCMS GraphQL API. The component
|
|
@@ -8,24 +8,26 @@
|
|
|
8
8
|
//
|
|
9
9
|
// [1]: https://www.mux.com/player
|
|
10
10
|
|
|
11
|
-
import React, { forwardRef } from
|
|
11
|
+
import React, { forwardRef } from 'react';
|
|
12
12
|
|
|
13
13
|
// We use and extend Typescript types defined in the MUX player.
|
|
14
14
|
|
|
15
|
-
import type MuxPlayerElement from
|
|
16
|
-
import type { MuxPlayerProps } from
|
|
15
|
+
import type MuxPlayerElement from '@mux/mux-player';
|
|
16
|
+
import type { MuxPlayerProps } from '@mux/mux-player-react';
|
|
17
17
|
|
|
18
18
|
// React MUX player is made available in two flavours: eager and lazy loaded. We
|
|
19
|
-
// choose to use the
|
|
20
|
-
//
|
|
19
|
+
// choose to use the lazy version to avoid loading the web component uselessly.
|
|
20
|
+
// MUX player lazy version loads internally the eager version using
|
|
21
|
+
// `React.lazy()`.
|
|
21
22
|
|
|
22
|
-
import MuxPlayer from
|
|
23
|
+
import MuxPlayer from '@mux/mux-player-react/lazy';
|
|
23
24
|
|
|
24
25
|
// The core of this component is the `useVideoPlayer` hook: it takes
|
|
25
26
|
// data from DatoCMS GraphQL API and returns props as expected by the
|
|
26
27
|
// `<MuxPlayer />` component.
|
|
27
28
|
|
|
28
|
-
import {
|
|
29
|
+
import { decodeStega } from '@datocms/content-link';
|
|
30
|
+
import { useVideoPlayer } from '../useVideoPlayer/index.js';
|
|
29
31
|
|
|
30
32
|
type Maybe<T> = T | null;
|
|
31
33
|
type Possibly<T> = Maybe<T> | undefined;
|
|
@@ -36,6 +38,8 @@ type Possibly<T> = Maybe<T> | undefined;
|
|
|
36
38
|
export type Video = {
|
|
37
39
|
/** Title attribute (`title`) for the video */
|
|
38
40
|
title?: Possibly<string>;
|
|
41
|
+
/** Alt attribute used for content link integration (passed as data-datocms-content-link-source) */
|
|
42
|
+
alt?: Possibly<string>;
|
|
39
43
|
/** The height of the video */
|
|
40
44
|
height?: Possibly<number>;
|
|
41
45
|
/** The width of the video */
|
|
@@ -62,18 +66,17 @@ export type VideoPlayerProps = MuxPlayerProps & {
|
|
|
62
66
|
data?: Video;
|
|
63
67
|
};
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
VideoPlayerProps
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
export const VideoPlayer: VideoPlayerType = forwardRef<
|
|
69
|
+
export const VideoPlayer: (
|
|
70
|
+
props: VideoPlayerProps,
|
|
71
|
+
) => ReturnType<typeof MuxPlayer> = forwardRef<
|
|
70
72
|
MuxPlayerElement,
|
|
71
73
|
VideoPlayerProps
|
|
72
|
-
>((props
|
|
74
|
+
>((props, ref) => {
|
|
73
75
|
const {
|
|
74
76
|
data = {},
|
|
75
77
|
disableCookies = true,
|
|
76
|
-
|
|
78
|
+
disableTracking = true,
|
|
79
|
+
preload = 'metadata',
|
|
77
80
|
style: styleFromProps,
|
|
78
81
|
...rest
|
|
79
82
|
} = props;
|
|
@@ -92,6 +95,29 @@ export const VideoPlayer: VideoPlayerType = forwardRef<
|
|
|
92
95
|
...styleFromProps,
|
|
93
96
|
};
|
|
94
97
|
|
|
98
|
+
// Extract alt for DatoCMS Content Link integration
|
|
99
|
+
// See: https://github.com/datocms/content-link
|
|
100
|
+
const { alt } = data;
|
|
101
|
+
|
|
102
|
+
// Only include data-datocms-content-link-source if alt contains stega encoding
|
|
103
|
+
const contentLinkSource = alt && decodeStega(alt) ? alt : undefined;
|
|
104
|
+
|
|
105
|
+
// Note: Custom data-* attributes can be passed to the underlying <mux-player> web component
|
|
106
|
+
// in two ways:
|
|
107
|
+
//
|
|
108
|
+
// 1. Kebab-case (passes through as-is):
|
|
109
|
+
// <VideoPlayer data-player-id="my-player" />
|
|
110
|
+
//
|
|
111
|
+
// 2. CamelCase (auto-converts to kebab-case):
|
|
112
|
+
// <VideoPlayer dataPlayerId="my-player" />
|
|
113
|
+
//
|
|
114
|
+
// Both result in: <mux-player data-player-id="my-player">
|
|
115
|
+
//
|
|
116
|
+
// The MuxPlayer React component transforms camelCase props to kebab-case to match
|
|
117
|
+
// web component attribute conventions. Any props not explicitly handled by VideoPlayer
|
|
118
|
+
// are spread via {...rest} and forwarded to MuxPlayer, which then applies them to
|
|
119
|
+
// the underlying <mux-player> element.
|
|
120
|
+
|
|
95
121
|
return (
|
|
96
122
|
<MuxPlayer
|
|
97
123
|
ref={ref}
|
|
@@ -99,12 +125,12 @@ export const VideoPlayer: VideoPlayerType = forwardRef<
|
|
|
99
125
|
preload={preload}
|
|
100
126
|
title={title}
|
|
101
127
|
disableCookies={disableCookies}
|
|
128
|
+
disableTracking={disableTracking}
|
|
102
129
|
playbackId={playbackId}
|
|
103
130
|
style={style}
|
|
104
131
|
placeholder={placeholder}
|
|
132
|
+
data-datocms-content-link-source={contentLinkSource}
|
|
105
133
|
{...rest}
|
|
106
134
|
/>
|
|
107
135
|
);
|
|
108
136
|
});
|
|
109
|
-
|
|
110
|
-
export default VideoPlayer;
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { type Controller, createController } from '@datocms/content-link';
|
|
4
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
5
|
+
|
|
6
|
+
// Re-export types and utilities from @datocms/content-link for convenience
|
|
7
|
+
export { decodeStega, stripStega } from '@datocms/content-link';
|
|
8
|
+
export type { Controller } from '@datocms/content-link';
|
|
9
|
+
|
|
10
|
+
export type UseContentLinkOptions = {
|
|
11
|
+
/**
|
|
12
|
+
* Whether the controller is enabled, or an options object.
|
|
13
|
+
* - Pass `true` (default): Enables the controller with stega encoding preserved (allows controller recreation)
|
|
14
|
+
* - Pass `false`: Disables the controller completely
|
|
15
|
+
* - Pass `{ stripStega: true }`: Enables the controller and strips stega encoding after stamping
|
|
16
|
+
*
|
|
17
|
+
* When stripStega is false (default): Stega encoding remains in the DOM, allowing controllers
|
|
18
|
+
* to be disposed and recreated on the same page. The invisible characters don't affect display
|
|
19
|
+
* but preserve the source of truth.
|
|
20
|
+
*
|
|
21
|
+
* When stripStega is true: Stega encoding is permanently removed from text nodes, providing
|
|
22
|
+
* clean textContent for programmatic access. However, recreating a controller on the same page
|
|
23
|
+
* won't detect elements since the encoding is lost.
|
|
24
|
+
*/
|
|
25
|
+
enabled?: boolean | { stripStega: boolean };
|
|
26
|
+
/** Callback when Web Previews plugin requests navigation */
|
|
27
|
+
onNavigateTo?: (path: string) => void;
|
|
28
|
+
/** Ref to limit scanning to this root instead of document */
|
|
29
|
+
root?: React.RefObject<HTMLElement>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export interface ClickToEditOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Whether to automatically scroll to the nearest editable element if none
|
|
35
|
+
* is currently visible in the viewport when click-to-edit mode is enabled.
|
|
36
|
+
*
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
scrollToNearestTarget?: boolean;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Only enable click-to-edit on devices that support hover (i.e., non-touch devices).
|
|
43
|
+
* Uses `window.matchMedia('(hover: hover)')` to detect hover capability.
|
|
44
|
+
*
|
|
45
|
+
* This is useful to avoid showing overlays on touch devices where they may
|
|
46
|
+
* interfere with normal scrolling and tapping behavior.
|
|
47
|
+
*
|
|
48
|
+
* When set to `true` on a touch-only device, click-to-edit will not be
|
|
49
|
+
* automatically enabled, but users can still toggle it manually using
|
|
50
|
+
* the Alt/Option key.
|
|
51
|
+
*
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
hoverOnly?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type UseContentLinkResult = {
|
|
58
|
+
/** The controller instance, or null if disabled */
|
|
59
|
+
controller: Controller | null;
|
|
60
|
+
/** Enable click-to-edit overlays */
|
|
61
|
+
enableClickToEdit: (options?: ClickToEditOptions) => void;
|
|
62
|
+
/** Disable click-to-edit overlays */
|
|
63
|
+
disableClickToEdit: () => void;
|
|
64
|
+
/** Check if click-to-edit is enabled */
|
|
65
|
+
isClickToEditEnabled: () => boolean;
|
|
66
|
+
/** Highlight all editable elements */
|
|
67
|
+
flashAll: (scrollToNearestTarget?: boolean) => void;
|
|
68
|
+
/** Notify Web Previews plugin of current path */
|
|
69
|
+
setCurrentPath: (path: string) => void;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Hook to control the ContentLink controller for Visual Editing.
|
|
74
|
+
*
|
|
75
|
+
* This hook provides low-level access to the @datocms/content-link controller,
|
|
76
|
+
* allowing you to programmatically control click-to-edit overlays and
|
|
77
|
+
* communicate with the DatoCMS Web Previews plugin.
|
|
78
|
+
*
|
|
79
|
+
* @param options - Configuration options for the controller
|
|
80
|
+
* @returns An object containing the controller instance and its methods
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* import { useContentLink } from 'react-datocms';
|
|
85
|
+
*
|
|
86
|
+
* function MyComponent() {
|
|
87
|
+
* const { enableClickToEdit, flashAll } = useContentLink({
|
|
88
|
+
* onNavigateTo: (path) => router.push(path),
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* return (
|
|
92
|
+
* <button onClick={() => enableClickToEdit({ scrollToNearestTarget: true })}>
|
|
93
|
+
* Enable Editing
|
|
94
|
+
* </button>
|
|
95
|
+
* );
|
|
96
|
+
* }
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export function useContentLink(
|
|
100
|
+
options: UseContentLinkOptions = {},
|
|
101
|
+
): UseContentLinkResult {
|
|
102
|
+
const { enabled = true, onNavigateTo, root } = options;
|
|
103
|
+
|
|
104
|
+
const controllerRef = useRef<Controller | null>(null);
|
|
105
|
+
// Store the callback in a ref to avoid recreating the controller when it changes
|
|
106
|
+
const onNavigateToRef = useRef(onNavigateTo);
|
|
107
|
+
|
|
108
|
+
// Keep the callback ref up to date
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
onNavigateToRef.current = onNavigateTo;
|
|
111
|
+
}, [onNavigateTo]);
|
|
112
|
+
|
|
113
|
+
// Create/dispose controller based on enabled flag and root only
|
|
114
|
+
// The onNavigateTo callback is accessed via ref, so changes don't trigger recreation
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
// Check if controller is disabled
|
|
117
|
+
const isEnabled =
|
|
118
|
+
enabled === true || (typeof enabled === 'object' && enabled !== null);
|
|
119
|
+
|
|
120
|
+
if (!isEnabled) {
|
|
121
|
+
if (controllerRef.current) {
|
|
122
|
+
controllerRef.current.dispose();
|
|
123
|
+
controllerRef.current = null;
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Extract stripStega option if enabled is an object
|
|
129
|
+
const stripStega = typeof enabled === 'object' ? enabled.stripStega : false;
|
|
130
|
+
|
|
131
|
+
const controller = createController({
|
|
132
|
+
onNavigateTo: (path: string) => onNavigateToRef.current?.(path),
|
|
133
|
+
root: root?.current || undefined,
|
|
134
|
+
stripStega,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
controllerRef.current = controller;
|
|
138
|
+
|
|
139
|
+
return () => {
|
|
140
|
+
controller.dispose();
|
|
141
|
+
controllerRef.current = null;
|
|
142
|
+
};
|
|
143
|
+
}, [enabled, root]);
|
|
144
|
+
|
|
145
|
+
// Stable method references that call through to the controller
|
|
146
|
+
const enableClickToEdit = useCallback((opts?: ClickToEditOptions) => {
|
|
147
|
+
// If hoverOnly is true, check if the device supports hover
|
|
148
|
+
if (
|
|
149
|
+
opts?.hoverOnly &&
|
|
150
|
+
(typeof window === 'undefined' ||
|
|
151
|
+
!window.matchMedia('(hover: hover)').matches)
|
|
152
|
+
) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
controllerRef.current?.enableClickToEdit(
|
|
157
|
+
opts?.scrollToNearestTarget
|
|
158
|
+
? { scrollToNearestTarget: opts.scrollToNearestTarget }
|
|
159
|
+
: undefined,
|
|
160
|
+
);
|
|
161
|
+
}, []);
|
|
162
|
+
|
|
163
|
+
const disableClickToEdit = useCallback(() => {
|
|
164
|
+
controllerRef.current?.disableClickToEdit();
|
|
165
|
+
}, []);
|
|
166
|
+
|
|
167
|
+
const isClickToEditEnabled = useCallback(() => {
|
|
168
|
+
return controllerRef.current?.isClickToEditEnabled() ?? false;
|
|
169
|
+
}, []);
|
|
170
|
+
|
|
171
|
+
const flashAll = useCallback((scrollToNearestTarget?: boolean) => {
|
|
172
|
+
controllerRef.current?.flashAll(scrollToNearestTarget);
|
|
173
|
+
}, []);
|
|
174
|
+
|
|
175
|
+
const setCurrentPath = useCallback((path: string) => {
|
|
176
|
+
controllerRef.current?.setCurrentPath(path);
|
|
177
|
+
}, []);
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
controller: controllerRef.current,
|
|
181
|
+
enableClickToEdit,
|
|
182
|
+
disableClickToEdit,
|
|
183
|
+
isClickToEditEnabled,
|
|
184
|
+
flashAll,
|
|
185
|
+
setCurrentPath,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
@@ -15,6 +15,7 @@ type SearchResultInstancesHrefSchema = {
|
|
|
15
15
|
fuzzy?: boolean;
|
|
16
16
|
query: string;
|
|
17
17
|
build_trigger_id?: string;
|
|
18
|
+
search_index_id?: string;
|
|
18
19
|
locale?: string;
|
|
19
20
|
[k: string]: unknown;
|
|
20
21
|
};
|
|
@@ -62,7 +63,8 @@ type Highlighter = (
|
|
|
62
63
|
|
|
63
64
|
export type UseSiteSearchConfig<Client extends GenericClient> = {
|
|
64
65
|
client: Client;
|
|
65
|
-
buildTriggerId
|
|
66
|
+
buildTriggerId?: string;
|
|
67
|
+
searchIndexId?: string;
|
|
66
68
|
fuzzySearch?: boolean;
|
|
67
69
|
resultsPerPage?: number;
|
|
68
70
|
highlightMatch?: Highlighter;
|
|
@@ -161,6 +163,8 @@ export function useSiteSearch<Client extends GenericClient>(
|
|
|
161
163
|
query: state.query,
|
|
162
164
|
locale: state.locale,
|
|
163
165
|
build_trigger_id: config.buildTriggerId,
|
|
166
|
+
search_index_id: config.searchIndexId,
|
|
167
|
+
...(config.fuzzySearch === true ? { fuzzy: true } : {}),
|
|
164
168
|
},
|
|
165
169
|
page: {
|
|
166
170
|
limit: resultsPerPage,
|
|
@@ -168,10 +172,6 @@ export function useSiteSearch<Client extends GenericClient>(
|
|
|
168
172
|
},
|
|
169
173
|
};
|
|
170
174
|
|
|
171
|
-
if (config.fuzzySearch) {
|
|
172
|
-
request.fuzzy = 'true';
|
|
173
|
-
}
|
|
174
|
-
|
|
175
175
|
const response = await config.client.searchResults.rawList(request);
|
|
176
176
|
|
|
177
177
|
if (!isCancelled) {
|
|
@@ -199,6 +199,7 @@ export function useSiteSearch<Client extends GenericClient>(
|
|
|
199
199
|
resultsPerPage,
|
|
200
200
|
state,
|
|
201
201
|
config.buildTriggerId,
|
|
202
|
+
config.searchIndexId,
|
|
202
203
|
config.fuzzySearch,
|
|
203
204
|
config.client.config.apiToken,
|
|
204
205
|
]);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { MuxPlayerProps } from '@mux/mux-player-react
|
|
1
|
+
import type { MuxPlayerProps } from '@mux/mux-player-react';
|
|
2
2
|
|
|
3
|
-
import type { Video } from '../VideoPlayer';
|
|
3
|
+
import type { Video } from '../VideoPlayer/index.js';
|
|
4
4
|
|
|
5
5
|
type Maybe<T> = T | null;
|
|
6
6
|
type Possibly<T> = Maybe<T> | undefined;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.VideoPlayer = void 0;
|
|
27
|
-
const react_1 = __importStar(require("react"));
|
|
28
|
-
const LazyLoadedVideoPlayer = react_1.default.lazy(() => Promise.resolve().then(() => __importStar(require("./index.js"))));
|
|
29
|
-
exports.VideoPlayer = (0, react_1.forwardRef)((props, ref) => {
|
|
30
|
-
const { className, style } = props;
|
|
31
|
-
return (react_1.default.createElement(react_1.Suspense, { fallback: react_1.default.createElement("div", { className: className, style: style }) },
|
|
32
|
-
react_1.default.createElement(LazyLoadedVideoPlayer, Object.assign({}, props, { ref: ref }))));
|
|
33
|
-
});
|
|
34
|
-
exports.default = exports.VideoPlayer;
|
|
35
|
-
//# sourceMappingURL=lazy.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lazy.js","sourceRoot":"","sources":["../../../src/VideoPlayer/lazy.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAoD;AAMpD,MAAM,qBAAqB,GAAG,eAAK,CAAC,IAAI,CAAC,GAAG,EAAE,mDAAQ,YAAY,GAAC,CAAC,CAAC;AAMxD,QAAA,WAAW,GAAoB,IAAA,kBAAU,EAGpD,CAAC,KAAuB,EAAE,GAAG,EAAE,EAAE;IACjC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAEnC,OAAO,CACL,8BAAC,gBAAQ,IAAC,QAAQ,EAAE,uCAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAI;QAC7D,8BAAC,qBAAqB,oBAAK,KAAK,IAAE,GAAG,EAAE,GAAG,IAAI,CACrC,CACZ,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,kBAAe,mBAAW,CAAC"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React, { Suspense, forwardRef } from "react";
|
|
2
|
-
const LazyLoadedVideoPlayer = React.lazy(() => import("./index.js"));
|
|
3
|
-
export const VideoPlayer = forwardRef((props, ref) => {
|
|
4
|
-
const { className, style } = props;
|
|
5
|
-
return (React.createElement(Suspense, { fallback: React.createElement("div", { className: className, style: style }) },
|
|
6
|
-
React.createElement(LazyLoadedVideoPlayer, Object.assign({}, props, { ref: ref }))));
|
|
7
|
-
});
|
|
8
|
-
export default VideoPlayer;
|
|
9
|
-
//# sourceMappingURL=lazy.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lazy.js","sourceRoot":"","sources":["../../../src/VideoPlayer/lazy.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAMpD,MAAM,qBAAqB,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;AAMrE,MAAM,CAAC,MAAM,WAAW,GAAoB,UAAU,CAGpD,CAAC,KAAuB,EAAE,GAAG,EAAE,EAAE;IACjC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAEnC,OAAO,CACL,oBAAC,QAAQ,IAAC,QAAQ,EAAE,6BAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAI;QAC7D,oBAAC,qBAAqB,oBAAK,KAAK,IAAE,GAAG,EAAE,GAAG,IAAI,CACrC,CACZ,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,eAAe,WAAW,CAAC"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type MuxPlayerElement from "@mux/mux-player";
|
|
3
|
-
import type { VideoPlayerProps } from "./index";
|
|
4
|
-
type VideoPlayerType = React.ForwardRefExoticComponent<VideoPlayerProps & React.RefAttributes<MuxPlayerElement>>;
|
|
5
|
-
export declare const VideoPlayer: VideoPlayerType;
|
|
6
|
-
export default VideoPlayer;
|
package/src/VideoPlayer/lazy.tsx
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import React, { Suspense, forwardRef } from "react";
|
|
2
|
-
import type MuxPlayerElement from "@mux/mux-player";
|
|
3
|
-
import type MuxPlayer from "@mux/mux-player-react/.";
|
|
4
|
-
|
|
5
|
-
import type { VideoPlayerProps } from "./index";
|
|
6
|
-
|
|
7
|
-
const LazyLoadedVideoPlayer = React.lazy(() => import("./index.js"));
|
|
8
|
-
|
|
9
|
-
type VideoPlayerType = React.ForwardRefExoticComponent<
|
|
10
|
-
VideoPlayerProps & React.RefAttributes<MuxPlayerElement>
|
|
11
|
-
>;
|
|
12
|
-
|
|
13
|
-
export const VideoPlayer: VideoPlayerType = forwardRef<
|
|
14
|
-
MuxPlayerElement,
|
|
15
|
-
VideoPlayerProps
|
|
16
|
-
>((props: VideoPlayerProps, ref) => {
|
|
17
|
-
const { className, style } = props;
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<Suspense fallback={<div className={className} style={style} />}>
|
|
21
|
-
<LazyLoadedVideoPlayer {...props} ref={ref} />
|
|
22
|
-
</Suspense>
|
|
23
|
-
);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
export default VideoPlayer;
|