tinacms 3.7.5 → 3.8.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/dist/react.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { addMetadata, hashFromQuery } from '@tinacms/bridge/metadata';
2
+ export { addMetadata, hashFromQuery };
1
3
  export declare function useTina<T extends object>(props: {
2
4
  query: string;
3
5
  variables: object;
@@ -10,32 +12,4 @@ export declare function useTina<T extends object>(props: {
10
12
  export declare function useEditState(): {
11
13
  edit: boolean;
12
14
  };
13
- /**
14
- * Grab the field name for the given attribute
15
- * to signal to Tina which DOM element the field
16
- * is working with.
17
- */
18
- /**
19
- * Generate a field identifier for Tina to associate DOM elements with form fields.
20
- * Format: "queryId---path.to.field" or "queryId---path.to.array.index"
21
- */
22
- export declare const tinaField: <T extends {
23
- _content_source?: {
24
- queryId: string;
25
- path: (number | string)[];
26
- };
27
- } | Record<string, unknown> | null | undefined>(object: T, property?: keyof Omit<NonNullable<T>, "__typename" | "_sys">, index?: number) => string;
28
- /**
29
- * FIX: This function is updated to be more robust. It explicitly checks for
30
- * `null` and `String` objects to prevent them from being processed as
31
- * iterable objects, which is the root cause of the "Objects are not valid
32
- * as a React child" error.
33
- */
34
- export declare const addMetadata: <T extends object>(id: string, obj: T, path?: (string | number)[]) => T;
35
- /**
36
- * This is a pretty rudimentary approach to hashing the query and variables to
37
- * ensure we treat multiple queries on the page uniquely. It's possible
38
- * that we would have collisions, and I'm not sure of the likeliness but seems
39
- * like it'd be rare.
40
- */
41
- export declare const hashFromQuery: (input: string) => string;
15
+ export { tinaField } from './tina-field';
package/dist/react.js CHANGED
@@ -1,4 +1,8 @@
1
+ import { hashFromQuery, addMetadata } from "@tinacms/bridge/metadata";
2
+ import { addMetadata as addMetadata2, hashFromQuery as hashFromQuery2 } from "@tinacms/bridge/metadata";
3
+ import { QUICK_EDIT_CSS } from "@tinacms/bridge/quick-edit-css";
1
4
  import React from "react";
5
+ import { tinaField } from "@tinacms/bridge/tina-field";
2
6
  function useTina(props) {
3
7
  const stringifiedQuery = JSON.stringify({
4
8
  query: props.query,
@@ -53,48 +57,16 @@ function useTina(props) {
53
57
  }
54
58
  }
55
59
  }
56
- if (fieldName) {
57
- if (isInTinaIframe) {
58
- parent.postMessage(
59
- { type: "field:selected", fieldName },
60
- window.location.origin
61
- );
62
- }
60
+ if (fieldName && isInTinaIframe) {
61
+ parent.postMessage(
62
+ { type: "field:selected", fieldName },
63
+ window.location.origin
64
+ );
63
65
  }
64
66
  };
65
67
  const style = document.createElement("style");
66
68
  style.type = "text/css";
67
- style.textContent = `
68
- [data-tina-field] {
69
- outline: 2px dashed rgba(34,150,254,0.5);
70
- transition: box-shadow ease-out 150ms;
71
- }
72
- [data-tina-field]:hover {
73
- box-shadow: inset 100vi 100vh rgba(34,150,254,0.3);
74
- outline: 2px solid rgba(34,150,254,1);
75
- cursor: pointer;
76
- }
77
- [data-tina-field-overlay] {
78
- outline: 2px dashed rgba(34,150,254,0.5);
79
- position: relative;
80
- }
81
- [data-tina-field-overlay]:hover {
82
- cursor: pointer;
83
- outline: 2px solid rgba(34,150,254,1);
84
- }
85
- [data-tina-field-overlay]::after {
86
- content: '';
87
- position: absolute;
88
- inset: 0;
89
- z-index: 20;
90
- transition: opacity ease-out 150ms;
91
- background-color: rgba(34,150,254,0.3);
92
- opacity: 0;
93
- }
94
- [data-tina-field-overlay]:hover::after {
95
- opacity: 1;
96
- }
97
- `;
69
+ style.textContent = QUICK_EDIT_CSS;
98
70
  document.head.appendChild(style);
99
71
  document.body.classList.add("__tina-quick-editing-enabled");
100
72
  document.addEventListener("click", mouseDownHandler, true);
@@ -166,88 +138,9 @@ function useEditState() {
166
138
  }, []);
167
139
  return { edit };
168
140
  }
169
- const tinaField = (object, property, index) => {
170
- const contentSource = object == null ? void 0 : object._content_source;
171
- if (!contentSource) {
172
- return "";
173
- }
174
- const { queryId, path } = contentSource;
175
- if (!property) {
176
- return `${queryId}---${path.join(".")}`;
177
- }
178
- const fullPath = typeof index === "number" ? [...path, property, index] : [...path, property];
179
- return `${queryId}---${fullPath.join(".")}`;
180
- };
181
- const addMetadata = (id, obj, path = []) => {
182
- if (obj === null) {
183
- return obj;
184
- }
185
- if (isScalarOrUndefined(obj)) {
186
- return obj;
187
- }
188
- if (obj instanceof String) {
189
- return obj.valueOf();
190
- }
191
- if (Array.isArray(obj)) {
192
- return obj.map(
193
- (item, index) => addMetadata(id, item, [...path, index])
194
- );
195
- }
196
- const transformedObj = {};
197
- for (const [key, value] of Object.entries(obj)) {
198
- const currentPath = [...path, key];
199
- if ([
200
- "__typename",
201
- "_sys",
202
- "_internalSys",
203
- "_values",
204
- "_internalValues",
205
- "_content_source",
206
- "_tina_metadata"
207
- ].includes(key)) {
208
- transformedObj[key] = value;
209
- } else {
210
- transformedObj[key] = addMetadata(id, value, currentPath);
211
- }
212
- }
213
- if (transformedObj && typeof transformedObj === "object" && "type" in transformedObj && transformedObj.type === "root") {
214
- return transformedObj;
215
- }
216
- return { ...transformedObj, _content_source: { queryId: id, path } };
217
- };
218
- function isScalarOrUndefined(value) {
219
- const type = typeof value;
220
- if (type === "string")
221
- return true;
222
- if (type === "number")
223
- return true;
224
- if (type === "boolean")
225
- return true;
226
- if (type === "undefined")
227
- return true;
228
- if (value == null)
229
- return true;
230
- if (value instanceof String)
231
- return true;
232
- if (value instanceof Number)
233
- return true;
234
- if (value instanceof Boolean)
235
- return true;
236
- return false;
237
- }
238
- const hashFromQuery = (input) => {
239
- let hash = 0;
240
- for (let i = 0; i < input.length; i++) {
241
- const char = input.charCodeAt(i);
242
- hash = (hash << 5) - hash + char & 4294967295;
243
- }
244
- const nonNegativeHash = Math.abs(hash);
245
- const alphanumericHash = nonNegativeHash.toString(36);
246
- return alphanumericHash;
247
- };
248
141
  export {
249
- addMetadata,
250
- hashFromQuery,
142
+ addMetadata2 as addMetadata,
143
+ hashFromQuery2 as hashFromQuery,
251
144
  tinaField,
252
145
  useEditState,
253
146
  useTina
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Re-export of the canonical `tinaField` helper from `@tinacms/bridge`.
3
+ * Kept here so `tinacms/tina-field` consumers don't need to switch imports.
4
+ */
5
+ export { tinaField } from '@tinacms/bridge/tina-field';
@@ -0,0 +1,4 @@
1
+ import { tinaField } from "@tinacms/bridge/tina-field";
2
+ export {
3
+ tinaField
4
+ };
@@ -1,5 +1,5 @@
1
- import { Media, MediaStore, MediaUploadOptions, MediaList, MediaListOptions } from './media';
2
1
  import { CMS } from './cms';
2
+ import { Media, MediaList, MediaListOptions, MediaStore, MediaUploadOptions } from './media';
3
3
  export declare class DummyMediaStore implements MediaStore {
4
4
  accept: string;
5
5
  persist(files: MediaUploadOptions[]): Promise<Media[]>;
@@ -35,7 +35,35 @@ export declare class TinaMediaStore implements MediaStore {
35
35
  isAuthenticated(): Promise<boolean>;
36
36
  accept: string;
37
37
  maxSize: number;
38
+ /**
39
+ * Returns the current branch as a single-encoded query-param value, or
40
+ * an empty string when no branch is set.
41
+ *
42
+ * `this.api.branch` is already URL-encoded by `Client.setBranch()`, so we
43
+ * decode then re-encode here to defend against double-encoding when this
44
+ * value is concatenated into a URL.
45
+ *
46
+ * `Client.setBranch()` runs the constructor's `options.branch` through
47
+ * `encodeURIComponent` without a guard, so an unset `options.branch`
48
+ * lands here as the literal string `"undefined"`. We treat that and the
49
+ * empty case as no-branch so we don't send `?branch=undefined` to the
50
+ * assets-api (which would route the call to a non-existent staging path).
51
+ */
52
+ private encodedBranchParam;
38
53
  private persist_cloud;
54
+ /**
55
+ * Resolves the just-uploaded items to canonical `Media` entries by hitting
56
+ * the assets-api `list` endpoint, which is the source of truth for the
57
+ * `src` URL — including the staging-branch path
58
+ * (`__staging/<branch>/__file/...`) and the per-stage CDN host.
59
+ * Constructing those URLs on the client would mirror server-side branch
60
+ * routing and CDN-host logic, which is fragile to keep in sync.
61
+ *
62
+ * Best-effort: items not found within the first page of their directory
63
+ * (e.g. very large directories) are omitted from the result rather than
64
+ * throwing — the upload itself already succeeded.
65
+ */
66
+ private fetchUploadedEntries;
39
67
  private persist_local;
40
68
  persist(media: MediaUploadOptions[]): Promise<Media[]>;
41
69
  private genThumbnail;
@@ -65,6 +65,14 @@ export interface MediaStore {
65
65
  * Lists all media in a specific directory.
66
66
  */
67
67
  list(options?: MediaListOptions): Promise<MediaList>;
68
+ /**
69
+ * Reserved hook for renaming a media object in the store.
70
+ *
71
+ * Not yet implemented in `TinaMediaStore` or surfaced in `MediaManager` —
72
+ * declared here as an extension point so stores can begin to opt in once
73
+ * the corresponding assets-api endpoint is built.
74
+ */
75
+ rename?(from: string, to: string): Promise<Media>;
68
76
  /**
69
77
  * Indicates that uploads and deletions are not supported
70
78
  *
@@ -5,6 +5,11 @@ import { CodeLeaf } from '../../components/plate-ui/code-leaf';
5
5
  import { CodeSyntaxLeaf } from '../../components/plate-ui/code-syntax-leaf';
6
6
  import { HrElement } from '../../components/plate-ui/hr-element';
7
7
  import { LinkElement } from '../../components/plate-ui/link-element';
8
+ type HeadingComponentProps = {
9
+ attributes: React.HTMLAttributes<HTMLHeadingElement>;
10
+ children: React.ReactNode;
11
+ className?: string;
12
+ };
8
13
  export declare const Components: () => {
9
14
  [CodeLinePlugin.key]: React.ForwardRefExoticComponent<Omit<Omit<import("@udecode/plate/react").PlateElementProps<import("@udecode/plate").TElement, import("@udecode/plate").AnyPluginConfig>, keyof {
10
15
  className?: string;
@@ -34,49 +39,12 @@ export declare const Components: () => {
34
39
  className?: string;
35
40
  style?: React.CSSProperties;
36
41
  }, "ref"> & React.RefAttributes<never>>;
37
- h1: ({ attributes, editor, element, className, ...props }: {
38
- [x: string]: any;
39
- attributes: any;
40
- editor: any;
41
- element: any;
42
- className: any;
43
- }) => React.JSX.Element;
44
- h2: ({ attributes, editor, element, className, ...props }: {
45
- [x: string]: any;
46
- attributes: any;
47
- editor: any;
48
- element: any;
49
- className: any;
50
- }) => React.JSX.Element;
51
- h3: ({ attributes, editor, element, className, ...props }: {
52
- [x: string]: any;
53
- attributes: any;
54
- editor: any;
55
- element: any;
56
- className: any;
57
- }) => React.JSX.Element;
58
- h4: ({ attributes, editor, element, className, ...props }: {
59
- [x: string]: any;
60
- attributes: any;
61
- editor: any;
62
- element: any;
63
- className: any;
64
- }) => React.JSX.Element;
65
- /** Tailwind prose doesn't style h5 and h6 elements */
66
- h5: ({ attributes, editor, element, className, ...props }: {
67
- [x: string]: any;
68
- attributes: any;
69
- editor: any;
70
- element: any;
71
- className: any;
72
- }) => React.JSX.Element;
73
- h6: ({ attributes, editor, element, className, ...props }: {
74
- [x: string]: any;
75
- attributes: any;
76
- editor: any;
77
- element: any;
78
- className: any;
79
- }) => React.JSX.Element;
42
+ h1: ({ attributes, children, className, }: HeadingComponentProps) => React.JSX.Element;
43
+ h2: ({ attributes, children, className, }: HeadingComponentProps) => React.JSX.Element;
44
+ h3: ({ attributes, children, className, }: HeadingComponentProps) => React.JSX.Element;
45
+ h4: ({ attributes, children, className, }: HeadingComponentProps) => React.JSX.Element;
46
+ h5: ({ attributes, children, className, }: HeadingComponentProps) => React.JSX.Element;
47
+ h6: ({ attributes, children, className, }: HeadingComponentProps) => React.JSX.Element;
80
48
  p: React.ForwardRefExoticComponent<Omit<Omit<import("@udecode/plate/react").PlateElementProps<import("@udecode/plate").TElement, import("@udecode/plate").AnyPluginConfig>, keyof {
81
49
  className?: string;
82
50
  style?: React.CSSProperties;
@@ -327,3 +295,4 @@ export declare const Components: () => {
327
295
  style?: React.CSSProperties;
328
296
  }, "ref"> & React.RefAttributes<never>>;
329
297
  };
298
+ export {};
@@ -35,7 +35,8 @@ interface RepositoryProviderProps {
35
35
  };
36
36
  }
37
37
  export declare const FileHistoryProvider: ({ contentRelativePath, tinaBranch, defaultBranchName, historyUrl, isLocalMode, }: RepositoryProviderProps) => React.JSX.Element;
38
- export declare const FormBreadcrumbs: ({ rootBreadcrumbName, ...props }: {
38
+ export declare const FormBreadcrumbs: ({ rootBreadcrumbName, contentPath, ...props }: {
39
39
  rootBreadcrumbName?: string;
40
+ contentPath?: string;
40
41
  } & React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
41
42
  export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "tinacms",
3
3
  "type": "module",
4
4
  "typings": "dist/index.d.ts",
5
- "version": "3.7.5",
5
+ "version": "3.8.0",
6
6
  "main": "dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "exports": {
@@ -10,6 +10,8 @@
10
10
  "./dist/client": "./dist/client.js",
11
11
  "./react": "./dist/react.js",
12
12
  "./dist/react": "./dist/react.js",
13
+ "./tina-field": "./dist/tina-field.js",
14
+ "./dist/tina-field": "./dist/tina-field.js",
13
15
  "./dist/rich-text": "./dist/rich-text/index.js",
14
16
  "./dist/rich-text/static": "./dist/rich-text/static.js",
15
17
  "./dist/rich-text/prism": "./dist/rich-text/prism.js"
@@ -24,6 +26,7 @@
24
26
  "src/rich-text/static.tsx",
25
27
  "src/rich-text/prism.tsx",
26
28
  "src/react.tsx",
29
+ "src/tina-field.ts",
27
30
  {
28
31
  "name": "src/client.ts",
29
32
  "target": "node"
@@ -114,9 +117,10 @@
114
117
  "webfontloader": "1.6.28",
115
118
  "yup": "^1.6.1",
116
119
  "zod": "^3.24.2",
117
- "@tinacms/mdx": "2.1.3",
118
- "@tinacms/schema-tools": "2.7.3",
119
- "@tinacms/search": "1.2.12"
120
+ "@tinacms/bridge": "0.2.0",
121
+ "@tinacms/schema-tools": "2.7.4",
122
+ "@tinacms/mdx": "2.1.4",
123
+ "@tinacms/search": "1.2.14"
120
124
  },
121
125
  "devDependencies": {
122
126
  "@graphql-tools/utils": "^10.8.1",
@@ -144,7 +148,7 @@
144
148
  "typescript": "^5.7.3",
145
149
  "vite": "^5.4.14",
146
150
  "vitest": "^2.1.9",
147
- "@tinacms/scripts": "1.6.0"
151
+ "@tinacms/scripts": "1.6.1"
148
152
  },
149
153
  "peerDependencies": {
150
154
  "react": ">=16.14.0",