@hortiview/shared-components 2.28.1 → 2.28.3

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,7 +1,8 @@
1
1
  import { ListItemProps } from '@element-public/react-list';
2
+ import { MenuProps } from '@element-public/react-menu';
2
3
  import { OfflineViewProps } from '../OfflineView/OfflineView';
3
4
  import { IsAllowedProps } from '../PermissionChecks/PermissionService.tsx';
4
- type ContextMenuProps = {
5
+ type ContextMenuProps = MenuProps & {
5
6
  /**
6
7
  * If not null, the menu will be open
7
8
  */
@@ -25,6 +26,10 @@ type ContextMenuProps = {
25
26
  * Props forwarded to <OfflineView /> when offline.
26
27
  */
27
28
  offlineViewProps?: Partial<OfflineViewProps>;
29
+ /**
30
+ * Optional className to apply to the menu container
31
+ */
32
+ className?: string;
28
33
  };
29
34
  export type ActionProps = ListItemProps & {
30
35
  closeOnClick?: boolean;
@@ -54,7 +59,8 @@ export type ActionProps = ListItemProps & {
54
59
  *
55
60
  * @param {boolean} triggerOpen indicates if the menu should be open
56
61
  * @param {ListItemProps[]} actions list of actions to display in the context menu as ListItems
57
- * @param testId id for testing, automatically prefixed
62
+ * @param {'vertical' | 'horizontal'} iconOrientation determines if menu icon should be vertical or horizontal three dots. Default is 'vertical'
63
+ * @param {string} data-testid id for testing, automatically prefixed
58
64
  * @recommended `primaryText`, `onClick`, `leadingBlock`
59
65
  * @requires `ListItemProps` from `@element-public/react-components`
60
66
  * @example const actions = [
@@ -64,7 +70,9 @@ export type ActionProps = ListItemProps & {
64
70
  * ];
65
71
  * @param {boolean} isOnline Whether the user is online; shows OfflineView when false.
66
72
  * @param {OfflineViewProps} offlineViewProps Props forwarded to OfflineView when offline.
73
+ * @param {string} className Optional className to apply to the menu container
74
+ * @param {MenuProps} menuProps Element Menu props, e.g. `hoistToBody`, `themeColor`, etc.
67
75
  * @returns a context menu with the given actions as ListItems
68
76
  */
69
- export declare const ContextMenu: ({ triggerOpen, actions, iconOrientation, "data-testid": dataTestId, isOnline, offlineViewProps, }: ContextMenuProps) => import("react").JSX.Element | null;
77
+ export declare const ContextMenu: ({ triggerOpen, actions, iconOrientation, "data-testid": dataTestId, isOnline, offlineViewProps, className, ...menuProps }: ContextMenuProps) => import("react").JSX.Element | null;
70
78
  export {};
@@ -1,120 +1,130 @@
1
- import { jsx as i, jsxs as M } from "react/jsx-runtime";
2
- import { I as k } from "../../index.es-BwhRJBEH.js";
3
- import { M as v, L as w, a as x, b as C } from "../../index.es-BIgjyLDa.js";
4
- import { useState as g, useCallback as T, useEffect as I, useMemo as b, Fragment as A, useRef as L, useLayoutEffect as B } from "react";
5
- import { c as V } from "../../index-_-9ybP20.js";
6
- import { u as z } from "../../uniqueId-CJo-XRQb.js";
7
- import { OfflineView as H } from "../OfflineView/OfflineView.js";
8
- import { useIsAllowed as N } from "../PermissionChecks/PermissionService.js";
9
- import { u as O } from "../../useBreakpoints-DDrM9Cfx.js";
10
- import '../../assets/ContextMenu.css';const E = "_menu_52omh_1", R = "_mobile_52omh_7", S = "_desktop_52omh_11", $ = "_icon_52omh_15", j = "_listItem_52omh_19", q = "_offlineViewMargin_52omh_51", D = "_primaryTextTruncated_52omh_55", n = {
11
- menu: E,
12
- mobile: R,
13
- desktop: S,
14
- icon: $,
15
- listItem: j,
16
- offlineViewMargin: q,
17
- primaryTextTruncated: D
18
- }, P = ({
19
- triggerOpen: t = null,
20
- actions: l,
21
- iconOrientation: a = "vertical",
22
- "data-testid": c,
23
- isOnline: m = !0,
24
- offlineViewProps: f
1
+ import { jsx as t, jsxs as L } from "react/jsx-runtime";
2
+ import { I as A } from "../../index.es-BwhRJBEH.js";
3
+ import { M as B, L as O, a as x, b as V } from "../../index.es-BIgjyLDa.js";
4
+ import { c as C } from "../../index-_-9ybP20.js";
5
+ import { u as E } from "../../uniqueId-CJo-XRQb.js";
6
+ import { useState as I, useRef as T, useCallback as M, useEffect as k, useMemo as N, Fragment as R, useLayoutEffect as z } from "react";
7
+ import { u as H } from "../../useBreakpoints-DDrM9Cfx.js";
8
+ import { OfflineView as S } from "../OfflineView/OfflineView.js";
9
+ import { useIsAllowed as $ } from "../PermissionChecks/PermissionService.js";
10
+ import { repositionMenuVertically as j } from "./ContextMenuService.js";
11
+ import '../../assets/ContextMenu.css';const q = "_menu_52omh_1", D = "_mobile_52omh_7", F = "_desktop_52omh_11", G = "_icon_52omh_15", J = "_listItem_52omh_19", K = "_offlineViewMargin_52omh_51", Q = "_primaryTextTruncated_52omh_55", o = {
12
+ menu: q,
13
+ mobile: D,
14
+ desktop: F,
15
+ icon: G,
16
+ listItem: J,
17
+ offlineViewMargin: K,
18
+ primaryTextTruncated: Q
19
+ }, oe = ({
20
+ triggerOpen: n = null,
21
+ actions: u,
22
+ iconOrientation: m = "vertical",
23
+ "data-testid": f,
24
+ isOnline: d = !0,
25
+ offlineViewProps: y,
26
+ className: g,
27
+ ...s
25
28
  }) => {
26
- const { isMobile: d } = O(), [u, o] = g(!1), _ = N(), p = T(() => {
27
- o(!1);
28
- }, []), h = T(() => {
29
- o((e) => !e);
29
+ const { isMobile: w } = H(), [l, c] = I(!1), _ = T(null), a = T(null), r = $(), i = M(() => {
30
+ c(!1);
31
+ }, []), p = M(() => {
32
+ c((e) => !e);
30
33
  }, []);
31
- I(() => {
32
- o(t !== null ? t : !1);
33
- }, [t]);
34
- const r = b(() => l.filter((e) => e.isHidden || e.isAllowed === !1 ? !1 : !(e.permissionConfiguration && !_(e.permissionConfiguration))), [l, _]);
35
- return r.length === 0 ? null : /* @__PURE__ */ i(
36
- v,
34
+ k(() => {
35
+ c(n !== null ? n : !1);
36
+ }, [n]), k(() => {
37
+ if (!l || !s.hoistToBody) return;
38
+ const e = () => {
39
+ j(_.current, a.current) && i();
40
+ };
41
+ return window.addEventListener("scroll", e, !0), () => window.removeEventListener("scroll", e, !0);
42
+ }, [l, i, s.hoistToBody]);
43
+ const b = C(o.menu, g), v = N(() => u.filter((e) => e.isHidden || e.isAllowed === !1 ? !1 : !(e.permissionConfiguration && !r(e.permissionConfiguration))), [u, r]);
44
+ return v.length === 0 ? null : /* @__PURE__ */ t("div", { ref: _, children: /* @__PURE__ */ t(
45
+ B,
37
46
  {
38
- className: n.menu,
39
- "data-testid": c ?? "contextMenu-undefined",
40
- open: u,
47
+ ref: a,
48
+ className: b,
49
+ "data-testid": f ?? "contextMenu-undefined",
50
+ open: l,
41
51
  surfaceOnly: !0,
42
- hoistToBody: !0,
43
- onClose: p,
44
- trigger: /* @__PURE__ */ i(
45
- k,
52
+ onClose: i,
53
+ trigger: /* @__PURE__ */ t(
54
+ A,
46
55
  {
47
- className: n.icon,
48
- variant: u ? "filled-primary" : void 0,
56
+ className: o.icon,
57
+ variant: l ? "filled-primary" : void 0,
49
58
  "data-testid": "open-button",
50
- icon: a === "vertical" ? "more_vert" : "more_horiz",
51
- onClick: h
59
+ icon: m === "vertical" ? "more_vert" : "more_horiz",
60
+ onClick: p
52
61
  }
53
62
  ),
54
- children: m ? /* @__PURE__ */ i(w, { "data-testid": "selection-list", children: r.map((e, s) => /* @__PURE__ */ M(A, { children: [
55
- e.dividerBefore && s !== 0 && /* @__PURE__ */ i(x, {}),
56
- /* @__PURE__ */ i(
57
- F,
63
+ ...s,
64
+ children: d ? /* @__PURE__ */ t(O, { "data-testid": "selection-list", children: v.map((e, h) => /* @__PURE__ */ L(R, { children: [
65
+ e.dividerBefore && h !== 0 && /* @__PURE__ */ t(x, {}),
66
+ /* @__PURE__ */ t(
67
+ U,
58
68
  {
59
69
  actionProps: e,
60
- index: s,
61
- isMobile: d,
62
- closeMenu: p
70
+ index: h,
71
+ isMobile: w,
72
+ closeMenu: i
63
73
  }
64
74
  ),
65
- e.dividerAfter && s !== r.length - 1 && /* @__PURE__ */ i(x, {})
66
- ] }, z(`LI_${e.primaryText?.toString()}_`))) }) : /* @__PURE__ */ i(
67
- H,
75
+ e.dividerAfter && h !== v.length - 1 && /* @__PURE__ */ t(x, {})
76
+ ] }, E(`LI_${e.primaryText?.toString()}_`))) }) : /* @__PURE__ */ t(
77
+ S,
68
78
  {
69
79
  size: "small",
70
80
  variant: "filled",
71
- className: n.offlineViewMargin,
72
- ...f
81
+ className: o.offlineViewMargin,
82
+ ...y
73
83
  }
74
84
  )
75
85
  },
76
- `contextMenu-${c ?? "undefined"}`
77
- );
78
- }, F = ({
79
- actionProps: t,
80
- index: l,
81
- isMobile: a,
82
- closeMenu: c
86
+ `contextMenu-${f ?? "undefined"}`
87
+ ) });
88
+ }, U = ({
89
+ actionProps: n,
90
+ index: u,
91
+ isMobile: m,
92
+ closeMenu: f
83
93
  }) => {
84
- const m = L(null), [f, d] = g(!1), { closeOnClick: u, permissionConfiguration: o, isAllowed: _, isHidden: p, ...h } = t;
85
- return B(() => {
86
- const r = m.current;
87
- if (!r) return;
88
- const e = r.querySelector(
94
+ const d = T(null), [y, g] = I(!1), { closeOnClick: s, permissionConfiguration: w, isAllowed: l, isHidden: c, ..._ } = n;
95
+ return z(() => {
96
+ const a = d.current;
97
+ if (!a) return;
98
+ const r = a.querySelector(
89
99
  "[class*='mdc-list-item__primary-text']"
90
100
  );
91
- if (!e) return;
92
- const s = () => {
93
- d(e.scrollHeight > e.clientHeight);
101
+ if (!r) return;
102
+ const i = () => {
103
+ g(r.scrollHeight > r.clientHeight);
94
104
  };
95
- s();
96
- const y = new ResizeObserver(s);
97
- return y.observe(e), () => {
98
- y.disconnect();
105
+ i();
106
+ const p = new ResizeObserver(i);
107
+ return p.observe(r), () => {
108
+ p.disconnect();
99
109
  };
100
- }, [t.primaryText, a]), /* @__PURE__ */ i(
101
- C,
110
+ }, [n.primaryText, m]), /* @__PURE__ */ t(
111
+ V,
102
112
  {
103
- ref: m,
104
- "data-testid": t["data-testid"] ?? `contextMenu-action-${l}`,
105
- className: V(
106
- n.listItem,
107
- f && n.primaryTextTruncated,
108
- a ? n.mobile : n.desktop
113
+ ref: d,
114
+ "data-testid": n["data-testid"] ?? `contextMenu-action-${u}`,
115
+ className: C(
116
+ o.listItem,
117
+ y && o.primaryTextTruncated,
118
+ m ? o.mobile : o.desktop
109
119
  ),
110
- ...h,
120
+ ..._,
111
121
  leadingBlockType: "icon",
112
122
  onClick: () => {
113
- t?.onClick?.(), u !== !1 && c();
123
+ n?.onClick?.(), s !== !1 && f();
114
124
  }
115
125
  }
116
126
  );
117
127
  };
118
128
  export {
119
- P as ContextMenu
129
+ oe as ContextMenu
120
130
  };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Repositions the menu vertically to stick with the trigger while scrolling.
3
+ * Horizontal scrolling is ignored.
4
+ *
5
+ * @returns `true` if the trigger has scrolled outside the vertical boundary of
6
+ * its nearest scrollable ancestor (or the viewport), otherwise `false`.
7
+ */
8
+ export declare const repositionMenuVertically: (triggerElement: HTMLElement | null, menuElement: HTMLElement | null) => boolean;
@@ -0,0 +1,19 @@
1
+ const c = (e, t) => {
2
+ const o = e?.getBoundingClientRect();
3
+ if (!o || !t) return !1;
4
+ t.style.top = `${o.bottom}px`;
5
+ const l = r(e), n = l ? l.getBoundingClientRect() : { top: 0, bottom: window.innerHeight };
6
+ return o.bottom < n.top || o.top > n.bottom;
7
+ }, r = (e) => {
8
+ let t = e?.parentElement ?? null;
9
+ for (; t; ) {
10
+ const { overflow: o, overflowY: l } = getComputedStyle(t);
11
+ if (/(auto|scroll|overlay)/.test(o + l) && t.scrollHeight > t.clientHeight)
12
+ return t;
13
+ t = t.parentElement;
14
+ }
15
+ return null;
16
+ };
17
+ export {
18
+ c as repositionMenuVertically
19
+ };
@@ -14,7 +14,7 @@ import '../../../assets/CustomTextField.css';const c = "_invalid_142hx_1", h = "
14
14
  trigger: i,
15
15
  watch: l
16
16
  } = s(), a = l(e.propertyName), d = v(() => {
17
- if (!(!a || e.value === null))
17
+ if (!(a == null || e.value == null))
18
18
  return e.value;
19
19
  }, [a, e.value]);
20
20
  return /* @__PURE__ */ m(
@@ -1,32 +1,32 @@
1
1
  import { jsx as p } from "react/jsx-runtime";
2
- import { useMemo as d } from "react";
2
+ import { useMemo as a } from "react";
3
3
  import { ContextMenu as l } from "../ContextMenu/ContextMenu.js";
4
4
  const P = ({
5
5
  data: n = [],
6
6
  order: e = [],
7
7
  cellTemplates: r = void 0,
8
- headerTranslation: t = (f) => f,
8
+ headerTranslation: t = (d) => d,
9
9
  getRowActions: c = void 0,
10
- customColumnWidth: o,
10
+ customColumnWidth: i,
11
11
  cardTitleColumn: u,
12
12
  cardSubTitleColumn: s
13
- }) => d(() => {
13
+ }) => a(() => {
14
14
  if (n?.length === 0) return [];
15
- const f = v(n[0], e).map((i) => {
16
- const a = o ? o[i] : void 0;
15
+ const d = h(n[0], e).map((o) => {
16
+ const f = i ? i[o] : void 0;
17
17
  return {
18
- id: i,
19
- header: t(i),
20
- accessor: i,
21
- cellTemplate: r ? r[i] : void 0,
22
- cellProps: a ? { width: a } : void 0,
23
- headerCellProps: a ? { width: a } : void 0,
24
- asCardTitle: i === u,
25
- asCardSubtitle: i === s
18
+ id: o,
19
+ header: t(o),
20
+ accessor: o,
21
+ cellTemplate: r ? r[o] : void 0,
22
+ cellProps: f ? { width: f } : void 0,
23
+ headerCellProps: f ? { width: f } : void 0,
24
+ asCardTitle: o === u,
25
+ asCardSubtitle: o === s
26
26
  };
27
27
  });
28
28
  return c ? [
29
- ...f,
29
+ ...d,
30
30
  {
31
31
  id: "actions",
32
32
  header: " ",
@@ -34,28 +34,28 @@ const P = ({
34
34
  cellProps: { id: "actions" },
35
35
  headerCellProps: { id: "actions" }
36
36
  }
37
- ] : f;
37
+ ] : d;
38
38
  }, [
39
39
  n,
40
40
  r,
41
41
  e,
42
42
  t,
43
43
  c,
44
- o,
44
+ i,
45
45
  u,
46
46
  s
47
47
  ]), C = ({
48
48
  data: n = [],
49
49
  getRowActions: e = void 0
50
- }) => d(() => e ? n.map((r) => {
50
+ }) => a(() => e ? n.map((r) => {
51
51
  const t = e(r);
52
- return !t || t.length === 0 ? r : { ...r, actions: /* @__PURE__ */ p(l, { actions: t, iconOrientation: "horizontal" }) };
53
- }) : n, [n, e]), v = (n, e) => {
52
+ return !t || t.length === 0 ? r : { ...r, actions: /* @__PURE__ */ p(l, { hoistToBody: !0, actions: t, iconOrientation: "horizontal" }) };
53
+ }) : n, [n, e]), h = (n, e) => {
54
54
  if (!n) return [];
55
55
  let r = Object.keys(n).map((t) => t);
56
56
  return e?.length > 0 && (r = r.sort((t, c) => {
57
- const o = e.findIndex((s) => s === t), u = e.findIndex((s) => s === c);
58
- return o === -1 ? 1 : u === -1 ? -1 : o - u;
57
+ const i = e.findIndex((s) => s === t), u = e.findIndex((s) => s === c);
58
+ return i === -1 ? 1 : u === -1 ? -1 : i - u;
59
59
  })), r;
60
60
  };
61
61
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hortiview/shared-components",
3
- "version": "2.28.1",
3
+ "version": "2.28.3",
4
4
  "description": "This is a shared component library. It should used in the HortiView platform and its modules.",
5
5
  "license": "GPL-3.0",
6
6
  "type": "module",