@thanh01.pmt/presentation-kit 0.2.8 → 0.2.10

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.
@@ -1,5 +1,5 @@
1
1
  import React$1 from 'react';
2
- import { B as BuiltInTheme, c as SlideTheme, e as SlideRenderResult, b as SlideLayoutData } from '../types-C1121XLt.cjs';
2
+ import { B as BuiltInTheme, c as SlideTheme, e as SlideRenderResult, b as SlideLayoutData, L as LayoutBoxConfig } from '../types-C1121XLt.cjs';
3
3
  export { M as MermaidBlock, f as SlideRenderAsyncResult, d as SlideRenderOptions, T as ThemeTokens } from '../types-C1121XLt.cjs';
4
4
  export { l as ClickCssOptions, k as ClickPostprocessResult, C as ClickPreprocessResult, d as defineTheme, i as extractMaxClicks, j as generateClickCss, h as postprocessClicks, p as preprocessClicks, r as renderSlide, a as renderSlideAsync, e as resolveTheme } from '../click-css-BP9oqy_1.cjs';
5
5
 
@@ -256,16 +256,24 @@ interface SlideDesignOverlayProps {
256
256
  layout: SlideLayoutData | null;
257
257
  /** Callback when layout changes in real-time */
258
258
  onLayoutChange: (layout: SlideLayoutData) => void;
259
- /** Callback when user explicitly clicks Save */
259
+ /** Callback when user explicitly clicks Save or finishes drag/resize */
260
260
  onSave?: (layout: SlideLayoutData) => void;
261
+ /** Callback when an item is deleted */
262
+ onDeleteBox?: (box: LayoutBoxConfig) => void;
263
+ /** Callback when a new box is added */
264
+ onAddBox?: (box: LayoutBoxConfig) => void;
261
265
  /** Callback when user cancels or closes design mode */
262
266
  onClose?: () => void;
263
267
  /** Whether design mode is active */
264
268
  isActive: boolean;
265
269
  /** Active theme (dark, light, navy) */
266
270
  theme?: BuiltInTheme;
271
+ /** External selected box ID control */
272
+ selectedBoxId?: string | null;
273
+ /** Callback when box selection changes */
274
+ onSelectBoxId?: (id: string | null) => void;
267
275
  }
268
- declare function SlideDesignOverlay({ slideIndex, layout, onLayoutChange, onSave, onClose, isActive, theme, }: SlideDesignOverlayProps): React$1.JSX.Element | null;
276
+ declare function SlideDesignOverlay({ slideIndex, layout, onLayoutChange, onSave, onDeleteBox, onAddBox, onClose, isActive, theme, selectedBoxId, onSelectBoxId, }: SlideDesignOverlayProps): React$1.JSX.Element | null;
269
277
 
270
278
  interface UseSlideStateOptions {
271
279
  markdown: string;
@@ -1,5 +1,5 @@
1
1
  import React$1 from 'react';
2
- import { B as BuiltInTheme, c as SlideTheme, e as SlideRenderResult, b as SlideLayoutData } from '../types-C1121XLt.js';
2
+ import { B as BuiltInTheme, c as SlideTheme, e as SlideRenderResult, b as SlideLayoutData, L as LayoutBoxConfig } from '../types-C1121XLt.js';
3
3
  export { M as MermaidBlock, f as SlideRenderAsyncResult, d as SlideRenderOptions, T as ThemeTokens } from '../types-C1121XLt.js';
4
4
  export { l as ClickCssOptions, k as ClickPostprocessResult, C as ClickPreprocessResult, d as defineTheme, i as extractMaxClicks, j as generateClickCss, h as postprocessClicks, p as preprocessClicks, r as renderSlide, a as renderSlideAsync, e as resolveTheme } from '../click-css-BbhqdtGr.js';
5
5
 
@@ -256,16 +256,24 @@ interface SlideDesignOverlayProps {
256
256
  layout: SlideLayoutData | null;
257
257
  /** Callback when layout changes in real-time */
258
258
  onLayoutChange: (layout: SlideLayoutData) => void;
259
- /** Callback when user explicitly clicks Save */
259
+ /** Callback when user explicitly clicks Save or finishes drag/resize */
260
260
  onSave?: (layout: SlideLayoutData) => void;
261
+ /** Callback when an item is deleted */
262
+ onDeleteBox?: (box: LayoutBoxConfig) => void;
263
+ /** Callback when a new box is added */
264
+ onAddBox?: (box: LayoutBoxConfig) => void;
261
265
  /** Callback when user cancels or closes design mode */
262
266
  onClose?: () => void;
263
267
  /** Whether design mode is active */
264
268
  isActive: boolean;
265
269
  /** Active theme (dark, light, navy) */
266
270
  theme?: BuiltInTheme;
271
+ /** External selected box ID control */
272
+ selectedBoxId?: string | null;
273
+ /** Callback when box selection changes */
274
+ onSelectBoxId?: (id: string | null) => void;
267
275
  }
268
- declare function SlideDesignOverlay({ slideIndex, layout, onLayoutChange, onSave, onClose, isActive, theme, }: SlideDesignOverlayProps): React$1.JSX.Element | null;
276
+ declare function SlideDesignOverlay({ slideIndex, layout, onLayoutChange, onSave, onDeleteBox, onAddBox, onClose, isActive, theme, selectedBoxId, onSelectBoxId, }: SlideDesignOverlayProps): React$1.JSX.Element | null;
269
277
 
270
278
  interface UseSlideStateOptions {
271
279
  markdown: string;
@@ -1,6 +1,6 @@
1
- import { renderSlide, renderMermaidDiagrams } from '../chunk-JOW4QWKF.js';
2
- export { defineTheme, extractMaxClicks, generateClickCss, postprocessClicks, preprocessClicks, renderSlide, renderSlideAsync, resolveTheme } from '../chunk-JOW4QWKF.js';
3
- import { SLIDE_LAYOUT_PRESETS, updateSlideLayoutInMarkdown } from '../chunk-26ZVUVXF.js';
1
+ import { renderSlide, renderMermaidDiagrams } from '../chunk-NYLDRVHZ.js';
2
+ export { defineTheme, extractMaxClicks, generateClickCss, postprocessClicks, preprocessClicks, renderSlide, renderSlideAsync, resolveTheme } from '../chunk-NYLDRVHZ.js';
3
+ import { SLIDE_LAYOUT_PRESETS, updateSlideLayoutInMarkdown } from '../chunk-V6X7D44H.js';
4
4
  import { useState, useRef, useMemo, useEffect, useCallback } from 'react';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
 
@@ -584,23 +584,38 @@ function SlideDesignOverlay({
584
584
  layout,
585
585
  onLayoutChange,
586
586
  onSave,
587
+ onDeleteBox,
588
+ onAddBox,
587
589
  onClose,
588
590
  isActive,
589
- theme = "dark"
591
+ theme = "dark",
592
+ selectedBoxId,
593
+ onSelectBoxId
590
594
  }) {
591
595
  if (!isActive) return null;
592
596
  const [boxes, setBoxes] = useState(layout?.boxes ?? []);
593
- const [selectedId, setSelectedId] = useState(null);
597
+ const [selectedId, setSelectedIdState] = useState(selectedBoxId ?? null);
594
598
  const [editingId, setEditingId] = useState(null);
595
599
  const containerRef = useRef(null);
600
+ const setSelectedId = useCallback((id) => {
601
+ setSelectedIdState(id);
602
+ if (onSelectBoxId) onSelectBoxId(id);
603
+ }, [onSelectBoxId]);
604
+ useEffect(() => {
605
+ if (selectedBoxId !== void 0) {
606
+ setSelectedIdState(selectedBoxId);
607
+ }
608
+ }, [selectedBoxId]);
596
609
  useEffect(() => {
597
610
  setBoxes(layout?.boxes ?? []);
598
611
  }, [layout, slideIndex]);
599
612
  const selectedBox = boxes.find((b) => b.id === selectedId) ?? null;
613
+ const hasMovedRef = useRef(false);
600
614
  const [dragging, setDragging] = useState(null);
601
615
  const handleMouseDown = (e, box, type) => {
602
616
  e.preventDefault();
603
617
  e.stopPropagation();
618
+ hasMovedRef.current = false;
604
619
  setSelectedId(box.id);
605
620
  setDragging({
606
621
  type,
@@ -617,6 +632,9 @@ function SlideDesignOverlay({
617
632
  const scaleY = 720 / rect.height;
618
633
  const dx = (e.clientX - dragging.startX) * scaleX;
619
634
  const dy = (e.clientY - dragging.startY) * scaleY;
635
+ if (Math.abs(dx) > 1 || Math.abs(dy) > 1) {
636
+ hasMovedRef.current = true;
637
+ }
620
638
  const [origX, origY, origW, origH] = dragging.startPos;
621
639
  setBoxes((prev) => {
622
640
  const updated = prev.map((b) => {
@@ -628,30 +646,38 @@ function SlideDesignOverlay({
628
646
  newY = Math.round(Math.max(0, Math.min(720 - origH, origY + dy)));
629
647
  break;
630
648
  case "se":
631
- newW = Math.round(Math.max(100, Math.min(1280 - origX, origW + dx)));
632
- newH = Math.round(Math.max(40, Math.min(720 - origY, origH + dy)));
649
+ newW = Math.round(Math.max(60, Math.min(1280 - origX, origW + dx)));
650
+ newH = Math.round(Math.max(30, Math.min(720 - origY, origH + dy)));
633
651
  break;
634
652
  case "e":
635
- newW = Math.round(Math.max(100, Math.min(1280 - origX, origW + dx)));
653
+ newW = Math.round(Math.max(60, Math.min(1280 - origX, origW + dx)));
636
654
  break;
637
655
  case "s":
638
- newH = Math.round(Math.max(40, Math.min(720 - origY, origH + dy)));
656
+ newH = Math.round(Math.max(30, Math.min(720 - origY, origH + dy)));
639
657
  break;
640
658
  case "sw":
641
- newW = Math.round(Math.max(100, origW - dx));
642
- newX = Math.round(Math.min(origX + origW - 100, origX + dx));
643
- newH = Math.round(Math.max(40, origH + dy));
659
+ newW = Math.round(Math.max(60, origW - dx));
660
+ newX = Math.round(Math.min(origX + origW - 60, origX + dx));
661
+ newH = Math.round(Math.max(30, origH + dy));
644
662
  break;
645
663
  case "ne":
646
- newW = Math.round(Math.max(100, origW + dx));
647
- newH = Math.round(Math.max(40, origH - dy));
648
- newY = Math.round(Math.min(origY + origH - 40, origY + dy));
664
+ newW = Math.round(Math.max(60, origW + dx));
665
+ newH = Math.round(Math.max(30, origH - dy));
666
+ newY = Math.round(Math.min(origY + origH - 30, origY + dy));
649
667
  break;
650
668
  case "nw":
651
- newW = Math.round(Math.max(100, origW - dx));
652
- newX = Math.round(Math.min(origX + origW - 100, origX + dx));
653
- newH = Math.round(Math.max(40, origH - dy));
654
- newY = Math.round(Math.min(origY + origH - 40, origY + dy));
669
+ newW = Math.round(Math.max(60, origW - dx));
670
+ newX = Math.round(Math.min(origX + origW - 60, origX + dx));
671
+ newH = Math.round(Math.max(30, origH - dy));
672
+ newY = Math.round(Math.min(origY + origH - 30, origY + dy));
673
+ break;
674
+ case "n":
675
+ newH = Math.round(Math.max(30, origH - dy));
676
+ newY = Math.round(Math.min(origY + origH - 30, origY + dy));
677
+ break;
678
+ case "w":
679
+ newW = Math.round(Math.max(60, origW - dx));
680
+ newX = Math.round(Math.min(origX + origW - 60, origX + dx));
655
681
  break;
656
682
  }
657
683
  return { ...b, pos: [newX, newY, newW, newH] };
@@ -661,8 +687,12 @@ function SlideDesignOverlay({
661
687
  });
662
688
  }, [dragging, onLayoutChange]);
663
689
  const handleMouseUp = useCallback(() => {
690
+ if (dragging && hasMovedRef.current && onSave) {
691
+ onSave({ boxes });
692
+ }
693
+ hasMovedRef.current = false;
664
694
  setDragging(null);
665
- }, []);
695
+ }, [dragging, boxes, onSave]);
666
696
  useEffect(() => {
667
697
  if (dragging) {
668
698
  window.addEventListener("mousemove", handleMouseMove);
@@ -673,6 +703,33 @@ function SlideDesignOverlay({
673
703
  };
674
704
  }
675
705
  }, [dragging, handleMouseMove, handleMouseUp]);
706
+ const removeSelectedBox = useCallback(() => {
707
+ if (!selectedId) return;
708
+ const boxToRemove = boxes.find((b) => b.id === selectedId);
709
+ setBoxes((prev) => {
710
+ const updated = prev.filter((b) => b.id !== selectedId);
711
+ onLayoutChange({ boxes: updated });
712
+ if (onSave) onSave({ boxes: updated });
713
+ return updated;
714
+ });
715
+ setSelectedId(null);
716
+ if (boxToRemove && onDeleteBox) {
717
+ onDeleteBox(boxToRemove);
718
+ }
719
+ }, [selectedId, boxes, onLayoutChange, onSave, onDeleteBox, setSelectedId]);
720
+ useEffect(() => {
721
+ const handleKeyDown = (e) => {
722
+ if (editingId) return;
723
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
724
+ if ((e.key === "Delete" || e.key === "Backspace") && selectedId) {
725
+ e.preventDefault();
726
+ e.stopPropagation();
727
+ removeSelectedBox();
728
+ }
729
+ };
730
+ window.addEventListener("keydown", handleKeyDown);
731
+ return () => window.removeEventListener("keydown", handleKeyDown);
732
+ }, [selectedId, editingId, removeSelectedBox]);
676
733
  const addNewBox = (targetTag) => {
677
734
  const isH1 = targetTag === "h1";
678
735
  const newId = `box-${Date.now().toString(36)}`;
@@ -690,6 +747,9 @@ function SlideDesignOverlay({
690
747
  setSelectedId(newId);
691
748
  setEditingId(newId);
692
749
  onLayoutChange({ boxes: updated });
750
+ if (onAddBox) {
751
+ onAddBox(newBox);
752
+ }
693
753
  };
694
754
  const updateSelectedBox = (partial) => {
695
755
  if (!selectedId) return;
@@ -699,15 +759,6 @@ function SlideDesignOverlay({
699
759
  return updated;
700
760
  });
701
761
  };
702
- const removeSelectedBox = () => {
703
- if (!selectedId) return;
704
- setBoxes((prev) => {
705
- const updated = prev.filter((b) => b.id !== selectedId);
706
- onLayoutChange({ boxes: updated });
707
- return updated;
708
- });
709
- setSelectedId(null);
710
- };
711
762
  const isLight = theme === "light";
712
763
  const isNavy = theme === "navy";
713
764
  const toolbarBg = isLight ? "rgba(255, 255, 255, 0.96)" : isNavy ? "rgba(11, 19, 43, 0.95)" : "rgba(24, 25, 32, 0.95)";
@@ -868,8 +919,9 @@ function SlideDesignOverlay({
868
919
  style: {
869
920
  width: "100%",
870
921
  height: "100%",
871
- background: "transparent",
872
- border: "none",
922
+ backgroundColor: isLight ? "#ffffff" : isNavy ? "#0b132b" : "#181920",
923
+ border: isLight ? "1px solid #0284c7" : "1px solid #38bdf8",
924
+ borderRadius: "4px",
873
925
  outline: "none",
874
926
  resize: "none",
875
927
  fontSize: box.fontSize ?? (box.target === "h1" ? "2.2rem" : "1.1rem"),
@@ -878,8 +930,9 @@ function SlideDesignOverlay({
878
930
  fontFamily: "Inter, sans-serif",
879
931
  fontWeight: box.target === "h1" ? 700 : 400,
880
932
  lineHeight: 1.25,
881
- padding: 0,
882
- margin: 0
933
+ padding: "4px 6px",
934
+ margin: 0,
935
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)"
883
936
  }
884
937
  }
885
938
  ) : /* @__PURE__ */ jsx(
@@ -893,20 +946,9 @@ function SlideDesignOverlay({
893
946
  style: {
894
947
  width: "100%",
895
948
  height: "100%",
896
- fontSize: box.fontSize ?? (box.target === "h1" ? "2.2rem" : "1.1rem"),
897
- color: box.color ?? (isLight ? "#0f172a" : "#f8fafc"),
898
- textAlign: box.textAlign ?? "left",
899
- fontFamily: "Inter, sans-serif",
900
- fontWeight: box.target === "h1" ? 700 : 400,
901
- lineHeight: 1.25,
902
- wordBreak: "break-word",
903
- userSelect: "none",
904
- cursor: "pointer",
905
- display: "flex",
906
- alignItems: "center"
949
+ cursor: "pointer"
907
950
  },
908
- title: "Click \u0111\xFAp \u0111\u1EC3 s\u1EEDa ch\u1EEF",
909
- children: box.content ? /* @__PURE__ */ jsx("span", { children: box.content }) : /* @__PURE__ */ jsx("span", { style: { opacity: 0.45, fontStyle: "italic", fontSize: "12px" }, children: box.target === "h1" ? "Ti\xEAu \u0111\u1EC1 (Click \u0111\xFAp \u0111\u1EC3 s\u1EEDa)..." : "N\u1ED9i dung (Click \u0111\xFAp \u0111\u1EC3 s\u1EEDa)..." })
951
+ title: "Click \u0111\xFAp \u0111\u1EC3 s\u1EEDa ch\u1EEF"
910
952
  }
911
953
  )
912
954
  }
@@ -939,12 +981,14 @@ function SlideDesignOverlay({
939
981
  }
940
982
  ),
941
983
  isSelected && /* @__PURE__ */ jsxs(Fragment, { children: [
942
- /* @__PURE__ */ jsx("div", { style: handleStyle("se", isLight), onMouseDown: (e) => handleMouseDown(e, box, "se") }),
943
- /* @__PURE__ */ jsx("div", { style: handleStyle("e", isLight), onMouseDown: (e) => handleMouseDown(e, box, "e") }),
944
- /* @__PURE__ */ jsx("div", { style: handleStyle("s", isLight), onMouseDown: (e) => handleMouseDown(e, box, "s") }),
945
- /* @__PURE__ */ jsx("div", { style: handleStyle("sw", isLight), onMouseDown: (e) => handleMouseDown(e, box, "sw") }),
946
- /* @__PURE__ */ jsx("div", { style: handleStyle("ne", isLight), onMouseDown: (e) => handleMouseDown(e, box, "ne") }),
947
- /* @__PURE__ */ jsx("div", { style: handleStyle("nw", isLight), onMouseDown: (e) => handleMouseDown(e, box, "nw") })
984
+ /* @__PURE__ */ jsx("div", { style: handleStyle("nw", isLight), onMouseDown: (e) => handleMouseDown(e, box, "nw"), title: "K\xE9o g\xF3c tr\xEAn-tr\xE1i" }),
985
+ /* @__PURE__ */ jsx("div", { style: handleStyle("n", isLight), onMouseDown: (e) => handleMouseDown(e, box, "n"), title: "K\xE9o c\u1EA1nh tr\xEAn" }),
986
+ /* @__PURE__ */ jsx("div", { style: handleStyle("ne", isLight), onMouseDown: (e) => handleMouseDown(e, box, "ne"), title: "K\xE9o g\xF3c tr\xEAn-ph\u1EA3i" }),
987
+ /* @__PURE__ */ jsx("div", { style: handleStyle("e", isLight), onMouseDown: (e) => handleMouseDown(e, box, "e"), title: "K\xE9o c\u1EA1nh ph\u1EA3i (Ch\u1EC9nh \u0111\u1ED9 r\u1ED9ng / wrap text)" }),
988
+ /* @__PURE__ */ jsx("div", { style: handleStyle("se", isLight), onMouseDown: (e) => handleMouseDown(e, box, "se"), title: "K\xE9o g\xF3c d\u01B0\u1EDBi-ph\u1EA3i (Ch\u1EC9nh k\xEDch th\u01B0\u1EDBc / wrap text)" }),
989
+ /* @__PURE__ */ jsx("div", { style: handleStyle("s", isLight), onMouseDown: (e) => handleMouseDown(e, box, "s"), title: "K\xE9o c\u1EA1nh d\u01B0\u1EDBi" }),
990
+ /* @__PURE__ */ jsx("div", { style: handleStyle("sw", isLight), onMouseDown: (e) => handleMouseDown(e, box, "sw"), title: "K\xE9o g\xF3c d\u01B0\u1EDBi-tr\xE1i" }),
991
+ /* @__PURE__ */ jsx("div", { style: handleStyle("w", isLight), onMouseDown: (e) => handleMouseDown(e, box, "w"), title: "K\xE9o c\u1EA1nh tr\xE1i (Ch\u1EC9nh \u0111\u1ED9 r\u1ED9ng / wrap text)" })
948
992
  ] })
949
993
  ]
950
994
  },
@@ -1028,7 +1072,7 @@ function SlideDesignOverlay({
1028
1072
  /* @__PURE__ */ jsx("input", { type: "color", value: selectedBox.color ?? (isLight ? "#0f172a" : "#38bdf8"), onChange: (e) => updateSelectedBox({ color: e.target.value }), style: { width: "22px", height: "22px", padding: "0", border: "none", borderRadius: "4px", cursor: "pointer", backgroundColor: "transparent" } })
1029
1073
  ] }),
1030
1074
  /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: "2px", whiteSpace: "nowrap" }, children: ["left", "center", "right"].map((align) => /* @__PURE__ */ jsx("button", { type: "button", onClick: () => updateSelectedBox({ textAlign: align }), style: { ...btnStyle, backgroundColor: selectedBox.textAlign === align ? "#0284c7" : isLight ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.05)", color: selectedBox.textAlign === align ? "#ffffff" : isLight ? "#0f172a" : "#e2e8f0", textTransform: "capitalize", fontSize: "11px", padding: "2px 6px", height: "22px" }, children: align }, align)) }),
1031
- /* @__PURE__ */ jsx("button", { type: "button", onClick: removeSelectedBox, style: { ...btnStyle, backgroundColor: isLight ? "rgba(239, 68, 68, 0.1)" : "rgba(239, 68, 68, 0.2)", color: isLight ? "#dc2626" : "#f87171", border: isLight ? "1px solid rgba(239, 68, 68, 0.3)" : "1px solid rgba(239, 68, 68, 0.4)", fontWeight: 500, height: "22px", padding: "0 8px" }, title: "X\xF3a \u0111\u1ECBnh v\u1ECB t\u1EF1 do c\u1EE7a khung n\xE0y", children: "X\xF3a v\u1ECB tr\xED" })
1075
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: removeSelectedBox, style: { ...btnStyle, backgroundColor: isLight ? "rgba(239, 68, 68, 0.1)" : "rgba(239, 68, 68, 0.2)", color: isLight ? "#dc2626" : "#f87171", border: isLight ? "1px solid rgba(239, 68, 68, 0.3)" : "1px solid rgba(239, 68, 68, 0.4)", fontWeight: 500, height: "22px", padding: "0 8px" }, title: "X\xF3a ph\u1EA7n t\u1EED (ho\u1EB7c nh\u1EA5n Delete tr\xEAn b\xE0n ph\xEDm)", children: "X\xF3a ph\u1EA7n t\u1EED (Del)" })
1032
1076
  ]
1033
1077
  }
1034
1078
  )
@@ -1061,6 +1105,10 @@ function handleStyle(pos, isLight = false) {
1061
1105
  return { ...base, right: `-${size / 2}px`, top: `-${size / 2}px`, cursor: "ne-resize" };
1062
1106
  case "nw":
1063
1107
  return { ...base, left: `-${size / 2}px`, top: `-${size / 2}px`, cursor: "nw-resize" };
1108
+ case "n":
1109
+ return { ...base, top: `-${size / 2}px`, left: `calc(50% - ${size / 2}px)`, cursor: "n-resize" };
1110
+ case "w":
1111
+ return { ...base, left: `-${size / 2}px`, top: `calc(50% - ${size / 2}px)`, cursor: "w-resize" };
1064
1112
  }
1065
1113
  }
1066
1114
  var DEFAULT_THEME_OPTIONS = [