tinacms 1.5.21 → 1.5.23

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/client.js CHANGED
@@ -7,12 +7,22 @@
7
7
  const HeadersDefined = typeof Headers === "undefined" ? HeadersPonyfill : Headers;
8
8
  const TINA_HOST = "content.tinajs.io";
9
9
  class TinaClient {
10
- constructor({ token, url, queries }) {
10
+ constructor({
11
+ token,
12
+ url,
13
+ queries,
14
+ errorPolicy
15
+ }) {
11
16
  this.apiUrl = url;
12
17
  this.readonlyToken = token == null ? void 0 : token.trim();
13
18
  this.queries = queries(this);
19
+ this.errorPolicy = errorPolicy || "throw";
14
20
  }
15
- async request(args) {
21
+ async request({
22
+ errorPolicy,
23
+ ...args
24
+ }) {
25
+ const errorPolicyDefined = errorPolicy || this.errorPolicy;
16
26
  const headers = new HeadersDefined();
17
27
  if (this.readonlyToken) {
18
28
  headers.append("X-API-KEY", this.readonlyToken);
@@ -39,16 +49,16 @@
39
49
  );
40
50
  }
41
51
  const json = await res.json();
42
- if (json.errors) {
52
+ if (json.errors && errorPolicyDefined === "throw") {
43
53
  throw new Error(
44
54
  `Unable to fetch, please see our FAQ for more information: https://tina.io/docs/errors/faq/
45
-
46
55
  Errors:
47
56
  ${json.errors.map((error) => error.message).join("\n")}`
48
57
  );
49
58
  }
50
59
  return {
51
60
  data: json == null ? void 0 : json.data,
61
+ errors: (json == null ? void 0 : json.errors) || null,
52
62
  query: args.query
53
63
  };
54
64
  }
package/dist/client.mjs CHANGED
@@ -4,12 +4,22 @@ const fetchDefined = typeof fetch === "undefined" ? fetchPonyfillFN : fetch;
4
4
  const HeadersDefined = typeof Headers === "undefined" ? HeadersPonyfill : Headers;
5
5
  const TINA_HOST = "content.tinajs.io";
6
6
  class TinaClient {
7
- constructor({ token, url, queries }) {
7
+ constructor({
8
+ token,
9
+ url,
10
+ queries,
11
+ errorPolicy
12
+ }) {
8
13
  this.apiUrl = url;
9
14
  this.readonlyToken = token == null ? void 0 : token.trim();
10
15
  this.queries = queries(this);
16
+ this.errorPolicy = errorPolicy || "throw";
11
17
  }
12
- async request(args) {
18
+ async request({
19
+ errorPolicy,
20
+ ...args
21
+ }) {
22
+ const errorPolicyDefined = errorPolicy || this.errorPolicy;
13
23
  const headers = new HeadersDefined();
14
24
  if (this.readonlyToken) {
15
25
  headers.append("X-API-KEY", this.readonlyToken);
@@ -36,16 +46,16 @@ class TinaClient {
36
46
  );
37
47
  }
38
48
  const json = await res.json();
39
- if (json.errors) {
49
+ if (json.errors && errorPolicyDefined === "throw") {
40
50
  throw new Error(
41
51
  `Unable to fetch, please see our FAQ for more information: https://tina.io/docs/errors/faq/
42
-
43
52
  Errors:
44
53
  ${json.errors.map((error) => error.message).join("\n")}`
45
54
  );
46
55
  }
47
56
  return {
48
57
  data: json == null ? void 0 : json.data,
58
+ errors: (json == null ? void 0 : json.errors) || null,
49
59
  query: args.query
50
60
  };
51
61
  }
package/dist/dev-tools.js CHANGED
@@ -72,6 +72,7 @@
72
72
  return MNode;
73
73
  };
74
74
  const Node = ({ components, child }) => {
75
+ var _a, _b, _c, _d, _e, _f;
75
76
  const { children, ...props } = child;
76
77
  switch (child.type) {
77
78
  case "h1":
@@ -147,6 +148,42 @@
147
148
  const props2 = child.props ? child.props : {};
148
149
  return /* @__PURE__ */ React.createElement(Component, { ...props2 });
149
150
  } else {
151
+ if (child.name === "table") {
152
+ const firstRowHeader = (_a = child.props) == null ? void 0 : _a.firstRowHeader;
153
+ const rows = (firstRowHeader ? (_b = child.props) == null ? void 0 : _b.tableRows.filter((_, i) => i !== 0) : (_c = child.props) == null ? void 0 : _c.tableRows) || [];
154
+ const header = (_e = (_d = child.props) == null ? void 0 : _d.tableRows) == null ? void 0 : _e.at(0);
155
+ const TableComponent = components["table"] || ((props2) => /* @__PURE__ */ React.createElement("table", { ...props2 }));
156
+ const TrComponent = components["tr"] || ((props2) => /* @__PURE__ */ React.createElement("tr", { ...props2 }));
157
+ const ThComponent = components["th"] || ((props2) => /* @__PURE__ */ React.createElement("th", { style: { textAlign: (props2 == null ? void 0 : props2.align) || "auto" }, ...props2 }));
158
+ const TdComponent = components["td"] || ((props2) => /* @__PURE__ */ React.createElement("td", { style: { textAlign: (props2 == null ? void 0 : props2.align) || "auto" }, ...props2 }));
159
+ const align = ((_f = child.props) == null ? void 0 : _f.align) || [];
160
+ return /* @__PURE__ */ React.createElement(TableComponent, null, firstRowHeader && /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement(TrComponent, null, header.tableCells.map((c, i) => {
161
+ return /* @__PURE__ */ React.createElement(
162
+ TinaMarkdown,
163
+ {
164
+ key: i,
165
+ components: {
166
+ p: (props2) => /* @__PURE__ */ React.createElement(ThComponent, { align: align[i], ...props2 })
167
+ },
168
+ content: c.value
169
+ }
170
+ );
171
+ }))), /* @__PURE__ */ React.createElement("tbody", null, rows.map((row, i) => {
172
+ var _a2;
173
+ return /* @__PURE__ */ React.createElement(TrComponent, { key: i }, (_a2 = row == null ? void 0 : row.tableCells) == null ? void 0 : _a2.map((c, i2) => {
174
+ return /* @__PURE__ */ React.createElement(
175
+ TinaMarkdown,
176
+ {
177
+ key: i2,
178
+ components: {
179
+ p: (props2) => /* @__PURE__ */ React.createElement(TdComponent, { align: align[i2], ...props2 })
180
+ },
181
+ content: c.value
182
+ }
183
+ );
184
+ }));
185
+ })));
186
+ }
150
187
  const ComponentMissing = components["component_missing"];
151
188
  if (ComponentMissing) {
152
189
  return /* @__PURE__ */ React.createElement(ComponentMissing, { name: child.name });
@@ -69,6 +69,7 @@ const MemoNode = (props) => {
69
69
  return MNode;
70
70
  };
71
71
  const Node = ({ components, child }) => {
72
+ var _a, _b, _c, _d, _e, _f;
72
73
  const { children, ...props } = child;
73
74
  switch (child.type) {
74
75
  case "h1":
@@ -144,6 +145,42 @@ const Node = ({ components, child }) => {
144
145
  const props2 = child.props ? child.props : {};
145
146
  return /* @__PURE__ */ React.createElement(Component, { ...props2 });
146
147
  } else {
148
+ if (child.name === "table") {
149
+ const firstRowHeader = (_a = child.props) == null ? void 0 : _a.firstRowHeader;
150
+ const rows = (firstRowHeader ? (_b = child.props) == null ? void 0 : _b.tableRows.filter((_, i) => i !== 0) : (_c = child.props) == null ? void 0 : _c.tableRows) || [];
151
+ const header = (_e = (_d = child.props) == null ? void 0 : _d.tableRows) == null ? void 0 : _e.at(0);
152
+ const TableComponent = components["table"] || ((props2) => /* @__PURE__ */ React.createElement("table", { ...props2 }));
153
+ const TrComponent = components["tr"] || ((props2) => /* @__PURE__ */ React.createElement("tr", { ...props2 }));
154
+ const ThComponent = components["th"] || ((props2) => /* @__PURE__ */ React.createElement("th", { style: { textAlign: (props2 == null ? void 0 : props2.align) || "auto" }, ...props2 }));
155
+ const TdComponent = components["td"] || ((props2) => /* @__PURE__ */ React.createElement("td", { style: { textAlign: (props2 == null ? void 0 : props2.align) || "auto" }, ...props2 }));
156
+ const align = ((_f = child.props) == null ? void 0 : _f.align) || [];
157
+ return /* @__PURE__ */ React.createElement(TableComponent, null, firstRowHeader && /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement(TrComponent, null, header.tableCells.map((c, i) => {
158
+ return /* @__PURE__ */ React.createElement(
159
+ TinaMarkdown,
160
+ {
161
+ key: i,
162
+ components: {
163
+ p: (props2) => /* @__PURE__ */ React.createElement(ThComponent, { align: align[i], ...props2 })
164
+ },
165
+ content: c.value
166
+ }
167
+ );
168
+ }))), /* @__PURE__ */ React.createElement("tbody", null, rows.map((row, i) => {
169
+ var _a2;
170
+ return /* @__PURE__ */ React.createElement(TrComponent, { key: i }, (_a2 = row == null ? void 0 : row.tableCells) == null ? void 0 : _a2.map((c, i2) => {
171
+ return /* @__PURE__ */ React.createElement(
172
+ TinaMarkdown,
173
+ {
174
+ key: i2,
175
+ components: {
176
+ p: (props2) => /* @__PURE__ */ React.createElement(TdComponent, { align: align[i2], ...props2 })
177
+ },
178
+ content: c.value
179
+ }
180
+ );
181
+ }));
182
+ })));
183
+ }
147
184
  const ComponentMissing = components["component_missing"];
148
185
  if (ComponentMissing) {
149
186
  return /* @__PURE__ */ React.createElement(ComponentMissing, { name: child.name });
package/dist/index.d.ts CHANGED
@@ -57,3 +57,4 @@ interface MediaStoreClass {
57
57
  }
58
58
  export declare const defineStaticConfig: (config: Config<(cms: TinaCMS) => TinaCMS, formifyCallback, import("./hooks/use-content-creator").DocumentCreatorArgs, MediaStoreClass, undefined>) => Config<(cms: TinaCMS) => TinaCMS, formifyCallback, import("./hooks/use-content-creator").DocumentCreatorArgs, MediaStoreClass, undefined>;
59
59
  export declare const defineConfig: (config: Config<(cms: TinaCMS) => TinaCMS, formifyCallback, import("./hooks/use-content-creator").DocumentCreatorArgs, MediaStoreClass, undefined>) => Config<(cms: TinaCMS) => TinaCMS, formifyCallback, import("./hooks/use-content-creator").DocumentCreatorArgs, MediaStoreClass, undefined>;
60
+ export { tinaTableTemplate } from './table';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("zod"), require("react"), require("react-dom"), require("@udecode/plate-headless"), require("@monaco-editor/react"), require("slate-react"), require("@headlessui/react"), require("slate"), require("final-form-arrays"), require("final-form-set-field-data"), require("final-form"), require("react-final-form"), require("prop-types"), require("react-beautiful-dnd"), require("@radix-ui/react-popover"), require("react-color"), require("color-string"), require("react-dropzone"), require("is-hotkey"), require("@floating-ui/dom"), require("moment"), require("date-fns"), require("@react-hook/window-size"), require("@tinacms/sharedctx"), require("graphql"), require("graphql-tag"), require("@tinacms/schema-tools"), require("yup"), require("@graphql-inspector/core"), require("react-router-dom")) : typeof define === "function" && define.amd ? define(["exports", "zod", "react", "react-dom", "@udecode/plate-headless", "@monaco-editor/react", "slate-react", "@headlessui/react", "slate", "final-form-arrays", "final-form-set-field-data", "final-form", "react-final-form", "prop-types", "react-beautiful-dnd", "@radix-ui/react-popover", "react-color", "color-string", "react-dropzone", "is-hotkey", "@floating-ui/dom", "moment", "date-fns", "@react-hook/window-size", "@tinacms/sharedctx", "graphql", "graphql-tag", "@tinacms/schema-tools", "yup", "@graphql-inspector/core", "react-router-dom"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.tinacms = {}, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP));
3
- })(this, function(exports2, zod, React, reactDom, plateHeadless, MonacoEditor, slateReact, react, slate, arrayMutators, setFieldData, finalForm, reactFinalForm, PropTypes, reactBeautifulDnd, Popover$1, pkg$1, pkg, dropzone, isHotkey, dom, moment, dateFns, windowSize, sharedctx, graphql, gql$1, schemaTools, yup, core, reactRouterDom) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("zod"), require("react"), require("react-dom"), require("@udecode/plate-headless"), require("@monaco-editor/react"), require("slate-react"), require("@headlessui/react"), require("slate"), require("final-form-arrays"), require("final-form-set-field-data"), require("final-form"), require("react-final-form"), require("prop-types"), require("react-beautiful-dnd"), require("@radix-ui/react-popover"), require("react-color"), require("color-string"), require("react-dropzone"), require("@tinacms/sharedctx"), require("is-hotkey"), require("@floating-ui/dom"), require("moment"), require("date-fns"), require("@react-hook/window-size"), require("graphql"), require("graphql-tag"), require("@tinacms/schema-tools"), require("yup"), require("@graphql-inspector/core"), require("react-router-dom"), require("@tinacms/mdx")) : typeof define === "function" && define.amd ? define(["exports", "zod", "react", "react-dom", "@udecode/plate-headless", "@monaco-editor/react", "slate-react", "@headlessui/react", "slate", "final-form-arrays", "final-form-set-field-data", "final-form", "react-final-form", "prop-types", "react-beautiful-dnd", "@radix-ui/react-popover", "react-color", "color-string", "react-dropzone", "@tinacms/sharedctx", "is-hotkey", "@floating-ui/dom", "moment", "date-fns", "@react-hook/window-size", "graphql", "graphql-tag", "@tinacms/schema-tools", "yup", "@graphql-inspector/core", "react-router-dom", "@tinacms/mdx"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.tinacms = {}, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP));
3
+ })(this, function(exports2, zod, React, reactDom, plateHeadless, MonacoEditor, slateReact, react, slate, arrayMutators, setFieldData, finalForm, reactFinalForm, PropTypes, reactBeautifulDnd, Popover$1, pkg$1, pkg, dropzone, sharedctx, isHotkey, dom, moment, dateFns, windowSize, graphql, gql$1, schemaTools, yup, core, reactRouterDom, mdx) {
4
4
  "use strict";var __defProp = Object.defineProperty;
5
5
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
6
  var __publicField = (obj, key, value) => {
@@ -1810,8 +1810,15 @@ var __publicField = (obj, key, value) => {
1810
1810
  return this.finalForm.subscribe(cb, options2);
1811
1811
  };
1812
1812
  this.handleSubmit = async (values, form, cb) => {
1813
+ var _a;
1813
1814
  try {
1814
- const response = await this.onSubmit(values, form, cb);
1815
+ const valOverride = await ((_a = this.beforeSubmit) == null ? void 0 : _a.call(this, values));
1816
+ if (valOverride) {
1817
+ for (const [key, value] of Object.entries(valOverride)) {
1818
+ form.change(key, value);
1819
+ }
1820
+ }
1821
+ const response = await this.onSubmit(valOverride || values, form, cb);
1815
1822
  form.initialize(values);
1816
1823
  return response;
1817
1824
  } catch (error) {
@@ -2349,6 +2356,7 @@ var __publicField = (obj, key, value) => {
2349
2356
  {
2350
2357
  name: field.name,
2351
2358
  key: field.name,
2359
+ isEqual: (a, b) => isEqual(field, a, b),
2352
2360
  type,
2353
2361
  parse: parse2 ? (value, name) => parse2(value, name, field) : void 0,
2354
2362
  format: format2 ? (value, name) => format2(value, name, field) : void 0,
@@ -2406,6 +2414,18 @@ var __publicField = (obj, key, value) => {
2406
2414
  }
2407
2415
  return prop;
2408
2416
  }
2417
+ const isEqual = (field, a, b) => {
2418
+ const replacer = (key, value) => {
2419
+ if (key === "id") {
2420
+ return void 0;
2421
+ }
2422
+ return value;
2423
+ };
2424
+ if (field.type === "rich-text") {
2425
+ return JSON.stringify(a, replacer) === JSON.stringify(b, replacer);
2426
+ }
2427
+ return a === b;
2428
+ };
2409
2429
  const FF = reactFinalForm.Form;
2410
2430
  const FormLegacy = ({ form, children }) => {
2411
2431
  const [i, setI] = React__namespace.useState(0);
@@ -2450,7 +2470,7 @@ var __publicField = (obj, key, value) => {
2450
2470
  disabled,
2451
2471
  rounded = "full",
2452
2472
  children,
2453
- className,
2473
+ className = "",
2454
2474
  ...props
2455
2475
  }) => {
2456
2476
  const baseClasses = "icon-parent inline-flex items-center font-medium focus:outline-none focus:ring-2 focus:shadow-outline text-center inline-flex justify-center transition-all duration-150 ease-out ";
@@ -7113,7 +7133,7 @@ var __publicField = (obj, key, value) => {
7113
7133
  toolbarItems: [
7114
7134
  branch.githubPullRequestUrl && {
7115
7135
  name: "github-pr",
7116
- label: "View in Github",
7136
+ label: "View in GitHub",
7117
7137
  Icon: /* @__PURE__ */ React__namespace.createElement(BiLinkExternal, { className: "w-5 h-auto text-blue-500 opacity-70" }),
7118
7138
  onMouseDown: () => {
7119
7139
  window.open(branch.githubPullRequestUrl, "_blank");
@@ -7535,6 +7555,10 @@ var __publicField = (obj, key, value) => {
7535
7555
  `${this.url}/upload_url/${path}`,
7536
7556
  { method: "GET" }
7537
7557
  );
7558
+ if (res.status === 412) {
7559
+ const { message = "Unexpected error generating upload url" } = await res.json();
7560
+ throw new Error(message);
7561
+ }
7538
7562
  const { signedUrl } = await res.json();
7539
7563
  if (!signedUrl) {
7540
7564
  throw new Error("Unexpected error generating upload url");
@@ -10111,7 +10135,7 @@ var __publicField = (obj, key, value) => {
10111
10135
  return {
10112
10136
  error: event.error,
10113
10137
  level: "error",
10114
- message: `Failed to upload file(s) ${event == null ? void 0 : event.uploaded.map((x) => x.file.name).join(", ")}. See error message:
10138
+ message: `Failed to upload file(s) ${event == null ? void 0 : event.uploaded.map((x) => x.file.name).join(", ")}.
10115
10139
 
10116
10140
  ${event == null ? void 0 : event.error.toString()}`
10117
10141
  };
@@ -10822,6 +10846,16 @@ var __publicField = (obj, key, value) => {
10822
10846
  const [createBranchModalOpen, setCreateBranchModalOpen] = React__namespace.useState(false);
10823
10847
  const tinaForm = form.tinaForm;
10824
10848
  const finalForm2 = form.tinaForm.finalForm;
10849
+ const schema = cms.api.tina.schema;
10850
+ React__namespace.useEffect(() => {
10851
+ var _a;
10852
+ const collection = schema.getCollectionByFullPath(tinaForm.relativePath);
10853
+ if ((_a = collection == null ? void 0 : collection.ui) == null ? void 0 : _a.beforeSubmit) {
10854
+ tinaForm.beforeSubmit = (values) => collection.ui.beforeSubmit({ cms, form: tinaForm, values });
10855
+ } else {
10856
+ tinaForm.beforeSubmit = void 0;
10857
+ }
10858
+ }, [tinaForm.relativePath]);
10825
10859
  const moveArrayItem = React__namespace.useCallback(
10826
10860
  (result) => {
10827
10861
  if (!result.destination || !finalForm2)
@@ -10854,18 +10888,7 @@ var __publicField = (obj, key, value) => {
10854
10888
  {
10855
10889
  key: tinaForm.id,
10856
10890
  form: tinaForm.finalForm,
10857
- onSubmit: async (values, form2, cb) => {
10858
- var _a, _b;
10859
- const schema = cms.api.tina.schema;
10860
- const collection = schema.getCollectionByFullPath(tinaForm.relativePath);
10861
- const valOverride = ((_a = collection == null ? void 0 : collection.ui) == null ? void 0 : _a.beforeSubmit) ? await ((_b = collection == null ? void 0 : collection.ui) == null ? void 0 : _b.beforeSubmit({ cms, values, form: tinaForm })) : false;
10862
- if (valOverride) {
10863
- for (const [key, value] of Object.entries(valOverride)) {
10864
- form2.change(key, value);
10865
- }
10866
- }
10867
- return tinaForm.onSubmit(valOverride || values, form2, cb);
10868
- }
10891
+ onSubmit: tinaForm.onSubmit
10869
10892
  },
10870
10893
  ({
10871
10894
  handleSubmit,
@@ -11105,7 +11128,7 @@ var __publicField = (obj, key, value) => {
11105
11128
  };
11106
11129
  const NestedForm = (props) => {
11107
11130
  const FormPortal = useFormPortal();
11108
- const id = React.useMemo(() => uuid(), [props.id]);
11131
+ const id = React.useMemo(() => uuid(), [props.id, props.initialValues]);
11109
11132
  const form = React.useMemo(() => {
11110
11133
  return new Form({
11111
11134
  ...props,
@@ -11116,7 +11139,7 @@ var __publicField = (obj, key, value) => {
11116
11139
  onSubmit: () => {
11117
11140
  }
11118
11141
  });
11119
- }, [id]);
11142
+ }, [id, props.onChange]);
11120
11143
  return /* @__PURE__ */ React.createElement(FormPortal, null, ({ zIndexShift }) => /* @__PURE__ */ React.createElement(GroupPanel, { isExpanded: true, style: { zIndex: zIndexShift + 1e3 } }, /* @__PURE__ */ React.createElement(PanelHeader$1, { onClick: props.onClose }, props.label), /* @__PURE__ */ React.createElement(FormBuilder, { form: { tinaForm: form }, hideFooter: true })));
11121
11144
  };
11122
11145
  const handleCloseBase = (editor, element) => {
@@ -11997,26 +12020,38 @@ var __publicField = (obj, key, value) => {
11997
12020
  }
11998
12021
  plateHeadless.wrapNodes(editor, baseLink, { split: true });
11999
12022
  };
12023
+ const matchLink = (n) => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === plateHeadless.ELEMENT_LINK;
12000
12024
  const LinkForm = (props) => {
12025
+ const [initialValues, setInitialValues] = React.useState({ url: "", title: "" });
12026
+ const [formValues, setFormValues] = React.useState({});
12001
12027
  const editor = plateHeadless.useEditorState();
12002
12028
  const selection = React.useMemo(() => editor.selection, []);
12003
- const handleChange = (values) => {
12029
+ React.useEffect(() => {
12030
+ const [link] = getLinks(editor);
12031
+ setInitialValues({
12032
+ url: link && link[0].url ? link[0].url : "",
12033
+ title: link && link[0].title ? link[0].title : ""
12034
+ });
12035
+ }, [editor, setInitialValues]);
12036
+ const handleUpdate = React.useCallback(() => {
12004
12037
  const linksInSelection = plateHeadless.getNodeEntries(editor, {
12005
- match: (n) => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === plateHeadless.ELEMENT_LINK,
12038
+ match: matchLink,
12006
12039
  at: selection
12007
12040
  });
12008
12041
  if (linksInSelection) {
12009
12042
  for (const [, location] of linksInSelection) {
12010
- plateHeadless.setNodes(editor, values, {
12011
- match: (n) => {
12012
- return !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === plateHeadless.ELEMENT_LINK;
12013
- },
12043
+ plateHeadless.setNodes(editor, formValues, {
12044
+ match: matchLink,
12014
12045
  at: location
12015
12046
  });
12016
12047
  }
12017
12048
  }
12018
- };
12019
- const [link] = getLinks(editor);
12049
+ props.onClose();
12050
+ }, [editor, formValues]);
12051
+ const UpdateLink = React.useCallback(
12052
+ () => /* @__PURE__ */ React.createElement(Button, { variant: "primary", onClick: handleUpdate }, "Update Link"),
12053
+ [handleUpdate]
12054
+ );
12020
12055
  return /* @__PURE__ */ React.createElement(
12021
12056
  NestedForm,
12022
12057
  {
@@ -12024,32 +12059,35 @@ var __publicField = (obj, key, value) => {
12024
12059
  label: "Link",
12025
12060
  fields: [
12026
12061
  { label: "URL", name: "url", component: "text" },
12027
- { label: "Title", name: "title", component: "text" }
12062
+ { label: "Title", name: "title", component: "text" },
12063
+ { component: UpdateLink, name: "update" }
12028
12064
  ],
12029
- initialValues: {
12030
- url: link ? link[0].url : "",
12031
- title: link ? link[0].title : ""
12032
- },
12033
- onChange: handleChange,
12034
- onClose: props.onClose
12065
+ initialValues,
12066
+ onChange: (values) => setFormValues(values),
12067
+ onClose: () => {
12068
+ if (initialValues.title === "" && initialValues.url === "") {
12069
+ unwrapLink(editor, selection);
12070
+ }
12071
+ props.onClose();
12072
+ }
12035
12073
  }
12036
12074
  );
12037
12075
  };
12038
- const isLinkActive = (editor) => {
12039
- const [link] = getLinks(editor);
12040
- return !!link;
12041
- };
12042
12076
  const unwrapLink = (editor, selection) => {
12043
12077
  plateHeadless.unwrapNodes(editor, {
12044
- match: (n) => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === plateHeadless.ELEMENT_LINK,
12078
+ match: matchLink,
12045
12079
  at: selection || void 0
12046
12080
  });
12047
12081
  };
12048
12082
  const getLinks = (editor) => {
12049
12083
  return plateHeadless.getNodeEntries(editor, {
12050
- match: (n) => !slate.Editor.isEditor(n) && slate.Element.isElement(n) && n.type === plateHeadless.ELEMENT_LINK
12084
+ match: matchLink
12051
12085
  });
12052
12086
  };
12087
+ const isLinkActive = (editor) => {
12088
+ const [link] = getLinks(editor);
12089
+ return !!link;
12090
+ };
12053
12091
  const ToolbarItem = ({
12054
12092
  hidden,
12055
12093
  label,
@@ -26220,7 +26258,7 @@ var __publicField = (obj, key, value) => {
26220
26258
  data["_id"] = `${collection.name}:${relPath}`;
26221
26259
  data["_relativePath"] = relPath;
26222
26260
  }
26223
- for (const f of collection.fields || (field == null ? void 0 : field.fields) || []) {
26261
+ for (const f of (field == null ? void 0 : field.fields) || collection.fields || []) {
26224
26262
  if (!f.searchable) {
26225
26263
  delete data[f.name];
26226
26264
  continue;
@@ -29415,8 +29453,7 @@ This will work when developing locally but NOT when deployed to production.
29415
29453
  setSearch(searchInput);
29416
29454
  setSearchLoaded(false);
29417
29455
  },
29418
- variant: "primary",
29419
- type: "submit"
29456
+ variant: "primary"
29420
29457
  },
29421
29458
  "Search ",
29422
29459
  /* @__PURE__ */ React.createElement(BiSearch, { className: "w-5 h-full ml-1.5 opacity-70" })
@@ -29700,13 +29737,13 @@ This will work when developing locally but NOT when deployed to production.
29700
29737
  }
29701
29738
  const defaultItem = customDefaults || // @ts-ignore internal types aren't up to date
29702
29739
  ((_d = template.ui) == null ? void 0 : _d.defaultItem) || // @ts-ignore
29703
- (template == null ? void 0 : template.defaultItem);
29740
+ (template == null ? void 0 : template.defaultItem) || {};
29704
29741
  const form = React.useMemo(() => {
29705
29742
  var _a2, _b2;
29706
29743
  const folderName = folder.fullyQualifiedName ? folder.name : "";
29707
29744
  return new Form({
29708
29745
  crudType: "create",
29709
- initialValues: typeof defaultItem === "function" ? defaultItem() : defaultItem,
29746
+ initialValues: typeof defaultItem === "function" ? { ...defaultItem(), _template: templateName } : { ...defaultItem, _template: templateName },
29710
29747
  extraSubscribeValues: { active: true, submitting: true, touched: true },
29711
29748
  onChange: (values) => {
29712
29749
  var _a3, _b3;
@@ -30238,10 +30275,12 @@ This will work when developing locally but NOT when deployed to production.
30238
30275
  redirect,
30239
30276
  children
30240
30277
  }) => {
30278
+ const cms = useCMS$1();
30241
30279
  const navigate = reactRouterDom.useNavigate();
30242
30280
  React.useEffect(() => {
30281
+ const basePath = cms.flags.get("tina-basepath");
30243
30282
  if (redirect) {
30244
- navigate("/~");
30283
+ navigate(`/~${basePath ? `/${basePath}` : ""}`);
30245
30284
  }
30246
30285
  }, [redirect]);
30247
30286
  return children;
@@ -30485,6 +30524,118 @@ This will work when developing locally but NOT when deployed to production.
30485
30524
  this.mapper = mapper;
30486
30525
  }
30487
30526
  }
30527
+ const tinaTableTemplate = {
30528
+ name: "table",
30529
+ label: "Table",
30530
+ fields: [
30531
+ {
30532
+ name: "firstRowHeader",
30533
+ label: "First row is a header",
30534
+ type: "boolean"
30535
+ },
30536
+ {
30537
+ name: "align",
30538
+ label: "Align",
30539
+ type: "string",
30540
+ list: true,
30541
+ description: 'Possible values: "left", "right", or "center".'
30542
+ },
30543
+ {
30544
+ name: "tableRows",
30545
+ label: "Rows",
30546
+ type: "object",
30547
+ list: true,
30548
+ ui: {
30549
+ itemProps: (value) => {
30550
+ if (value == null ? void 0 : value.tableCells) {
30551
+ if (Array.isArray(value.tableCells)) {
30552
+ return {
30553
+ label: value.tableCells.map((cellItem) => {
30554
+ var _a;
30555
+ return (_a = stringifyCell(cellItem.value)) == null ? void 0 : _a.trim();
30556
+ }).join(" | ")
30557
+ };
30558
+ }
30559
+ }
30560
+ return { label: "Row" };
30561
+ }
30562
+ },
30563
+ fields: [
30564
+ {
30565
+ name: "tableCells",
30566
+ label: "Table Cells",
30567
+ list: true,
30568
+ type: "object",
30569
+ ui: {
30570
+ itemProps: (cell) => {
30571
+ var _a;
30572
+ if (cell) {
30573
+ if (cell.value) {
30574
+ return {
30575
+ label: (_a = stringifyCell(cell.value)) == null ? void 0 : _a.trim()
30576
+ };
30577
+ }
30578
+ }
30579
+ return { label: "Value" };
30580
+ }
30581
+ },
30582
+ fields: [
30583
+ {
30584
+ label: "Value",
30585
+ description: "Note: table cells do not support block-level elements like headers, code blocks, or lists. Any block-level items other than the first paragraph will be considered invalid.",
30586
+ name: "value",
30587
+ type: "rich-text",
30588
+ ui: {
30589
+ validate(value) {
30590
+ try {
30591
+ tableCellSchema.parse(value);
30592
+ } catch (e) {
30593
+ if (e instanceof zod.z.ZodError) {
30594
+ return e.errors[0].message;
30595
+ }
30596
+ return e.message;
30597
+ }
30598
+ }
30599
+ }
30600
+ }
30601
+ ]
30602
+ }
30603
+ ]
30604
+ }
30605
+ ]
30606
+ };
30607
+ const tableCellSchema = zod.z.object({
30608
+ type: zod.z.literal("root"),
30609
+ children: zod.z.array(
30610
+ zod.z.object({
30611
+ type: zod.z.string(),
30612
+ children: zod.z.any().array()
30613
+ })
30614
+ ).refine(
30615
+ (value) => {
30616
+ const firstValue = value[0];
30617
+ return firstValue && firstValue.type === "p";
30618
+ },
30619
+ {
30620
+ message: `Table cell content cannot contain block elements like headers, blockquotes, or lists.`
30621
+ }
30622
+ ).refine(
30623
+ (value) => {
30624
+ var _a;
30625
+ if (value.length > 1) {
30626
+ const secondBlock = value[1];
30627
+ return secondBlock && secondBlock.children.length === 1 && !((_a = secondBlock.children[0]) == null ? void 0 : _a.text);
30628
+ }
30629
+ return true;
30630
+ },
30631
+ {
30632
+ message: `Table cells can only have 1 block level element.`
30633
+ }
30634
+ )
30635
+ });
30636
+ const stringifyCell = (cell) => {
30637
+ return mdx.stringifyMDX(cell, { name: "body", type: "rich-text" }, () => "");
30638
+ };
30488
30639
  const defineSchema = (config) => {
30489
30640
  schemaTools.validateSchema({ schema: config });
30490
30641
  return config;
@@ -30713,6 +30864,7 @@ This will work when developing locally but NOT when deployed to production.
30713
30864
  exports2.selectFieldClasses = selectFieldClasses;
30714
30865
  exports2.staticRequest = staticRequest;
30715
30866
  exports2.textFieldClasses = textFieldClasses;
30867
+ exports2.tinaTableTemplate = tinaTableTemplate;
30716
30868
  exports2.useBranchData = useBranchData;
30717
30869
  exports2.useCMS = useCMS$1;
30718
30870
  exports2.useDismissible = useDismissible;
package/dist/index.mjs CHANGED
@@ -23,12 +23,12 @@ import * as Popover$1 from "@radix-ui/react-popover";
23
23
  import * as pkg$1 from "react-color";
24
24
  import * as pkg from "color-string";
25
25
  import * as dropzone from "react-dropzone";
26
+ import { EditContext, useEditState, setEditing } from "@tinacms/sharedctx";
26
27
  import { isHotkey } from "is-hotkey";
27
28
  import { computePosition, flip, shift } from "@floating-ui/dom";
28
29
  import moment from "moment";
29
30
  import { formatDistanceToNow } from "date-fns";
30
31
  import { useWindowWidth } from "@react-hook/window-size";
31
- import { EditContext, useEditState, setEditing } from "@tinacms/sharedctx";
32
32
  import { getIntrospectionQuery, buildClientSchema, print, parse as parse$3, buildSchema } from "graphql";
33
33
  import gql$1 from "graphql-tag";
34
34
  import { TinaSchema, addNamespaceToSchema, parseURL, resolveForm, normalizePath, validateSchema } from "@tinacms/schema-tools";
@@ -36,6 +36,7 @@ import { NAMER, resolveField } from "@tinacms/schema-tools";
36
36
  import * as yup from "yup";
37
37
  import { diff } from "@graphql-inspector/core";
38
38
  import { NavLink, useSearchParams, useNavigate, useLocation, useParams, Link, HashRouter, Routes, Route } from "react-router-dom";
39
+ import { stringifyMDX } from "@tinacms/mdx";
39
40
  function popupWindow(url, title, window2, w, h) {
40
41
  const y = window2.top.outerHeight / 2 + window2.top.screenY - h / 2;
41
42
  const x = window2.top.outerWidth / 2 + window2.top.screenX - w / 2;
@@ -1816,8 +1817,15 @@ class Form {
1816
1817
  return this.finalForm.subscribe(cb, options2);
1817
1818
  };
1818
1819
  this.handleSubmit = async (values, form, cb) => {
1820
+ var _a;
1819
1821
  try {
1820
- const response = await this.onSubmit(values, form, cb);
1822
+ const valOverride = await ((_a = this.beforeSubmit) == null ? void 0 : _a.call(this, values));
1823
+ if (valOverride) {
1824
+ for (const [key, value] of Object.entries(valOverride)) {
1825
+ form.change(key, value);
1826
+ }
1827
+ }
1828
+ const response = await this.onSubmit(valOverride || values, form, cb);
1821
1829
  form.initialize(values);
1822
1830
  return response;
1823
1831
  } catch (error) {
@@ -2355,6 +2363,7 @@ const InnerField = ({ field, form, fieldPlugins, index, activeFieldName }) => {
2355
2363
  {
2356
2364
  name: field.name,
2357
2365
  key: field.name,
2366
+ isEqual: (a, b) => isEqual(field, a, b),
2358
2367
  type,
2359
2368
  parse: parse2 ? (value, name) => parse2(value, name, field) : void 0,
2360
2369
  format: format2 ? (value, name) => format2(value, name, field) : void 0,
@@ -2412,6 +2421,18 @@ function getProp(name, field, plugin) {
2412
2421
  }
2413
2422
  return prop;
2414
2423
  }
2424
+ const isEqual = (field, a, b) => {
2425
+ const replacer = (key, value) => {
2426
+ if (key === "id") {
2427
+ return void 0;
2428
+ }
2429
+ return value;
2430
+ };
2431
+ if (field.type === "rich-text") {
2432
+ return JSON.stringify(a, replacer) === JSON.stringify(b, replacer);
2433
+ }
2434
+ return a === b;
2435
+ };
2415
2436
  const FF = Form$1;
2416
2437
  const FormLegacy = ({ form, children }) => {
2417
2438
  const [i, setI] = React.useState(0);
@@ -2456,7 +2477,7 @@ const Button = ({
2456
2477
  disabled,
2457
2478
  rounded = "full",
2458
2479
  children,
2459
- className,
2480
+ className = "",
2460
2481
  ...props
2461
2482
  }) => {
2462
2483
  const baseClasses = "icon-parent inline-flex items-center font-medium focus:outline-none focus:ring-2 focus:shadow-outline text-center inline-flex justify-center transition-all duration-150 ease-out ";
@@ -7119,7 +7140,7 @@ const BranchSelector = ({
7119
7140
  toolbarItems: [
7120
7141
  branch.githubPullRequestUrl && {
7121
7142
  name: "github-pr",
7122
- label: "View in Github",
7143
+ label: "View in GitHub",
7123
7144
  Icon: /* @__PURE__ */ React.createElement(BiLinkExternal, { className: "w-5 h-auto text-blue-500 opacity-70" }),
7124
7145
  onMouseDown: () => {
7125
7146
  window.open(branch.githubPullRequestUrl, "_blank");
@@ -7541,6 +7562,10 @@ class TinaMediaStore {
7541
7562
  `${this.url}/upload_url/${path}`,
7542
7563
  { method: "GET" }
7543
7564
  );
7565
+ if (res.status === 412) {
7566
+ const { message = "Unexpected error generating upload url" } = await res.json();
7567
+ throw new Error(message);
7568
+ }
7544
7569
  const { signedUrl } = await res.json();
7545
7570
  if (!signedUrl) {
7546
7571
  throw new Error("Unexpected error generating upload url");
@@ -10117,7 +10142,7 @@ class TinaCMS extends CMS {
10117
10142
  return {
10118
10143
  error: event.error,
10119
10144
  level: "error",
10120
- message: `Failed to upload file(s) ${event == null ? void 0 : event.uploaded.map((x) => x.file.name).join(", ")}. See error message:
10145
+ message: `Failed to upload file(s) ${event == null ? void 0 : event.uploaded.map((x) => x.file.name).join(", ")}.
10121
10146
 
10122
10147
  ${event == null ? void 0 : event.error.toString()}`
10123
10148
  };
@@ -10828,6 +10853,16 @@ const FormBuilder = ({
10828
10853
  const [createBranchModalOpen, setCreateBranchModalOpen] = React.useState(false);
10829
10854
  const tinaForm = form.tinaForm;
10830
10855
  const finalForm = form.tinaForm.finalForm;
10856
+ const schema = cms.api.tina.schema;
10857
+ React.useEffect(() => {
10858
+ var _a;
10859
+ const collection = schema.getCollectionByFullPath(tinaForm.relativePath);
10860
+ if ((_a = collection == null ? void 0 : collection.ui) == null ? void 0 : _a.beforeSubmit) {
10861
+ tinaForm.beforeSubmit = (values) => collection.ui.beforeSubmit({ cms, form: tinaForm, values });
10862
+ } else {
10863
+ tinaForm.beforeSubmit = void 0;
10864
+ }
10865
+ }, [tinaForm.relativePath]);
10831
10866
  const moveArrayItem = React.useCallback(
10832
10867
  (result) => {
10833
10868
  if (!result.destination || !finalForm)
@@ -10860,18 +10895,7 @@ const FormBuilder = ({
10860
10895
  {
10861
10896
  key: tinaForm.id,
10862
10897
  form: tinaForm.finalForm,
10863
- onSubmit: async (values, form2, cb) => {
10864
- var _a, _b;
10865
- const schema = cms.api.tina.schema;
10866
- const collection = schema.getCollectionByFullPath(tinaForm.relativePath);
10867
- const valOverride = ((_a = collection == null ? void 0 : collection.ui) == null ? void 0 : _a.beforeSubmit) ? await ((_b = collection == null ? void 0 : collection.ui) == null ? void 0 : _b.beforeSubmit({ cms, values, form: tinaForm })) : false;
10868
- if (valOverride) {
10869
- for (const [key, value] of Object.entries(valOverride)) {
10870
- form2.change(key, value);
10871
- }
10872
- }
10873
- return tinaForm.onSubmit(valOverride || values, form2, cb);
10874
- }
10898
+ onSubmit: tinaForm.onSubmit
10875
10899
  },
10876
10900
  ({
10877
10901
  handleSubmit,
@@ -11111,7 +11135,7 @@ const PrefixedTextField = ({ prefix = "tina/", ...props }) => {
11111
11135
  };
11112
11136
  const NestedForm = (props) => {
11113
11137
  const FormPortal = useFormPortal();
11114
- const id = React__default.useMemo(() => uuid(), [props.id]);
11138
+ const id = React__default.useMemo(() => uuid(), [props.id, props.initialValues]);
11115
11139
  const form = React__default.useMemo(() => {
11116
11140
  return new Form({
11117
11141
  ...props,
@@ -11122,7 +11146,7 @@ const NestedForm = (props) => {
11122
11146
  onSubmit: () => {
11123
11147
  }
11124
11148
  });
11125
- }, [id]);
11149
+ }, [id, props.onChange]);
11126
11150
  return /* @__PURE__ */ React__default.createElement(FormPortal, null, ({ zIndexShift }) => /* @__PURE__ */ React__default.createElement(GroupPanel, { isExpanded: true, style: { zIndex: zIndexShift + 1e3 } }, /* @__PURE__ */ React__default.createElement(PanelHeader$1, { onClick: props.onClose }, props.label), /* @__PURE__ */ React__default.createElement(FormBuilder, { form: { tinaForm: form }, hideFooter: true })));
11127
11151
  };
11128
11152
  const handleCloseBase = (editor, element) => {
@@ -12003,26 +12027,38 @@ const wrapOrRewrapLink = (editor) => {
12003
12027
  }
12004
12028
  wrapNodes(editor, baseLink, { split: true });
12005
12029
  };
12030
+ const matchLink = (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === ELEMENT_LINK;
12006
12031
  const LinkForm = (props) => {
12032
+ const [initialValues, setInitialValues] = React__default.useState({ url: "", title: "" });
12033
+ const [formValues, setFormValues] = React__default.useState({});
12007
12034
  const editor = useEditorState();
12008
12035
  const selection = React__default.useMemo(() => editor.selection, []);
12009
- const handleChange = (values) => {
12036
+ useEffect(() => {
12037
+ const [link] = getLinks(editor);
12038
+ setInitialValues({
12039
+ url: link && link[0].url ? link[0].url : "",
12040
+ title: link && link[0].title ? link[0].title : ""
12041
+ });
12042
+ }, [editor, setInitialValues]);
12043
+ const handleUpdate = React__default.useCallback(() => {
12010
12044
  const linksInSelection = getNodeEntries(editor, {
12011
- match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === ELEMENT_LINK,
12045
+ match: matchLink,
12012
12046
  at: selection
12013
12047
  });
12014
12048
  if (linksInSelection) {
12015
12049
  for (const [, location] of linksInSelection) {
12016
- setNodes(editor, values, {
12017
- match: (n) => {
12018
- return !Editor.isEditor(n) && Element.isElement(n) && n.type === ELEMENT_LINK;
12019
- },
12050
+ setNodes(editor, formValues, {
12051
+ match: matchLink,
12020
12052
  at: location
12021
12053
  });
12022
12054
  }
12023
12055
  }
12024
- };
12025
- const [link] = getLinks(editor);
12056
+ props.onClose();
12057
+ }, [editor, formValues]);
12058
+ const UpdateLink = React__default.useCallback(
12059
+ () => /* @__PURE__ */ React__default.createElement(Button, { variant: "primary", onClick: handleUpdate }, "Update Link"),
12060
+ [handleUpdate]
12061
+ );
12026
12062
  return /* @__PURE__ */ React__default.createElement(
12027
12063
  NestedForm,
12028
12064
  {
@@ -12030,32 +12066,35 @@ const LinkForm = (props) => {
12030
12066
  label: "Link",
12031
12067
  fields: [
12032
12068
  { label: "URL", name: "url", component: "text" },
12033
- { label: "Title", name: "title", component: "text" }
12069
+ { label: "Title", name: "title", component: "text" },
12070
+ { component: UpdateLink, name: "update" }
12034
12071
  ],
12035
- initialValues: {
12036
- url: link ? link[0].url : "",
12037
- title: link ? link[0].title : ""
12038
- },
12039
- onChange: handleChange,
12040
- onClose: props.onClose
12072
+ initialValues,
12073
+ onChange: (values) => setFormValues(values),
12074
+ onClose: () => {
12075
+ if (initialValues.title === "" && initialValues.url === "") {
12076
+ unwrapLink(editor, selection);
12077
+ }
12078
+ props.onClose();
12079
+ }
12041
12080
  }
12042
12081
  );
12043
12082
  };
12044
- const isLinkActive = (editor) => {
12045
- const [link] = getLinks(editor);
12046
- return !!link;
12047
- };
12048
12083
  const unwrapLink = (editor, selection) => {
12049
12084
  unwrapNodes(editor, {
12050
- match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === ELEMENT_LINK,
12085
+ match: matchLink,
12051
12086
  at: selection || void 0
12052
12087
  });
12053
12088
  };
12054
12089
  const getLinks = (editor) => {
12055
12090
  return getNodeEntries(editor, {
12056
- match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === ELEMENT_LINK
12091
+ match: matchLink
12057
12092
  });
12058
12093
  };
12094
+ const isLinkActive = (editor) => {
12095
+ const [link] = getLinks(editor);
12096
+ return !!link;
12097
+ };
12059
12098
  const ToolbarItem = ({
12060
12099
  hidden,
12061
12100
  label,
@@ -26226,7 +26265,7 @@ const processDocumentForIndexing = (data, path, collection, textIndexLength, fie
26226
26265
  data["_id"] = `${collection.name}:${relPath}`;
26227
26266
  data["_relativePath"] = relPath;
26228
26267
  }
26229
- for (const f of collection.fields || (field == null ? void 0 : field.fields) || []) {
26268
+ for (const f of (field == null ? void 0 : field.fields) || collection.fields || []) {
26230
26269
  if (!f.searchable) {
26231
26270
  delete data[f.name];
26232
26271
  continue;
@@ -29421,8 +29460,7 @@ const SearchInput = ({
29421
29460
  setSearch(searchInput);
29422
29461
  setSearchLoaded(false);
29423
29462
  },
29424
- variant: "primary",
29425
- type: "submit"
29463
+ variant: "primary"
29426
29464
  },
29427
29465
  "Search ",
29428
29466
  /* @__PURE__ */ React__default.createElement(BiSearch, { className: "w-5 h-full ml-1.5 opacity-70" })
@@ -29706,13 +29744,13 @@ const RenderForm$1 = ({
29706
29744
  }
29707
29745
  const defaultItem = customDefaults || // @ts-ignore internal types aren't up to date
29708
29746
  ((_d = template.ui) == null ? void 0 : _d.defaultItem) || // @ts-ignore
29709
- (template == null ? void 0 : template.defaultItem);
29747
+ (template == null ? void 0 : template.defaultItem) || {};
29710
29748
  const form = useMemo(() => {
29711
29749
  var _a2, _b2;
29712
29750
  const folderName = folder.fullyQualifiedName ? folder.name : "";
29713
29751
  return new Form({
29714
29752
  crudType: "create",
29715
- initialValues: typeof defaultItem === "function" ? defaultItem() : defaultItem,
29753
+ initialValues: typeof defaultItem === "function" ? { ...defaultItem(), _template: templateName } : { ...defaultItem, _template: templateName },
29716
29754
  extraSubscribeValues: { active: true, submitting: true, touched: true },
29717
29755
  onChange: (values) => {
29718
29756
  var _a3, _b3;
@@ -30244,10 +30282,12 @@ const MaybeRedirectToPreview = ({
30244
30282
  redirect,
30245
30283
  children
30246
30284
  }) => {
30285
+ const cms = useCMS$1();
30247
30286
  const navigate = useNavigate();
30248
30287
  React__default.useEffect(() => {
30288
+ const basePath = cms.flags.get("tina-basepath");
30249
30289
  if (redirect) {
30250
- navigate("/~");
30290
+ navigate(`/~${basePath ? `/${basePath}` : ""}`);
30251
30291
  }
30252
30292
  }, [redirect]);
30253
30293
  return children;
@@ -30491,6 +30531,118 @@ class RouteMappingPlugin {
30491
30531
  this.mapper = mapper;
30492
30532
  }
30493
30533
  }
30534
+ const tinaTableTemplate = {
30535
+ name: "table",
30536
+ label: "Table",
30537
+ fields: [
30538
+ {
30539
+ name: "firstRowHeader",
30540
+ label: "First row is a header",
30541
+ type: "boolean"
30542
+ },
30543
+ {
30544
+ name: "align",
30545
+ label: "Align",
30546
+ type: "string",
30547
+ list: true,
30548
+ description: 'Possible values: "left", "right", or "center".'
30549
+ },
30550
+ {
30551
+ name: "tableRows",
30552
+ label: "Rows",
30553
+ type: "object",
30554
+ list: true,
30555
+ ui: {
30556
+ itemProps: (value) => {
30557
+ if (value == null ? void 0 : value.tableCells) {
30558
+ if (Array.isArray(value.tableCells)) {
30559
+ return {
30560
+ label: value.tableCells.map((cellItem) => {
30561
+ var _a;
30562
+ return (_a = stringifyCell(cellItem.value)) == null ? void 0 : _a.trim();
30563
+ }).join(" | ")
30564
+ };
30565
+ }
30566
+ }
30567
+ return { label: "Row" };
30568
+ }
30569
+ },
30570
+ fields: [
30571
+ {
30572
+ name: "tableCells",
30573
+ label: "Table Cells",
30574
+ list: true,
30575
+ type: "object",
30576
+ ui: {
30577
+ itemProps: (cell) => {
30578
+ var _a;
30579
+ if (cell) {
30580
+ if (cell.value) {
30581
+ return {
30582
+ label: (_a = stringifyCell(cell.value)) == null ? void 0 : _a.trim()
30583
+ };
30584
+ }
30585
+ }
30586
+ return { label: "Value" };
30587
+ }
30588
+ },
30589
+ fields: [
30590
+ {
30591
+ label: "Value",
30592
+ description: "Note: table cells do not support block-level elements like headers, code blocks, or lists. Any block-level items other than the first paragraph will be considered invalid.",
30593
+ name: "value",
30594
+ type: "rich-text",
30595
+ ui: {
30596
+ validate(value) {
30597
+ try {
30598
+ tableCellSchema.parse(value);
30599
+ } catch (e) {
30600
+ if (e instanceof z.ZodError) {
30601
+ return e.errors[0].message;
30602
+ }
30603
+ return e.message;
30604
+ }
30605
+ }
30606
+ }
30607
+ }
30608
+ ]
30609
+ }
30610
+ ]
30611
+ }
30612
+ ]
30613
+ };
30614
+ const tableCellSchema = z.object({
30615
+ type: z.literal("root"),
30616
+ children: z.array(
30617
+ z.object({
30618
+ type: z.string(),
30619
+ children: z.any().array()
30620
+ })
30621
+ ).refine(
30622
+ (value) => {
30623
+ const firstValue = value[0];
30624
+ return firstValue && firstValue.type === "p";
30625
+ },
30626
+ {
30627
+ message: `Table cell content cannot contain block elements like headers, blockquotes, or lists.`
30628
+ }
30629
+ ).refine(
30630
+ (value) => {
30631
+ var _a;
30632
+ if (value.length > 1) {
30633
+ const secondBlock = value[1];
30634
+ return secondBlock && secondBlock.children.length === 1 && !((_a = secondBlock.children[0]) == null ? void 0 : _a.text);
30635
+ }
30636
+ return true;
30637
+ },
30638
+ {
30639
+ message: `Table cells can only have 1 block level element.`
30640
+ }
30641
+ )
30642
+ });
30643
+ const stringifyCell = (cell) => {
30644
+ return stringifyMDX(cell, { name: "body", type: "rich-text" }, () => "");
30645
+ };
30494
30646
  const defineSchema = (config) => {
30495
30647
  validateSchema({ schema: config });
30496
30648
  return config;
@@ -30714,6 +30866,7 @@ export {
30714
30866
  selectFieldClasses,
30715
30867
  staticRequest,
30716
30868
  textFieldClasses,
30869
+ tinaTableTemplate,
30717
30870
  useBranchData,
30718
30871
  useCMS$1 as useCMS,
30719
30872
  useDismissible,
@@ -81,6 +81,14 @@ declare type BaseComponents = {
81
81
  html_inline?: {
82
82
  value: string;
83
83
  };
84
+ table?: {
85
+ align?: ('left' | 'right' | 'center')[];
86
+ tableRows: {
87
+ tableCells: {
88
+ value: TinaMarkdownContent;
89
+ }[];
90
+ }[];
91
+ };
84
92
  component_missing?: {
85
93
  name: string;
86
94
  };
@@ -72,6 +72,7 @@
72
72
  return MNode;
73
73
  };
74
74
  const Node = ({ components, child }) => {
75
+ var _a, _b, _c, _d, _e, _f;
75
76
  const { children, ...props } = child;
76
77
  switch (child.type) {
77
78
  case "h1":
@@ -147,6 +148,42 @@
147
148
  const props2 = child.props ? child.props : {};
148
149
  return /* @__PURE__ */ React.createElement(Component, { ...props2 });
149
150
  } else {
151
+ if (child.name === "table") {
152
+ const firstRowHeader = (_a = child.props) == null ? void 0 : _a.firstRowHeader;
153
+ const rows = (firstRowHeader ? (_b = child.props) == null ? void 0 : _b.tableRows.filter((_, i) => i !== 0) : (_c = child.props) == null ? void 0 : _c.tableRows) || [];
154
+ const header = (_e = (_d = child.props) == null ? void 0 : _d.tableRows) == null ? void 0 : _e.at(0);
155
+ const TableComponent = components["table"] || ((props2) => /* @__PURE__ */ React.createElement("table", { ...props2 }));
156
+ const TrComponent = components["tr"] || ((props2) => /* @__PURE__ */ React.createElement("tr", { ...props2 }));
157
+ const ThComponent = components["th"] || ((props2) => /* @__PURE__ */ React.createElement("th", { style: { textAlign: (props2 == null ? void 0 : props2.align) || "auto" }, ...props2 }));
158
+ const TdComponent = components["td"] || ((props2) => /* @__PURE__ */ React.createElement("td", { style: { textAlign: (props2 == null ? void 0 : props2.align) || "auto" }, ...props2 }));
159
+ const align = ((_f = child.props) == null ? void 0 : _f.align) || [];
160
+ return /* @__PURE__ */ React.createElement(TableComponent, null, firstRowHeader && /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement(TrComponent, null, header.tableCells.map((c, i) => {
161
+ return /* @__PURE__ */ React.createElement(
162
+ TinaMarkdown,
163
+ {
164
+ key: i,
165
+ components: {
166
+ p: (props2) => /* @__PURE__ */ React.createElement(ThComponent, { align: align[i], ...props2 })
167
+ },
168
+ content: c.value
169
+ }
170
+ );
171
+ }))), /* @__PURE__ */ React.createElement("tbody", null, rows.map((row, i) => {
172
+ var _a2;
173
+ return /* @__PURE__ */ React.createElement(TrComponent, { key: i }, (_a2 = row == null ? void 0 : row.tableCells) == null ? void 0 : _a2.map((c, i2) => {
174
+ return /* @__PURE__ */ React.createElement(
175
+ TinaMarkdown,
176
+ {
177
+ key: i2,
178
+ components: {
179
+ p: (props2) => /* @__PURE__ */ React.createElement(TdComponent, { align: align[i2], ...props2 })
180
+ },
181
+ content: c.value
182
+ }
183
+ );
184
+ }));
185
+ })));
186
+ }
150
187
  const ComponentMissing = components["component_missing"];
151
188
  if (ComponentMissing) {
152
189
  return /* @__PURE__ */ React.createElement(ComponentMissing, { name: child.name });
@@ -69,6 +69,7 @@ const MemoNode = (props) => {
69
69
  return MNode;
70
70
  };
71
71
  const Node = ({ components, child }) => {
72
+ var _a, _b, _c, _d, _e, _f;
72
73
  const { children, ...props } = child;
73
74
  switch (child.type) {
74
75
  case "h1":
@@ -144,6 +145,42 @@ const Node = ({ components, child }) => {
144
145
  const props2 = child.props ? child.props : {};
145
146
  return /* @__PURE__ */ React.createElement(Component, { ...props2 });
146
147
  } else {
148
+ if (child.name === "table") {
149
+ const firstRowHeader = (_a = child.props) == null ? void 0 : _a.firstRowHeader;
150
+ const rows = (firstRowHeader ? (_b = child.props) == null ? void 0 : _b.tableRows.filter((_, i) => i !== 0) : (_c = child.props) == null ? void 0 : _c.tableRows) || [];
151
+ const header = (_e = (_d = child.props) == null ? void 0 : _d.tableRows) == null ? void 0 : _e.at(0);
152
+ const TableComponent = components["table"] || ((props2) => /* @__PURE__ */ React.createElement("table", { ...props2 }));
153
+ const TrComponent = components["tr"] || ((props2) => /* @__PURE__ */ React.createElement("tr", { ...props2 }));
154
+ const ThComponent = components["th"] || ((props2) => /* @__PURE__ */ React.createElement("th", { style: { textAlign: (props2 == null ? void 0 : props2.align) || "auto" }, ...props2 }));
155
+ const TdComponent = components["td"] || ((props2) => /* @__PURE__ */ React.createElement("td", { style: { textAlign: (props2 == null ? void 0 : props2.align) || "auto" }, ...props2 }));
156
+ const align = ((_f = child.props) == null ? void 0 : _f.align) || [];
157
+ return /* @__PURE__ */ React.createElement(TableComponent, null, firstRowHeader && /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement(TrComponent, null, header.tableCells.map((c, i) => {
158
+ return /* @__PURE__ */ React.createElement(
159
+ TinaMarkdown,
160
+ {
161
+ key: i,
162
+ components: {
163
+ p: (props2) => /* @__PURE__ */ React.createElement(ThComponent, { align: align[i], ...props2 })
164
+ },
165
+ content: c.value
166
+ }
167
+ );
168
+ }))), /* @__PURE__ */ React.createElement("tbody", null, rows.map((row, i) => {
169
+ var _a2;
170
+ return /* @__PURE__ */ React.createElement(TrComponent, { key: i }, (_a2 = row == null ? void 0 : row.tableCells) == null ? void 0 : _a2.map((c, i2) => {
171
+ return /* @__PURE__ */ React.createElement(
172
+ TinaMarkdown,
173
+ {
174
+ key: i2,
175
+ components: {
176
+ p: (props2) => /* @__PURE__ */ React.createElement(TdComponent, { align: align[i2], ...props2 })
177
+ },
178
+ content: c.value
179
+ }
180
+ );
181
+ }));
182
+ })));
183
+ }
147
184
  const ComponentMissing = components["component_missing"];
148
185
  if (ComponentMissing) {
149
186
  return /* @__PURE__ */ React.createElement(ComponentMissing, { name: child.name });
@@ -0,0 +1,5 @@
1
+ import { RichTextTemplate } from '@tinacms/schema-tools';
2
+ /**
3
+ * Out-of-the-box template for rendering basic markdown tables
4
+ */
5
+ export declare const tinaTableTemplate: RichTextTemplate;
@@ -9,6 +9,6 @@ declare type LinkElement = {
9
9
  };
10
10
  export declare const wrapOrRewrapLink: (editor: any) => void;
11
11
  export declare const LinkForm: (props: any) => JSX.Element;
12
- export declare const isLinkActive: (editor: any) => boolean;
13
12
  export declare const unwrapLink: (editor: PlateEditor, selection?: BaseRange) => void;
14
13
  export declare const getLinks: (editor: any) => Generator<import("@udecode/plate-headless").TNodeEntry<LinkElement>, void, undefined>;
14
+ export declare const isLinkActive: (editor: any) => boolean;
@@ -44,6 +44,7 @@ export declare class Form<S = any, F extends Field = AnyField> implements Plugin
44
44
  loading: boolean;
45
45
  relativePath: string;
46
46
  crudType?: 'create' | 'update';
47
+ beforeSubmit?: (values: S) => Promise<void | S>;
47
48
  constructor({ id, label, fields, actions, buttons, global, reset, loadInitialValues, onChange, queries, ...options }: FormOptions<S, F>);
48
49
  /**
49
50
  * A unique identifier for Forms.
@@ -1,12 +1,16 @@
1
+ import type { GraphQLError } from 'graphql';
2
+ import type { Config } from '@tinacms/schema-tools';
1
3
  export declare const TINA_HOST = "content.tinajs.io";
2
4
  export interface TinaClientArgs<GenQueries = Record<string, unknown>> {
3
5
  url: string;
4
6
  token?: string;
5
7
  queries: (client: TinaClient<GenQueries>) => GenQueries;
8
+ errorPolicy?: Config['client']['errorPolicy'];
6
9
  }
7
10
  export declare type TinaClientRequestArgs = {
8
11
  variables?: Record<string, any>;
9
12
  query: string;
13
+ errorPolicy?: 'throw' | 'include';
10
14
  } & Partial<Omit<TinaClientArgs, 'queries'>>;
11
15
  export declare type TinaClientURLParts = {
12
16
  host: string;
@@ -17,13 +21,12 @@ export declare type TinaClientURLParts = {
17
21
  export declare class TinaClient<GenQueries> {
18
22
  apiUrl: string;
19
23
  readonlyToken?: string;
20
- /**
21
- *
22
- */
23
24
  queries: GenQueries;
24
- constructor({ token, url, queries }: TinaClientArgs<GenQueries>);
25
- request<DataType extends Record<string, any> = any>(args: TinaClientRequestArgs): Promise<{
25
+ errorPolicy: Config['client']['errorPolicy'];
26
+ constructor({ token, url, queries, errorPolicy, }: TinaClientArgs<GenQueries>);
27
+ request<DataType extends Record<string, any> = any>({ errorPolicy, ...args }: TinaClientRequestArgs): Promise<{
26
28
  data: DataType;
29
+ errors: GraphQLError[];
27
30
  query: string;
28
31
  }>;
29
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tinacms",
3
- "version": "1.5.21",
3
+ "version": "1.5.23",
4
4
  "main": "dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "exports": {
@@ -69,8 +69,9 @@
69
69
  "@react-types/combobox": "^3.2.0",
70
70
  "@react-types/shared": "^3.10.0",
71
71
  "@sambego/storybook-styles": "^1.0.0",
72
- "@tinacms/schema-tools": "1.4.12",
73
- "@tinacms/search": "1.0.11",
72
+ "@tinacms/schema-tools": "1.4.13",
73
+ "@tinacms/search": "1.0.13",
74
+ "@tinacms/mdx": "1.3.21",
74
75
  "@tinacms/sharedctx": "1.0.2",
75
76
  "@udecode/plate-headless": "^21.4.0",
76
77
  "atob": "2.1.2",
@@ -79,7 +80,7 @@
79
80
  "date-fns": "2.30.0",
80
81
  "encoding": "0.1.13",
81
82
  "fetch-ponyfill": "^7.1.0",
82
- "final-form": "4.20.4",
83
+ "final-form": "4.20.10",
83
84
  "final-form-arrays": "^3.0.1",
84
85
  "final-form-set-field-data": "^1.0.2",
85
86
  "graphql": "15.8.0",