pixelize-design-library 2.4.2-beta.40 → 2.4.2-beta.41
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/COMPONENT-GOTCHAS.md +32 -17
- package/dist/Components/OrganizationDetails/OrganizationDetailsProps.d.ts +2 -1
- package/dist/Components/SideBar/SideBarProps.d.ts +1 -1
- package/dist/Components/SideBar/components/MenuItemBox.js +15 -11
- package/dist/Components/SideBar/components/MenuPopoverContent.js +12 -9
- package/dist/Components/Table/TableProps.d.ts +6 -6
- package/dist/esm/Components/SideBar/components/MenuItemBox.js +15 -11
- package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +12 -9
- package/package.json +1 -1
package/COMPONENT-GOTCHAS.md
CHANGED
|
@@ -972,10 +972,12 @@ matches either, even when the "same" stage is clearly intended.
|
|
|
972
972
|
current `stages` list whenever it's filtered, and keep `id`/`value` the same primitive type at every
|
|
973
973
|
call site.
|
|
974
974
|
|
|
975
|
-
### `Table`'s `isVisiblity`, `tableSettings`, `handleExportChange`/`exportOptions`/`exportLabel`, and `onColumnFilter` are dead props
|
|
975
|
+
### `Table`'s `isVisiblity`, `tableSettings`, `handleExportChange`/`exportOptions`/`exportLabel`, and `onColumnFilter` are dead props — now `@deprecated`
|
|
976
976
|
|
|
977
|
-
|
|
978
|
-
|
|
977
|
+
Grepped account-frontend, tickets, crm-frontend, hrms and social-frontend: zero consumers set any of
|
|
978
|
+
these six. All six are now marked `@deprecated` in `TableProps.tsx` rather than removed outright, in
|
|
979
|
+
case an app outside those five repos relies on one. None of these do anything in the current
|
|
980
|
+
implementation, despite being typed on `TableProps` (and some documented on the demo page):
|
|
979
981
|
|
|
980
982
|
- `isVisiblity` — `Table`'s own destructure never lists it, so a consumer's value is dropped. It's
|
|
981
983
|
hardcoded `true` into both `Pagination` call sites regardless, and `Pagination` doesn't read it
|
|
@@ -1048,36 +1050,49 @@ consumer-managed state, not driven by this component resolving the promise.
|
|
|
1048
1050
|
**Apply:** don't assume symmetry with the awaited callbacks in the same props interface; manage your
|
|
1049
1051
|
own loading state for these three around the promise you return.
|
|
1050
1052
|
|
|
1051
|
-
### `OrganizationDetails`' `CreateOrgFormValues.default`/`defaultOrg` are dead fields
|
|
1053
|
+
### `OrganizationDetails`' `CreateOrgFormValues.default`/`defaultOrg` are dead fields — now `@deprecated`
|
|
1052
1054
|
|
|
1053
1055
|
The built-in `CreateOrgModal`'s internal state and submit handler never set or forward them, and the
|
|
1054
1056
|
public `onCreateOrganization` callback's parameter type doesn't declare them at all — populating
|
|
1055
|
-
them in a custom create form has no effect on the built-in flow.
|
|
1057
|
+
them in a custom create form has no effect on the built-in flow. account-frontend, the only real
|
|
1058
|
+
consumer of `OrganizationDetails`, never reads either field. Both are now `@deprecated` in
|
|
1059
|
+
`OrganizationDetailsProps.tsx`.
|
|
1056
1060
|
|
|
1057
1061
|
**Apply:** don't rely on these two fields to pre-mark a newly created org as default.
|
|
1058
1062
|
|
|
1059
|
-
### `SideBar`'s `selectedSubMenu` prop is accepted but never read
|
|
1063
|
+
### `SideBar`'s `selectedSubMenu` prop is accepted but never read — now `@deprecated`
|
|
1060
1064
|
|
|
1061
1065
|
`SidebarProps.selectedSubMenu` is threaded through `SideBar` → `MenuItems` → `MenuItemBox`, but the
|
|
1062
|
-
component's destructure never includes it — passing it has no effect of any kind.
|
|
1066
|
+
component's destructure never includes it — passing it has no effect of any kind. Zero consumers set
|
|
1067
|
+
it; now `@deprecated` in `SideBarProps.tsx`.
|
|
1063
1068
|
|
|
1064
1069
|
**Apply:** don't pass `selectedSubMenu` expecting it to highlight a submenu row.
|
|
1065
1070
|
|
|
1066
|
-
### `
|
|
1071
|
+
### `KanbanBoard`'s `kanbanSelect`/`onOpen` don't check `permissions` — decision: accepted as-is
|
|
1067
1072
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1073
|
+
Looked like an asymmetry with the gated `kanbanEdit`/`kanbanCreate`/`onDelete`, but on inspection
|
|
1074
|
+
`kanbanSelect` is a header filter dropdown (`options`/`onSelectChange`/`value`) — a view-level
|
|
1075
|
+
affordance, not a mutation trigger — and `onOpen` (opening a card) is a view action too. Both are
|
|
1076
|
+
already covered by the existing `permissions.isViewable` gate: the whole board (including the
|
|
1077
|
+
header) returns `null` before rendering when `isViewable` is `false`, so a viewer who can't see the
|
|
1078
|
+
board can't reach either. Gating them a second time against `isEditable`/`isCreatable`/`isDeletable`
|
|
1079
|
+
would be wrong — filtering/opening isn't an edit, create, or delete. No code change made.
|
|
1070
1080
|
|
|
1071
|
-
**Apply:**
|
|
1081
|
+
**Apply:** if you need `kanbanSelect` or `onOpen` gated on something narrower than view access, gate
|
|
1082
|
+
it yourself (e.g. `isSelect: canEdit`); the component won't do it for you.
|
|
1072
1083
|
|
|
1073
|
-
### `
|
|
1084
|
+
### `SideBar`'s `locked` menu-item flag — decision: fixed to actually block interaction
|
|
1074
1085
|
|
|
1075
|
-
`
|
|
1076
|
-
`
|
|
1077
|
-
|
|
1086
|
+
Previously `locked` only swapped in a padlock icon; the click handler checked `disabled`, not
|
|
1087
|
+
`locked`, so a `locked: true` item still fully navigated. Zero consumers set `locked: true` in
|
|
1088
|
+
account-frontend, tickets, crm-frontend, hrms, social-frontend or crm-mobile, so there was no
|
|
1089
|
+
working behavior to preserve — the misleading name was a trap waiting for the first adopter. As of
|
|
1090
|
+
this release, `locked` blocks clicks/expansion the same way `disabled` does, in both the expanded
|
|
1091
|
+
sidebar (`MenuItemBox`) and the collapsed-sidebar flyout (`MenuPopoverContent`), for both top-level
|
|
1092
|
+
and submenu items. `disabled` is unchanged and still works independently.
|
|
1078
1093
|
|
|
1079
|
-
**Apply:**
|
|
1080
|
-
|
|
1094
|
+
**Apply:** `locked: true` alone now prevents interaction; you no longer need to also set
|
|
1095
|
+
`disabled: true`.
|
|
1081
1096
|
|
|
1082
1097
|
### `KanbanBoard`'s `permissions.isViewable: false` hides everything, including header actions
|
|
1083
1098
|
|
|
@@ -28,8 +28,9 @@ export type CreateOrgFormValues = {
|
|
|
28
28
|
company_name: string;
|
|
29
29
|
company_address: string;
|
|
30
30
|
phone?: string;
|
|
31
|
-
/**
|
|
31
|
+
/** @deprecated Dead — never populated by the built-in CreateOrgModal, no known consumer usage. */
|
|
32
32
|
default?: number;
|
|
33
|
+
/** @deprecated Dead — never populated by the built-in CreateOrgModal, no known consumer usage. */
|
|
33
34
|
defaultOrg?: number;
|
|
34
35
|
};
|
|
35
36
|
/** All copy in OrganizationDetails is overridable here; unset fields fall back to its built-in DEFAULT_LABELS. */
|
|
@@ -25,7 +25,7 @@ export type SidebarProps = {
|
|
|
25
25
|
/** true hides the Add New row completely, rather than disabling it. */
|
|
26
26
|
isDisableAddNew?: boolean;
|
|
27
27
|
onAddNewClick?: () => void;
|
|
28
|
-
/**
|
|
28
|
+
/** @deprecated Dead — threaded down to MenuItemBox but never read there; no known consumer usage. */
|
|
29
29
|
selectedSubMenu?: string | null;
|
|
30
30
|
selectedSecondaryItem?: string | number | null;
|
|
31
31
|
/** Shows skeleton rows instead of MenuItems; header and footer render normally. */
|
|
@@ -21,6 +21,9 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
21
21
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
22
22
|
const activeAccent = (0, sidebarAccentColors_1.getSidebarActiveAccent)(theme.colors);
|
|
23
23
|
const { title, icon, disabled, locked, badge, subMenu, url } = menu;
|
|
24
|
+
// `locked` blocks interaction the same as `disabled` — a locked item showing the padlock icon
|
|
25
|
+
// but still fully clickable was a real gotcha (see COMPONENT-GOTCHAS.md).
|
|
26
|
+
const isBlocked = disabled || locked;
|
|
24
27
|
const hasSubMenu = !!(subMenu === null || subMenu === void 0 ? void 0 : subMenu.length);
|
|
25
28
|
const submenuPanelId = `submenu-${String((_a = menu.id) !== null && _a !== void 0 ? _a : title)}`;
|
|
26
29
|
// A submenu parent only expands, so it is a button; every other row navigates.
|
|
@@ -40,11 +43,11 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
40
43
|
const isParentActive = activeMenu === menu.id;
|
|
41
44
|
const isAnyActive = isParentActive || isChildActive;
|
|
42
45
|
const handleToggleSubMenu = (e) => {
|
|
43
|
-
if (!
|
|
46
|
+
if (!isBlocked && hasSubMenu) {
|
|
44
47
|
onToggle === null || onToggle === void 0 ? void 0 : onToggle(menu, e === null || e === void 0 ? void 0 : e.clientY);
|
|
45
48
|
return;
|
|
46
49
|
}
|
|
47
|
-
if (!
|
|
50
|
+
if (!isBlocked)
|
|
48
51
|
handleClick === null || handleClick === void 0 ? void 0 : handleClick(menu);
|
|
49
52
|
};
|
|
50
53
|
// Pill-style nav item. The fill is a white overlay, not a brand tint: the sidebar is a
|
|
@@ -68,14 +71,14 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
68
71
|
borderLeft: "2px solid",
|
|
69
72
|
borderLeftColor: isAnyActive ? theme.colors.sidebar.activeIcon : "transparent",
|
|
70
73
|
backgroundColor: isAnyActive ? "whiteAlpha.300" : "transparent",
|
|
71
|
-
color:
|
|
72
|
-
_hover:
|
|
74
|
+
color: isBlocked ? "whiteAlpha.400" : "whiteAlpha.700",
|
|
75
|
+
_hover: isBlocked
|
|
73
76
|
? {}
|
|
74
77
|
: {
|
|
75
78
|
backgroundColor: isAnyActive ? "whiteAlpha.400" : "whiteAlpha.200",
|
|
76
79
|
},
|
|
77
|
-
cursor:
|
|
78
|
-
opacity:
|
|
80
|
+
cursor: isBlocked ? "not-allowed" : "pointer",
|
|
81
|
+
opacity: isBlocked ? 0.6 : 1,
|
|
79
82
|
transition: "background 0.12s",
|
|
80
83
|
};
|
|
81
84
|
const buttonContent = ((0, jsx_runtime_1.jsxs)(react_1.Flex, { align: "center", gap: "9px", justify: toggle ? "center" : "space-between", children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { align: "center", gap: "9px", overflow: "hidden", children: [(0, jsx_runtime_1.jsxs)(react_1.Box, { position: "relative", display: "flex", alignItems: "center", children: [(0, jsx_runtime_1.jsx)(react_1.Icon, { as: icon, fontSize: "1rem", verticalAlign: "middle", color: isAnyActive ? activeAccent.icon : "whiteAlpha.800", _groupHover: isAnyActive ? undefined : { color: "white" }, transition: "color 0.12s" }), toggle && (badgeValue != null || hasSubmenuBadge) && ((0, jsx_runtime_1.jsx)(react_1.Box, { position: "absolute", top: "0", right: "0", w: "0.375rem", h: "0.375rem", borderRadius: "full", bg: theme.colors.red[400] }))] }), !toggle && ((0, jsx_runtime_1.jsx)(TextTruncation_1.default
|
|
@@ -92,14 +95,14 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
92
95
|
})(), locked && (0, jsx_runtime_1.jsx)(react_1.Icon, { as: lucide_react_1.LockKeyhole, fontSize: "1rem", color: "whiteAlpha.600" }), hasSubMenu && ((0, jsx_runtime_1.jsx)(react_1.Icon, { as: isOpen ? lucide_react_1.ChevronDown : lucide_react_1.ChevronRight, fontSize: "1rem", color: theme.colors.sidebar.mutedIcon }))] }))] }));
|
|
93
96
|
if (toggle) {
|
|
94
97
|
// Collapsed sidebar: show Popover
|
|
95
|
-
return ((0, jsx_runtime_1.jsxs)(react_1.Popover, { trigger: "hover", placement: "right-start", closeOnBlur: false, isLazy: true, children: [(0, jsx_runtime_1.jsx)(react_1.PopoverTrigger, { children: (0, jsx_runtime_1.jsx)(react_1.Box, { ...commonStyles, as: rowAs, type: hasSubMenu ? "button" : undefined, href: hasSubMenu ? undefined : url !== null && url !== void 0 ? url : "#", "aria-label": title, "aria-current": isAnyActive ? "page" : undefined, "aria-disabled":
|
|
98
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Popover, { trigger: "hover", placement: "right-start", closeOnBlur: false, isLazy: true, children: [(0, jsx_runtime_1.jsx)(react_1.PopoverTrigger, { children: (0, jsx_runtime_1.jsx)(react_1.Box, { ...commonStyles, as: rowAs, type: hasSubMenu ? "button" : undefined, href: hasSubMenu ? undefined : url !== null && url !== void 0 ? url : "#", "aria-label": title, "aria-current": isAnyActive ? "page" : undefined, "aria-disabled": isBlocked ? true : undefined, onClick: (e) => {
|
|
96
99
|
if (!hasSubMenu && url && isBrowserNavigationGesture(e))
|
|
97
100
|
return;
|
|
98
101
|
e.preventDefault();
|
|
99
102
|
handleToggleSubMenu(e);
|
|
100
|
-
}, "data-menu-id": String((_d = menu.id) !== null && _d !== void 0 ? _d : title), children: buttonContent }) }), (0, jsx_runtime_1.jsx)(react_1.Portal, { children: (0, jsx_runtime_1.jsx)(react_1.PopoverContent, { borderRadius: "lg", bg: theme.colors.sidebar.elevated, boxShadow: "lg", minW: "10rem", maxW: "14rem", py: "0.125rem", ml: "-0.5rem", border: 'none', cursor:
|
|
103
|
+
}, "data-menu-id": String((_d = menu.id) !== null && _d !== void 0 ? _d : title), children: buttonContent }) }), (0, jsx_runtime_1.jsx)(react_1.Portal, { children: (0, jsx_runtime_1.jsx)(react_1.PopoverContent, { borderRadius: "lg", bg: theme.colors.sidebar.elevated, boxShadow: "lg", minW: "10rem", maxW: "14rem", py: "0.125rem", ml: "-0.5rem", border: 'none', cursor: isBlocked ? "not-allowed" : "pointer", zIndex: 1500, children: (0, jsx_runtime_1.jsx)(react_1.PopoverBody, { p: "0.625rem", color: "white", children: (0, jsx_runtime_1.jsx)(MenuPopoverContent_1.default, { menu: menu, handleMenuClick: handleClick, activeMenu: activeMenu }) }) }) })] }));
|
|
101
104
|
}
|
|
102
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_1.Box, { ...commonStyles, as: rowAs, type: hasSubMenu ? "button" : undefined, href: hasSubMenu ? undefined : url !== null && url !== void 0 ? url : "#", "aria-expanded": hasSubMenu ? !!isOpen : undefined, "aria-controls": hasSubMenu ? submenuPanelId : undefined, "aria-current": isAnyActive ? "page" : undefined, "aria-disabled":
|
|
105
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_1.Box, { ...commonStyles, as: rowAs, type: hasSubMenu ? "button" : undefined, href: hasSubMenu ? undefined : url !== null && url !== void 0 ? url : "#", "aria-expanded": hasSubMenu ? !!isOpen : undefined, "aria-controls": hasSubMenu ? submenuPanelId : undefined, "aria-current": isAnyActive ? "page" : undefined, "aria-disabled": isBlocked ? true : undefined, onClick: (e) => {
|
|
103
106
|
if (!hasSubMenu && url && isBrowserNavigationGesture(e))
|
|
104
107
|
return;
|
|
105
108
|
e.preventDefault();
|
|
@@ -111,11 +114,12 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
111
114
|
}, children: subMenu.map((sub, sIdx) => {
|
|
112
115
|
var _a, _b, _c;
|
|
113
116
|
const isSubActive = activeMenu === sub.id;
|
|
114
|
-
|
|
117
|
+
const isSubBlocked = sub.disabled || sub.locked;
|
|
118
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Box, { as: "a", href: (_a = sub.url) !== null && _a !== void 0 ? _a : "#", "data-group": "", "aria-current": isSubActive ? "page" : undefined, "aria-disabled": isSubBlocked ? true : undefined, textDecoration: "none", py: "5px", pl: "30px", pr: "10px", borderRadius: "md", fontSize: "11.5px", backgroundColor: "transparent", _hover: { backgroundColor: "whiteAlpha.100" }, opacity: isSubBlocked ? 0.6 : 1, cursor: isSubBlocked ? "not-allowed" : "pointer", pointerEvents: isSubBlocked ? "none" : "auto", onClick: (e) => {
|
|
115
119
|
if (sub.url && isBrowserNavigationGesture(e))
|
|
116
120
|
return;
|
|
117
121
|
e.preventDefault();
|
|
118
|
-
if (!
|
|
122
|
+
if (!isSubBlocked)
|
|
119
123
|
handleClick === null || handleClick === void 0 ? void 0 : handleClick(sub);
|
|
120
124
|
}, display: "flex", alignItems: "center", justifyContent: "space-between", gap: "0.5rem", transition: "background 0.12s", children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { align: "center", gap: "9px", overflow: "hidden", flex: "1", children: [sub.icon ? ((0, jsx_runtime_1.jsx)(react_1.Icon, { as: sub.icon, fontSize: "0.95rem", flexShrink: 0, color: isSubActive ? activeAccent.icon : "whiteAlpha.800", _groupHover: isSubActive ? undefined : { color: "white" }, transition: "color 0.12s" })) : ((0, jsx_runtime_1.jsx)(react_1.Box, { w: "5px", h: "5px", borderRadius: "full", flexShrink: 0, bg: isSubActive ? activeAccent.icon : "whiteAlpha.500", _groupHover: isSubActive ? undefined : { bg: "whiteAlpha.700" }, transition: "background 0.12s" })), (0, jsx_runtime_1.jsx)(TextTruncation_1.default, { fontSize: "12px", fontWeight: isSubActive ? 600 : 500, lineHeight: "1",
|
|
121
125
|
// open={isOpen}
|
|
@@ -13,15 +13,17 @@ const MenuPopoverContent = ({ menu, handleMenuClick, activeMenu, }) => {
|
|
|
13
13
|
var _a;
|
|
14
14
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
15
15
|
const activeAccent = (0, sidebarAccentColors_1.getSidebarActiveAccent)(theme.colors);
|
|
16
|
-
const { title, icon, subMenu, badge, locked } = menu;
|
|
16
|
+
const { title, icon, subMenu, badge, locked, disabled } = menu;
|
|
17
|
+
const isBlocked = disabled || locked;
|
|
17
18
|
const hasSubMenu = !!(subMenu === null || subMenu === void 0 ? void 0 : subMenu.length);
|
|
18
19
|
const isParentActive = activeMenu === menu.id;
|
|
19
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { as: (hasSubMenu ? "div" : "a"), href: hasSubMenu ? undefined : (_a = menu.url) !== null && _a !== void 0 ? _a : "#", "aria-current": !hasSubMenu && isParentActive ? "page" : undefined, textDecoration: "none", align: "center", gap: "8px", px: "8px", py: hasSubMenu ? "6px" : "8px", mb: hasSubMenu ? "4px" : 0, borderBottom: hasSubMenu ? "1px solid" : undefined, borderColor: hasSubMenu ? "whiteAlpha.100" : undefined, borderRadius: hasSubMenu ? "none" : "md", bg: !hasSubMenu && isParentActive ? "whiteAlpha.300" : "transparent", _hover: !hasSubMenu ? { bg: "whiteAlpha.100" } : undefined, onClick: (e) => {
|
|
20
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { as: (hasSubMenu ? "div" : "a"), href: hasSubMenu ? undefined : (_a = menu.url) !== null && _a !== void 0 ? _a : "#", "aria-current": !hasSubMenu && isParentActive ? "page" : undefined, textDecoration: "none", align: "center", gap: "8px", px: "8px", py: hasSubMenu ? "6px" : "8px", mb: hasSubMenu ? "4px" : 0, borderBottom: hasSubMenu ? "1px solid" : undefined, borderColor: hasSubMenu ? "whiteAlpha.100" : undefined, borderRadius: hasSubMenu ? "none" : "md", bg: !hasSubMenu && isParentActive ? "whiteAlpha.300" : "transparent", _hover: !hasSubMenu && !isBlocked ? { bg: "whiteAlpha.100" } : undefined, onClick: (e) => {
|
|
20
21
|
if (!hasSubMenu) {
|
|
21
22
|
e.preventDefault();
|
|
22
|
-
|
|
23
|
+
if (!isBlocked)
|
|
24
|
+
handleMenuClick === null || handleMenuClick === void 0 ? void 0 : handleMenuClick(menu);
|
|
23
25
|
}
|
|
24
|
-
}, cursor: !hasSubMenu ? "pointer" : "default", children: [(0, jsx_runtime_1.jsx)(react_1.Icon, { as: icon, fontSize: "1rem", flexShrink: 0, color: !hasSubMenu && isParentActive ? activeAccent.icon : "whiteAlpha.700" }), (0, jsx_runtime_1.jsx)(TextTruncation_1.default, { fontWeight: 600, fontSize: "13px", flex: "1", color: theme.colors.white, children: title }), (() => {
|
|
26
|
+
}, cursor: !hasSubMenu ? (isBlocked ? "not-allowed" : "pointer") : "default", "aria-disabled": !hasSubMenu && isBlocked ? true : undefined, children: [(0, jsx_runtime_1.jsx)(react_1.Icon, { as: icon, fontSize: "1rem", flexShrink: 0, color: !hasSubMenu && isParentActive ? activeAccent.icon : "whiteAlpha.700" }), (0, jsx_runtime_1.jsx)(TextTruncation_1.default, { fontWeight: 600, fontSize: "13px", flex: "1", color: theme.colors.white, children: title }), (() => {
|
|
25
27
|
var _a, _b, _c, _d;
|
|
26
28
|
const cfg = typeof badge === "object" && badge !== null
|
|
27
29
|
? badge
|
|
@@ -34,14 +36,15 @@ const MenuPopoverContent = ({ menu, handleMenuClick, activeMenu, }) => {
|
|
|
34
36
|
})(), locked && (0, jsx_runtime_1.jsx)(react_1.Icon, { as: lucide_react_1.LockKeyhole, fontSize: "1rem", color: theme.colors.sidebar.mutedIcon })] }), subMenu === null || subMenu === void 0 ? void 0 : subMenu.map((sub) => {
|
|
35
37
|
var _a;
|
|
36
38
|
const isSubActive = activeMenu === sub.id;
|
|
37
|
-
|
|
39
|
+
const isSubBlocked = sub.disabled || sub.locked;
|
|
40
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "a", href: (_a = sub.url) !== null && _a !== void 0 ? _a : "#", "data-group": "", "aria-current": isSubActive ? "page" : undefined, "aria-disabled": isSubBlocked ? true : undefined, textDecoration: "none", onClick: (e) => {
|
|
38
41
|
e.preventDefault();
|
|
39
|
-
if (!
|
|
42
|
+
if (!isSubBlocked)
|
|
40
43
|
handleMenuClick === null || handleMenuClick === void 0 ? void 0 : handleMenuClick(sub);
|
|
41
44
|
}, px: "8px", py: "7px", mb: "2px", borderRadius: "md", align: "center", justify: "space-between", gap: "8px", borderLeft: "2px solid", borderLeftColor: isSubActive ? theme.colors.sidebar.activeIcon : "transparent", bg: isSubActive ? "whiteAlpha.300" : "transparent", _hover: {
|
|
42
|
-
bg: !
|
|
43
|
-
cursor: !
|
|
44
|
-
}, opacity:
|
|
45
|
+
bg: !isSubBlocked ? "whiteAlpha.100" : "transparent",
|
|
46
|
+
cursor: !isSubBlocked ? "pointer" : "not-allowed",
|
|
47
|
+
}, opacity: isSubBlocked ? 0.5 : 1, pointerEvents: isSubBlocked ? "none" : "auto", transition: "background 0.12s", children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { align: "center", gap: "9px", overflow: "hidden", flex: "1", children: [(0, jsx_runtime_1.jsx)(react_1.Icon, { as: sub.icon, fontSize: "0.95rem", flexShrink: 0, color: isSubActive ? activeAccent.icon : "whiteAlpha.800", _groupHover: isSubActive ? undefined : { color: "white" }, transition: "color 0.12s" }), (0, jsx_runtime_1.jsx)(TextTruncation_1.default, { fontSize: "12.5px", fontWeight: isSubActive ? 600 : 500, flex: 1, color: isSubActive ? activeAccent.label : theme.colors.white, _groupHover: isSubActive ? undefined : { color: "white" }, children: sub.title })] }), sub.locked ? ((0, jsx_runtime_1.jsx)(react_1.Icon, { as: lucide_react_1.LockKeyhole, fontSize: "1rem", color: theme.colors.sidebar.mutedIcon })) : (() => {
|
|
45
48
|
var _a, _b, _c, _d;
|
|
46
49
|
const cfg = typeof sub.badge === "object" && sub.badge !== null
|
|
47
50
|
? sub.badge
|
|
@@ -29,14 +29,14 @@ export type TableProps = {
|
|
|
29
29
|
/** Accepted and forwarded to TableBody but never read there — no visual effect (see COMPONENT-GOTCHAS.md). */
|
|
30
30
|
stripe?: boolean;
|
|
31
31
|
emptyState?: TableEmptyState | ReactNode;
|
|
32
|
-
/** Dead
|
|
32
|
+
/** @deprecated Dead — Table renders no export UI and never reads this; no known consumer usage. */
|
|
33
33
|
handleExportChange?: (value: string) => void | undefined;
|
|
34
|
-
/** Dead
|
|
34
|
+
/** @deprecated Dead — same unwired export feature as handleExportChange/exportLabel. */
|
|
35
35
|
exportOptions?: ExportOption[];
|
|
36
|
-
/** Dead
|
|
36
|
+
/** @deprecated Dead — same unwired export feature as handleExportChange/exportOptions. */
|
|
37
37
|
exportLabel?: string;
|
|
38
38
|
isLoading?: boolean;
|
|
39
|
-
/**
|
|
39
|
+
/** @deprecated Dead — misspelling of isVisibility; Table never destructures it, no effect, no known consumer usage. */
|
|
40
40
|
isVisiblity?: boolean;
|
|
41
41
|
isCheckbox?: boolean;
|
|
42
42
|
headerBgColor?: string;
|
|
@@ -81,11 +81,11 @@ export type TableProps = {
|
|
|
81
81
|
loadingSkeletonRows?: number;
|
|
82
82
|
headerActions?: HeaderActionsProps;
|
|
83
83
|
onGlobalSearch?: (searchVal: string) => void;
|
|
84
|
-
/** Dead
|
|
84
|
+
/** @deprecated Dead — Table.tsx has it commented out of its own destructure; never called, no known consumer usage. */
|
|
85
85
|
onColumnFilter?: (filters: FilterValues) => void;
|
|
86
86
|
/** Gates the Settings gear (Density / Manage Columns / Group) — the real switch for column-visibility UI, unlike tableSettings or isVisiblity. */
|
|
87
87
|
isTableSettings?: boolean;
|
|
88
|
-
/** Dead
|
|
88
|
+
/** @deprecated Dead — TableSettings receives it but never destructures it; no known consumer usage. */
|
|
89
89
|
tableSettings?: {
|
|
90
90
|
filters?: string[];
|
|
91
91
|
filterSelected?: string[];
|
|
@@ -16,6 +16,9 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
16
16
|
const theme = useCustomTheme();
|
|
17
17
|
const activeAccent = getSidebarActiveAccent(theme.colors);
|
|
18
18
|
const { title, icon, disabled, locked, badge, subMenu, url } = menu;
|
|
19
|
+
// `locked` blocks interaction the same as `disabled` — a locked item showing the padlock icon
|
|
20
|
+
// but still fully clickable was a real gotcha (see COMPONENT-GOTCHAS.md).
|
|
21
|
+
const isBlocked = disabled || locked;
|
|
19
22
|
const hasSubMenu = !!(subMenu === null || subMenu === void 0 ? void 0 : subMenu.length);
|
|
20
23
|
const submenuPanelId = `submenu-${String((_a = menu.id) !== null && _a !== void 0 ? _a : title)}`;
|
|
21
24
|
// A submenu parent only expands, so it is a button; every other row navigates.
|
|
@@ -35,11 +38,11 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
35
38
|
const isParentActive = activeMenu === menu.id;
|
|
36
39
|
const isAnyActive = isParentActive || isChildActive;
|
|
37
40
|
const handleToggleSubMenu = (e) => {
|
|
38
|
-
if (!
|
|
41
|
+
if (!isBlocked && hasSubMenu) {
|
|
39
42
|
onToggle === null || onToggle === void 0 ? void 0 : onToggle(menu, e === null || e === void 0 ? void 0 : e.clientY);
|
|
40
43
|
return;
|
|
41
44
|
}
|
|
42
|
-
if (!
|
|
45
|
+
if (!isBlocked)
|
|
43
46
|
handleClick === null || handleClick === void 0 ? void 0 : handleClick(menu);
|
|
44
47
|
};
|
|
45
48
|
// Pill-style nav item. The fill is a white overlay, not a brand tint: the sidebar is a
|
|
@@ -63,14 +66,14 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
63
66
|
borderLeft: "2px solid",
|
|
64
67
|
borderLeftColor: isAnyActive ? theme.colors.sidebar.activeIcon : "transparent",
|
|
65
68
|
backgroundColor: isAnyActive ? "whiteAlpha.300" : "transparent",
|
|
66
|
-
color:
|
|
67
|
-
_hover:
|
|
69
|
+
color: isBlocked ? "whiteAlpha.400" : "whiteAlpha.700",
|
|
70
|
+
_hover: isBlocked
|
|
68
71
|
? {}
|
|
69
72
|
: {
|
|
70
73
|
backgroundColor: isAnyActive ? "whiteAlpha.400" : "whiteAlpha.200",
|
|
71
74
|
},
|
|
72
|
-
cursor:
|
|
73
|
-
opacity:
|
|
75
|
+
cursor: isBlocked ? "not-allowed" : "pointer",
|
|
76
|
+
opacity: isBlocked ? 0.6 : 1,
|
|
74
77
|
transition: "background 0.12s",
|
|
75
78
|
};
|
|
76
79
|
const buttonContent = (_jsxs(Flex, { align: "center", gap: "9px", justify: toggle ? "center" : "space-between", children: [_jsxs(Flex, { align: "center", gap: "9px", overflow: "hidden", children: [_jsxs(Box, { position: "relative", display: "flex", alignItems: "center", children: [_jsx(Icon, { as: icon, fontSize: "1rem", verticalAlign: "middle", color: isAnyActive ? activeAccent.icon : "whiteAlpha.800", _groupHover: isAnyActive ? undefined : { color: "white" }, transition: "color 0.12s" }), toggle && (badgeValue != null || hasSubmenuBadge) && (_jsx(Box, { position: "absolute", top: "0", right: "0", w: "0.375rem", h: "0.375rem", borderRadius: "full", bg: theme.colors.red[400] }))] }), !toggle && (_jsx(TruncatedTextWithTooltip
|
|
@@ -87,14 +90,14 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
87
90
|
})(), locked && _jsx(Icon, { as: LockKeyhole, fontSize: "1rem", color: "whiteAlpha.600" }), hasSubMenu && (_jsx(Icon, { as: isOpen ? ChevronDown : ChevronRight, fontSize: "1rem", color: theme.colors.sidebar.mutedIcon }))] }))] }));
|
|
88
91
|
if (toggle) {
|
|
89
92
|
// Collapsed sidebar: show Popover
|
|
90
|
-
return (_jsxs(Popover, { trigger: "hover", placement: "right-start", closeOnBlur: false, isLazy: true, children: [_jsx(PopoverTrigger, { children: _jsx(Box, { ...commonStyles, as: rowAs, type: hasSubMenu ? "button" : undefined, href: hasSubMenu ? undefined : url !== null && url !== void 0 ? url : "#", "aria-label": title, "aria-current": isAnyActive ? "page" : undefined, "aria-disabled":
|
|
93
|
+
return (_jsxs(Popover, { trigger: "hover", placement: "right-start", closeOnBlur: false, isLazy: true, children: [_jsx(PopoverTrigger, { children: _jsx(Box, { ...commonStyles, as: rowAs, type: hasSubMenu ? "button" : undefined, href: hasSubMenu ? undefined : url !== null && url !== void 0 ? url : "#", "aria-label": title, "aria-current": isAnyActive ? "page" : undefined, "aria-disabled": isBlocked ? true : undefined, onClick: (e) => {
|
|
91
94
|
if (!hasSubMenu && url && isBrowserNavigationGesture(e))
|
|
92
95
|
return;
|
|
93
96
|
e.preventDefault();
|
|
94
97
|
handleToggleSubMenu(e);
|
|
95
|
-
}, "data-menu-id": String((_d = menu.id) !== null && _d !== void 0 ? _d : title), children: buttonContent }) }), _jsx(Portal, { children: _jsx(PopoverContent, { borderRadius: "lg", bg: theme.colors.sidebar.elevated, boxShadow: "lg", minW: "10rem", maxW: "14rem", py: "0.125rem", ml: "-0.5rem", border: 'none', cursor:
|
|
98
|
+
}, "data-menu-id": String((_d = menu.id) !== null && _d !== void 0 ? _d : title), children: buttonContent }) }), _jsx(Portal, { children: _jsx(PopoverContent, { borderRadius: "lg", bg: theme.colors.sidebar.elevated, boxShadow: "lg", minW: "10rem", maxW: "14rem", py: "0.125rem", ml: "-0.5rem", border: 'none', cursor: isBlocked ? "not-allowed" : "pointer", zIndex: 1500, children: _jsx(PopoverBody, { p: "0.625rem", color: "white", children: _jsx(MenuPopoverContent, { menu: menu, handleMenuClick: handleClick, activeMenu: activeMenu }) }) }) })] }));
|
|
96
99
|
}
|
|
97
|
-
return (_jsxs(_Fragment, { children: [_jsx(Box, { ...commonStyles, as: rowAs, type: hasSubMenu ? "button" : undefined, href: hasSubMenu ? undefined : url !== null && url !== void 0 ? url : "#", "aria-expanded": hasSubMenu ? !!isOpen : undefined, "aria-controls": hasSubMenu ? submenuPanelId : undefined, "aria-current": isAnyActive ? "page" : undefined, "aria-disabled":
|
|
100
|
+
return (_jsxs(_Fragment, { children: [_jsx(Box, { ...commonStyles, as: rowAs, type: hasSubMenu ? "button" : undefined, href: hasSubMenu ? undefined : url !== null && url !== void 0 ? url : "#", "aria-expanded": hasSubMenu ? !!isOpen : undefined, "aria-controls": hasSubMenu ? submenuPanelId : undefined, "aria-current": isAnyActive ? "page" : undefined, "aria-disabled": isBlocked ? true : undefined, onClick: (e) => {
|
|
98
101
|
if (!hasSubMenu && url && isBrowserNavigationGesture(e))
|
|
99
102
|
return;
|
|
100
103
|
e.preventDefault();
|
|
@@ -106,11 +109,12 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
|
|
|
106
109
|
}, children: subMenu.map((sub, sIdx) => {
|
|
107
110
|
var _a, _b, _c;
|
|
108
111
|
const isSubActive = activeMenu === sub.id;
|
|
109
|
-
|
|
112
|
+
const isSubBlocked = sub.disabled || sub.locked;
|
|
113
|
+
return (_jsxs(Box, { as: "a", href: (_a = sub.url) !== null && _a !== void 0 ? _a : "#", "data-group": "", "aria-current": isSubActive ? "page" : undefined, "aria-disabled": isSubBlocked ? true : undefined, textDecoration: "none", py: "5px", pl: "30px", pr: "10px", borderRadius: "md", fontSize: "11.5px", backgroundColor: "transparent", _hover: { backgroundColor: "whiteAlpha.100" }, opacity: isSubBlocked ? 0.6 : 1, cursor: isSubBlocked ? "not-allowed" : "pointer", pointerEvents: isSubBlocked ? "none" : "auto", onClick: (e) => {
|
|
110
114
|
if (sub.url && isBrowserNavigationGesture(e))
|
|
111
115
|
return;
|
|
112
116
|
e.preventDefault();
|
|
113
|
-
if (!
|
|
117
|
+
if (!isSubBlocked)
|
|
114
118
|
handleClick === null || handleClick === void 0 ? void 0 : handleClick(sub);
|
|
115
119
|
}, display: "flex", alignItems: "center", justifyContent: "space-between", gap: "0.5rem", transition: "background 0.12s", children: [_jsxs(Flex, { align: "center", gap: "9px", overflow: "hidden", flex: "1", children: [sub.icon ? (_jsx(Icon, { as: sub.icon, fontSize: "0.95rem", flexShrink: 0, color: isSubActive ? activeAccent.icon : "whiteAlpha.800", _groupHover: isSubActive ? undefined : { color: "white" }, transition: "color 0.12s" })) : (_jsx(Box, { w: "5px", h: "5px", borderRadius: "full", flexShrink: 0, bg: isSubActive ? activeAccent.icon : "whiteAlpha.500", _groupHover: isSubActive ? undefined : { bg: "whiteAlpha.700" }, transition: "background 0.12s" })), _jsx(TruncatedTextWithTooltip, { fontSize: "12px", fontWeight: isSubActive ? 600 : 500, lineHeight: "1",
|
|
116
120
|
// open={isOpen}
|
|
@@ -8,15 +8,17 @@ const MenuPopoverContent = ({ menu, handleMenuClick, activeMenu, }) => {
|
|
|
8
8
|
var _a;
|
|
9
9
|
const theme = useCustomTheme();
|
|
10
10
|
const activeAccent = getSidebarActiveAccent(theme.colors);
|
|
11
|
-
const { title, icon, subMenu, badge, locked } = menu;
|
|
11
|
+
const { title, icon, subMenu, badge, locked, disabled } = menu;
|
|
12
|
+
const isBlocked = disabled || locked;
|
|
12
13
|
const hasSubMenu = !!(subMenu === null || subMenu === void 0 ? void 0 : subMenu.length);
|
|
13
14
|
const isParentActive = activeMenu === menu.id;
|
|
14
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Flex, { as: (hasSubMenu ? "div" : "a"), href: hasSubMenu ? undefined : (_a = menu.url) !== null && _a !== void 0 ? _a : "#", "aria-current": !hasSubMenu && isParentActive ? "page" : undefined, textDecoration: "none", align: "center", gap: "8px", px: "8px", py: hasSubMenu ? "6px" : "8px", mb: hasSubMenu ? "4px" : 0, borderBottom: hasSubMenu ? "1px solid" : undefined, borderColor: hasSubMenu ? "whiteAlpha.100" : undefined, borderRadius: hasSubMenu ? "none" : "md", bg: !hasSubMenu && isParentActive ? "whiteAlpha.300" : "transparent", _hover: !hasSubMenu ? { bg: "whiteAlpha.100" } : undefined, onClick: (e) => {
|
|
15
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Flex, { as: (hasSubMenu ? "div" : "a"), href: hasSubMenu ? undefined : (_a = menu.url) !== null && _a !== void 0 ? _a : "#", "aria-current": !hasSubMenu && isParentActive ? "page" : undefined, textDecoration: "none", align: "center", gap: "8px", px: "8px", py: hasSubMenu ? "6px" : "8px", mb: hasSubMenu ? "4px" : 0, borderBottom: hasSubMenu ? "1px solid" : undefined, borderColor: hasSubMenu ? "whiteAlpha.100" : undefined, borderRadius: hasSubMenu ? "none" : "md", bg: !hasSubMenu && isParentActive ? "whiteAlpha.300" : "transparent", _hover: !hasSubMenu && !isBlocked ? { bg: "whiteAlpha.100" } : undefined, onClick: (e) => {
|
|
15
16
|
if (!hasSubMenu) {
|
|
16
17
|
e.preventDefault();
|
|
17
|
-
|
|
18
|
+
if (!isBlocked)
|
|
19
|
+
handleMenuClick === null || handleMenuClick === void 0 ? void 0 : handleMenuClick(menu);
|
|
18
20
|
}
|
|
19
|
-
}, cursor: !hasSubMenu ? "pointer" : "default", children: [_jsx(Icon, { as: icon, fontSize: "1rem", flexShrink: 0, color: !hasSubMenu && isParentActive ? activeAccent.icon : "whiteAlpha.700" }), _jsx(TruncatedTextWithTooltip, { fontWeight: 600, fontSize: "13px", flex: "1", color: theme.colors.white, children: title }), (() => {
|
|
21
|
+
}, cursor: !hasSubMenu ? (isBlocked ? "not-allowed" : "pointer") : "default", "aria-disabled": !hasSubMenu && isBlocked ? true : undefined, children: [_jsx(Icon, { as: icon, fontSize: "1rem", flexShrink: 0, color: !hasSubMenu && isParentActive ? activeAccent.icon : "whiteAlpha.700" }), _jsx(TruncatedTextWithTooltip, { fontWeight: 600, fontSize: "13px", flex: "1", color: theme.colors.white, children: title }), (() => {
|
|
20
22
|
var _a, _b, _c, _d;
|
|
21
23
|
const cfg = typeof badge === "object" && badge !== null
|
|
22
24
|
? badge
|
|
@@ -29,14 +31,15 @@ const MenuPopoverContent = ({ menu, handleMenuClick, activeMenu, }) => {
|
|
|
29
31
|
})(), locked && _jsx(Icon, { as: LockKeyhole, fontSize: "1rem", color: theme.colors.sidebar.mutedIcon })] }), subMenu === null || subMenu === void 0 ? void 0 : subMenu.map((sub) => {
|
|
30
32
|
var _a;
|
|
31
33
|
const isSubActive = activeMenu === sub.id;
|
|
32
|
-
|
|
34
|
+
const isSubBlocked = sub.disabled || sub.locked;
|
|
35
|
+
return (_jsxs(Flex, { as: "a", href: (_a = sub.url) !== null && _a !== void 0 ? _a : "#", "data-group": "", "aria-current": isSubActive ? "page" : undefined, "aria-disabled": isSubBlocked ? true : undefined, textDecoration: "none", onClick: (e) => {
|
|
33
36
|
e.preventDefault();
|
|
34
|
-
if (!
|
|
37
|
+
if (!isSubBlocked)
|
|
35
38
|
handleMenuClick === null || handleMenuClick === void 0 ? void 0 : handleMenuClick(sub);
|
|
36
39
|
}, px: "8px", py: "7px", mb: "2px", borderRadius: "md", align: "center", justify: "space-between", gap: "8px", borderLeft: "2px solid", borderLeftColor: isSubActive ? theme.colors.sidebar.activeIcon : "transparent", bg: isSubActive ? "whiteAlpha.300" : "transparent", _hover: {
|
|
37
|
-
bg: !
|
|
38
|
-
cursor: !
|
|
39
|
-
}, opacity:
|
|
40
|
+
bg: !isSubBlocked ? "whiteAlpha.100" : "transparent",
|
|
41
|
+
cursor: !isSubBlocked ? "pointer" : "not-allowed",
|
|
42
|
+
}, opacity: isSubBlocked ? 0.5 : 1, pointerEvents: isSubBlocked ? "none" : "auto", transition: "background 0.12s", children: [_jsxs(Flex, { align: "center", gap: "9px", overflow: "hidden", flex: "1", children: [_jsx(Icon, { as: sub.icon, fontSize: "0.95rem", flexShrink: 0, color: isSubActive ? activeAccent.icon : "whiteAlpha.800", _groupHover: isSubActive ? undefined : { color: "white" }, transition: "color 0.12s" }), _jsx(TruncatedTextWithTooltip, { fontSize: "12.5px", fontWeight: isSubActive ? 600 : 500, flex: 1, color: isSubActive ? activeAccent.label : theme.colors.white, _groupHover: isSubActive ? undefined : { color: "white" }, children: sub.title })] }), sub.locked ? (_jsx(Icon, { as: LockKeyhole, fontSize: "1rem", color: theme.colors.sidebar.mutedIcon })) : (() => {
|
|
40
43
|
var _a, _b, _c, _d;
|
|
41
44
|
const cfg = typeof sub.badge === "object" && sub.badge !== null
|
|
42
45
|
? sub.badge
|
package/package.json
CHANGED