gantt-task-react-v 1.1.16 → 1.1.18

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.
@@ -18001,9 +18001,9 @@ const useContextMenu = (wrapperRef, scrollToTask) => {
18001
18001
  handleOpenContextMenu
18002
18002
  };
18003
18003
  };
18004
- const menuOption = "_menuOption_1frh4_3";
18005
- const icon = "_icon_1frh4_79";
18006
- const label = "_label_1frh4_87";
18004
+ const menuOption = "_menuOption_1c3e3_3";
18005
+ const icon = "_icon_1c3e3_81";
18006
+ const label = "_label_1c3e3_89";
18007
18007
  const styles$1 = {
18008
18008
  menuOption,
18009
18009
  icon,
@@ -18019,46 +18019,179 @@ function MenuOption(props) {
18019
18019
  },
18020
18020
  handleAction,
18021
18021
  option,
18022
- option: { icon: icon2, label: label2, disabled }
18022
+ option: { icon: icon2, label: label2, disabled, children }
18023
18023
  } = props;
18024
+ const [hovered, setHovered] = useState(false);
18025
+ const [coords, setCoords] = useState(null);
18026
+ const btnRef = useRef(null);
18027
+ const nestedRef = useRef(null);
18028
+ const closeTimeoutRef = useRef(null);
18024
18029
  const onClick = useCallback(
18025
18030
  (e) => {
18026
18031
  e.preventDefault();
18027
18032
  if (disabled) {
18028
18033
  return;
18029
18034
  }
18035
+ if (children && children.length > 0) {
18036
+ setHovered(true);
18037
+ return;
18038
+ }
18030
18039
  handleAction(option);
18031
18040
  onClose == null ? void 0 : onClose();
18032
18041
  },
18033
- [onClose, handleAction, option, disabled]
18042
+ [onClose, handleAction, option, disabled, children]
18034
18043
  );
18044
+ useEffect(() => {
18045
+ if (!hovered || !nestedRef.current)
18046
+ return;
18047
+ const cssVars = [
18048
+ "--gantt-context-menu-bg-color",
18049
+ "--gantt-context-menu-box-shadow",
18050
+ "--gantt-shape-border-radius",
18051
+ "--gantt-font-family",
18052
+ "--gantt-font-size",
18053
+ "--gantt-context-menu-empty-color"
18054
+ ];
18055
+ const sourceEl = btnRef.current || (typeof document !== "undefined" ? document.body : null);
18056
+ if (!sourceEl)
18057
+ return;
18058
+ const cs = getComputedStyle(sourceEl);
18059
+ cssVars.forEach((v) => {
18060
+ const val = cs.getPropertyValue(v).trim();
18061
+ if (val) {
18062
+ nestedRef.current.style.setProperty(v, val);
18063
+ }
18064
+ });
18065
+ }, [hovered, coords]);
18035
18066
  return /* @__PURE__ */ jsxs(
18036
- "button",
18067
+ "div",
18037
18068
  {
18038
- className: styles$1.menuOption,
18039
- "aria-disabled": disabled,
18040
- disabled,
18041
- style: {
18042
- height: contextMenuOptionHeight,
18043
- paddingLeft: contextMenuSidePadding,
18044
- paddingRight: contextMenuSidePadding,
18045
- color: "var(--gantt-context-menu-text-color)"
18069
+ onMouseEnter: () => {
18070
+ var _a, _b;
18071
+ if (closeTimeoutRef.current) {
18072
+ window.clearTimeout(closeTimeoutRef.current);
18073
+ closeTimeoutRef.current = null;
18074
+ }
18075
+ setHovered(true);
18076
+ const rect = (_a = btnRef.current) == null ? void 0 : _a.getBoundingClientRect();
18077
+ const parentMenu = (_b = btnRef.current) == null ? void 0 : _b.closest(
18078
+ '[role="menu"]'
18079
+ );
18080
+ const parentRect = (parentMenu == null ? void 0 : parentMenu.getBoundingClientRect()) ?? null;
18081
+ if (rect) {
18082
+ setCoords({
18083
+ left: parentRect ? parentRect.right : rect.right,
18084
+ top: rect.top,
18085
+ parentWidth: parentRect ? Math.round(parentRect.width) : void 0
18086
+ });
18087
+ }
18046
18088
  },
18047
- onClick,
18089
+ onMouseLeave: () => {
18090
+ closeTimeoutRef.current = window.setTimeout(() => {
18091
+ setHovered(false);
18092
+ setCoords(null);
18093
+ closeTimeoutRef.current = null;
18094
+ }, 200);
18095
+ },
18096
+ style: { position: "relative", width: "100%" },
18048
18097
  children: [
18049
- /* @__PURE__ */ jsx(
18050
- "div",
18098
+ /* @__PURE__ */ jsxs(
18099
+ "button",
18051
18100
  {
18052
- className: styles$1.icon,
18101
+ className: styles$1.menuOption,
18102
+ "aria-disabled": disabled,
18103
+ disabled,
18053
18104
  style: {
18054
- width: contextMenuIconWidth,
18055
- color: "var(--gantt-context-menu-text-color)",
18056
- opacity: disabled ? 0.3 : 0.5
18105
+ height: contextMenuOptionHeight,
18106
+ paddingLeft: contextMenuSidePadding,
18107
+ paddingRight: contextMenuSidePadding,
18108
+ color: "var(--gantt-context-menu-text-color)"
18057
18109
  },
18058
- children: icon2
18110
+ onClick,
18111
+ ref: btnRef,
18112
+ children: [
18113
+ /* @__PURE__ */ jsx(
18114
+ "div",
18115
+ {
18116
+ className: styles$1.icon,
18117
+ style: {
18118
+ width: contextMenuIconWidth,
18119
+ color: "var(--gantt-context-menu-text-color)",
18120
+ opacity: disabled ? 0.3 : 0.5
18121
+ },
18122
+ children: icon2
18123
+ }
18124
+ ),
18125
+ /* @__PURE__ */ jsx("div", { className: styles$1.label, children: label2 }),
18126
+ children && children.length > 0 && /* @__PURE__ */ jsx(
18127
+ "div",
18128
+ {
18129
+ style: {
18130
+ marginLeft: 8,
18131
+ opacity: 0.6,
18132
+ pointerEvents: "none"
18133
+ },
18134
+ children: /* @__PURE__ */ jsx("span", { style: { fontSize: "14px" }, children: "▶" })
18135
+ }
18136
+ )
18137
+ ]
18059
18138
  }
18060
18139
  ),
18061
- /* @__PURE__ */ jsx("div", { className: styles$1.label, children: label2 })
18140
+ children && children.length > 0 && hovered && typeof document !== "undefined" && coords && createPortal(
18141
+ /* @__PURE__ */ jsx(
18142
+ "div",
18143
+ {
18144
+ role: "menu",
18145
+ ref: nestedRef,
18146
+ onMouseEnter: () => {
18147
+ if (closeTimeoutRef.current) {
18148
+ window.clearTimeout(closeTimeoutRef.current);
18149
+ closeTimeoutRef.current = null;
18150
+ }
18151
+ setHovered(true);
18152
+ },
18153
+ onMouseLeave: () => {
18154
+ closeTimeoutRef.current = window.setTimeout(() => {
18155
+ setHovered(false);
18156
+ setCoords(null);
18157
+ closeTimeoutRef.current = null;
18158
+ }, 200);
18159
+ },
18160
+ style: {
18161
+ position: "fixed",
18162
+ // place submenu just to the right of parent menu
18163
+ left: (coords.left ?? 0) + 8,
18164
+ top: coords.top,
18165
+ // match the parent menu container look & behavior
18166
+ backgroundColor: "var(--gantt-context-menu-bg-color)",
18167
+ boxShadow: "var(--gantt-context-menu-box-shadow)",
18168
+ borderRadius: "var(--gantt-shape-border-radius)",
18169
+ fontFamily: "var(--gantt-font-family)",
18170
+ display: "flex",
18171
+ flexDirection: "column",
18172
+ width: "max-content",
18173
+ minWidth: coords && coords.parentWidth ? coords.parentWidth : 140,
18174
+ maxHeight: "calc(100vh - 24px)",
18175
+ overflowY: "auto",
18176
+ overflowX: "hidden",
18177
+ gap: 6,
18178
+ zIndex: 1e4,
18179
+ pointerEvents: "auto"
18180
+ },
18181
+ children: children.map((child, index2) => /* @__PURE__ */ jsx(
18182
+ MenuOption,
18183
+ {
18184
+ distances: props.distances,
18185
+ handleAction,
18186
+ option: child,
18187
+ onClose
18188
+ },
18189
+ index2
18190
+ ))
18191
+ }
18192
+ ),
18193
+ document.body
18194
+ )
18062
18195
  ]
18063
18196
  }
18064
18197
  );
@@ -18018,9 +18018,9 @@
18018
18018
  handleOpenContextMenu
18019
18019
  };
18020
18020
  };
18021
- const menuOption = "_menuOption_1frh4_3";
18022
- const icon = "_icon_1frh4_79";
18023
- const label = "_label_1frh4_87";
18021
+ const menuOption = "_menuOption_1c3e3_3";
18022
+ const icon = "_icon_1c3e3_81";
18023
+ const label = "_label_1c3e3_89";
18024
18024
  const styles$1 = {
18025
18025
  menuOption,
18026
18026
  icon,
@@ -18036,46 +18036,179 @@
18036
18036
  },
18037
18037
  handleAction,
18038
18038
  option,
18039
- option: { icon: icon2, label: label2, disabled }
18039
+ option: { icon: icon2, label: label2, disabled, children }
18040
18040
  } = props;
18041
+ const [hovered, setHovered] = React.useState(false);
18042
+ const [coords, setCoords] = React.useState(null);
18043
+ const btnRef = React.useRef(null);
18044
+ const nestedRef = React.useRef(null);
18045
+ const closeTimeoutRef = React.useRef(null);
18041
18046
  const onClick = React.useCallback(
18042
18047
  (e) => {
18043
18048
  e.preventDefault();
18044
18049
  if (disabled) {
18045
18050
  return;
18046
18051
  }
18052
+ if (children && children.length > 0) {
18053
+ setHovered(true);
18054
+ return;
18055
+ }
18047
18056
  handleAction(option);
18048
18057
  onClose == null ? void 0 : onClose();
18049
18058
  },
18050
- [onClose, handleAction, option, disabled]
18059
+ [onClose, handleAction, option, disabled, children]
18051
18060
  );
18061
+ React.useEffect(() => {
18062
+ if (!hovered || !nestedRef.current)
18063
+ return;
18064
+ const cssVars = [
18065
+ "--gantt-context-menu-bg-color",
18066
+ "--gantt-context-menu-box-shadow",
18067
+ "--gantt-shape-border-radius",
18068
+ "--gantt-font-family",
18069
+ "--gantt-font-size",
18070
+ "--gantt-context-menu-empty-color"
18071
+ ];
18072
+ const sourceEl = btnRef.current || (typeof document !== "undefined" ? document.body : null);
18073
+ if (!sourceEl)
18074
+ return;
18075
+ const cs = getComputedStyle(sourceEl);
18076
+ cssVars.forEach((v) => {
18077
+ const val = cs.getPropertyValue(v).trim();
18078
+ if (val) {
18079
+ nestedRef.current.style.setProperty(v, val);
18080
+ }
18081
+ });
18082
+ }, [hovered, coords]);
18052
18083
  return /* @__PURE__ */ jsxRuntime.jsxs(
18053
- "button",
18084
+ "div",
18054
18085
  {
18055
- className: styles$1.menuOption,
18056
- "aria-disabled": disabled,
18057
- disabled,
18058
- style: {
18059
- height: contextMenuOptionHeight,
18060
- paddingLeft: contextMenuSidePadding,
18061
- paddingRight: contextMenuSidePadding,
18062
- color: "var(--gantt-context-menu-text-color)"
18086
+ onMouseEnter: () => {
18087
+ var _a, _b;
18088
+ if (closeTimeoutRef.current) {
18089
+ window.clearTimeout(closeTimeoutRef.current);
18090
+ closeTimeoutRef.current = null;
18091
+ }
18092
+ setHovered(true);
18093
+ const rect = (_a = btnRef.current) == null ? void 0 : _a.getBoundingClientRect();
18094
+ const parentMenu = (_b = btnRef.current) == null ? void 0 : _b.closest(
18095
+ '[role="menu"]'
18096
+ );
18097
+ const parentRect = (parentMenu == null ? void 0 : parentMenu.getBoundingClientRect()) ?? null;
18098
+ if (rect) {
18099
+ setCoords({
18100
+ left: parentRect ? parentRect.right : rect.right,
18101
+ top: rect.top,
18102
+ parentWidth: parentRect ? Math.round(parentRect.width) : void 0
18103
+ });
18104
+ }
18063
18105
  },
18064
- onClick,
18106
+ onMouseLeave: () => {
18107
+ closeTimeoutRef.current = window.setTimeout(() => {
18108
+ setHovered(false);
18109
+ setCoords(null);
18110
+ closeTimeoutRef.current = null;
18111
+ }, 200);
18112
+ },
18113
+ style: { position: "relative", width: "100%" },
18065
18114
  children: [
18066
- /* @__PURE__ */ jsxRuntime.jsx(
18067
- "div",
18115
+ /* @__PURE__ */ jsxRuntime.jsxs(
18116
+ "button",
18068
18117
  {
18069
- className: styles$1.icon,
18118
+ className: styles$1.menuOption,
18119
+ "aria-disabled": disabled,
18120
+ disabled,
18070
18121
  style: {
18071
- width: contextMenuIconWidth,
18072
- color: "var(--gantt-context-menu-text-color)",
18073
- opacity: disabled ? 0.3 : 0.5
18122
+ height: contextMenuOptionHeight,
18123
+ paddingLeft: contextMenuSidePadding,
18124
+ paddingRight: contextMenuSidePadding,
18125
+ color: "var(--gantt-context-menu-text-color)"
18074
18126
  },
18075
- children: icon2
18127
+ onClick,
18128
+ ref: btnRef,
18129
+ children: [
18130
+ /* @__PURE__ */ jsxRuntime.jsx(
18131
+ "div",
18132
+ {
18133
+ className: styles$1.icon,
18134
+ style: {
18135
+ width: contextMenuIconWidth,
18136
+ color: "var(--gantt-context-menu-text-color)",
18137
+ opacity: disabled ? 0.3 : 0.5
18138
+ },
18139
+ children: icon2
18140
+ }
18141
+ ),
18142
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles$1.label, children: label2 }),
18143
+ children && children.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
18144
+ "div",
18145
+ {
18146
+ style: {
18147
+ marginLeft: 8,
18148
+ opacity: 0.6,
18149
+ pointerEvents: "none"
18150
+ },
18151
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "14px" }, children: "▶" })
18152
+ }
18153
+ )
18154
+ ]
18076
18155
  }
18077
18156
  ),
18078
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles$1.label, children: label2 })
18157
+ children && children.length > 0 && hovered && typeof document !== "undefined" && coords && ReactDOM.createPortal(
18158
+ /* @__PURE__ */ jsxRuntime.jsx(
18159
+ "div",
18160
+ {
18161
+ role: "menu",
18162
+ ref: nestedRef,
18163
+ onMouseEnter: () => {
18164
+ if (closeTimeoutRef.current) {
18165
+ window.clearTimeout(closeTimeoutRef.current);
18166
+ closeTimeoutRef.current = null;
18167
+ }
18168
+ setHovered(true);
18169
+ },
18170
+ onMouseLeave: () => {
18171
+ closeTimeoutRef.current = window.setTimeout(() => {
18172
+ setHovered(false);
18173
+ setCoords(null);
18174
+ closeTimeoutRef.current = null;
18175
+ }, 200);
18176
+ },
18177
+ style: {
18178
+ position: "fixed",
18179
+ // place submenu just to the right of parent menu
18180
+ left: (coords.left ?? 0) + 8,
18181
+ top: coords.top,
18182
+ // match the parent menu container look & behavior
18183
+ backgroundColor: "var(--gantt-context-menu-bg-color)",
18184
+ boxShadow: "var(--gantt-context-menu-box-shadow)",
18185
+ borderRadius: "var(--gantt-shape-border-radius)",
18186
+ fontFamily: "var(--gantt-font-family)",
18187
+ display: "flex",
18188
+ flexDirection: "column",
18189
+ width: "max-content",
18190
+ minWidth: coords && coords.parentWidth ? coords.parentWidth : 140,
18191
+ maxHeight: "calc(100vh - 24px)",
18192
+ overflowY: "auto",
18193
+ overflowX: "hidden",
18194
+ gap: 6,
18195
+ zIndex: 1e4,
18196
+ pointerEvents: "auto"
18197
+ },
18198
+ children: children.map((child, index2) => /* @__PURE__ */ jsxRuntime.jsx(
18199
+ MenuOption,
18200
+ {
18201
+ distances: props.distances,
18202
+ handleAction,
18203
+ option: child,
18204
+ onClose
18205
+ },
18206
+ index2
18207
+ ))
18208
+ }
18209
+ ),
18210
+ document.body
18211
+ )
18079
18212
  ]
18080
18213
  }
18081
18214
  );
package/dist/style.css CHANGED
@@ -636,7 +636,7 @@
636
636
  cursor: grabbing;
637
637
  }
638
638
  /*noinspection CssUnresolvedCustomProperty*/
639
- ._menuOption_1frh4_3 {
639
+ ._menuOption_1c3e3_3 {
640
640
  display: flex;
641
641
  align-items: center;
642
642
  text-align: left;
@@ -644,6 +644,7 @@
644
644
  background-color: #fff;
645
645
  cursor: pointer;
646
646
  font-size: 16px;
647
+ width: 100%;
647
648
  min-width: 120px;
648
649
  border: none;
649
650
  padding-block: unset;
@@ -653,32 +654,32 @@
653
654
  fill: var(--gantt-table-action-color);
654
655
  }
655
656
 
656
- ._menuOption_1frh4_3 svg {
657
+ ._menuOption_1c3e3_3 svg {
657
658
  width: 20px;
658
659
  height: 20px;
659
660
  }
660
661
 
661
- ._menuOption_1frh4_3:hover {
662
+ ._menuOption_1c3e3_3:hover {
662
663
  background-color: #eeeeee;
663
664
  }
664
665
 
665
- ._menuOption_1frh4_3[aria-disabled="true"],
666
- ._menuOption_1frh4_3:disabled {
666
+ ._menuOption_1c3e3_3[aria-disabled="true"],
667
+ ._menuOption_1c3e3_3:disabled {
667
668
  opacity: 0.6;
668
669
  cursor: default;
669
670
  background-color: transparent;
670
671
  }
671
672
 
672
- ._menuOption_1frh4_3[aria-disabled="true"]:hover,
673
- ._menuOption_1frh4_3:disabled:hover {
673
+ ._menuOption_1c3e3_3[aria-disabled="true"]:hover,
674
+ ._menuOption_1c3e3_3:disabled:hover {
674
675
  background-color: transparent;
675
676
  }
676
677
 
677
- ._icon_1frh4_79 {
678
+ ._icon_1c3e3_81 {
678
679
  font-size: inherit;
679
680
  }
680
681
 
681
- ._label_1frh4_87 {
682
+ ._label_1c3e3_89 {
682
683
  flex: 1;
683
684
  }
684
685
  /* HTML: <div class="loader"></div> */
@@ -580,6 +580,8 @@ export type ContextMenuOptionType = {
580
580
  disabled?: boolean;
581
581
  label: ReactNode;
582
582
  icon?: ReactNode;
583
+ /** Optional nested submenu options. If present this option will render a nested submenu when hovered. */
584
+ children?: ContextMenuOptionType[];
583
585
  };
584
586
  export type CheckTaskIdExistsAtLevel = (newId: string, comparisonLevel?: number) => boolean;
585
587
  export type GetCopiedTaskId = (task: RenderTask, checkExists: (newId: string) => boolean) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gantt-task-react-v",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "description": "Interactive Gantt Chart for React with TypeScript.",
5
5
  "author": "aguilanbon",
6
6
  "homepage": "https://github.com/aguilanbon/gantt-task-react-v",