@measured/puck-field-contentful 0.14.0-canary.053d4c6
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +132 -0
- package/dist/index.d.ts +123 -0
- package/dist/index.js +120 -0
- package/package.json +36 -0
package/README.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# field-contentful
|
2
|
+
|
3
|
+
Select [entries](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/entries) from a [Contentful](https://www.contentful.com) space.
|
4
|
+
|
5
|
+
## Quick start
|
6
|
+
|
7
|
+
```sh
|
8
|
+
npm i @measured/puck-field-contentful
|
9
|
+
```
|
10
|
+
|
11
|
+
```jsx
|
12
|
+
import createFieldContentful from "@measured/puck-field-contentful";
|
13
|
+
|
14
|
+
const config = {
|
15
|
+
components: {
|
16
|
+
Example: {
|
17
|
+
fields: {
|
18
|
+
movie: createFieldContentful("movies", {
|
19
|
+
space: "my_space",
|
20
|
+
accessToken: "abcdefg123456",
|
21
|
+
}),
|
22
|
+
},
|
23
|
+
render: ({ data }) => {
|
24
|
+
return <p>{data?.fields.title || "No data selected"}</p>;
|
25
|
+
},
|
26
|
+
},
|
27
|
+
},
|
28
|
+
};
|
29
|
+
```
|
30
|
+
|
31
|
+
## Args
|
32
|
+
|
33
|
+
| Param | Example | Type | Status |
|
34
|
+
| ----------------------------- | -------- | ------ | -------- |
|
35
|
+
| [`contentType`](#contenttype) | `movies` | String | Required |
|
36
|
+
| [`options`](#options) | `{}` | Object | Required |
|
37
|
+
|
38
|
+
### Required args
|
39
|
+
|
40
|
+
#### `contentType`
|
41
|
+
|
42
|
+
ID of the Contentful [Content Type](https://www.contentful.com/help/content-model-and-content-type/) to query.
|
43
|
+
|
44
|
+
#### `options`
|
45
|
+
|
46
|
+
| Param | Example | Type | Status |
|
47
|
+
| ------------------------------------------ | --------------------------------------- | --------------------------------------------------------------- | ------------------------------ |
|
48
|
+
| [`accessToken`](#optionsaccesstoken) | `"abc123"` | String | Required (unless using client) |
|
49
|
+
| [`space`](#optionsspace) | `"my-space"` | String | Required (unless using client) |
|
50
|
+
| [`client`](#optionsclient) | `createClient()` | [ContentfulClientApi](https://www.npmjs.com/package/contentful) | - |
|
51
|
+
| [`filterFields`](#optionsfilterfields) | `{ "rating[gte]": { type: "number" } }` | Object | - |
|
52
|
+
| [`initialFilters`](#optionsinitialfilters) | `{ "rating[gte]": 1 }` | Object | - |
|
53
|
+
| [`titleField`](#optionstitlefield) | `"name"` | String | - |
|
54
|
+
|
55
|
+
##### `options.accessToken`
|
56
|
+
|
57
|
+
Your Contentful access token.
|
58
|
+
|
59
|
+
##### `options.space`
|
60
|
+
|
61
|
+
The id for the Contentful space that contains your content.
|
62
|
+
|
63
|
+
##### `options.client`
|
64
|
+
|
65
|
+
A Contentful client as created by the [`contentful` Node.js package](https://www.npmjs.com/package/contentful). You can use this instead of `accessToken` and `space` if you want to reuse your client, or customise it more fully.
|
66
|
+
|
67
|
+
##### `options.filterFields`
|
68
|
+
|
69
|
+
An object describing which [`filterFields`](https://puckeditor.com/docs/api-reference/fields/external#filterfields) to render and pass the result directly to Contentful as [search parameters](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters).
|
70
|
+
|
71
|
+
```jsx
|
72
|
+
createFieldContentful("movies", {
|
73
|
+
// ...
|
74
|
+
filterFields: {
|
75
|
+
// Filter the "rating" field by value greater than the user input
|
76
|
+
"fields.rating[gte]": {
|
77
|
+
type: "number",
|
78
|
+
},
|
79
|
+
},
|
80
|
+
});
|
81
|
+
```
|
82
|
+
|
83
|
+
##### `options.initialFilters`
|
84
|
+
|
85
|
+
The initial values for the filters defined in [`filterFields`](#optionsfilterfields). This data is passed directly directly to Contentful as [search parameters](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters).
|
86
|
+
|
87
|
+
```jsx
|
88
|
+
createFieldContentful("movies", {
|
89
|
+
// ...
|
90
|
+
initialFilters: {
|
91
|
+
"fields.rating[gte]": 1,
|
92
|
+
select: "name,rating", // Can include search parameters not included in filterFields
|
93
|
+
},
|
94
|
+
});
|
95
|
+
```
|
96
|
+
|
97
|
+
##### `options.titleField`
|
98
|
+
|
99
|
+
The field to use as the title for the selected item. Defaults to `"title"`.
|
100
|
+
|
101
|
+
```jsx
|
102
|
+
createFieldContentful("movies", {
|
103
|
+
// ...
|
104
|
+
titleField: "name",
|
105
|
+
});
|
106
|
+
```
|
107
|
+
|
108
|
+
## Returns
|
109
|
+
|
110
|
+
An [External field](https://puckeditor.com/docs/api-reference/fields/external) type that loads Contentful [entries](https://contentful.github.io/contentful.js/contentful/10.6.16/types/Entry.html).
|
111
|
+
|
112
|
+
## TypeScript
|
113
|
+
|
114
|
+
You can use the `Entry` type for data loaded via Contentful:
|
115
|
+
|
116
|
+
```tsx
|
117
|
+
import createFieldContentful, { Entry } from "@/field-contentful";
|
118
|
+
|
119
|
+
type MyProps = {
|
120
|
+
Example: {
|
121
|
+
movie: Entry<{ title: string; description: string; rating: number }>;
|
122
|
+
};
|
123
|
+
};
|
124
|
+
|
125
|
+
const config: Config<MyProps> = {
|
126
|
+
// ...
|
127
|
+
};
|
128
|
+
```
|
129
|
+
|
130
|
+
## License
|
131
|
+
|
132
|
+
MIT © [Measured Corporation Ltd](https://measured.co)
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
import { ReactElement } from 'react';
|
2
|
+
import { BaseEntry, ContentfulClientApi } from 'contentful';
|
3
|
+
export { createClient } from 'contentful';
|
4
|
+
|
5
|
+
type FieldOption = {
|
6
|
+
label: string;
|
7
|
+
value: string | number | boolean;
|
8
|
+
};
|
9
|
+
type FieldOptions = Array<FieldOption> | ReadonlyArray<FieldOption>;
|
10
|
+
type BaseField = {
|
11
|
+
label?: string;
|
12
|
+
};
|
13
|
+
type TextField = BaseField & {
|
14
|
+
type: "text";
|
15
|
+
};
|
16
|
+
type NumberField = BaseField & {
|
17
|
+
type: "number";
|
18
|
+
min?: number;
|
19
|
+
max?: number;
|
20
|
+
};
|
21
|
+
type TextareaField = BaseField & {
|
22
|
+
type: "textarea";
|
23
|
+
};
|
24
|
+
type SelectField = BaseField & {
|
25
|
+
type: "select";
|
26
|
+
options: FieldOptions;
|
27
|
+
};
|
28
|
+
type RadioField = BaseField & {
|
29
|
+
type: "radio";
|
30
|
+
options: FieldOptions;
|
31
|
+
};
|
32
|
+
type ArrayField<Props extends {
|
33
|
+
[key: string]: any;
|
34
|
+
} = {
|
35
|
+
[key: string]: any;
|
36
|
+
}> = BaseField & {
|
37
|
+
type: "array";
|
38
|
+
arrayFields: {
|
39
|
+
[SubPropName in keyof Props[0]]: Field<Props[0][SubPropName]>;
|
40
|
+
};
|
41
|
+
defaultItemProps?: Props[0];
|
42
|
+
getItemSummary?: (item: Props[0], index?: number) => string;
|
43
|
+
max?: number;
|
44
|
+
min?: number;
|
45
|
+
};
|
46
|
+
type ObjectField<Props extends {
|
47
|
+
[key: string]: any;
|
48
|
+
} = {
|
49
|
+
[key: string]: any;
|
50
|
+
}> = BaseField & {
|
51
|
+
type: "object";
|
52
|
+
objectFields: {
|
53
|
+
[SubPropName in keyof Props]: Field<Props[SubPropName]>;
|
54
|
+
};
|
55
|
+
};
|
56
|
+
type Adaptor<AdaptorParams = {}, TableShape extends Record<string, any> = {}, PropShape = TableShape> = {
|
57
|
+
name: string;
|
58
|
+
fetchList: (adaptorParams?: AdaptorParams) => Promise<TableShape[] | null>;
|
59
|
+
mapProp?: (value: TableShape) => PropShape;
|
60
|
+
};
|
61
|
+
type ExternalFieldWithAdaptor<Props extends {
|
62
|
+
[key: string]: any;
|
63
|
+
} = {
|
64
|
+
[key: string]: any;
|
65
|
+
}> = BaseField & {
|
66
|
+
type: "external";
|
67
|
+
placeholder?: string;
|
68
|
+
adaptor: Adaptor<any, any, Props>;
|
69
|
+
adaptorParams?: object;
|
70
|
+
getItemSummary: (item: Props, index?: number) => string;
|
71
|
+
};
|
72
|
+
type ExternalField<Props extends {
|
73
|
+
[key: string]: any;
|
74
|
+
} = {
|
75
|
+
[key: string]: any;
|
76
|
+
}> = BaseField & {
|
77
|
+
type: "external";
|
78
|
+
placeholder?: string;
|
79
|
+
fetchList: (params: {
|
80
|
+
query: string;
|
81
|
+
filters: Record<string, any>;
|
82
|
+
}) => Promise<any[] | null>;
|
83
|
+
mapProp?: (value: any) => Props;
|
84
|
+
mapRow?: (value: any) => Record<string, string | number>;
|
85
|
+
getItemSummary?: (item: Props, index?: number) => string;
|
86
|
+
showSearch?: boolean;
|
87
|
+
initialQuery?: string;
|
88
|
+
filterFields?: Record<string, Field>;
|
89
|
+
initialFilters?: Record<string, any>;
|
90
|
+
};
|
91
|
+
type CustomField<Props extends {
|
92
|
+
[key: string]: any;
|
93
|
+
} = {
|
94
|
+
[key: string]: any;
|
95
|
+
}> = BaseField & {
|
96
|
+
type: "custom";
|
97
|
+
render: (props: {
|
98
|
+
field: CustomField;
|
99
|
+
name: string;
|
100
|
+
value: any;
|
101
|
+
onChange: (value: Props) => void;
|
102
|
+
readOnly?: boolean;
|
103
|
+
}) => ReactElement;
|
104
|
+
};
|
105
|
+
type Field<Props extends {
|
106
|
+
[key: string]: any;
|
107
|
+
} = {
|
108
|
+
[key: string]: any;
|
109
|
+
}> = TextField | NumberField | TextareaField | SelectField | RadioField | ArrayField<Props> | ObjectField<Props> | ExternalField<Props> | ExternalFieldWithAdaptor<Props> | CustomField;
|
110
|
+
|
111
|
+
type Entry<Fields extends Record<string, any> = {}> = BaseEntry & {
|
112
|
+
fields: Fields;
|
113
|
+
};
|
114
|
+
declare function createFieldContentful<T extends Entry = Entry>(contentType: string, options?: {
|
115
|
+
client?: ContentfulClientApi<undefined>;
|
116
|
+
space?: string;
|
117
|
+
accessToken?: string;
|
118
|
+
titleField?: string;
|
119
|
+
filterFields?: ExternalField["filterFields"];
|
120
|
+
initialFilters?: ExternalField["initialFilters"];
|
121
|
+
}): ExternalField<T>;
|
122
|
+
|
123
|
+
export { Entry, createFieldContentful, createFieldContentful as default };
|
package/dist/index.js
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __create = Object.create;
|
3
|
+
var __defProp = Object.defineProperty;
|
4
|
+
var __defProps = Object.defineProperties;
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
6
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
8
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
9
|
+
var __getProtoOf = Object.getPrototypeOf;
|
10
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
11
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
13
|
+
var __spreadValues = (a, b) => {
|
14
|
+
for (var prop in b || (b = {}))
|
15
|
+
if (__hasOwnProp.call(b, prop))
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
17
|
+
if (__getOwnPropSymbols)
|
18
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
19
|
+
if (__propIsEnum.call(b, prop))
|
20
|
+
__defNormalProp(a, prop, b[prop]);
|
21
|
+
}
|
22
|
+
return a;
|
23
|
+
};
|
24
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
25
|
+
var __export = (target, all) => {
|
26
|
+
for (var name in all)
|
27
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
28
|
+
};
|
29
|
+
var __copyProps = (to, from, except, desc) => {
|
30
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
31
|
+
for (let key of __getOwnPropNames(from))
|
32
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
33
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
34
|
+
}
|
35
|
+
return to;
|
36
|
+
};
|
37
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
38
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
39
|
+
// file that has been converted to a CommonJS file using a Babel-
|
40
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
41
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
42
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
43
|
+
mod
|
44
|
+
));
|
45
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
46
|
+
var __async = (__this, __arguments, generator) => {
|
47
|
+
return new Promise((resolve, reject) => {
|
48
|
+
var fulfilled = (value) => {
|
49
|
+
try {
|
50
|
+
step(generator.next(value));
|
51
|
+
} catch (e) {
|
52
|
+
reject(e);
|
53
|
+
}
|
54
|
+
};
|
55
|
+
var rejected = (value) => {
|
56
|
+
try {
|
57
|
+
step(generator.throw(value));
|
58
|
+
} catch (e) {
|
59
|
+
reject(e);
|
60
|
+
}
|
61
|
+
};
|
62
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
63
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
64
|
+
});
|
65
|
+
};
|
66
|
+
|
67
|
+
// index.ts
|
68
|
+
var field_contentful_exports = {};
|
69
|
+
__export(field_contentful_exports, {
|
70
|
+
createClient: () => import_contentful.createClient,
|
71
|
+
createFieldContentful: () => createFieldContentful,
|
72
|
+
default: () => field_contentful_default
|
73
|
+
});
|
74
|
+
module.exports = __toCommonJS(field_contentful_exports);
|
75
|
+
|
76
|
+
// ../tsup-config/react-import.js
|
77
|
+
var import_react = __toESM(require("react"));
|
78
|
+
|
79
|
+
// index.ts
|
80
|
+
var import_contentful = require("contentful");
|
81
|
+
function createFieldContentful(contentType, options = {}) {
|
82
|
+
const {
|
83
|
+
space,
|
84
|
+
accessToken,
|
85
|
+
titleField = "title",
|
86
|
+
filterFields,
|
87
|
+
initialFilters
|
88
|
+
} = options;
|
89
|
+
if (!options.client) {
|
90
|
+
if (!space || !accessToken) {
|
91
|
+
throw new Error(
|
92
|
+
'field-contentful: Must either specify "client", or "space" and "accessToken"'
|
93
|
+
);
|
94
|
+
}
|
95
|
+
}
|
96
|
+
const client = options.client || (0, import_contentful.createClient)({ space, accessToken });
|
97
|
+
const field = {
|
98
|
+
type: "external",
|
99
|
+
placeholder: "Select from Contentful",
|
100
|
+
showSearch: true,
|
101
|
+
fetchList: (_0) => __async(this, [_0], function* ({ query, filters = {} }) {
|
102
|
+
const entries = yield client.getEntries(__spreadProps(__spreadValues({}, filters), {
|
103
|
+
content_type: contentType,
|
104
|
+
query
|
105
|
+
}));
|
106
|
+
return entries.items;
|
107
|
+
}),
|
108
|
+
mapRow: ({ fields }) => fields,
|
109
|
+
getItemSummary: (item) => item.fields[titleField],
|
110
|
+
filterFields,
|
111
|
+
initialFilters
|
112
|
+
};
|
113
|
+
return field;
|
114
|
+
}
|
115
|
+
var field_contentful_default = createFieldContentful;
|
116
|
+
// Annotate the CommonJS export names for ESM import in node:
|
117
|
+
0 && (module.exports = {
|
118
|
+
createClient,
|
119
|
+
createFieldContentful
|
120
|
+
});
|
package/package.json
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
{
|
2
|
+
"name": "@measured/puck-field-contentful",
|
3
|
+
"version": "0.14.0-canary.053d4c6",
|
4
|
+
"author": "Measured Corporation Ltd <hello@measured.co>",
|
5
|
+
"repository": "measuredco/puck",
|
6
|
+
"bugs": "https://github.com/measuredco/puck/issues",
|
7
|
+
"homepage": "https://puckeditor.com",
|
8
|
+
"private": false,
|
9
|
+
"main": "./dist/index.js",
|
10
|
+
"types": "./dist/index.d.ts",
|
11
|
+
"license": "MIT",
|
12
|
+
"scripts": {
|
13
|
+
"lint": "eslint \"**/*.ts*\"",
|
14
|
+
"build": "rm -rf dist && tsup index.ts",
|
15
|
+
"prepare": "yarn build"
|
16
|
+
},
|
17
|
+
"files": [
|
18
|
+
"dist"
|
19
|
+
],
|
20
|
+
"devDependencies": {
|
21
|
+
"@measured/puck": "^0.14.0-canary.053d4c6",
|
22
|
+
"@types/react": "^18.2.0",
|
23
|
+
"@types/react-dom": "^18.2.0",
|
24
|
+
"contentful": "^10.8.6",
|
25
|
+
"eslint": "^7.32.0",
|
26
|
+
"eslint-config-custom": "*",
|
27
|
+
"tsconfig": "*",
|
28
|
+
"tsup-config": "*",
|
29
|
+
"typescript": "^4.5.2"
|
30
|
+
},
|
31
|
+
"peerDependencies": {
|
32
|
+
"@measured/puck": "^0.13.0",
|
33
|
+
"contentful": "^10.0.0",
|
34
|
+
"react": "^17.0.0 || ^18.0.0"
|
35
|
+
}
|
36
|
+
}
|