@rufous/ui 0.2.58 → 0.2.60

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/main.cjs CHANGED
@@ -165,6 +165,7 @@ __export(main_exports, {
165
165
  ViewIcon: () => viewIcon_default,
166
166
  WorkItemIcon: () => workItemIcon_default,
167
167
  Zoom: () => Zoom,
168
+ transformLegacyTodos: () => transformLegacyTodos,
168
169
  useRufousTheme: () => useRufousTheme
169
170
  });
170
171
  module.exports = __toCommonJS(main_exports);
@@ -9615,7 +9616,7 @@ var STATUS_COLORS = {
9615
9616
  };
9616
9617
  var CustomTaskItem = import_extension_task_item.default.extend({
9617
9618
  addStorage() {
9618
- return { taskTodoEnabled: true };
9619
+ return { taskTodoEnabled: false };
9619
9620
  },
9620
9621
  addAttributes() {
9621
9622
  return {
@@ -9660,27 +9661,6 @@ var CustomTaskItem = import_extension_task_item.default.extend({
9660
9661
  }
9661
9662
  return false;
9662
9663
  }
9663
- if (this.editor.storage.taskTodoEnabled === false) {
9664
- const afterContent2 = $from.parent.content.cut($from.parentOffset);
9665
- const schema2 = state.schema;
9666
- const tr2 = state.tr;
9667
- if (afterContent2.size > 0) {
9668
- tr2.delete($from.pos, $from.end());
9669
- }
9670
- let taskListDepth = -1;
9671
- for (let d = taskDepth - 1; d >= 0; d--) {
9672
- if ($from.node(d).type.name === "taskList") {
9673
- taskListDepth = d;
9674
- break;
9675
- }
9676
- }
9677
- const insertPos2 = taskListDepth !== -1 ? tr2.mapping.map($from.after(taskListDepth)) : tr2.mapping.map($from.after(taskDepth));
9678
- const newPara2 = afterContent2.size > 0 ? schema2.nodes.paragraph.create(null, afterContent2) : schema2.nodes.paragraph.create();
9679
- tr2.insert(insertPos2, newPara2);
9680
- tr2.setSelection(import_prosemirror_state.TextSelection.near(tr2.doc.resolve(insertPos2 + 1)));
9681
- this.editor.view.dispatch(tr2);
9682
- return true;
9683
- }
9684
9664
  const taskNode = $from.node(taskDepth);
9685
9665
  const status = taskNode.attrs.status || "todo";
9686
9666
  const schema = state.schema;
@@ -9783,7 +9763,7 @@ var CustomTaskItem = import_extension_task_item.default.extend({
9783
9763
  addNodeView() {
9784
9764
  return ({ node, HTMLAttributes, getPos, editor }) => {
9785
9765
  const li = document.createElement("li");
9786
- li.style.cssText = "display: flex; align-items: flex-start; gap: 8px; margin: 4px 0; list-style: none; ";
9766
+ li.style.cssText = "display: flex; align-items: flex-start; gap: 6px; margin: 0; list-style: none; ";
9787
9767
  Object.entries(HTMLAttributes || {}).forEach(([key, val]) => {
9788
9768
  if (val != null && val !== false) {
9789
9769
  li.setAttribute(key, String(val));
@@ -9792,7 +9772,7 @@ var CustomTaskItem = import_extension_task_item.default.extend({
9792
9772
  li.setAttribute("data-status", node.attrs.status || "todo");
9793
9773
  const label = document.createElement("label");
9794
9774
  label.contentEditable = "false";
9795
- label.style.cssText = "flex-shrink: 0; margin-top: 3px;";
9775
+ label.style.cssText = "flex-shrink: 0;";
9796
9776
  const checkbox = document.createElement("span");
9797
9777
  const updateCheckbox = (status) => {
9798
9778
  const colors = STATUS_COLORS[status] || STATUS_COLORS.todo;
@@ -9807,7 +9787,7 @@ var CustomTaskItem = import_extension_task_item.default.extend({
9807
9787
  "border-radius: 3px",
9808
9788
  "cursor: pointer",
9809
9789
  "user-select: none",
9810
- "margin-top: 7px",
9790
+ "margin-top: 3px",
9811
9791
  "border: 2px solid " + colors.border,
9812
9792
  `background-image: url("${imageUrl}")`,
9813
9793
  "background-repeat: no-repeat",
@@ -10033,34 +10013,42 @@ var Dropdown = ({ trigger, children, className = "", keepOpen = false }) => {
10033
10013
  const menuRef = (0, import_react55.useRef)(null);
10034
10014
  (0, import_react55.useEffect)(() => {
10035
10015
  const handleClick = (e) => {
10036
- if (ref.current && !ref.current.contains(e.target)) setOpen(false);
10016
+ const target = e.target;
10017
+ if (ref.current && !ref.current.contains(target) && menuRef.current && !menuRef.current.contains(target)) {
10018
+ setOpen(false);
10019
+ }
10037
10020
  };
10038
10021
  document.addEventListener("mousedown", handleClick);
10039
10022
  return () => document.removeEventListener("mousedown", handleClick);
10040
10023
  }, []);
10041
10024
  (0, import_react55.useEffect)(() => {
10042
- if (!open || !menuRef.current) return;
10025
+ if (!open || !menuRef.current || !ref.current) return;
10043
10026
  const menu = menuRef.current;
10044
- menu.style.left = "0";
10045
- menu.style.right = "auto";
10046
- requestAnimationFrame(() => {
10047
- if (!menu) return;
10048
- const rect = menu.getBoundingClientRect();
10027
+ const trigger2 = ref.current;
10028
+ const position = () => {
10029
+ const triggerRect = trigger2.getBoundingClientRect();
10049
10030
  const vw = window.innerWidth;
10050
- const parentLeft = ref.current?.getBoundingClientRect().left || 0;
10051
- if (rect.right > vw - 8) {
10052
- menu.style.left = "auto";
10053
- menu.style.right = "0";
10054
- const newRect = menu.getBoundingClientRect();
10055
- if (newRect.left < 8) {
10056
- menu.style.left = `${8 - parentLeft}px`;
10057
- menu.style.right = "auto";
10031
+ const vh = window.innerHeight;
10032
+ let left = triggerRect.left;
10033
+ let top = triggerRect.bottom + 4;
10034
+ menu.style.left = `${left}px`;
10035
+ menu.style.top = `${top}px`;
10036
+ requestAnimationFrame(() => {
10037
+ if (!menu) return;
10038
+ const menuRect = menu.getBoundingClientRect();
10039
+ if (menuRect.right > vw - 8) {
10040
+ left = Math.max(8, triggerRect.right - menuRect.width);
10058
10041
  }
10059
- } else if (rect.left < 8) {
10060
- menu.style.left = `${8 - parentLeft}px`;
10061
- menu.style.right = "auto";
10062
- }
10063
- });
10042
+ if (left < 8) left = 8;
10043
+ if (menuRect.bottom > vh - 8) {
10044
+ top = triggerRect.top - menuRect.height - 4;
10045
+ if (top < 8) top = 8;
10046
+ }
10047
+ menu.style.left = `${left}px`;
10048
+ menu.style.top = `${top}px`;
10049
+ });
10050
+ };
10051
+ position();
10064
10052
  }, [open]);
10065
10053
  return /* @__PURE__ */ import_react55.default.createElement("div", { className: `dropdown ${className}`, ref }, /* @__PURE__ */ import_react55.default.createElement(
10066
10054
  "button",
@@ -10071,7 +10059,7 @@ var Dropdown = ({ trigger, children, className = "", keepOpen = false }) => {
10071
10059
  },
10072
10060
  trigger.label,
10073
10061
  /* @__PURE__ */ import_react55.default.createElement("span", { className: "dropdown-arrow" }, "\u25BE")
10074
- ), open && /* @__PURE__ */ import_react55.default.createElement("div", { ref: menuRef, className: "dropdown-menu", onClick: keepOpen ? void 0 : () => setOpen(false) }, typeof children === "function" ? children(() => setOpen(false)) : children));
10062
+ ), open && /* @__PURE__ */ import_react55.default.createElement("div", { ref: menuRef, className: "dropdown-menu dropdown-menu-fixed", onClick: keepOpen ? void 0 : () => setOpen(false) }, typeof children === "function" ? children(() => setOpen(false)) : children));
10075
10063
  };
10076
10064
  var InsertPanel = ({ editor, onClose, mode = "video" }) => {
10077
10065
  const [activeTab, setActiveTab] = (0, import_react55.useState)("link");
@@ -11489,6 +11477,79 @@ var VideoToolbar = ({ editor }) => {
11489
11477
  };
11490
11478
  var VideoToolbar_default = VideoToolbar;
11491
11479
 
11480
+ // lib/RufousTextEditor/legacyTodoTransform.ts
11481
+ var IMAGE_TO_STATUS = {
11482
+ "todo-blank.svg": "todo",
11483
+ "working.svg": "working",
11484
+ "blocked.svg": "blocked",
11485
+ "closed.svg": "resolved"
11486
+ };
11487
+ function getStatusFromImgSrc(src) {
11488
+ for (const [key, status] of Object.entries(IMAGE_TO_STATUS)) {
11489
+ if (src.includes(key)) return status;
11490
+ }
11491
+ return "todo";
11492
+ }
11493
+ function transformLegacyTodos(html) {
11494
+ if (!html || !html.includes("todo-item")) return html;
11495
+ const div = document.createElement("div");
11496
+ div.innerHTML = html;
11497
+ const children = Array.from(div.childNodes);
11498
+ const result = [];
11499
+ let currentTaskList = null;
11500
+ for (const child of children) {
11501
+ if (child instanceof HTMLElement && child.classList.contains("todo-item")) {
11502
+ const img = child.querySelector("button img, .todo-item-button img");
11503
+ const status = img ? getStatusFromImgSrc(img.src) : "todo";
11504
+ let text = "";
11505
+ const classSpan = child.querySelector(".todo-item-text");
11506
+ if (classSpan) {
11507
+ text = classSpan.textContent?.trim() || "";
11508
+ } else {
11509
+ const spans = child.querySelectorAll("span");
11510
+ for (const span of Array.from(spans)) {
11511
+ if (!span.closest("button") && !span.closest(".todo-item-button")) {
11512
+ text = span.textContent?.trim() || "";
11513
+ break;
11514
+ }
11515
+ }
11516
+ }
11517
+ const li = document.createElement("li");
11518
+ li.setAttribute("data-type", "taskItem");
11519
+ li.setAttribute("data-status", status);
11520
+ li.setAttribute("data-checked", "false");
11521
+ const label = document.createElement("label");
11522
+ const checkbox = document.createElement("input");
11523
+ checkbox.type = "checkbox";
11524
+ const labelSpan = document.createElement("span");
11525
+ label.appendChild(checkbox);
11526
+ label.appendChild(labelSpan);
11527
+ const contentDiv = document.createElement("div");
11528
+ const p = document.createElement("p");
11529
+ if (text) {
11530
+ p.textContent = text;
11531
+ }
11532
+ contentDiv.appendChild(p);
11533
+ li.appendChild(label);
11534
+ li.appendChild(contentDiv);
11535
+ if (!currentTaskList) {
11536
+ currentTaskList = document.createElement("ul");
11537
+ currentTaskList.setAttribute("data-type", "taskList");
11538
+ result.push(currentTaskList);
11539
+ }
11540
+ currentTaskList.appendChild(li);
11541
+ } else {
11542
+ currentTaskList = null;
11543
+ result.push(child);
11544
+ }
11545
+ }
11546
+ const output = document.createElement("div");
11547
+ for (const node of result) {
11548
+ output.appendChild(node);
11549
+ }
11550
+ return output.innerHTML;
11551
+ }
11552
+
11492
11553
  // lib/RufousTextEditor/RufousTextEditor.tsx
11493
11554
  var VARIANT_BUTTONS = {
11494
11555
  default: [
@@ -11564,6 +11625,9 @@ var RufousTextEditor = ({
11564
11625
  variant = "default",
11565
11626
  buttons,
11566
11627
  hideButtons,
11628
+ disabled = false,
11629
+ error = false,
11630
+ helperText,
11567
11631
  width,
11568
11632
  height,
11569
11633
  resizable = false,
@@ -11589,8 +11653,9 @@ var RufousTextEditor = ({
11589
11653
  (0, import_react58.useEffect)(() => {
11590
11654
  onBlurRef.current = onBlur;
11591
11655
  }, [onBlur]);
11656
+ const isEditable = editable && !disabled;
11592
11657
  const editor = (0, import_react59.useEditor)({
11593
- editable,
11658
+ editable: isEditable,
11594
11659
  extensions: [
11595
11660
  import_starter_kit.default,
11596
11661
  import_extension_placeholder.default.configure({
@@ -11599,7 +11664,10 @@ var RufousTextEditor = ({
11599
11664
  import_extension_link.default.configure({
11600
11665
  openOnClick: false,
11601
11666
  autolink: true,
11602
- HTMLAttributes: { class: "editor-link" }
11667
+ // Don't set default target/_blank — let the user choose via the link popup.
11668
+ // This prevents TipTap from adding target="_blank" to all links by default
11669
+ // and ensures checkbox state persists across editor remounts.
11670
+ HTMLAttributes: { class: "editor-link", target: null, rel: null }
11603
11671
  }),
11604
11672
  CustomImage.configure({
11605
11673
  inline: false,
@@ -11676,14 +11744,17 @@ var RufousTextEditor = ({
11676
11744
  return false;
11677
11745
  }
11678
11746
  },
11679
- content: initialContent || "",
11747
+ content: transformLegacyTodos(initialContent || ""),
11680
11748
  onUpdate: ({ editor: e }) => {
11681
11749
  onChangeRef.current?.(e.getHTML(), e.getJSON());
11682
11750
  }
11683
11751
  });
11752
+ const wrapperRef = (0, import_react58.useRef)(null);
11684
11753
  (0, import_react58.useEffect)(() => {
11685
11754
  if (!editor) return;
11686
- const handler = () => {
11755
+ const handler = ({ event }) => {
11756
+ const relatedTarget = event?.relatedTarget;
11757
+ if (relatedTarget && wrapperRef.current?.contains(relatedTarget)) return;
11687
11758
  onBlurRef.current?.(editor.getHTML(), editor.getJSON());
11688
11759
  };
11689
11760
  editor.on("blur", handler);
@@ -11839,7 +11910,8 @@ var RufousTextEditor = ({
11839
11910
  return /* @__PURE__ */ import_react58.default.createElement(
11840
11911
  "div",
11841
11912
  {
11842
- className: `rf-rte-wrapper editor-wrapper ${resizable ? "rf-rte-resizable" : ""} ${sxClass} ${className || ""}`,
11913
+ ref: wrapperRef,
11914
+ className: `rf-rte-wrapper editor-wrapper ${resizable ? "rf-rte-resizable" : ""} ${disabled ? "rf-rte-disabled" : ""} ${error ? "rf-rte-error" : ""} ${sxClass} ${className || ""}`,
11843
11915
  style: {
11844
11916
  ...style,
11845
11917
  ...width ? { width: typeof width === "number" ? `${width}px` : width } : {},
@@ -12014,17 +12086,19 @@ var RufousTextEditor = ({
12014
12086
  }
12015
12087
  ), "No follow"))), /* @__PURE__ */ import_react58.default.createElement("div", { className: "link-modal-footer" }, /* @__PURE__ */ import_react58.default.createElement("button", { className: "link-modal-btn-unlink", onClick: handleLinkRemove }, "Unlink"), /* @__PURE__ */ import_react58.default.createElement("button", { className: "link-modal-btn-apply", onClick: handleLinkSubmit }, "Update")))),
12016
12088
  document.body
12017
- ))
12089
+ )),
12090
+ helperText && /* @__PURE__ */ import_react58.default.createElement("div", { className: `rf-rte-helper-text ${error ? "rf-rte-helper-error" : ""}` }, helperText)
12018
12091
  );
12019
12092
  };
12020
12093
  var RufousTextContent = ({ content, className, style, sx }) => {
12021
12094
  const sxClass = useSx(sx);
12095
+ const transformedContent = (0, import_react58.useMemo)(() => transformLegacyTodos(content || ""), [content]);
12022
12096
  return /* @__PURE__ */ import_react58.default.createElement(
12023
12097
  "div",
12024
12098
  {
12025
12099
  className: `rf-rte-content ${sxClass} ${className || ""}`,
12026
12100
  style,
12027
- dangerouslySetInnerHTML: { __html: content }
12101
+ dangerouslySetInnerHTML: { __html: transformedContent }
12028
12102
  }
12029
12103
  );
12030
12104
  };
@@ -12166,5 +12240,6 @@ var RufousTextContent = ({ content, className, style, sx }) => {
12166
12240
  ViewIcon,
12167
12241
  WorkItemIcon,
12168
12242
  Zoom,
12243
+ transformLegacyTodos,
12169
12244
  useRufousTheme
12170
12245
  });
package/dist/main.css CHANGED
@@ -72,7 +72,7 @@
72
72
  display: flex;
73
73
  justify-content: center;
74
74
  align-items: center;
75
- z-index: 1000;
75
+ z-index: 99999;
76
76
  backdrop-filter: blur(2px);
77
77
  }
78
78
  .dialog-container {
@@ -6595,11 +6595,37 @@ pre {
6595
6595
  }
6596
6596
  .rf-rte-wrapper.rf-rte-resizable {
6597
6597
  resize: vertical;
6598
- overflow: visible;
6598
+ overflow: hidden;
6599
6599
  min-height: 200px;
6600
6600
  }
6601
6601
  .rf-rte-wrapper.rf-rte-resizable .editor-content-wrapper {
6602
6602
  overflow: auto;
6603
+ flex: 1;
6604
+ min-height: 0;
6605
+ }
6606
+ .rf-rte-wrapper.rf-rte-disabled {
6607
+ opacity: 0.6;
6608
+ pointer-events: none;
6609
+ user-select: none;
6610
+ }
6611
+ .rf-rte-wrapper.rf-rte-disabled .toolbar {
6612
+ opacity: 0.5;
6613
+ }
6614
+ .rf-rte-wrapper.rf-rte-error {
6615
+ border-color: #dc2626;
6616
+ }
6617
+ .rf-rte-wrapper.rf-rte-error:focus-within {
6618
+ border-color: #dc2626;
6619
+ box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.1);
6620
+ }
6621
+ .rf-rte-helper-text {
6622
+ font-size: 0.75rem;
6623
+ color: var(--text-secondary, #6b7280);
6624
+ margin-top: 4px;
6625
+ padding: 0 4px;
6626
+ }
6627
+ .rf-rte-helper-text.rf-rte-helper-error {
6628
+ color: #dc2626;
6603
6629
  }
6604
6630
  .rf-rte-wrapper:focus-within,
6605
6631
  .rf-rte-wrapper:has(.dropdown-menu),
@@ -6615,6 +6641,8 @@ pre {
6615
6641
  left: 0;
6616
6642
  right: 0;
6617
6643
  bottom: 0;
6644
+ width: 100% !important;
6645
+ height: 100% !important;
6618
6646
  z-index: 99999;
6619
6647
  border-radius: 0;
6620
6648
  margin: 0;
@@ -6729,11 +6757,13 @@ pre {
6729
6757
  margin-left: 2px;
6730
6758
  opacity: 0.5;
6731
6759
  }
6760
+ .rf-rte-wrapper .dropdown-menu.dropdown-menu-fixed {
6761
+ position: fixed;
6762
+ top: auto;
6763
+ left: auto;
6764
+ z-index: 99999;
6765
+ }
6732
6766
  .rf-rte-wrapper .dropdown-menu {
6733
- position: absolute;
6734
- top: 100%;
6735
- left: 0;
6736
- z-index: 1000;
6737
6767
  min-width: 170px;
6738
6768
  padding: 4px;
6739
6769
  background: #fff;
@@ -7236,7 +7266,7 @@ pre {
7236
7266
  letter-spacing: 0.05em;
7237
7267
  }
7238
7268
  .rf-rte-wrapper .tiptap p {
7239
- margin: 0.5em 0;
7269
+ margin: 1px 0;
7240
7270
  }
7241
7271
  .rf-rte-wrapper .tiptap ul,
7242
7272
  .rf-rte-wrapper .tiptap ol {
@@ -7254,7 +7284,10 @@ pre {
7254
7284
  display: flex;
7255
7285
  align-items: flex-start;
7256
7286
  gap: 8px;
7257
- margin: 4px 0;
7287
+ margin: 0;
7288
+ }
7289
+ .rf-rte-wrapper .tiptap ul[data-type=taskList] li.task-item p {
7290
+ margin: 0;
7258
7291
  }
7259
7292
  .rf-rte-wrapper .tiptap ul[data-type=taskList] li.task-item > label {
7260
7293
  flex-shrink: 0;
package/dist/main.d.cts CHANGED
@@ -1646,6 +1646,9 @@ interface RufousTextEditorProps {
1646
1646
  variant?: EditorVariant;
1647
1647
  buttons?: ToolbarButton[];
1648
1648
  hideButtons?: ToolbarButton[];
1649
+ disabled?: boolean;
1650
+ error?: boolean;
1651
+ helperText?: string;
1649
1652
  width?: number | string;
1650
1653
  height?: number | string;
1651
1654
  resizable?: boolean;
@@ -1662,6 +1665,42 @@ interface RufousTextContentProps {
1662
1665
  }
1663
1666
  declare const RufousTextContent: React__default.FC<RufousTextContentProps>;
1664
1667
 
1668
+ /**
1669
+ * Transforms legacy Jodit-style todo HTML into TipTap-compatible taskList HTML.
1670
+ *
1671
+ * Old Jodit format (two variants):
1672
+ *
1673
+ * Variant 1 (class-based):
1674
+ * <p class="todo-item">
1675
+ * <button class="todo-item-button" contenteditable="false">
1676
+ * <img src="...todo-blank.svg" alt="icon">
1677
+ * </button>
1678
+ * <span class="todo-item-text"> Todo text</span>
1679
+ * </p>
1680
+ *
1681
+ * Variant 2 (inline-styled):
1682
+ * <p class="todo-item" style="display: flex; ...">
1683
+ * <button class="todo-item-button" contenteditable="false" style="...">
1684
+ * <img src="...todo-blank.svg" alt="icon" style="...">
1685
+ * </button>
1686
+ * <span style="margin-top: -2px; flex-grow: 1;"> Todo text</span>
1687
+ * </p>
1688
+ *
1689
+ * New TipTap format:
1690
+ * <ul data-type="taskList">
1691
+ * <li data-status="todo">
1692
+ * <label><input type="checkbox"></label>
1693
+ * <div><p>Todo text</p></div>
1694
+ * </li>
1695
+ * </ul>
1696
+ */
1697
+ /**
1698
+ * Transforms legacy Jodit todo HTML to TipTap taskList format.
1699
+ * Non-todo content is passed through unchanged.
1700
+ * Consecutive todo items are grouped into a single <ul data-type="taskList">.
1701
+ */
1702
+ declare function transformLegacyTodos(html: string): string;
1703
+
1665
1704
  interface MentionItemData {
1666
1705
  id: string;
1667
1706
  name: string;
@@ -1669,4 +1708,4 @@ interface MentionItemData {
1669
1708
  shortName?: string;
1670
1709
  }
1671
1710
 
1672
- export { APP_THEMES, Accordion, AccordionDetails, type AccordionDetailsProps, type AccordionProps, AccordionSummary, type AccordionSummaryProps, type Action, ActivateUserIcon, AddButton, AddressLookup, ArchivedIcon, AssignGroupIcon, Autocomplete, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BaseDialog, Box, type BoxProps, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, CameraIcon, CancelButton, Card, CardActions, type CardActionsProps, CardContent, type CardContentProps, CardHeader, type CardHeaderProps, CardMedia, type CardMediaProps, type CardProps, Checkbox, type CheckboxProps, Chip, type ChipProps, CircularProgress, CircularProgressIcon, type CircularProgressIconProps, CloseIcon, Collapse, type CollapseProps, type Column, CopyIcon, DataGrid, type DataGridProps, DateField, type DateFieldProps, type DateFormatString, DateRangeField, type DateRangeFieldProps, type DateRangeValue, DifficultyAllIcon, DifficultyEasyIcon, DifficultyHardIcon, DifficultyMediumIcon, Divider, type DividerProps, DollarIcon, DownloadIcon, DownloadPdfIcon, Drawer, type DrawerProps, EditChatIcon, EditIcon, EngagementIcon, Fade, type FadeProps, FunctionIcon, Grid, type GridProps, Grow, type GrowProps, HelpOutlinedIcon, HierarchyIcon, IconButton, type IconButtonProps, ImageField, type ImageFieldProps, InactiveGroupIcon, IndustryIcon, InvoiceIcon, Link, type LinkProps, List, ListItem, ListItemButton, type ListItemButtonProps, ListItemIcon, type ListItemIconProps, type ListItemProps, ListItemText, type ListItemTextProps, type ListProps, ListSubheader, type ListSubheaderProps, LocationPinIcon, LogsIcon, Menu, MenuDivider, MenuItem, type MenuItemProps, MenuList, type MenuListProps, type MenuProps, MinExperienceIcon, NineDotMenuIcon, NotificationIcon, Paper, type PaperProps, PhoneField, type PhoneFieldProps, Popover, type PopoverProps, Popper, type PopperProps, ProjectIcon, QualificationsIcon, QuestionStatusAllIcon, QuestionStatusPrivateIcon, QuestionStatusPublicIcon, QuestionTypeAllIcon, QuestionTypeCodingIcon, QuestionTypeDescriptiveIcon, QuestionTypeMultipleIcon, QuestionTypeSingleIcon, Radio, RadioGroup, type RadioGroupProps, type RadioProps, Rating, type RatingProps, RefreshIcon, ResendInviteIcon, RolesIcon, RufousAiIcon, RufousBirdIcon, RufousLauncherIcon, RufousLogoLoader, type RufousLogoLoaderProps, RufousTextContent, type RufousTextContentProps, RufousTextEditor, type MentionItemData as RufousTextEditorMentionItem, type RufousTextEditorProps, RufousThemeProvider, Select, type SelectProps, SidebarIcon, Skeleton, type SkeletonProps, Slide, type SlideProps, Slider, type SliderProps, Snackbar, type SnackbarProps, SoftSkillsIcon, type SortDirection, Stack, type StackProps, StandardButton, Step, StepButton, type StepButtonProps, StepContent, type StepContentProps, StepLabel, type StepLabelProps, type StepProps, Stepper, type StepperProps, SubmitButton, SubscribeIcon, SuspendUserIcon, Switch, type SwitchProps, type SxProp, Tab, TabPanel, type TabPanelProps, type TabProps, Tabs, type TabsProps, TechnicalSkillsIcon, TextField, type TextFieldProps, TickIcon, TimerIcon, ToggleButton, ToggleButtonGroup, type ToggleButtonGroupProps, type ToggleButtonProps, Tooltip, type TooltipProps, TrashIcon, Typography, type TypographyProps, UnArchivedIcon, UnsubscribeIcon, UploadIcon, UserAssignIcon, ViewIcon, WorkItemIcon, Zoom, type ZoomProps, useRufousTheme };
1711
+ export { APP_THEMES, Accordion, AccordionDetails, type AccordionDetailsProps, type AccordionProps, AccordionSummary, type AccordionSummaryProps, type Action, ActivateUserIcon, AddButton, AddressLookup, ArchivedIcon, AssignGroupIcon, Autocomplete, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BaseDialog, Box, type BoxProps, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, CameraIcon, CancelButton, Card, CardActions, type CardActionsProps, CardContent, type CardContentProps, CardHeader, type CardHeaderProps, CardMedia, type CardMediaProps, type CardProps, Checkbox, type CheckboxProps, Chip, type ChipProps, CircularProgress, CircularProgressIcon, type CircularProgressIconProps, CloseIcon, Collapse, type CollapseProps, type Column, CopyIcon, DataGrid, type DataGridProps, DateField, type DateFieldProps, type DateFormatString, DateRangeField, type DateRangeFieldProps, type DateRangeValue, DifficultyAllIcon, DifficultyEasyIcon, DifficultyHardIcon, DifficultyMediumIcon, Divider, type DividerProps, DollarIcon, DownloadIcon, DownloadPdfIcon, Drawer, type DrawerProps, EditChatIcon, EditIcon, EngagementIcon, Fade, type FadeProps, FunctionIcon, Grid, type GridProps, Grow, type GrowProps, HelpOutlinedIcon, HierarchyIcon, IconButton, type IconButtonProps, ImageField, type ImageFieldProps, InactiveGroupIcon, IndustryIcon, InvoiceIcon, Link, type LinkProps, List, ListItem, ListItemButton, type ListItemButtonProps, ListItemIcon, type ListItemIconProps, type ListItemProps, ListItemText, type ListItemTextProps, type ListProps, ListSubheader, type ListSubheaderProps, LocationPinIcon, LogsIcon, Menu, MenuDivider, MenuItem, type MenuItemProps, MenuList, type MenuListProps, type MenuProps, MinExperienceIcon, NineDotMenuIcon, NotificationIcon, Paper, type PaperProps, PhoneField, type PhoneFieldProps, Popover, type PopoverProps, Popper, type PopperProps, ProjectIcon, QualificationsIcon, QuestionStatusAllIcon, QuestionStatusPrivateIcon, QuestionStatusPublicIcon, QuestionTypeAllIcon, QuestionTypeCodingIcon, QuestionTypeDescriptiveIcon, QuestionTypeMultipleIcon, QuestionTypeSingleIcon, Radio, RadioGroup, type RadioGroupProps, type RadioProps, Rating, type RatingProps, RefreshIcon, ResendInviteIcon, RolesIcon, RufousAiIcon, RufousBirdIcon, RufousLauncherIcon, RufousLogoLoader, type RufousLogoLoaderProps, RufousTextContent, type RufousTextContentProps, RufousTextEditor, type MentionItemData as RufousTextEditorMentionItem, type RufousTextEditorProps, RufousThemeProvider, Select, type SelectProps, SidebarIcon, Skeleton, type SkeletonProps, Slide, type SlideProps, Slider, type SliderProps, Snackbar, type SnackbarProps, SoftSkillsIcon, type SortDirection, Stack, type StackProps, StandardButton, Step, StepButton, type StepButtonProps, StepContent, type StepContentProps, StepLabel, type StepLabelProps, type StepProps, Stepper, type StepperProps, SubmitButton, SubscribeIcon, SuspendUserIcon, Switch, type SwitchProps, type SxProp, Tab, TabPanel, type TabPanelProps, type TabProps, Tabs, type TabsProps, TechnicalSkillsIcon, TextField, type TextFieldProps, TickIcon, TimerIcon, ToggleButton, ToggleButtonGroup, type ToggleButtonGroupProps, type ToggleButtonProps, Tooltip, type TooltipProps, TrashIcon, Typography, type TypographyProps, UnArchivedIcon, UnsubscribeIcon, UploadIcon, UserAssignIcon, ViewIcon, WorkItemIcon, Zoom, type ZoomProps, transformLegacyTodos, useRufousTheme };
package/dist/main.d.ts CHANGED
@@ -1646,6 +1646,9 @@ interface RufousTextEditorProps {
1646
1646
  variant?: EditorVariant;
1647
1647
  buttons?: ToolbarButton[];
1648
1648
  hideButtons?: ToolbarButton[];
1649
+ disabled?: boolean;
1650
+ error?: boolean;
1651
+ helperText?: string;
1649
1652
  width?: number | string;
1650
1653
  height?: number | string;
1651
1654
  resizable?: boolean;
@@ -1662,6 +1665,42 @@ interface RufousTextContentProps {
1662
1665
  }
1663
1666
  declare const RufousTextContent: React__default.FC<RufousTextContentProps>;
1664
1667
 
1668
+ /**
1669
+ * Transforms legacy Jodit-style todo HTML into TipTap-compatible taskList HTML.
1670
+ *
1671
+ * Old Jodit format (two variants):
1672
+ *
1673
+ * Variant 1 (class-based):
1674
+ * <p class="todo-item">
1675
+ * <button class="todo-item-button" contenteditable="false">
1676
+ * <img src="...todo-blank.svg" alt="icon">
1677
+ * </button>
1678
+ * <span class="todo-item-text"> Todo text</span>
1679
+ * </p>
1680
+ *
1681
+ * Variant 2 (inline-styled):
1682
+ * <p class="todo-item" style="display: flex; ...">
1683
+ * <button class="todo-item-button" contenteditable="false" style="...">
1684
+ * <img src="...todo-blank.svg" alt="icon" style="...">
1685
+ * </button>
1686
+ * <span style="margin-top: -2px; flex-grow: 1;"> Todo text</span>
1687
+ * </p>
1688
+ *
1689
+ * New TipTap format:
1690
+ * <ul data-type="taskList">
1691
+ * <li data-status="todo">
1692
+ * <label><input type="checkbox"></label>
1693
+ * <div><p>Todo text</p></div>
1694
+ * </li>
1695
+ * </ul>
1696
+ */
1697
+ /**
1698
+ * Transforms legacy Jodit todo HTML to TipTap taskList format.
1699
+ * Non-todo content is passed through unchanged.
1700
+ * Consecutive todo items are grouped into a single <ul data-type="taskList">.
1701
+ */
1702
+ declare function transformLegacyTodos(html: string): string;
1703
+
1665
1704
  interface MentionItemData {
1666
1705
  id: string;
1667
1706
  name: string;
@@ -1669,4 +1708,4 @@ interface MentionItemData {
1669
1708
  shortName?: string;
1670
1709
  }
1671
1710
 
1672
- export { APP_THEMES, Accordion, AccordionDetails, type AccordionDetailsProps, type AccordionProps, AccordionSummary, type AccordionSummaryProps, type Action, ActivateUserIcon, AddButton, AddressLookup, ArchivedIcon, AssignGroupIcon, Autocomplete, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BaseDialog, Box, type BoxProps, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, CameraIcon, CancelButton, Card, CardActions, type CardActionsProps, CardContent, type CardContentProps, CardHeader, type CardHeaderProps, CardMedia, type CardMediaProps, type CardProps, Checkbox, type CheckboxProps, Chip, type ChipProps, CircularProgress, CircularProgressIcon, type CircularProgressIconProps, CloseIcon, Collapse, type CollapseProps, type Column, CopyIcon, DataGrid, type DataGridProps, DateField, type DateFieldProps, type DateFormatString, DateRangeField, type DateRangeFieldProps, type DateRangeValue, DifficultyAllIcon, DifficultyEasyIcon, DifficultyHardIcon, DifficultyMediumIcon, Divider, type DividerProps, DollarIcon, DownloadIcon, DownloadPdfIcon, Drawer, type DrawerProps, EditChatIcon, EditIcon, EngagementIcon, Fade, type FadeProps, FunctionIcon, Grid, type GridProps, Grow, type GrowProps, HelpOutlinedIcon, HierarchyIcon, IconButton, type IconButtonProps, ImageField, type ImageFieldProps, InactiveGroupIcon, IndustryIcon, InvoiceIcon, Link, type LinkProps, List, ListItem, ListItemButton, type ListItemButtonProps, ListItemIcon, type ListItemIconProps, type ListItemProps, ListItemText, type ListItemTextProps, type ListProps, ListSubheader, type ListSubheaderProps, LocationPinIcon, LogsIcon, Menu, MenuDivider, MenuItem, type MenuItemProps, MenuList, type MenuListProps, type MenuProps, MinExperienceIcon, NineDotMenuIcon, NotificationIcon, Paper, type PaperProps, PhoneField, type PhoneFieldProps, Popover, type PopoverProps, Popper, type PopperProps, ProjectIcon, QualificationsIcon, QuestionStatusAllIcon, QuestionStatusPrivateIcon, QuestionStatusPublicIcon, QuestionTypeAllIcon, QuestionTypeCodingIcon, QuestionTypeDescriptiveIcon, QuestionTypeMultipleIcon, QuestionTypeSingleIcon, Radio, RadioGroup, type RadioGroupProps, type RadioProps, Rating, type RatingProps, RefreshIcon, ResendInviteIcon, RolesIcon, RufousAiIcon, RufousBirdIcon, RufousLauncherIcon, RufousLogoLoader, type RufousLogoLoaderProps, RufousTextContent, type RufousTextContentProps, RufousTextEditor, type MentionItemData as RufousTextEditorMentionItem, type RufousTextEditorProps, RufousThemeProvider, Select, type SelectProps, SidebarIcon, Skeleton, type SkeletonProps, Slide, type SlideProps, Slider, type SliderProps, Snackbar, type SnackbarProps, SoftSkillsIcon, type SortDirection, Stack, type StackProps, StandardButton, Step, StepButton, type StepButtonProps, StepContent, type StepContentProps, StepLabel, type StepLabelProps, type StepProps, Stepper, type StepperProps, SubmitButton, SubscribeIcon, SuspendUserIcon, Switch, type SwitchProps, type SxProp, Tab, TabPanel, type TabPanelProps, type TabProps, Tabs, type TabsProps, TechnicalSkillsIcon, TextField, type TextFieldProps, TickIcon, TimerIcon, ToggleButton, ToggleButtonGroup, type ToggleButtonGroupProps, type ToggleButtonProps, Tooltip, type TooltipProps, TrashIcon, Typography, type TypographyProps, UnArchivedIcon, UnsubscribeIcon, UploadIcon, UserAssignIcon, ViewIcon, WorkItemIcon, Zoom, type ZoomProps, useRufousTheme };
1711
+ export { APP_THEMES, Accordion, AccordionDetails, type AccordionDetailsProps, type AccordionProps, AccordionSummary, type AccordionSummaryProps, type Action, ActivateUserIcon, AddButton, AddressLookup, ArchivedIcon, AssignGroupIcon, Autocomplete, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BaseDialog, Box, type BoxProps, Breadcrumbs, type BreadcrumbsProps, Button, type ButtonProps, CameraIcon, CancelButton, Card, CardActions, type CardActionsProps, CardContent, type CardContentProps, CardHeader, type CardHeaderProps, CardMedia, type CardMediaProps, type CardProps, Checkbox, type CheckboxProps, Chip, type ChipProps, CircularProgress, CircularProgressIcon, type CircularProgressIconProps, CloseIcon, Collapse, type CollapseProps, type Column, CopyIcon, DataGrid, type DataGridProps, DateField, type DateFieldProps, type DateFormatString, DateRangeField, type DateRangeFieldProps, type DateRangeValue, DifficultyAllIcon, DifficultyEasyIcon, DifficultyHardIcon, DifficultyMediumIcon, Divider, type DividerProps, DollarIcon, DownloadIcon, DownloadPdfIcon, Drawer, type DrawerProps, EditChatIcon, EditIcon, EngagementIcon, Fade, type FadeProps, FunctionIcon, Grid, type GridProps, Grow, type GrowProps, HelpOutlinedIcon, HierarchyIcon, IconButton, type IconButtonProps, ImageField, type ImageFieldProps, InactiveGroupIcon, IndustryIcon, InvoiceIcon, Link, type LinkProps, List, ListItem, ListItemButton, type ListItemButtonProps, ListItemIcon, type ListItemIconProps, type ListItemProps, ListItemText, type ListItemTextProps, type ListProps, ListSubheader, type ListSubheaderProps, LocationPinIcon, LogsIcon, Menu, MenuDivider, MenuItem, type MenuItemProps, MenuList, type MenuListProps, type MenuProps, MinExperienceIcon, NineDotMenuIcon, NotificationIcon, Paper, type PaperProps, PhoneField, type PhoneFieldProps, Popover, type PopoverProps, Popper, type PopperProps, ProjectIcon, QualificationsIcon, QuestionStatusAllIcon, QuestionStatusPrivateIcon, QuestionStatusPublicIcon, QuestionTypeAllIcon, QuestionTypeCodingIcon, QuestionTypeDescriptiveIcon, QuestionTypeMultipleIcon, QuestionTypeSingleIcon, Radio, RadioGroup, type RadioGroupProps, type RadioProps, Rating, type RatingProps, RefreshIcon, ResendInviteIcon, RolesIcon, RufousAiIcon, RufousBirdIcon, RufousLauncherIcon, RufousLogoLoader, type RufousLogoLoaderProps, RufousTextContent, type RufousTextContentProps, RufousTextEditor, type MentionItemData as RufousTextEditorMentionItem, type RufousTextEditorProps, RufousThemeProvider, Select, type SelectProps, SidebarIcon, Skeleton, type SkeletonProps, Slide, type SlideProps, Slider, type SliderProps, Snackbar, type SnackbarProps, SoftSkillsIcon, type SortDirection, Stack, type StackProps, StandardButton, Step, StepButton, type StepButtonProps, StepContent, type StepContentProps, StepLabel, type StepLabelProps, type StepProps, Stepper, type StepperProps, SubmitButton, SubscribeIcon, SuspendUserIcon, Switch, type SwitchProps, type SxProp, Tab, TabPanel, type TabPanelProps, type TabProps, Tabs, type TabsProps, TechnicalSkillsIcon, TextField, type TextFieldProps, TickIcon, TimerIcon, ToggleButton, ToggleButtonGroup, type ToggleButtonGroupProps, type ToggleButtonProps, Tooltip, type TooltipProps, TrashIcon, Typography, type TypographyProps, UnArchivedIcon, UnsubscribeIcon, UploadIcon, UserAssignIcon, ViewIcon, WorkItemIcon, Zoom, type ZoomProps, transformLegacyTodos, useRufousTheme };
package/dist/main.js CHANGED
@@ -9550,7 +9550,7 @@ var STATUS_COLORS = {
9550
9550
  };
9551
9551
  var CustomTaskItem = TaskItem.extend({
9552
9552
  addStorage() {
9553
- return { taskTodoEnabled: true };
9553
+ return { taskTodoEnabled: false };
9554
9554
  },
9555
9555
  addAttributes() {
9556
9556
  return {
@@ -9595,27 +9595,6 @@ var CustomTaskItem = TaskItem.extend({
9595
9595
  }
9596
9596
  return false;
9597
9597
  }
9598
- if (this.editor.storage.taskTodoEnabled === false) {
9599
- const afterContent2 = $from.parent.content.cut($from.parentOffset);
9600
- const schema2 = state.schema;
9601
- const tr2 = state.tr;
9602
- if (afterContent2.size > 0) {
9603
- tr2.delete($from.pos, $from.end());
9604
- }
9605
- let taskListDepth = -1;
9606
- for (let d = taskDepth - 1; d >= 0; d--) {
9607
- if ($from.node(d).type.name === "taskList") {
9608
- taskListDepth = d;
9609
- break;
9610
- }
9611
- }
9612
- const insertPos2 = taskListDepth !== -1 ? tr2.mapping.map($from.after(taskListDepth)) : tr2.mapping.map($from.after(taskDepth));
9613
- const newPara2 = afterContent2.size > 0 ? schema2.nodes.paragraph.create(null, afterContent2) : schema2.nodes.paragraph.create();
9614
- tr2.insert(insertPos2, newPara2);
9615
- tr2.setSelection(TextSelection.near(tr2.doc.resolve(insertPos2 + 1)));
9616
- this.editor.view.dispatch(tr2);
9617
- return true;
9618
- }
9619
9598
  const taskNode = $from.node(taskDepth);
9620
9599
  const status = taskNode.attrs.status || "todo";
9621
9600
  const schema = state.schema;
@@ -9718,7 +9697,7 @@ var CustomTaskItem = TaskItem.extend({
9718
9697
  addNodeView() {
9719
9698
  return ({ node, HTMLAttributes, getPos, editor }) => {
9720
9699
  const li = document.createElement("li");
9721
- li.style.cssText = "display: flex; align-items: flex-start; gap: 8px; margin: 4px 0; list-style: none; ";
9700
+ li.style.cssText = "display: flex; align-items: flex-start; gap: 6px; margin: 0; list-style: none; ";
9722
9701
  Object.entries(HTMLAttributes || {}).forEach(([key, val]) => {
9723
9702
  if (val != null && val !== false) {
9724
9703
  li.setAttribute(key, String(val));
@@ -9727,7 +9706,7 @@ var CustomTaskItem = TaskItem.extend({
9727
9706
  li.setAttribute("data-status", node.attrs.status || "todo");
9728
9707
  const label = document.createElement("label");
9729
9708
  label.contentEditable = "false";
9730
- label.style.cssText = "flex-shrink: 0; margin-top: 3px;";
9709
+ label.style.cssText = "flex-shrink: 0;";
9731
9710
  const checkbox = document.createElement("span");
9732
9711
  const updateCheckbox = (status) => {
9733
9712
  const colors = STATUS_COLORS[status] || STATUS_COLORS.todo;
@@ -9742,7 +9721,7 @@ var CustomTaskItem = TaskItem.extend({
9742
9721
  "border-radius: 3px",
9743
9722
  "cursor: pointer",
9744
9723
  "user-select: none",
9745
- "margin-top: 7px",
9724
+ "margin-top: 3px",
9746
9725
  "border: 2px solid " + colors.border,
9747
9726
  `background-image: url("${imageUrl}")`,
9748
9727
  "background-repeat: no-repeat",
@@ -9968,34 +9947,42 @@ var Dropdown = ({ trigger, children, className = "", keepOpen = false }) => {
9968
9947
  const menuRef = useRef26(null);
9969
9948
  useEffect23(() => {
9970
9949
  const handleClick = (e) => {
9971
- if (ref.current && !ref.current.contains(e.target)) setOpen(false);
9950
+ const target = e.target;
9951
+ if (ref.current && !ref.current.contains(target) && menuRef.current && !menuRef.current.contains(target)) {
9952
+ setOpen(false);
9953
+ }
9972
9954
  };
9973
9955
  document.addEventListener("mousedown", handleClick);
9974
9956
  return () => document.removeEventListener("mousedown", handleClick);
9975
9957
  }, []);
9976
9958
  useEffect23(() => {
9977
- if (!open || !menuRef.current) return;
9959
+ if (!open || !menuRef.current || !ref.current) return;
9978
9960
  const menu = menuRef.current;
9979
- menu.style.left = "0";
9980
- menu.style.right = "auto";
9981
- requestAnimationFrame(() => {
9982
- if (!menu) return;
9983
- const rect = menu.getBoundingClientRect();
9961
+ const trigger2 = ref.current;
9962
+ const position = () => {
9963
+ const triggerRect = trigger2.getBoundingClientRect();
9984
9964
  const vw = window.innerWidth;
9985
- const parentLeft = ref.current?.getBoundingClientRect().left || 0;
9986
- if (rect.right > vw - 8) {
9987
- menu.style.left = "auto";
9988
- menu.style.right = "0";
9989
- const newRect = menu.getBoundingClientRect();
9990
- if (newRect.left < 8) {
9991
- menu.style.left = `${8 - parentLeft}px`;
9992
- menu.style.right = "auto";
9965
+ const vh = window.innerHeight;
9966
+ let left = triggerRect.left;
9967
+ let top = triggerRect.bottom + 4;
9968
+ menu.style.left = `${left}px`;
9969
+ menu.style.top = `${top}px`;
9970
+ requestAnimationFrame(() => {
9971
+ if (!menu) return;
9972
+ const menuRect = menu.getBoundingClientRect();
9973
+ if (menuRect.right > vw - 8) {
9974
+ left = Math.max(8, triggerRect.right - menuRect.width);
9993
9975
  }
9994
- } else if (rect.left < 8) {
9995
- menu.style.left = `${8 - parentLeft}px`;
9996
- menu.style.right = "auto";
9997
- }
9998
- });
9976
+ if (left < 8) left = 8;
9977
+ if (menuRect.bottom > vh - 8) {
9978
+ top = triggerRect.top - menuRect.height - 4;
9979
+ if (top < 8) top = 8;
9980
+ }
9981
+ menu.style.left = `${left}px`;
9982
+ menu.style.top = `${top}px`;
9983
+ });
9984
+ };
9985
+ position();
9999
9986
  }, [open]);
10000
9987
  return /* @__PURE__ */ React112.createElement("div", { className: `dropdown ${className}`, ref }, /* @__PURE__ */ React112.createElement(
10001
9988
  "button",
@@ -10006,7 +9993,7 @@ var Dropdown = ({ trigger, children, className = "", keepOpen = false }) => {
10006
9993
  },
10007
9994
  trigger.label,
10008
9995
  /* @__PURE__ */ React112.createElement("span", { className: "dropdown-arrow" }, "\u25BE")
10009
- ), open && /* @__PURE__ */ React112.createElement("div", { ref: menuRef, className: "dropdown-menu", onClick: keepOpen ? void 0 : () => setOpen(false) }, typeof children === "function" ? children(() => setOpen(false)) : children));
9996
+ ), open && /* @__PURE__ */ React112.createElement("div", { ref: menuRef, className: "dropdown-menu dropdown-menu-fixed", onClick: keepOpen ? void 0 : () => setOpen(false) }, typeof children === "function" ? children(() => setOpen(false)) : children));
10010
9997
  };
10011
9998
  var InsertPanel = ({ editor, onClose, mode = "video" }) => {
10012
9999
  const [activeTab, setActiveTab] = useState30("link");
@@ -11424,6 +11411,79 @@ var VideoToolbar = ({ editor }) => {
11424
11411
  };
11425
11412
  var VideoToolbar_default = VideoToolbar;
11426
11413
 
11414
+ // lib/RufousTextEditor/legacyTodoTransform.ts
11415
+ var IMAGE_TO_STATUS = {
11416
+ "todo-blank.svg": "todo",
11417
+ "working.svg": "working",
11418
+ "blocked.svg": "blocked",
11419
+ "closed.svg": "resolved"
11420
+ };
11421
+ function getStatusFromImgSrc(src) {
11422
+ for (const [key, status] of Object.entries(IMAGE_TO_STATUS)) {
11423
+ if (src.includes(key)) return status;
11424
+ }
11425
+ return "todo";
11426
+ }
11427
+ function transformLegacyTodos(html) {
11428
+ if (!html || !html.includes("todo-item")) return html;
11429
+ const div = document.createElement("div");
11430
+ div.innerHTML = html;
11431
+ const children = Array.from(div.childNodes);
11432
+ const result = [];
11433
+ let currentTaskList = null;
11434
+ for (const child of children) {
11435
+ if (child instanceof HTMLElement && child.classList.contains("todo-item")) {
11436
+ const img = child.querySelector("button img, .todo-item-button img");
11437
+ const status = img ? getStatusFromImgSrc(img.src) : "todo";
11438
+ let text = "";
11439
+ const classSpan = child.querySelector(".todo-item-text");
11440
+ if (classSpan) {
11441
+ text = classSpan.textContent?.trim() || "";
11442
+ } else {
11443
+ const spans = child.querySelectorAll("span");
11444
+ for (const span of Array.from(spans)) {
11445
+ if (!span.closest("button") && !span.closest(".todo-item-button")) {
11446
+ text = span.textContent?.trim() || "";
11447
+ break;
11448
+ }
11449
+ }
11450
+ }
11451
+ const li = document.createElement("li");
11452
+ li.setAttribute("data-type", "taskItem");
11453
+ li.setAttribute("data-status", status);
11454
+ li.setAttribute("data-checked", "false");
11455
+ const label = document.createElement("label");
11456
+ const checkbox = document.createElement("input");
11457
+ checkbox.type = "checkbox";
11458
+ const labelSpan = document.createElement("span");
11459
+ label.appendChild(checkbox);
11460
+ label.appendChild(labelSpan);
11461
+ const contentDiv = document.createElement("div");
11462
+ const p = document.createElement("p");
11463
+ if (text) {
11464
+ p.textContent = text;
11465
+ }
11466
+ contentDiv.appendChild(p);
11467
+ li.appendChild(label);
11468
+ li.appendChild(contentDiv);
11469
+ if (!currentTaskList) {
11470
+ currentTaskList = document.createElement("ul");
11471
+ currentTaskList.setAttribute("data-type", "taskList");
11472
+ result.push(currentTaskList);
11473
+ }
11474
+ currentTaskList.appendChild(li);
11475
+ } else {
11476
+ currentTaskList = null;
11477
+ result.push(child);
11478
+ }
11479
+ }
11480
+ const output = document.createElement("div");
11481
+ for (const node of result) {
11482
+ output.appendChild(node);
11483
+ }
11484
+ return output.innerHTML;
11485
+ }
11486
+
11427
11487
  // lib/RufousTextEditor/RufousTextEditor.tsx
11428
11488
  var VARIANT_BUTTONS = {
11429
11489
  default: [
@@ -11499,6 +11559,9 @@ var RufousTextEditor = ({
11499
11559
  variant = "default",
11500
11560
  buttons,
11501
11561
  hideButtons,
11562
+ disabled = false,
11563
+ error = false,
11564
+ helperText,
11502
11565
  width,
11503
11566
  height,
11504
11567
  resizable = false,
@@ -11524,8 +11587,9 @@ var RufousTextEditor = ({
11524
11587
  useEffect26(() => {
11525
11588
  onBlurRef.current = onBlur;
11526
11589
  }, [onBlur]);
11590
+ const isEditable = editable && !disabled;
11527
11591
  const editor = useEditor({
11528
- editable,
11592
+ editable: isEditable,
11529
11593
  extensions: [
11530
11594
  StarterKit,
11531
11595
  Placeholder.configure({
@@ -11534,7 +11598,10 @@ var RufousTextEditor = ({
11534
11598
  Link2.configure({
11535
11599
  openOnClick: false,
11536
11600
  autolink: true,
11537
- HTMLAttributes: { class: "editor-link" }
11601
+ // Don't set default target/_blank — let the user choose via the link popup.
11602
+ // This prevents TipTap from adding target="_blank" to all links by default
11603
+ // and ensures checkbox state persists across editor remounts.
11604
+ HTMLAttributes: { class: "editor-link", target: null, rel: null }
11538
11605
  }),
11539
11606
  CustomImage.configure({
11540
11607
  inline: false,
@@ -11611,14 +11678,17 @@ var RufousTextEditor = ({
11611
11678
  return false;
11612
11679
  }
11613
11680
  },
11614
- content: initialContent || "",
11681
+ content: transformLegacyTodos(initialContent || ""),
11615
11682
  onUpdate: ({ editor: e }) => {
11616
11683
  onChangeRef.current?.(e.getHTML(), e.getJSON());
11617
11684
  }
11618
11685
  });
11686
+ const wrapperRef = useRef29(null);
11619
11687
  useEffect26(() => {
11620
11688
  if (!editor) return;
11621
- const handler = () => {
11689
+ const handler = ({ event }) => {
11690
+ const relatedTarget = event?.relatedTarget;
11691
+ if (relatedTarget && wrapperRef.current?.contains(relatedTarget)) return;
11622
11692
  onBlurRef.current?.(editor.getHTML(), editor.getJSON());
11623
11693
  };
11624
11694
  editor.on("blur", handler);
@@ -11774,7 +11844,8 @@ var RufousTextEditor = ({
11774
11844
  return /* @__PURE__ */ React115.createElement(
11775
11845
  "div",
11776
11846
  {
11777
- className: `rf-rte-wrapper editor-wrapper ${resizable ? "rf-rte-resizable" : ""} ${sxClass} ${className || ""}`,
11847
+ ref: wrapperRef,
11848
+ className: `rf-rte-wrapper editor-wrapper ${resizable ? "rf-rte-resizable" : ""} ${disabled ? "rf-rte-disabled" : ""} ${error ? "rf-rte-error" : ""} ${sxClass} ${className || ""}`,
11778
11849
  style: {
11779
11850
  ...style,
11780
11851
  ...width ? { width: typeof width === "number" ? `${width}px` : width } : {},
@@ -11949,17 +12020,19 @@ var RufousTextEditor = ({
11949
12020
  }
11950
12021
  ), "No follow"))), /* @__PURE__ */ React115.createElement("div", { className: "link-modal-footer" }, /* @__PURE__ */ React115.createElement("button", { className: "link-modal-btn-unlink", onClick: handleLinkRemove }, "Unlink"), /* @__PURE__ */ React115.createElement("button", { className: "link-modal-btn-apply", onClick: handleLinkSubmit }, "Update")))),
11951
12022
  document.body
11952
- ))
12023
+ )),
12024
+ helperText && /* @__PURE__ */ React115.createElement("div", { className: `rf-rte-helper-text ${error ? "rf-rte-helper-error" : ""}` }, helperText)
11953
12025
  );
11954
12026
  };
11955
12027
  var RufousTextContent = ({ content, className, style, sx }) => {
11956
12028
  const sxClass = useSx(sx);
12029
+ const transformedContent = useMemo4(() => transformLegacyTodos(content || ""), [content]);
11957
12030
  return /* @__PURE__ */ React115.createElement(
11958
12031
  "div",
11959
12032
  {
11960
12033
  className: `rf-rte-content ${sxClass} ${className || ""}`,
11961
12034
  style,
11962
- dangerouslySetInnerHTML: { __html: content }
12035
+ dangerouslySetInnerHTML: { __html: transformedContent }
11963
12036
  }
11964
12037
  );
11965
12038
  };
@@ -12100,5 +12173,6 @@ export {
12100
12173
  viewIcon_default as ViewIcon,
12101
12174
  workItemIcon_default as WorkItemIcon,
12102
12175
  Zoom,
12176
+ transformLegacyTodos,
12103
12177
  useRufousTheme
12104
12178
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rufous/ui",
3
3
  "private": false,
4
- "version": "0.2.58",
4
+ "version": "0.2.60",
5
5
  "type": "module",
6
6
  "description": "Experimental: A lightweight React UI component library (Beta)",
7
7
  "style": "./dist/main.css",