tinacms 3.9.4 → 3.10.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/index.js CHANGED
@@ -10,7 +10,7 @@ import React__default, { useState, useCallback, useRef, useEffect, createContext
10
10
  import { createPortal } from "react-dom";
11
11
  import { clsx } from "clsx";
12
12
  import { twMerge } from "tailwind-merge";
13
- import { withRef, cn as cn$1, withCn, createPrimitiveElement, withVariants, withProps, useComposedRef as useComposedRef$1, PortalBody } from "@udecode/cn";
13
+ import { withRef, cn as cn$1, withCn, createPrimitiveElement, withVariants, withProps, PortalBody, useComposedRef as useComposedRef$1 } from "@udecode/cn";
14
14
  import { CodePlugin, UnderlinePlugin, StrikethroughPlugin, ItalicPlugin, BoldPlugin, BasicMarksPlugin } from "@udecode/plate-basic-marks/react";
15
15
  import { BlockquotePlugin } from "@udecode/plate-block-quote/react";
16
16
  import { CodeBlockPlugin, CodeLinePlugin, CodeSyntaxPlugin } from "@udecode/plate-code-block/react";
@@ -23,7 +23,7 @@ import { SlashInputPlugin, SlashPlugin } from "@udecode/plate-slash-command/reac
23
23
  import { TablePlugin, TableRowPlugin, useTableCellElement, TableProvider, useTableElement, useTableMergeState, useTableBordersDropdownMenuContentState, TableCellHeaderPlugin, TableCellPlugin } from "@udecode/plate-table/react";
24
24
  import { PlateElement, useReadOnly, useElement, PlateLeaf, useSelected, useFocused, useEditorRef, withRef as withRef$1, useComposedRef, useEditorPlugin, useElementSelector, withHOC, usePluginOption, useRemoveNodeButton, useEditorSelector, ParagraphPlugin, createPlatePlugin, PlateContent, PlateContainer, useEditorState, useMarkToolbarButtonState, useMarkToolbarButton, usePlateEditor, useFormInputProps, useEditorSelection, useEditorReadOnly, useOnClickOutside, useEventEditorValue, Plate } from "@udecode/plate/react";
25
25
  import { isLangSupported, formatCodeBlock, insertEmptyCodeBlock, unwrapCodeBlock, isCodeBlockEmpty, isSelectionAtCodeBlockStart } from "@udecode/plate-code-block";
26
- import { X, Search, ChevronDown, Check, AlertTriangle, BracesIcon, Plus, AlignCenter as AlignCenter$1, AlignJustify, AlignLeft as AlignLeft$1, AlignRight as AlignRight$1, PaintBucket, Quote, ChevronRight, ChevronsUpDown, FileCode, Baseline, RectangleVertical, Combine, Ungroup, MessageSquare, MessageSquarePlus, Trash, GripVertical, Edit2, Smile, ExternalLink, Heading1 as Heading1$1, Heading2 as Heading2$1, Heading3 as Heading3$1, Heading4 as Heading4$1, Heading5 as Heading5$1, Heading6 as Heading6$1, Indent, Keyboard, WrapText, Minus, MoreHorizontal, Outdent, Pilcrow, RotateCcw, RectangleHorizontal, Settings, Highlighter, Strikethrough, Subscript, Superscript, Table as Table$1, Text, Underline, Link2Off, Eye, SeparatorHorizontal, Moon, SunMedium, Twitter, PaintBucketIcon, CombineIcon, SquareSplitHorizontalIcon, Grid2X2Icon, Trash2Icon, ArrowUp, ArrowDown, XIcon, ArrowLeft, ArrowRight, EraserIcon, ChevronDownIcon as ChevronDownIcon$1, ChevronUp, Clock, CalendarCheck, Calendar as Calendar$1, CalendarDays, RotateCw, ChevronLeft, ArrowUpDown, AlertCircle, Image, RefreshCw, Database, CheckCircle2, ImageOff, LoaderCircle, TriangleAlert, FileStack, History, GitBranchIcon, List as List$1, ListOrdered, Grid3x3Icon, CircleX, Link, Unlink } from "lucide-react";
26
+ import { X, Search, ChevronDown, Check, AlertTriangle, BracesIcon, Plus, AlignCenter as AlignCenter$1, AlignJustify, AlignLeft as AlignLeft$1, AlignRight as AlignRight$1, PaintBucket, Quote, ChevronRight, ChevronsUpDown, FileCode, Baseline, RectangleVertical, Combine, Ungroup, MessageSquare, MessageSquarePlus, Trash, GripVertical, Edit2, Smile, ExternalLink, Heading1 as Heading1$1, Heading2 as Heading2$1, Heading3 as Heading3$1, Heading4 as Heading4$1, Heading5 as Heading5$1, Heading6 as Heading6$1, Indent, Keyboard, WrapText, Minus, MoreHorizontal, Outdent, Pilcrow, RotateCcw, RectangleHorizontal, Settings, Highlighter, Strikethrough, Subscript, Superscript, Table as Table$1, Text, Underline, Link2Off, Eye, SeparatorHorizontal, Moon, SunMedium, Twitter, PaintBucketIcon, CombineIcon, SquareSplitHorizontalIcon, Grid2X2Icon, Trash2Icon, ArrowUp, ArrowDown, XIcon, ArrowLeft, ArrowRight, EraserIcon, ChevronDownIcon as ChevronDownIcon$1, ChevronUp, Clock, CalendarCheck, Calendar as Calendar$1, CalendarDays, RotateCw, ChevronLeft, ArrowUpDown, AlertCircle, Image, RefreshCw, Database, CheckCircle2, ImageOff, LoaderCircle, TriangleAlert, FileStack, History, GitBranchIcon, FileText, Globe, List as List$1, ListOrdered, Grid3x3Icon, CircleX, Link, Unlink } from "lucide-react";
27
27
  import mermaid from "mermaid";
28
28
  import { Slot } from "@radix-ui/react-slot";
29
29
  import { cva } from "class-variance-authority";
@@ -36,7 +36,7 @@ import { useHTMLInputCursorState, useComboboxInput } from "@udecode/plate-combob
36
36
  import { useComboboxContext, Combobox as Combobox$1, useComboboxStore, ComboboxProvider, Portal, ComboboxPopover, ComboboxItem } from "@ariakit/react";
37
37
  import { filterWords } from "@udecode/plate-combobox";
38
38
  import { toggleList, unwrapList, getListItemEntry } from "@udecode/plate-list";
39
- import { normalizeHeadingLevels, ALL_HEADING_LEVELS, canonicalPath, isHeadingLevel, TinaSchema, addNamespaceToSchema, parseURL, resolveForm, normalizePath, validateSchema } from "@tinacms/schema-tools";
39
+ import { normalizeHeadingLevels, ALL_HEADING_LEVELS, ASYNC_POLLER_ERROR, canonicalPath, ERR_BRANCH_EXISTS, ERR_BRANCH_CONFLICT, isHeadingLevel, TinaSchema, addNamespaceToSchema, parseURL, ERR_HAS_REFERENCES, ERR_ALREADY_EXISTS, isValidRelativePath, RELATIVE_PATH_ALLOWED_CHARS_MESSAGE, resolveForm, RELATIVE_PATH_REGEX, normalizePath, ERR_NOT_INDEXED, validateSchema } from "@tinacms/schema-tools";
40
40
  import { NAMER, resolveField } from "@tinacms/schema-tools";
41
41
  import { useBlockSelected, BlockSelectionPlugin } from "@udecode/plate-selection/react";
42
42
  import "@udecode/plate-dnd";
@@ -67,7 +67,7 @@ import { MdKeyboardArrowDown, MdOutlineClear, MdArrowForward, MdAccessTime, MdOu
67
67
  import * as dropzone from "react-dropzone";
68
68
  import { IoMdArrowDropup, IoMdArrowDropdown, IoMdClose, IoMdRefresh } from "react-icons/io";
69
69
  import { Command as Command$3 } from "@udecode/cmdk";
70
- import { Popover as Popover$4, PopoverButton, Transition, PopoverPanel, TransitionChild, Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItems, MenuItem } from "@headlessui/react";
70
+ import { Popover as Popover$4, PopoverButton, Transition, PopoverPanel, TransitionChild, Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItems, MenuItem as MenuItem$1 } from "@headlessui/react";
71
71
  import { isHotkey } from "is-hotkey";
72
72
  import { AutoformatPlugin } from "@udecode/plate-autoformat/react";
73
73
  import { ExitBreakPlugin, SoftBreakPlugin } from "@udecode/plate-break/react";
@@ -77,7 +77,7 @@ import { BaseBlockquotePlugin } from "@udecode/plate-block-quote";
77
77
  import { HeadingPlugin } from "@udecode/plate-heading/react";
78
78
  import { IndentListPlugin } from "@udecode/plate-indent-list/react";
79
79
  import { NodeIdPlugin } from "@udecode/plate-node-id";
80
- import { offset, flip, useVirtualFloating, getSelectionBoundingClientRect } from "@udecode/plate-floating";
80
+ import { offset, flip, shift, limitShift, useVirtualFloating, getSelectionBoundingClientRect } from "@udecode/plate-floating";
81
81
  import { getLinkAttributes } from "@udecode/plate-link";
82
82
  import "@heroicons/react/solid";
83
83
  import { autoformatSmartQuotes, autoformatPunctuation, autoformatLegal, autoformatArrow, autoformatMath } from "@udecode/plate-autoformat";
@@ -2280,6 +2280,7 @@ const BranchSwitcherDropDownEvent = "branch-switcher-dropdown";
2280
2280
  const BranchSwitcherPRClickedEvent = "branch-switcher-pr-clicked";
2281
2281
  const SavedContentEvent = "saved-content";
2282
2282
  const SaveContentErrorEvent = "save-content-error";
2283
+ const EditorialWorkflowSaveEvent = "editorial-workflow-save";
2283
2284
  const FormResetEvent = "form-reset";
2284
2285
  const MediaManagerContentUploadedEvent = "media-manager-content-uploaded";
2285
2286
  const MediaManagerContentDeletedEvent = "media-manager-content-deleted";
@@ -4493,6 +4494,21 @@ function DropdownMenuSeparator({
4493
4494
  }
4494
4495
  );
4495
4496
  }
4497
+ const MenuItem = ({ item }) => {
4498
+ const menuItem = /* @__PURE__ */ React.createElement(
4499
+ DropdownMenuItem,
4500
+ {
4501
+ onClick: item.onClick,
4502
+ disabled: item.disabled,
4503
+ variant: item.variant
4504
+ },
4505
+ item.icon && item.icon,
4506
+ item.label
4507
+ );
4508
+ if (!item.tooltip)
4509
+ return menuItem;
4510
+ return /* @__PURE__ */ React.createElement(Tooltip$1, null, /* @__PURE__ */ React.createElement(TooltipTrigger$1, { asChild: true }, /* @__PURE__ */ React.createElement("div", null, menuItem)), /* @__PURE__ */ React.createElement(TooltipPortal, null, /* @__PURE__ */ React.createElement(TooltipContent$1, { side: "left" }, item.tooltip)));
4511
+ };
4496
4512
  const DropdownButton = React.forwardRef(
4497
4513
  ({
4498
4514
  variant = "primary",
@@ -4530,17 +4546,7 @@ const DropdownButton = React.forwardRef(
4530
4546
  )
4531
4547
  }
4532
4548
  )
4533
- )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom", className: "z-[100000]" }, items.map((item, index) => /* @__PURE__ */ React.createElement(
4534
- DropdownMenuItem,
4535
- {
4536
- key: index,
4537
- onClick: item.onClick,
4538
- disabled: item.disabled,
4539
- variant: item.variant
4540
- },
4541
- item.icon && item.icon,
4542
- item.label
4543
- ))));
4549
+ )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom", className: "z-[100000]" }, /* @__PURE__ */ React.createElement(TooltipProvider$1, null, items.map((item, index) => /* @__PURE__ */ React.createElement(MenuItem, { key: index, item })))));
4544
4550
  }
4545
4551
  return /* @__PURE__ */ React.createElement("div", { className: cn("inline-flex", className) }, /* @__PURE__ */ React.createElement(
4546
4552
  Button$2,
@@ -4577,19 +4583,10 @@ const DropdownButton = React.forwardRef(
4577
4583
  style: { fill: "none" }
4578
4584
  }
4579
4585
  )
4580
- )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom" }, items.map((item, index) => {
4586
+ )), /* @__PURE__ */ React.createElement(DropdownMenuContent, { align: "end", side: "bottom" }, /* @__PURE__ */ React.createElement(TooltipProvider$1, null, items.map((item, index) => {
4581
4587
  var _a;
4582
- return /* @__PURE__ */ React.createElement(React.Fragment, { key: index }, /* @__PURE__ */ React.createElement(
4583
- DropdownMenuItem,
4584
- {
4585
- onClick: item.onClick,
4586
- disabled: item.disabled,
4587
- variant: item.variant
4588
- },
4589
- item.icon && item.icon,
4590
- item.label
4591
- ), item.variant === "destructive" && index < items.length - 1 && ((_a = items[index + 1]) == null ? void 0 : _a.variant) !== "destructive" && /* @__PURE__ */ React.createElement(DropdownMenuSeparator, null));
4592
- }))));
4588
+ return /* @__PURE__ */ React.createElement(React.Fragment, { key: index }, /* @__PURE__ */ React.createElement(MenuItem, { item }), item.variant === "destructive" && index < items.length - 1 && ((_a = items[index + 1]) == null ? void 0 : _a.variant) !== "destructive" && /* @__PURE__ */ React.createElement(DropdownMenuSeparator, null));
4589
+ })))));
4593
4590
  }
4594
4591
  );
4595
4592
  DropdownButton.displayName = "DropdownButton";
@@ -8780,7 +8777,7 @@ const BranchSwitcherLegacy = ({
8780
8777
  return newBranchList;
8781
8778
  });
8782
8779
  }).catch((e) => {
8783
- if (e.message === "AsyncPoller: cancelled")
8780
+ if (e.message === ASYNC_POLLER_ERROR.CANCELLED)
8784
8781
  return;
8785
8782
  console.error(e);
8786
8783
  });
@@ -9508,7 +9505,7 @@ const EditoralBranchSwitcher = ({
9508
9505
  return newBranchList;
9509
9506
  });
9510
9507
  }).catch((e) => {
9511
- if (e.message === "AsyncPoller: cancelled")
9508
+ if (e.message === ASYNC_POLLER_ERROR.CANCELLED)
9512
9509
  return;
9513
9510
  console.error(e);
9514
9511
  });
@@ -13264,7 +13261,7 @@ const FormModal = ({ plugin, close: close2 }) => {
13264
13261
  const name = "tinacms";
13265
13262
  const type = "module";
13266
13263
  const typings = "dist/index.d.ts";
13267
- const version$1 = "3.9.4";
13264
+ const version$1 = "3.10.0";
13268
13265
  const main = "dist/index.js";
13269
13266
  const module = "./dist/index.js";
13270
13267
  const exports = {
@@ -13313,13 +13310,12 @@ const dependencies = {
13313
13310
  "@headlessui/react": "2.1.8",
13314
13311
  "@heroicons/react": "^1.0.6",
13315
13312
  "@monaco-editor/react": "catalog:",
13316
- "@radix-ui/react-checkbox": "catalog:",
13317
13313
  "@radix-ui/react-dialog": "catalog:",
13318
13314
  "@radix-ui/react-dropdown-menu": "catalog:",
13319
13315
  "@radix-ui/react-popover": "catalog:",
13320
13316
  "@radix-ui/react-select": "catalog:",
13321
13317
  "@radix-ui/react-separator": "catalog:",
13322
- "@radix-ui/react-slot": "^1.1.2",
13318
+ "@radix-ui/react-slot": "catalog:",
13323
13319
  "@radix-ui/react-toolbar": "catalog:",
13324
13320
  "@radix-ui/react-tooltip": "catalog:",
13325
13321
  "@react-hook/window-size": "catalog:",
@@ -16370,9 +16366,9 @@ const getEditorialWorkflowErrorMessage = (e) => {
16370
16366
  break;
16371
16367
  }
16372
16368
  } else if (err.message) {
16373
- if (err.message.toLowerCase().includes("already exists")) {
16369
+ if (err.message.toLowerCase().includes(ERR_BRANCH_EXISTS)) {
16374
16370
  errMessage = "A branch with this name already exists";
16375
- } else if (err.message.toLowerCase().includes("conflict")) {
16371
+ } else if (err.message.toLowerCase().includes(ERR_BRANCH_CONFLICT)) {
16376
16372
  errMessage = err.message;
16377
16373
  }
16378
16374
  }
@@ -16412,12 +16408,13 @@ function useEditorialWorkflow() {
16412
16408
  values,
16413
16409
  crudType,
16414
16410
  tinaForm,
16415
- signal
16411
+ signal,
16412
+ isDraft
16416
16413
  }) => {
16417
16414
  var _a;
16418
16415
  try {
16419
16416
  if (signal == null ? void 0 : signal.aborted)
16420
- return false;
16417
+ return { success: false };
16421
16418
  const targetBranchExists = await checkTargetBranchExists(
16422
16419
  tinaApi,
16423
16420
  branchName,
@@ -16425,12 +16422,12 @@ function useEditorialWorkflow() {
16425
16422
  signal
16426
16423
  );
16427
16424
  if (signal == null ? void 0 : signal.aborted)
16428
- return false;
16425
+ return { success: false };
16429
16426
  if (targetBranchExists) {
16430
16427
  setErrorMessage(TARGET_BRANCH_EXISTS_ERROR);
16431
16428
  setIsExecuting(false);
16432
16429
  setCurrentStep(0);
16433
- return false;
16430
+ return { success: false, error: TARGET_BRANCH_EXISTS_ERROR };
16434
16431
  }
16435
16432
  setIsExecuting(true);
16436
16433
  setCurrentStep(1);
@@ -16456,6 +16453,7 @@ function useEditorialWorkflow() {
16456
16453
  branchName,
16457
16454
  baseBranch,
16458
16455
  prTitle: getEditorialWorkflowPrTitle(branchName),
16456
+ isDraft,
16459
16457
  graphQLContentOp: {
16460
16458
  query: graphql2,
16461
16459
  variables: {
@@ -16501,14 +16499,14 @@ function useEditorialWorkflow() {
16501
16499
  const folderPath = relativePath.includes("/") ? relativePath.substring(0, relativePath.lastIndexOf("/")) : "";
16502
16500
  window.location.hash = `#/collections/${collection.name}${folderPath ? `/${folderPath}` : ""}`;
16503
16501
  }
16504
- return true;
16502
+ return { success: true };
16505
16503
  } catch (e) {
16506
16504
  console.error(e);
16507
16505
  const errMessage = getEditorialWorkflowErrorMessage(e);
16508
16506
  setErrorMessage(errMessage);
16509
16507
  setIsExecuting(false);
16510
16508
  setCurrentStep(0);
16511
- return false;
16509
+ return { success: false, error: errMessage };
16512
16510
  }
16513
16511
  };
16514
16512
  return {
@@ -16604,6 +16602,41 @@ const EditorialWorkflowProgressModal = ({
16604
16602
  elapsedTime
16605
16603
  }
16606
16604
  ))));
16605
+ function useLocalStorage(key, initialValue) {
16606
+ const [storedValue, setStoredValue] = React.useState(() => {
16607
+ try {
16608
+ const valueFromStorage = typeof window !== "undefined" && window.localStorage && window.localStorage.getItem(key);
16609
+ if (valueFromStorage != null) {
16610
+ return JSON.parse(valueFromStorage);
16611
+ }
16612
+ } catch (error2) {
16613
+ }
16614
+ return initialValue;
16615
+ });
16616
+ React.useEffect(() => {
16617
+ const valueFromStorage = window.localStorage && window.localStorage.getItem(key);
16618
+ if (valueFromStorage != null && valueFromStorage != void 0) {
16619
+ setStoredValue(JSON.parse(valueFromStorage));
16620
+ }
16621
+ }, [key]);
16622
+ const setValue = (value) => {
16623
+ try {
16624
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
16625
+ setStoredValue(valueToStore);
16626
+ localStorage.setItem(key, JSON.stringify(valueToStore));
16627
+ } catch (error2) {
16628
+ console.log(error2);
16629
+ }
16630
+ };
16631
+ return [storedValue, setValue];
16632
+ }
16633
+ const SAVE_CHOICE_KEY = "tina.editorialWorkflow.saveChoice";
16634
+ const SAVE_CHOICE_ORDER = ["draft", "review", "publish"];
16635
+ const resolveSaveOptions = (lastChoice, disablePublish) => {
16636
+ const main2 = lastChoice === "publish" && disablePublish ? "draft" : lastChoice;
16637
+ const menu = SAVE_CHOICE_ORDER.filter((choice) => choice !== main2);
16638
+ return { main: main2, menu };
16639
+ };
16607
16640
  const formatDefaultBranchName = (filePath, crudType) => {
16608
16641
  let result = filePath;
16609
16642
  const contentPrefix = "content/";
@@ -16656,7 +16689,7 @@ const CreateBranchModal = ({
16656
16689
  (_a = branchGuardAbortRef.current) == null ? void 0 : _a.abort();
16657
16690
  };
16658
16691
  }, []);
16659
- const executeEditorialWorkflow = async () => {
16692
+ const executeEditorialWorkflow = async (isDraft) => {
16660
16693
  abortBranchGuard();
16661
16694
  const abortController = new AbortController();
16662
16695
  branchGuardAbortRef.current = abortController;
@@ -16680,18 +16713,26 @@ const CreateBranchModal = ({
16680
16713
  return;
16681
16714
  }
16682
16715
  setIsBranchGuardChecking(false);
16683
- const success = await executeWorkflow({
16716
+ const { success, error: error2 } = await executeWorkflow({
16684
16717
  branchName: targetBranch,
16685
16718
  baseBranch,
16686
16719
  path,
16687
16720
  values,
16688
16721
  crudType,
16689
16722
  tinaForm,
16690
- signal: abortController.signal
16723
+ signal: abortController.signal,
16724
+ isDraft
16691
16725
  });
16692
16726
  if (branchGuardAbortRef.current === abortController) {
16693
16727
  branchGuardAbortRef.current = null;
16694
16728
  }
16729
+ if (abortController.signal.aborted)
16730
+ return;
16731
+ captureEvent(EditorialWorkflowSaveEvent, {
16732
+ choice: isDraft ? "draft" : "review",
16733
+ success,
16734
+ error: error2
16735
+ });
16695
16736
  if (success) {
16696
16737
  close2();
16697
16738
  }
@@ -16725,8 +16766,10 @@ const CreateBranchModal = ({
16725
16766
  onSaveToProtectedBranch: () => {
16726
16767
  abortBranchGuard();
16727
16768
  close2();
16728
- safeSubmit();
16729
- }
16769
+ safeSubmit("publish");
16770
+ },
16771
+ showSaveOptions: true,
16772
+ disablePublish: !!tinaApi.usingProtectedBranch()
16730
16773
  }
16731
16774
  );
16732
16775
  };
@@ -16737,8 +16780,36 @@ const CreateBranchPromptModal = ({
16737
16780
  errorMessage,
16738
16781
  onBranchNameChange,
16739
16782
  onCreateBranch,
16740
- onSaveToProtectedBranch
16783
+ onSaveToProtectedBranch,
16784
+ showSaveOptions = false,
16785
+ disablePublish = false
16741
16786
  }) => {
16787
+ const [lastChoice, setLastChoice] = useLocalStorage(
16788
+ SAVE_CHOICE_KEY,
16789
+ "draft"
16790
+ );
16791
+ const { main: main2, menu } = resolveSaveOptions(lastChoice, disablePublish);
16792
+ const choices = {
16793
+ draft: {
16794
+ label: "Save draft",
16795
+ Icon: FileText,
16796
+ run: () => onCreateBranch(true)
16797
+ },
16798
+ review: {
16799
+ label: "Save (ready for review)",
16800
+ Icon: Eye,
16801
+ run: () => onCreateBranch(false)
16802
+ },
16803
+ publish: {
16804
+ label: "Save and publish",
16805
+ Icon: Globe,
16806
+ run: onSaveToProtectedBranch,
16807
+ disabled: disablePublish,
16808
+ tooltip: disablePublish ? "This branch is protected. Save a draft or send it for review instead." : void 0
16809
+ }
16810
+ };
16811
+ const mainChoice = choices[main2] ?? choices.draft;
16812
+ const MainIcon = mainChoice.Icon;
16742
16813
  return /* @__PURE__ */ React.createElement(Modal, { className: "flex" }, /* @__PURE__ */ React.createElement(PopupModal, { className: "w-auto" }, /* @__PURE__ */ React.createElement(ModalHeader, { close: close2 }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between w-full" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, "Save changes to new branch"))), /* @__PURE__ */ React.createElement(ModalBody, { padded: true }, /* @__PURE__ */ React.createElement("div", { className: "max-w-sm" }, errorMessage && /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1 text-red-700 py-2 px-3 mb-4 bg-red-50 border border-red-200 rounded" }, /* @__PURE__ */ React.createElement(BiError, { className: "w-5 h-auto text-red-400 flex-shrink-0" }), /* @__PURE__ */ React.createElement("span", { className: "text-sm" }, /* @__PURE__ */ React.createElement("b", null, "Error:"), " ", errorMessage)), /* @__PURE__ */ React.createElement("p", { className: "text-lg text-gray-700 font-bold mb-2" }, "First, let's create a copy"), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-700 mb-4 max-w-sm" }, "To make changes, you need to create a copy then get it approved and merged for it to go live.", /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("span", { className: "text-gray-500" }, "Learn more about "), /* @__PURE__ */ React.createElement(
16743
16814
  "a",
16744
16815
  {
@@ -16766,14 +16837,39 @@ const CreateBranchPromptModal = ({
16766
16837
  onClick: close2
16767
16838
  },
16768
16839
  "Cancel"
16769
- ), /* @__PURE__ */ React.createElement(
16840
+ ), showSaveOptions ? /* @__PURE__ */ React.createElement(
16841
+ DropdownButton,
16842
+ {
16843
+ variant: "primary",
16844
+ align: "start",
16845
+ className: "w-full sm:w-auto",
16846
+ disabled,
16847
+ onMainAction: mainChoice.run,
16848
+ items: menu.map((choice) => {
16849
+ const option = choices[choice];
16850
+ const OptionIcon = option.Icon;
16851
+ return {
16852
+ label: option.label,
16853
+ icon: /* @__PURE__ */ React.createElement(OptionIcon, { className: "w-4 h-4" }),
16854
+ disabled: option.disabled,
16855
+ tooltip: option.tooltip,
16856
+ onClick: () => {
16857
+ setLastChoice(choice);
16858
+ option.run();
16859
+ }
16860
+ };
16861
+ })
16862
+ },
16863
+ /* @__PURE__ */ React.createElement(MainIcon, { className: "w-4 h-4 mr-1", style: { fill: "none" } }),
16864
+ mainChoice.label
16865
+ ) : /* @__PURE__ */ React.createElement(
16770
16866
  DropdownButton,
16771
16867
  {
16772
16868
  variant: "primary",
16773
16869
  align: "start",
16774
16870
  className: "w-full sm:w-auto",
16775
16871
  disabled,
16776
- onMainAction: onCreateBranch,
16872
+ onMainAction: () => onCreateBranch(false),
16777
16873
  items: [
16778
16874
  {
16779
16875
  label: "Save to Protected Branch",
@@ -16782,7 +16878,13 @@ const CreateBranchPromptModal = ({
16782
16878
  }
16783
16879
  ]
16784
16880
  },
16785
- /* @__PURE__ */ React.createElement(GitBranchIcon, { className: "w-4 h-4 mr-1", style: { fill: "none" } }),
16881
+ /* @__PURE__ */ React.createElement(
16882
+ GitBranchIcon,
16883
+ {
16884
+ className: "w-4 h-4 mr-1",
16885
+ style: { fill: "none" }
16886
+ }
16887
+ ),
16786
16888
  "Save to a new branch"
16787
16889
  ))));
16788
16890
  };
@@ -16822,7 +16924,7 @@ const BranchDeletedModal = ({
16822
16924
  reset
16823
16925
  } = useEditorialWorkflow();
16824
16926
  const handleCreate = async () => {
16825
- const success = await executeWorkflow({
16927
+ const { success } = await executeWorkflow({
16826
16928
  branchName: `tina/${newBranchName}`,
16827
16929
  baseBranch,
16828
16930
  path,
@@ -16999,7 +17101,16 @@ const FormBuilder = ({
16999
17101
  }) => {
17000
17102
  const usingProtectedBranch = cms.api.tina.usingProtectedBranch();
17001
17103
  const canSubmit = !pristine && !submitting && !hasValidationErrors && !(invalid && !dirtySinceLastSubmit);
17002
- const safeSubmit = async () => {
17104
+ const safeSubmit = async (editorialWorkflowChoice) => {
17105
+ const captureWorkflowChoice = (success, error2) => {
17106
+ if (editorialWorkflowChoice) {
17107
+ captureEvent(EditorialWorkflowSaveEvent, {
17108
+ choice: editorialWorkflowChoice,
17109
+ success,
17110
+ error: error2
17111
+ });
17112
+ }
17113
+ };
17003
17114
  if (canSubmit) {
17004
17115
  const alertsBefore = new Set(cms.alerts.all.map((a) => a.id));
17005
17116
  console.debug(
@@ -17022,6 +17133,7 @@ const FormBuilder = ({
17022
17133
  cms.alerts.dismiss(alert);
17023
17134
  }
17024
17135
  }
17136
+ captureWorkflowChoice(false, errorMsg);
17025
17137
  setDeletedBranchModalOpen(true);
17026
17138
  return;
17027
17139
  }
@@ -17029,10 +17141,12 @@ const FormBuilder = ({
17029
17141
  documentPath: tinaForm.path,
17030
17142
  error: errorMsg
17031
17143
  });
17144
+ captureWorkflowChoice(false, errorMsg);
17032
17145
  } else {
17033
17146
  captureEvent(SavedContentEvent, {
17034
17147
  documentPath: tinaForm.path
17035
17148
  });
17149
+ captureWorkflowChoice(true);
17036
17150
  }
17037
17151
  } else {
17038
17152
  console.debug(
@@ -18909,7 +19023,7 @@ const useCreateEditor = ({
18909
19023
  });
18910
19024
  };
18911
19025
  const popoverVariants = cva(
18912
- "z-50 w-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden"
19026
+ "z-[999999] w-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden"
18913
19027
  );
18914
19028
  const inputVariants = cva(
18915
19029
  "flex h-[28px] w-full rounded-md border-none bg-transparent px-1.5 py-1 text-base placeholder:text-muted-foreground focus-visible:ring-transparent focus-visible:outline-none md:text-sm"
@@ -18943,7 +19057,8 @@ function LinkFloatingToolbar({
18943
19057
  flip({
18944
19058
  fallbackPlacements: ["bottom-end", "top-start", "top-end"],
18945
19059
  padding: 12
18946
- })
19060
+ }),
19061
+ shift({ padding: 8, limiter: limitShift() })
18947
19062
  ],
18948
19063
  placement: activeSuggestionId || activeCommentId ? "top-start" : "bottom-start"
18949
19064
  };
@@ -19063,13 +19178,13 @@ function LinkFloatingToolbar({
19063
19178
  },
19064
19179
  /* @__PURE__ */ React.createElement(Unlink, { width: 18 })
19065
19180
  ));
19066
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
19181
+ return /* @__PURE__ */ React.createElement(PortalBody, null, /* @__PURE__ */ React.createElement(
19067
19182
  "div",
19068
19183
  {
19069
19184
  ref: insertRef,
19070
19185
  className: popoverVariants(),
19071
19186
  ...insertProps,
19072
- style: insertProps.style
19187
+ style: { ...insertProps.style }
19073
19188
  },
19074
19189
  input
19075
19190
  ), /* @__PURE__ */ React.createElement(
@@ -19078,7 +19193,7 @@ function LinkFloatingToolbar({
19078
19193
  ref: editRef,
19079
19194
  className: popoverVariants(),
19080
19195
  ...editProps,
19081
- style: editProps.style
19196
+ style: { ...editProps.style }
19082
19197
  },
19083
19198
  editContent
19084
19199
  ));
@@ -70525,25 +70640,6 @@ class FormMetaPlugin {
70525
70640
  this.Component = options.Component;
70526
70641
  }
70527
70642
  }
70528
- function useLocalStorage(key, initialValue) {
70529
- const [storedValue, setStoredValue] = React.useState(initialValue);
70530
- React.useEffect(() => {
70531
- const valueFromStorage = window.localStorage && window.localStorage.getItem(key);
70532
- if (valueFromStorage != null && valueFromStorage != void 0) {
70533
- setStoredValue(JSON.parse(valueFromStorage));
70534
- }
70535
- }, [key]);
70536
- const setValue = (value) => {
70537
- try {
70538
- const valueToStore = value instanceof Function ? value(storedValue) : value;
70539
- setStoredValue(valueToStore);
70540
- localStorage.setItem(key, JSON.stringify(valueToStore));
70541
- } catch (error2) {
70542
- console.log(error2);
70543
- }
70544
- };
70545
- return [storedValue, setValue];
70546
- }
70547
70643
  const MediaWorkflowOverlay = () => {
70548
70644
  const cms = useCMS$1();
70549
70645
  const { setCurrentBranch } = useBranchData();
@@ -70739,13 +70835,13 @@ function asyncPoll(fn, pollInterval = 5 * 1e3, pollTimeout = 30 * 1e3) {
70739
70835
  Promise.resolve(fn()).then((result) => {
70740
70836
  const now = (/* @__PURE__ */ new Date()).getTime();
70741
70837
  if (stop) {
70742
- reject(new Error("AsyncPoller: cancelled"));
70838
+ reject(new Error(ASYNC_POLLER_ERROR.CANCELLED));
70743
70839
  } else if (result.done) {
70744
70840
  resolve(result.data);
70745
70841
  } else if (now < endTime) {
70746
70842
  setTimeout(checkCondition, pollInterval, resolve, reject);
70747
70843
  } else {
70748
- reject(new Error("AsyncPoller: reached timeout"));
70844
+ reject(new Error(ASYNC_POLLER_ERROR.TIMEOUT));
70749
70845
  }
70750
70846
  }).catch((err) => {
70751
70847
  reject(err);
@@ -71359,9 +71455,7 @@ mutation addPendingDocumentMutation(
71359
71455
  if (result.status === "unknown") {
71360
71456
  unknownCount++;
71361
71457
  if (unknownCount > 5) {
71362
- throw new Error(
71363
- "AsyncPoller: status unknown for too long, please check indexing progress the TinaCloud dashboard"
71364
- );
71458
+ throw new Error(ASYNC_POLLER_ERROR.STATUS_UNKNOWN);
71365
71459
  }
71366
71460
  }
71367
71461
  return Promise.resolve({
@@ -71381,7 +71475,7 @@ mutation addPendingDocumentMutation(
71381
71475
  );
71382
71476
  return [prom, cancel];
71383
71477
  } catch (error2) {
71384
- if (error2.message === "AsyncPoller: reached timeout") {
71478
+ if (error2.message === ASYNC_POLLER_ERROR.TIMEOUT) {
71385
71479
  console.warn(error2);
71386
71480
  return [Promise.resolve({ status: "timeout" }), () => {
71387
71481
  }];
@@ -71575,6 +71669,7 @@ mutation addPendingDocumentMutation(
71575
71669
  branchName: options.branchName,
71576
71670
  baseBranch: options.baseBranch,
71577
71671
  prTitle: options.prTitle,
71672
+ isDraft: options.isDraft,
71578
71673
  graphQLContentOp: options.graphQLContentOp
71579
71674
  },
71580
71675
  "Failed to start editorial workflow"
@@ -73014,7 +73109,7 @@ const TemplateMenu = ({
73014
73109
  leaveFrom: "transform opacity-100 scale-100",
73015
73110
  leaveTo: "transform opacity-0 scale-95"
73016
73111
  },
73017
- /* @__PURE__ */ React__default.createElement(MenuItems, { className: "origin-top-right absolute right-0 mt-2 z-menu w-56 rounded shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" }, /* @__PURE__ */ React__default.createElement("div", { className: "py-1" }, templates.map((template) => /* @__PURE__ */ React__default.createElement(MenuItem, { key: `${template.label}-${template.name}` }, ({ focus }) => /* @__PURE__ */ React__default.createElement(
73112
+ /* @__PURE__ */ React__default.createElement(MenuItems, { className: "origin-top-right absolute right-0 mt-2 z-menu w-56 rounded shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" }, /* @__PURE__ */ React__default.createElement("div", { className: "py-1" }, templates.map((template) => /* @__PURE__ */ React__default.createElement(MenuItem$1, { key: `${template.label}-${template.name}` }, ({ focus }) => /* @__PURE__ */ React__default.createElement(
73018
73113
  Link$1,
73019
73114
  {
73020
73115
  to: `/${folder.fullyQualifiedName ? [
@@ -73238,7 +73333,7 @@ const CollectionListPage = () => {
73238
73333
  );
73239
73334
  reFetchCollection();
73240
73335
  } catch (error2) {
73241
- if (error2.message.indexOf("has references")) {
73336
+ if (error2.message.includes(ERR_HAS_REFERENCES)) {
73242
73337
  cms.alerts.error(
73243
73338
  error2.message.split("\n ").filter(Boolean)[1]
73244
73339
  );
@@ -73248,7 +73343,6 @@ const CollectionListPage = () => {
73248
73343
  "Document was not deleted, ask a developer for help or check the console for an error message"
73249
73344
  );
73250
73345
  console.error(error2);
73251
- throw error2;
73252
73346
  }
73253
73347
  },
73254
73348
  close: () => setDeleteModalOpen(false)
@@ -73300,11 +73394,11 @@ const CollectionListPage = () => {
73300
73394
  );
73301
73395
  reFetchCollection();
73302
73396
  } catch (error2) {
73303
- if (error2.message && error2.message.includes("already exists")) {
73397
+ if (error2.message && error2.message.includes(ERR_ALREADY_EXISTS)) {
73304
73398
  cms.alerts.error(
73305
73399
  `Document was not renamed. The filename "${vars.newRelativePath}" is already used by another document, please choose a different name.`
73306
73400
  );
73307
- } else if (error2.message && error2.message.includes("has references")) {
73401
+ } else if (error2.message && error2.message.includes(ERR_HAS_REFERENCES)) {
73308
73402
  cms.alerts.error(
73309
73403
  error2.message.split("\n ").filter(Boolean)[1]
73310
73404
  );
@@ -73346,7 +73440,7 @@ const CollectionListPage = () => {
73346
73440
  );
73347
73441
  cms.alerts.info("Folder was successfully created");
73348
73442
  } catch (error2) {
73349
- if (error2.message && error2.message.includes("already exists")) {
73443
+ if (error2.message && error2.message.includes(ERR_ALREADY_EXISTS)) {
73350
73444
  cms.alerts.error(
73351
73445
  `Folder was not created, folder with name "${vars.folderName}" already exists ${folder.name ? `in ${folder.name}` : ""}`
73352
73446
  );
@@ -73932,38 +74026,56 @@ const FolderModal = ({
73932
74026
  validationRegex
73933
74027
  }) => {
73934
74028
  const [isFolderNameValid, setIsFolderNameValid] = useState(false);
74029
+ const [validationMessage, setValidationMessage] = useState(
74030
+ null
74031
+ );
73935
74032
  const [isInteracted, setIsInteracted] = useState(false);
73936
74033
  useEffect(() => {
73937
74034
  validateFolderName(folderName);
73938
74035
  }, [folderName]);
73939
74036
  const validateFolderName = (name2) => {
73940
- if (!validationRegex || !name2.trim()) {
73941
- setIsFolderNameValid(!!name2.trim());
73942
- return !!name2.trim();
74037
+ if (!name2.trim()) {
74038
+ setIsFolderNameValid(false);
74039
+ setValidationMessage(null);
74040
+ return false;
73943
74041
  }
73944
- try {
73945
- const regex = new RegExp(validationRegex);
73946
- const valid = regex.test(name2);
73947
- setIsFolderNameValid(valid);
73948
- return valid;
73949
- } catch (error2) {
74042
+ if (!isValidRelativePath(name2)) {
73950
74043
  setIsFolderNameValid(false);
74044
+ setValidationMessage(RELATIVE_PATH_ALLOWED_CHARS_MESSAGE);
73951
74045
  return false;
73952
74046
  }
74047
+ if (validationRegex) {
74048
+ let passesCustomRegex = false;
74049
+ try {
74050
+ passesCustomRegex = new RegExp(validationRegex).test(name2);
74051
+ } catch (error2) {
74052
+ passesCustomRegex = false;
74053
+ }
74054
+ if (!passesCustomRegex) {
74055
+ setIsFolderNameValid(false);
74056
+ setValidationMessage(
74057
+ "Folder name is not valid, please enter a valid folder name."
74058
+ );
74059
+ return false;
74060
+ }
74061
+ }
74062
+ setIsFolderNameValid(true);
74063
+ setValidationMessage(null);
74064
+ return true;
73953
74065
  };
73954
74066
  return /* @__PURE__ */ React__default.createElement(Modal, null, /* @__PURE__ */ React__default.createElement(PopupModal, null, /* @__PURE__ */ React__default.createElement(ModalHeader, { close: close2 }, "Create Folder"), /* @__PURE__ */ React__default.createElement(ModalBody, { padded: true }, /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
73955
74067
  BaseTextField,
73956
74068
  {
73957
74069
  placeholder: "Enter the name of the new folder",
73958
74070
  value: folderName,
73959
- className: `mb-4 ${!isFolderNameValid && isInteracted ? "border-red-500" : ""}`,
74071
+ className: `mb-4 ${isInteracted && validationMessage ? "border-red-500" : ""}`,
73960
74072
  onChange: (event) => {
73961
74073
  setFolderName(event.target.value);
73962
74074
  setIsInteracted(true);
73963
74075
  validateFolderName(event.target.value);
73964
74076
  }
73965
74077
  }
73966
- ), !isFolderNameValid && isInteracted && /* @__PURE__ */ React__default.createElement("p", { className: "text-red-500 text-sm pl-1" }, "Folder name is not valid – please enter a valid folder name."))), /* @__PURE__ */ React__default.createElement(ModalActions, null, /* @__PURE__ */ React__default.createElement(Button$2, { style: { flexGrow: 2 }, onClick: close2 }, "Cancel"), /* @__PURE__ */ React__default.createElement(
74078
+ ), isInteracted && validationMessage && /* @__PURE__ */ React__default.createElement("p", { className: "text-red-500 text-sm pl-1" }, validationMessage))), /* @__PURE__ */ React__default.createElement(ModalActions, null, /* @__PURE__ */ React__default.createElement(Button$2, { style: { flexGrow: 2 }, onClick: close2 }, "Cancel"), /* @__PURE__ */ React__default.createElement(
73967
74079
  Button$2,
73968
74080
  {
73969
74081
  style: { flexGrow: 3 },
@@ -74403,9 +74515,8 @@ const RenderForm$1 = ({
74403
74515
  }
74404
74516
  return true;
74405
74517
  }
74406
- const isValid = /^[\.\-_\/a-zA-Z0-9]*$/.test(value);
74407
- if (value && !isValid) {
74408
- return "Must contain only a-z, A-Z, 0-9, -, _, ., or /.";
74518
+ if (!RELATIVE_PATH_REGEX.test(value)) {
74519
+ return RELATIVE_PATH_ALLOWED_CHARS_MESSAGE;
74409
74520
  }
74410
74521
  if (((_a2 = schemaCollection.match) == null ? void 0 : _a2.exclude) || ((_b2 = schemaCollection.match) == null ? void 0 : _b2.include)) {
74411
74522
  const filePath = `${normalizePath(schemaCollection.path)}/${value}.${schemaCollection.format || "md"}`;
@@ -74467,7 +74578,7 @@ const RenderForm$1 = ({
74467
74578
  }, 10);
74468
74579
  } catch (error2) {
74469
74580
  const defaultErrorText = "There was a problem saving your document.";
74470
- if (error2.message && error2.message.includes("already exists")) {
74581
+ if (error2.message && error2.message.includes(ERR_ALREADY_EXISTS)) {
74471
74582
  cms.alerts.error(
74472
74583
  `${defaultErrorText} The filename "${form.values.filename}.${collection.format || "md"}" is already used for another document, please modify it.`
74473
74584
  );
@@ -74928,7 +75039,7 @@ const CheckSchema = ({
74928
75039
  );
74929
75040
  }
74930
75041
  }).catch((error2) => {
74931
- if (error2.message.includes("has not been indexed by TinaCloud")) {
75042
+ if (error2.message.includes(ERR_NOT_INDEXED)) {
74932
75043
  setSchemaMissingError(true);
74933
75044
  } else {
74934
75045
  console.error("Unexpected error checking schema:", error2);
@@ -170,6 +170,7 @@ export declare class Client {
170
170
  branchName: string;
171
171
  baseBranch: string;
172
172
  prTitle?: string;
173
+ isDraft?: boolean;
173
174
  graphQLContentOp?: {
174
175
  query: string;
175
176
  variables: Record<string, unknown>;
@@ -23,6 +23,12 @@ export type SaveContentErrorPayload = {
23
23
  documentPath?: string;
24
24
  error?: string;
25
25
  };
26
+ export declare const EditorialWorkflowSaveEvent: string;
27
+ export type EditorialWorkflowSavePayload = {
28
+ choice: 'draft' | 'review' | 'publish';
29
+ success: boolean;
30
+ error?: string;
31
+ };
26
32
  export declare const FormResetEvent: string;
27
33
  export declare const MediaManagerContentUploadedEvent: string;
28
34
  export type MediaManagerContentUploadedPayload = {
package/dist/react.js CHANGED
@@ -147,17 +147,24 @@ function useTina(props) {
147
147
  }
148
148
  function useEditState() {
149
149
  const [edit, setEdit] = React.useState(false);
150
+ const trustedAdminOrigins = useTrustedAdminOrigins();
150
151
  React.useEffect(() => {
151
- if (typeof window !== "undefined") {
152
- parent.postMessage({ type: "isEditMode" }, window.location.origin);
153
- window.addEventListener("message", (event) => {
154
- var _a;
155
- if (((_a = event.data) == null ? void 0 : _a.type) === "tina:editMode") {
156
- setEdit(true);
157
- }
158
- });
159
- }
160
- }, []);
152
+ if (typeof window === "undefined")
153
+ return;
154
+ parent.postMessage({ type: "isEditMode" }, window.location.origin);
155
+ const handleMessage = (event) => {
156
+ var _a;
157
+ if (!isFromAdmin(event, trustedAdminOrigins))
158
+ return;
159
+ if (((_a = event.data) == null ? void 0 : _a.type) === "tina:editMode") {
160
+ setEdit(true);
161
+ }
162
+ };
163
+ window.addEventListener("message", handleMessage);
164
+ return () => {
165
+ window.removeEventListener("message", handleMessage);
166
+ };
167
+ }, [trustedAdminOrigins]);
161
168
  return { edit };
162
169
  }
163
170
  export {
@@ -1,7 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { Form } from '../forms';
3
+ import { type SaveChoice } from './save-options';
3
4
  export declare const CreateBranchModal: ({ close, safeSubmit, path, values, crudType, tinaForm, onBaseBranchDeleted, }: {
4
- safeSubmit: () => Promise<void>;
5
+ safeSubmit: (editorialWorkflowChoice?: SaveChoice) => Promise<void>;
5
6
  close: () => void;
6
7
  path: string;
7
8
  values: Record<string, unknown>;
@@ -9,14 +10,16 @@ export declare const CreateBranchModal: ({ close, safeSubmit, path, values, crud
9
10
  tinaForm?: Form;
10
11
  onBaseBranchDeleted?: () => void;
11
12
  }) => React.JSX.Element;
12
- export declare const CreateBranchPromptModal: ({ branchName, close, disabled, errorMessage, onBranchNameChange, onCreateBranch, onSaveToProtectedBranch, }: {
13
+ export declare const CreateBranchPromptModal: ({ branchName, close, disabled, errorMessage, onBranchNameChange, onCreateBranch, onSaveToProtectedBranch, showSaveOptions, disablePublish, }: {
13
14
  branchName: string;
14
15
  close: () => void;
15
16
  disabled?: boolean;
16
17
  errorMessage?: string;
17
18
  onBranchNameChange: (value: string) => void;
18
- onCreateBranch: () => void;
19
+ onCreateBranch: (isDraft: boolean) => void;
19
20
  onSaveToProtectedBranch: () => void;
21
+ showSaveOptions?: boolean;
22
+ disablePublish?: boolean;
20
23
  }) => React.JSX.Element;
21
24
  export declare const PrefixedTextField: ({ label, prefix, ...props }: {
22
25
  [x: string]: any;
@@ -0,0 +1,7 @@
1
+ export type SaveChoice = 'draft' | 'review' | 'publish';
2
+ export declare const SAVE_CHOICE_KEY = "tina.editorialWorkflow.saveChoice";
3
+ export declare const SAVE_CHOICE_ORDER: SaveChoice[];
4
+ export declare const resolveSaveOptions: (lastChoice: SaveChoice, disablePublish: boolean) => {
5
+ main: SaveChoice;
6
+ menu: SaveChoice[];
7
+ };
@@ -13,14 +13,18 @@ export interface ExecuteWorkflowOptions {
13
13
  crudType: string;
14
14
  tinaForm?: Form;
15
15
  signal?: AbortSignal;
16
+ isDraft?: boolean;
16
17
  }
17
18
  export interface UseEditorialWorkflowResult {
18
19
  isExecuting: boolean;
19
20
  errorMessage: string;
20
21
  currentStep: number;
21
22
  elapsedTime: number;
22
- /** Returns true on success, false on failure (error captured in errorMessage) */
23
- executeWorkflow: (opts: ExecuteWorkflowOptions) => Promise<boolean>;
23
+ /** Resolves with the outcome; on failure `error` holds the message. */
24
+ executeWorkflow: (opts: ExecuteWorkflowOptions) => Promise<{
25
+ success: boolean;
26
+ error?: string;
27
+ }>;
24
28
  /** Reset error/executing state so the form can be retried */
25
29
  reset: () => void;
26
30
  }
@@ -6,6 +6,8 @@ export interface DropdownButtonItem {
6
6
  variant?: 'default' | 'destructive';
7
7
  icon?: React.ReactNode;
8
8
  disabled?: boolean;
9
+ /** Optional hover tooltip. Shown even when the item is disabled. */
10
+ tooltip?: string;
9
11
  }
10
12
  export interface DropdownButtonProps extends Omit<ButtonProps, 'onClick'> {
11
13
  /**
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.9.4",
5
+ "version": "3.10.0",
6
6
  "main": "dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "exports": {
@@ -45,15 +45,14 @@
45
45
  "@headlessui/react": "2.1.8",
46
46
  "@heroicons/react": "^1.0.6",
47
47
  "@monaco-editor/react": "4.7.0-rc.0",
48
- "@radix-ui/react-checkbox": "^1.1.4",
49
- "@radix-ui/react-dialog": "^1.1.6",
50
- "@radix-ui/react-dropdown-menu": "^2.1.6",
51
- "@radix-ui/react-popover": "^1.1.15",
52
- "@radix-ui/react-select": "^2.2.6",
53
- "@radix-ui/react-separator": "^1.1.2",
54
- "@radix-ui/react-slot": "^1.1.2",
55
- "@radix-ui/react-toolbar": "^1.1.2",
56
- "@radix-ui/react-tooltip": "^1.2.8",
48
+ "@radix-ui/react-dialog": "^1.1.18",
49
+ "@radix-ui/react-dropdown-menu": "^2.1.19",
50
+ "@radix-ui/react-popover": "^1.1.18",
51
+ "@radix-ui/react-select": "^2.3.2",
52
+ "@radix-ui/react-separator": "^1.1.11",
53
+ "@radix-ui/react-slot": "^1.3.0",
54
+ "@radix-ui/react-toolbar": "^1.1.14",
55
+ "@radix-ui/react-tooltip": "^1.2.11",
57
56
  "@react-hook/window-size": "^3.1.1",
58
57
  "@tanstack/react-table": "^8.21.3",
59
58
  "@udecode/cmdk": "^0.2.1",
@@ -114,9 +113,9 @@
114
113
  "yup": "^1.6.1",
115
114
  "zod": "^3.24.2",
116
115
  "@tinacms/bridge": "0.3.0",
117
- "@tinacms/search": "1.2.20",
118
- "@tinacms/mdx": "2.1.8",
119
- "@tinacms/schema-tools": "2.8.2"
116
+ "@tinacms/schema-tools": "2.8.3",
117
+ "@tinacms/search": "1.2.21",
118
+ "@tinacms/mdx": "2.1.9"
120
119
  },
121
120
  "devDependencies": {
122
121
  "@graphql-tools/utils": "^10.8.1",