tinacms 0.60.2 → 0.63.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/CHANGELOG.md +63 -0
- package/README.md +3 -288
- package/dist/client/index.d.ts +1 -0
- package/dist/edit-state.d.ts +20 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.es.js +69 -35
- package/dist/index.js +71 -37
- package/dist/rich-text.d.ts +118 -1
- package/dist/style.css +1484 -0
- package/dist/tina-cms.d.ts +27 -4
- package/package.json +11 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# tinacms
|
|
2
2
|
|
|
3
|
+
## 0.63.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3897ec5d9: Replace `branch`, `clientId`, `isLocalClient` props with single `apiURL`. When working locally, this should be `http://localhost:4001/graphql`. For Tina Cloud, use `https://content.tinajs.io/content/<my-client-id>/github/<my-branch>`
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
// _app.tsx
|
|
11
|
+
// ...
|
|
12
|
+
<TinaCMS apiURL={process.env.NEXT_PUBLIC_TINA_API_URL} {...pageProps}>
|
|
13
|
+
{livePageProps => <Component {...livePageProps} />}
|
|
14
|
+
</TinaCMS>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
DEPRECATION NOTICE: `branch`, `clientId`, `isLocalClient` props will be deprecated in the future
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- 96e4a77e2: Fixed types
|
|
22
|
+
- b5c22503a: Changes messaging on login page for TinaAdmin when in local-mode
|
|
23
|
+
- Updated dependencies [60f939f34]
|
|
24
|
+
- @tinacms/toolkit@0.56.6
|
|
25
|
+
|
|
26
|
+
## 0.62.0
|
|
27
|
+
|
|
28
|
+
### Minor Changes
|
|
29
|
+
|
|
30
|
+
- 70da62fe8: deprecated the use of `getStaticPropsForTina`
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- 0afa75df1: 2342 - [TinaAdmin] Truncates the `filename` column for the Document List View
|
|
35
|
+
- 7dafce89d: Fixed issue where content creator was invalid
|
|
36
|
+
- 3de8c6165: Enabled branch creation in branch switcher
|
|
37
|
+
- fee183f8f: add "switch to default branch" recover option to error boundary
|
|
38
|
+
- 5c070a83f: feat: Add UI banner for when in localMode
|
|
39
|
+
- Updated dependencies [ddf81a4fd]
|
|
40
|
+
- Updated dependencies [20260a82d]
|
|
41
|
+
- Updated dependencies [0370147fb]
|
|
42
|
+
- Updated dependencies [3de8c6165]
|
|
43
|
+
- Updated dependencies [2eaad97bf]
|
|
44
|
+
- Updated dependencies [5c070a83f]
|
|
45
|
+
- @tinacms/toolkit@0.56.5
|
|
46
|
+
|
|
47
|
+
## 0.61.1
|
|
48
|
+
|
|
49
|
+
### Patch Changes
|
|
50
|
+
|
|
51
|
+
- Updated dependencies [2c7718636]
|
|
52
|
+
- @tinacms/toolkit@0.56.4
|
|
53
|
+
|
|
54
|
+
## 0.61.0
|
|
55
|
+
|
|
56
|
+
### Minor Changes
|
|
57
|
+
|
|
58
|
+
- 229feda1d: add .nvmrc file for setting preferred node version
|
|
59
|
+
|
|
60
|
+
## 0.60.3
|
|
61
|
+
|
|
62
|
+
### Patch Changes
|
|
63
|
+
|
|
64
|
+
- 4adaf15af: Fix types which weren't included in previous patch
|
|
65
|
+
|
|
3
66
|
## 0.60.2
|
|
4
67
|
|
|
5
68
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,289 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# TinaCMS
|
|
2
|
+
> The Fastest Way to Edit Next.js Content
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Getting started
|
|
6
|
-
|
|
7
|
-
Npm:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install --save-dev tinacms
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Yarn:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
yarn add --dev tinacms
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## The TinaCMS API Client
|
|
20
|
-
|
|
21
|
-
This package exports a class which acts as [TinaCMS external API](https://tina.io/docs/apis/) for the Tina Content API. This is a headless GraphQL API that's serverd via Tina Cloud or locally from within the Tina CLI.
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
import { Client, LocalClient } from "tinacms";
|
|
25
|
-
const client = new Client({
|
|
26
|
-
organizationId: "the ID you get from Tina Cloud",
|
|
27
|
-
clientId: "the client ID you get from Tina Cloud",
|
|
28
|
-
branch: "main",
|
|
29
|
-
tokenStorage: "LOCAL_STORAGE" | "MEMORY" | "CUSTOM",
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// For a simpler setup while working locally you can instantiate the LocalClient as a convenience
|
|
33
|
-
const client = new LocalClient();
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
The `Client` does a few things:
|
|
37
|
-
|
|
38
|
-
- Manages auth with Tina Cloud
|
|
39
|
-
- Provides a `request` function for working with the GraphQL API
|
|
40
|
-
|
|
41
|
-
Start by initializing the `LocalClient` - which automatically connects with your locally-running GraphQL server. From there, you can make GraphQL requests:
|
|
42
|
-
|
|
43
|
-
### `client.request`
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
const client = new LocalClient();
|
|
47
|
-
|
|
48
|
-
await client.request(
|
|
49
|
-
(gql) => gql`#graphql
|
|
50
|
-
query BlogPostQuery($relativePath: String!) {
|
|
51
|
-
{
|
|
52
|
-
getPostsDocument(relativePath: "") {
|
|
53
|
-
data {
|
|
54
|
-
title
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
`,
|
|
60
|
-
{ variables: { relativePath: "hello-world.md" } }
|
|
61
|
-
);
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
> This API currently doesn't support filtering and sorting "list" queries. We have plans to tackle that in upcoming cycles.
|
|
65
|
-
|
|
66
|
-
## `useGraphQLForms`
|
|
67
|
-
|
|
68
|
-
While GraphQL is a great tool, using it with Tina can be difficult. GraphQL can query across multiple nodes, but since each document would require its own Tina form it could be difficult to sync the data with your query with all of the forms you'd need to build. The Tina GraphQL server knows all about your content schema so we're actually able to build forms automatically by inspecting your query. To see this in action, pass your query into the `useGraphqlForms` hook:
|
|
69
|
-
|
|
70
|
-
```tsx
|
|
71
|
-
import { useGraphqlForms } from 'tinacms'
|
|
72
|
-
|
|
73
|
-
const query = gql => gql`#graphql
|
|
74
|
-
query BlogPostQuery($relativePath: String!) {
|
|
75
|
-
{
|
|
76
|
-
getPostsDocument(relativePath: $relativePath) {
|
|
77
|
-
data {
|
|
78
|
-
title
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
`
|
|
84
|
-
|
|
85
|
-
const MyPage = (props) => {
|
|
86
|
-
const [payload, isLoading] = useGraphqlForms<PostQueryResponseType>({
|
|
87
|
-
query,
|
|
88
|
-
variables: { relativePath: `${props.filename}.md` },
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
isLoading ? <div>Loading...</div> : <MyComponent {...payload}>
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
If Tina is enabled you can see a form for the `getPostsDocument` request. If you query for multiple documents, you should see multiple forms:
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
const query = (gql) => gql`#graphql
|
|
99
|
-
query BlogPostQuery($relativePath: String!) {
|
|
100
|
-
{
|
|
101
|
-
# this generates a Tina Form
|
|
102
|
-
getSiteNavsDocument(relativePath: "site-nav.md") {
|
|
103
|
-
data {
|
|
104
|
-
items {
|
|
105
|
-
title
|
|
106
|
-
link
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
# this generates a separate Tina Form
|
|
111
|
-
getPostsDocument(relativePath: $relativePath) {
|
|
112
|
-
data {
|
|
113
|
-
title
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
`;
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Formify
|
|
122
|
-
|
|
123
|
-
If you'd like to control the output of those forms, tap into the `formify` callback:
|
|
124
|
-
|
|
125
|
-
##### Form customization:
|
|
126
|
-
|
|
127
|
-
```tsx
|
|
128
|
-
import { useGraphqlForms } from "tinacms";
|
|
129
|
-
import { useCMS } from "tinacms";
|
|
130
|
-
|
|
131
|
-
const [payload, isLoading] = useGraphqlForms({
|
|
132
|
-
query,
|
|
133
|
-
formify: ({ formConfig, createForm, createGlobalForm, skip }) => {
|
|
134
|
-
if (formConfig.id === "getSiteNavsDocument") {
|
|
135
|
-
return createGlobalForm(formConfig);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return createForm(formConfig);
|
|
139
|
-
},
|
|
140
|
-
variables: { relativePath: `${props.filename}.md` },
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// or to skip the nav from creating a form altogether:
|
|
144
|
-
const [payload, isLoading] = useGraphqlForms({
|
|
145
|
-
query,
|
|
146
|
-
formify: ({ formConfig, createForm, skip }) => {
|
|
147
|
-
if (formConfig.id === "getSiteNavsDocument") {
|
|
148
|
-
return skip();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return createForm(formConfig);
|
|
152
|
-
},
|
|
153
|
-
variables: { relativePath: `${props.filename}.md` },
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
##### Field customization:
|
|
158
|
-
|
|
159
|
-
Since your forms are built automatically, `formify` can also be used to customize fields:
|
|
160
|
-
|
|
161
|
-
```tsx
|
|
162
|
-
const [payload, isLoading] = useGraphqlForms({
|
|
163
|
-
query,
|
|
164
|
-
formify: ({ formConfig, createForm, skip }) => {
|
|
165
|
-
return createForm({
|
|
166
|
-
...formConfig,
|
|
167
|
-
fields: formConfig.fields.map((field) => {
|
|
168
|
-
if (field.name === "title") {
|
|
169
|
-
// replace `text` with `textarea`
|
|
170
|
-
field.component = "textarea";
|
|
171
|
-
}
|
|
172
|
-
return field;
|
|
173
|
-
}),
|
|
174
|
-
});
|
|
175
|
-
},
|
|
176
|
-
variables: { relativePath: `${props.filename}.md` },
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## `useDocumentCreatorPlugin`
|
|
181
|
-
|
|
182
|
-
This hook allows your editors to safely create new pages. Note that you'll be responsible for redirecting the user after a new document has been created. To use this:
|
|
183
|
-
|
|
184
|
-
```tsx
|
|
185
|
-
import { useDocumentCreatorPlugin } from "tinacms";
|
|
186
|
-
|
|
187
|
-
// args is of type:
|
|
188
|
-
// {
|
|
189
|
-
// collection: {
|
|
190
|
-
// slug: string;
|
|
191
|
-
// };
|
|
192
|
-
// relativePath: string;
|
|
193
|
-
// breadcrumbs: string[];
|
|
194
|
-
// path: string;
|
|
195
|
-
// }
|
|
196
|
-
useDocumentCreatorPlugin((args) => window.location.assign(buildMyRouter(args)));
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Customizing the content creator options
|
|
200
|
-
|
|
201
|
-
To prevent editors from creating documents from certain collections, provide a filter function:
|
|
202
|
-
|
|
203
|
-
```tsx
|
|
204
|
-
// options are of type:
|
|
205
|
-
// {
|
|
206
|
-
// label: string;
|
|
207
|
-
// value: string;
|
|
208
|
-
// }[]
|
|
209
|
-
useDocumentCreatorPlugin(null, (options) =>
|
|
210
|
-
options.filter((option) => option.name !== "post")
|
|
211
|
-
);
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
## Authentication with Tina Cloud
|
|
215
|
-
|
|
216
|
-
While this package comes with low-level APIs for authentication with Tina Cloud, the easiest way to get started is to use the `TinaCloudAuthWall` component, which prevents children from rendering until a valid session has been established with Tina Cloud.
|
|
217
|
-
|
|
218
|
-
### `TinaCloudAuthWall`
|
|
219
|
-
|
|
220
|
-
```tsx
|
|
221
|
-
import { TinaCloudAuthWall, Client } from "tinacms";
|
|
222
|
-
|
|
223
|
-
const TinaWrapper = ({ children }) => {
|
|
224
|
-
const cms = React.useMemo(() => {
|
|
225
|
-
return new TinaCMS({
|
|
226
|
-
apis: {
|
|
227
|
-
tina: new Client({
|
|
228
|
-
// config
|
|
229
|
-
})
|
|
230
|
-
},
|
|
231
|
-
...
|
|
232
|
-
});
|
|
233
|
-
}, []);
|
|
234
|
-
|
|
235
|
-
return <TinaCloudAuthWall cms={cms}>{children}</TinaCloudAuthWall>;
|
|
236
|
-
};
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
Props for TinaCloudAuthWall
|
|
240
|
-
|
|
241
|
-
| Prop | Description |
|
|
242
|
-
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
243
|
-
| `cms` | An instance of a [CMS](http://localhost:3000/docs/cms/#cms-configuration) |
|
|
244
|
-
| `getModalActions` (optional) | A function that returns a list of actions / buttons that will be rendered to the model. Each button has name, action, and can be primary or not. The name is the text that will be displayed. The action is a function that will be run when the button is clicked. See example below for more details |
|
|
245
|
-
|
|
246
|
-
```tsx
|
|
247
|
-
return (
|
|
248
|
-
<TinaCloudAuthWall
|
|
249
|
-
cms={cms}
|
|
250
|
-
getModalActions={({ closeModal }) => {
|
|
251
|
-
return [
|
|
252
|
-
{
|
|
253
|
-
action: async () => {
|
|
254
|
-
// use your own state to get in and out of edit mode
|
|
255
|
-
closeModal();
|
|
256
|
-
},
|
|
257
|
-
name: "close",
|
|
258
|
-
primary: false,
|
|
259
|
-
},
|
|
260
|
-
];
|
|
261
|
-
}}
|
|
262
|
-
>
|
|
263
|
-
<Component {...pageProps} />
|
|
264
|
-
</TinaCloudAuthWall>
|
|
265
|
-
);
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
> Note: when using the LocalClient, TinaCloudAuthWall won't display a login screen, there is no authentication for the local GraphQL server.
|
|
269
|
-
|
|
270
|
-
### Authenticating without TinaCloudAuthWall
|
|
271
|
-
|
|
272
|
-
You can also authenticate with the `Client` directly:
|
|
273
|
-
|
|
274
|
-
```ts
|
|
275
|
-
const client = new Client({
|
|
276
|
-
// config
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
const EditSiteButton = () => {
|
|
280
|
-
const cms = useCMS();
|
|
281
|
-
const onClick = async () => {
|
|
282
|
-
await client.authenticate().then((token) => {
|
|
283
|
-
cms.enable();
|
|
284
|
-
});
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
return <button onClick={onClick}>Edit This Site</button>;
|
|
288
|
-
};
|
|
289
|
-
```
|
|
4
|
+
[Checkout the docs](https://tina.io/docs/setup-overview/) for information on how to get started
|
package/dist/client/index.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export declare class Client {
|
|
|
42
42
|
private options;
|
|
43
43
|
events: EventBus;
|
|
44
44
|
constructor({ tokenStorage, ...options }: ServerOptions);
|
|
45
|
+
get isLocalMode(): boolean;
|
|
45
46
|
setBranch(branchName: string): void;
|
|
46
47
|
addPendingContent: (props: any) => Promise<unknown>;
|
|
47
48
|
getSchema: () => Promise<GraphQLSchema>;
|
package/dist/edit-state.d.ts
CHANGED
|
@@ -1 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
Copyright 2021 Forestry.io Holdings, Inc.
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
See the License for the specific language governing permissions and
|
|
11
|
+
limitations under the License.
|
|
12
|
+
*/
|
|
13
|
+
import { isEditing, setEditing, useEditState } from '@tinacms/sharedctx';
|
|
14
|
+
import React from 'react';
|
|
15
|
+
export { isEditing, setEditing, useEditState };
|
|
16
|
+
export declare const TinaEditProvider: ({ showEditButton, ...props }: {
|
|
17
|
+
showEditButton?: boolean;
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
editMode: React.ReactNode;
|
|
20
|
+
}) => JSX.Element;
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
Copyright 2021 Forestry.io Holdings, Inc.
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
See the License for the specific language governing permissions and
|
|
11
|
+
limitations under the License.
|
|
12
|
+
*/
|
|
13
|
+
export * from './client';
|
|
14
|
+
export * from './auth';
|
|
15
|
+
export * from './utils';
|
|
16
|
+
export * from './tina-cms';
|
|
17
|
+
export { useGraphqlForms } from './hooks/use-graphql-forms';
|
|
18
|
+
export { useDocumentCreatorPlugin } from './hooks/use-content-creator';
|
|
19
|
+
export * from '@tinacms/toolkit';
|
|
20
|
+
export { TinaAdmin } from './admin';
|
|
21
|
+
export { RouteMappingPlugin } from './admin/plugins/route-mapping';
|
|
22
|
+
import { TinaCMSProvider2 } from './tina-cms';
|
|
23
|
+
export default TinaCMSProvider2;
|
package/dist/index.es.js
CHANGED
|
@@ -39,6 +39,7 @@ import styled from "styled-components";
|
|
|
39
39
|
import * as yup from "yup";
|
|
40
40
|
import { setEditing, useEditState } from "@tinacms/sharedctx";
|
|
41
41
|
import { getIn, setIn } from "final-form";
|
|
42
|
+
import UrlPattern from "url-pattern";
|
|
42
43
|
import { NavLink, useLocation, useParams, Link, useNavigate, BrowserRouter, Routes, Route } from "react-router-dom";
|
|
43
44
|
import { Menu, Transition } from "@headlessui/react";
|
|
44
45
|
function popupWindow(url, title, window2, w, h) {
|
|
@@ -374,6 +375,11 @@ function assertIsUnionType(type) {
|
|
|
374
375
|
throw new Error(`Expected an instance of GraphQLUnionType for type ${type.name}`);
|
|
375
376
|
}
|
|
376
377
|
}
|
|
378
|
+
const captureBranchName = /^refs\/heads\/(.*)/;
|
|
379
|
+
const parseRefForBranchName = (ref) => {
|
|
380
|
+
const matches = ref.match(captureBranchName);
|
|
381
|
+
return matches[1];
|
|
382
|
+
};
|
|
377
383
|
class Client {
|
|
378
384
|
constructor(_a) {
|
|
379
385
|
var _b = _a, { tokenStorage = "MEMORY" } = _b, options = __objRest(_b, ["tokenStorage"]);
|
|
@@ -464,6 +470,9 @@ mutation addPendingDocumentMutation(
|
|
|
464
470
|
break;
|
|
465
471
|
}
|
|
466
472
|
}
|
|
473
|
+
get isLocalMode() {
|
|
474
|
+
return this.contentApiUrl.includes("localhost");
|
|
475
|
+
}
|
|
467
476
|
setBranch(branchName) {
|
|
468
477
|
var _a, _b, _c;
|
|
469
478
|
const encodedBranch = encodeURIComponent(branchName);
|
|
@@ -591,15 +600,15 @@ mutation addPendingDocumentMutation(
|
|
|
591
600
|
try {
|
|
592
601
|
const res = await this.fetchWithToken(url, {
|
|
593
602
|
method: "POST",
|
|
594
|
-
body: {
|
|
603
|
+
body: JSON.stringify({
|
|
595
604
|
baseBranch,
|
|
596
605
|
branchName
|
|
597
|
-
},
|
|
606
|
+
}),
|
|
598
607
|
headers: {
|
|
599
608
|
"Content-Type": "application/json"
|
|
600
609
|
}
|
|
601
610
|
});
|
|
602
|
-
return
|
|
611
|
+
return await res.json().then((r) => parseRefForBranchName(r.data.ref));
|
|
603
612
|
} catch (error) {
|
|
604
613
|
console.error("There was an error creating a new branch.", error);
|
|
605
614
|
return null;
|
|
@@ -1370,7 +1379,7 @@ const useDocumentCreatorPlugin = (args) => {
|
|
|
1370
1379
|
}));
|
|
1371
1380
|
};
|
|
1372
1381
|
run();
|
|
1373
|
-
}, [cms
|
|
1382
|
+
}, [cms]);
|
|
1374
1383
|
React.useEffect(() => {
|
|
1375
1384
|
if (plugin) {
|
|
1376
1385
|
cms.plugins.add(plugin);
|
|
@@ -1382,6 +1391,18 @@ const useDocumentCreatorPlugin = (args) => {
|
|
|
1382
1391
|
};
|
|
1383
1392
|
}, [plugin]);
|
|
1384
1393
|
};
|
|
1394
|
+
const errorButtonStyles = {
|
|
1395
|
+
background: "#eb6337",
|
|
1396
|
+
padding: "12px 18px",
|
|
1397
|
+
cursor: "pointer",
|
|
1398
|
+
borderRadius: "50px",
|
|
1399
|
+
textTransform: "uppercase",
|
|
1400
|
+
letterSpacing: "2px",
|
|
1401
|
+
fontWeight: "bold",
|
|
1402
|
+
border: "none",
|
|
1403
|
+
color: "white",
|
|
1404
|
+
margin: "1rem 0"
|
|
1405
|
+
};
|
|
1385
1406
|
const SetupHooks = (props) => {
|
|
1386
1407
|
const cms = useCMS();
|
|
1387
1408
|
const [payload, isLoading] = useGraphqlForms({
|
|
@@ -1411,6 +1432,8 @@ class ErrorBoundary extends React.Component {
|
|
|
1411
1432
|
return { hasError: true, message: error.message };
|
|
1412
1433
|
}
|
|
1413
1434
|
render() {
|
|
1435
|
+
const branchData = window.localStorage.getItem("tinacms-current-branch");
|
|
1436
|
+
const hasBranchData = branchData && branchData.length > 0;
|
|
1414
1437
|
if (this.state.hasError && !this.state.pageRefresh) {
|
|
1415
1438
|
return /* @__PURE__ */ React.createElement("div", {
|
|
1416
1439
|
style: {
|
|
@@ -1431,25 +1454,21 @@ class ErrorBoundary extends React.Component {
|
|
|
1431
1454
|
}
|
|
1432
1455
|
}, /* @__PURE__ */ React.createElement("h3", {
|
|
1433
1456
|
style: { color: "#eb6337" }
|
|
1434
|
-
}, "TinaCMS Render Error"), /* @__PURE__ */ React.createElement("p", null, "Tina caught an error while updating the page:"), /* @__PURE__ */ React.createElement("pre",
|
|
1435
|
-
style: {
|
|
1436
|
-
}, /* @__PURE__ */ React.createElement("button", {
|
|
1437
|
-
style:
|
|
1438
|
-
background: "#eb6337",
|
|
1439
|
-
padding: "12px 18px",
|
|
1440
|
-
cursor: "pointer",
|
|
1441
|
-
borderRadius: "50px",
|
|
1442
|
-
textTransform: "uppercase",
|
|
1443
|
-
letterSpacing: "2px",
|
|
1444
|
-
fontWeight: "bold",
|
|
1445
|
-
border: "none",
|
|
1446
|
-
color: "white"
|
|
1447
|
-
},
|
|
1457
|
+
}, "TinaCMS Render Error"), /* @__PURE__ */ React.createElement("p", null, "Tina caught an error while updating the page:"), /* @__PURE__ */ React.createElement("pre", {
|
|
1458
|
+
style: { marginTop: "1rem", overflowX: "auto" }
|
|
1459
|
+
}, this.state.message), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("p", null, `If you've just updated the form, undo your most recent changes and click "refresh". If after a few refreshes, you're still encountering this error. There is a bigger issue with the site. Please reach out to your site admin.`), /* @__PURE__ */ React.createElement("button", {
|
|
1460
|
+
style: errorButtonStyles,
|
|
1448
1461
|
onClick: () => {
|
|
1449
1462
|
this.setState({ pageRefresh: true });
|
|
1450
1463
|
setTimeout(() => this.setState({ hasError: false, pageRefresh: false }), 3e3);
|
|
1451
1464
|
}
|
|
1452
|
-
}, "Refresh"))
|
|
1465
|
+
}, "Refresh"), hasBranchData && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("p", null, `If you're using the branch switcher, you may currently be on a "stale" branch that has been deleted or whose content is not compatible with the latest version of the site's layout. Click the button below to switch back to the default branch for this deployment.`), /* @__PURE__ */ React.createElement("button", {
|
|
1466
|
+
style: errorButtonStyles,
|
|
1467
|
+
onClick: () => {
|
|
1468
|
+
window.localStorage.removeItem("tinacms-current-branch");
|
|
1469
|
+
window.location.reload();
|
|
1470
|
+
}
|
|
1471
|
+
}, "Switch to default branch"))));
|
|
1453
1472
|
}
|
|
1454
1473
|
if (this.state.pageRefresh) {
|
|
1455
1474
|
return /* @__PURE__ */ React.createElement(Loader, null, "Let's try that again.");
|
|
@@ -1457,20 +1476,29 @@ class ErrorBoundary extends React.Component {
|
|
|
1457
1476
|
return this.props.children;
|
|
1458
1477
|
}
|
|
1459
1478
|
}
|
|
1479
|
+
const parseURL = (url) => {
|
|
1480
|
+
if (url.includes("localhost")) {
|
|
1481
|
+
return { branch: null, isLocalClient: true, clientId: null };
|
|
1482
|
+
}
|
|
1483
|
+
const tinaHost = "content.tinajs.io";
|
|
1484
|
+
const params = new URL(url);
|
|
1485
|
+
const pattern = new UrlPattern("/content/:clientId/github/:branch");
|
|
1486
|
+
const result = pattern.match(params.pathname);
|
|
1487
|
+
if (params.host !== tinaHost) {
|
|
1488
|
+
throw new Error(`The only supported hosts are ${tinaHost} or localhost, but received ${params.host}.`);
|
|
1489
|
+
}
|
|
1490
|
+
return __spreadProps(__spreadValues({}, result), {
|
|
1491
|
+
isLocalClient: false
|
|
1492
|
+
});
|
|
1493
|
+
};
|
|
1460
1494
|
const TinaCMSProvider2 = (_c) => {
|
|
1461
1495
|
var _d = _c, {
|
|
1462
1496
|
children,
|
|
1463
|
-
branch,
|
|
1464
|
-
clientId,
|
|
1465
|
-
isLocalClient,
|
|
1466
1497
|
cmsCallback,
|
|
1467
1498
|
mediaStore,
|
|
1468
1499
|
tinaioConfig
|
|
1469
1500
|
} = _d, props = __objRest(_d, [
|
|
1470
1501
|
"children",
|
|
1471
|
-
"branch",
|
|
1472
|
-
"clientId",
|
|
1473
|
-
"isLocalClient",
|
|
1474
1502
|
"cmsCallback",
|
|
1475
1503
|
"mediaStore",
|
|
1476
1504
|
"tinaioConfig"
|
|
@@ -1478,6 +1506,14 @@ const TinaCMSProvider2 = (_c) => {
|
|
|
1478
1506
|
if (typeof props.query === "string") {
|
|
1479
1507
|
props.query;
|
|
1480
1508
|
}
|
|
1509
|
+
if (!props.apiURL && !((props == null ? void 0 : props.clientId) || (props == null ? void 0 : props.isLocalClient))) {
|
|
1510
|
+
throw new Error(`apiURL is a required field`);
|
|
1511
|
+
}
|
|
1512
|
+
const { branch, clientId, isLocalClient } = props.apiURL ? parseURL(props.apiURL) : {
|
|
1513
|
+
branch: props.branch,
|
|
1514
|
+
clientId: props.clientId,
|
|
1515
|
+
isLocalClient: props.isLocalClient
|
|
1516
|
+
};
|
|
1481
1517
|
return /* @__PURE__ */ React.createElement(TinaCloudProvider, {
|
|
1482
1518
|
branch,
|
|
1483
1519
|
clientId,
|
|
@@ -1619,10 +1655,10 @@ const staticRequest = async ({
|
|
|
1619
1655
|
if (!is_server()) {
|
|
1620
1656
|
console.warn(`Whoops! Looks like you are using \`staticRequest\` in the browser to fetch data.
|
|
1621
1657
|
|
|
1622
|
-
The local server is not available outside of \`getStaticProps\` or \`getStaticPaths\` functions.
|
|
1658
|
+
The local server is not available outside of \`getStaticProps\` or \`getStaticPaths\` functions.
|
|
1623
1659
|
This function should only be called on the server at build time.
|
|
1624
1660
|
|
|
1625
|
-
This will work when developing locally but NOT when deployed to production.
|
|
1661
|
+
This will work when developing locally but NOT when deployed to production.
|
|
1626
1662
|
`);
|
|
1627
1663
|
}
|
|
1628
1664
|
return client.request(query, { variables });
|
|
@@ -1766,7 +1802,7 @@ const AuthTemplate = ({
|
|
|
1766
1802
|
return /* @__PURE__ */ React.createElement("div", {
|
|
1767
1803
|
className: "h-screen w-full bg-gradient-to-b from-blue-900 to-gray-900 flex items-center justify-center px-4 py-6"
|
|
1768
1804
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
1769
|
-
className: "bg-white rounded-lg overflow-hidden shadow-lg w-full max-w-
|
|
1805
|
+
className: "bg-white rounded-lg overflow-hidden shadow-lg w-full max-w-lg"
|
|
1770
1806
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
1771
1807
|
className: "px-5 py-4 border-b border-gray-150"
|
|
1772
1808
|
}, /* @__PURE__ */ React.createElement("h2", {
|
|
@@ -1791,9 +1827,7 @@ const AuthTemplate = ({
|
|
|
1791
1827
|
const LoginPage = () => {
|
|
1792
1828
|
const { setEdit } = useEditState();
|
|
1793
1829
|
const login = () => setEdit(true);
|
|
1794
|
-
return /* @__PURE__ */ React.createElement(AuthTemplate, {
|
|
1795
|
-
message: "Please log in to Tina Cloud to access your content."
|
|
1796
|
-
}, /* @__PURE__ */ React.createElement("a", {
|
|
1830
|
+
return /* @__PURE__ */ React.createElement(AuthTemplate, null, /* @__PURE__ */ React.createElement("a", {
|
|
1797
1831
|
href: "/",
|
|
1798
1832
|
className: "flex-1 text-center inline-flex justify-center items-center px-8 py-3 shadow-sm text-sm leading-4 font-medium rounded-full text-gray-600 border border-gray-150 hover:opacity-80 hover:bg-gray-50 focus:outline-none focus:shadow-outline-blue transition duration-150 ease-out"
|
|
1799
1833
|
}, /* @__PURE__ */ React.createElement(MdOutlineArrowBack, {
|
|
@@ -1805,7 +1839,7 @@ const LoginPage = () => {
|
|
|
1805
1839
|
style: { background: "#0084FF" }
|
|
1806
1840
|
}, /* @__PURE__ */ React.createElement(BiLogIn, {
|
|
1807
1841
|
className: "w-6 h-auto mr-1.5 opacity-80"
|
|
1808
|
-
}), "
|
|
1842
|
+
}), " Enter edit-mode"));
|
|
1809
1843
|
};
|
|
1810
1844
|
const logout = () => {
|
|
1811
1845
|
setEditing(false);
|
|
@@ -1973,11 +2007,11 @@ const CollectionListPage = () => {
|
|
|
1973
2007
|
className: "block text-xs mb-0.5 text-gray-400 uppercase"
|
|
1974
2008
|
}, "Filename"), /* @__PURE__ */ React.createElement(Link, {
|
|
1975
2009
|
to: `${location2.pathname}/${document2.node.sys.filename}`,
|
|
1976
|
-
className: "h-5 leading-5
|
|
2010
|
+
className: "h-5 leading-5 flex max-w-xs"
|
|
1977
2011
|
}, /* @__PURE__ */ React.createElement("span", {
|
|
1978
|
-
className: "leading-5 font-medium text-base overflow-ellipsis overflow-hidden whitespace-nowrap text-gray-700"
|
|
2012
|
+
className: "flex-shrink-1 leading-5 font-medium text-base overflow-ellipsis overflow-hidden whitespace-nowrap text-gray-700"
|
|
1979
2013
|
}, document2.node.sys.filename), /* @__PURE__ */ React.createElement("span", {
|
|
1980
|
-
className: "leading-5 text-base font-medium text-gray-300"
|
|
2014
|
+
className: "flex-shrink-0 leading-5 text-base font-medium text-gray-300"
|
|
1981
2015
|
}, document2.node.sys.extension))), /* @__PURE__ */ React.createElement("td", {
|
|
1982
2016
|
className: "px-5 py-3 whitespace-nowrap"
|
|
1983
2017
|
}, /* @__PURE__ */ React.createElement("span", {
|