@codebit-programando-solucoes/codebit-web-antd 1.1.50 → 1.1.52

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.
@@ -11,12 +11,21 @@ export interface UserMenuProps {
11
11
  onItemClick?: (key: string) => void;
12
12
  /** Icon shown next to the avatar to indicate the dropdown affordance. Defaults to `<DownOutlined />`. */
13
13
  caretIcon?: ReactNode;
14
+ /**
15
+ * How items carrying `children` are rendered. `'submenu'` keeps Ant Design's sideways flyout; `'group'` renders them inline, inside the panel's own scroll container; `'auto'`
16
+ * (the default) picks `'group'` on touch devices, where a flyout has neither the horizontal room nor a reliable hover to open on. A `disabled` parent always stays a submenu,
17
+ * since that is what keeps its children unreachable.
18
+ */
19
+ nestedItemsMode?: 'auto' | 'group' | 'submenu';
14
20
  }
15
21
 
16
22
  /**
17
23
  * Header user-profile control. Renders the current user's avatar and, when `items` is non-empty, a dropdown showing the user's name/subtitle followed by the given menu items (e.g.
18
24
  * a "Minhas preferências" entry supplied by the host application).
19
25
  *
26
+ * The dropdown is given a real height budget: the popup is measured against the room left under the trigger when it opens, and the item list becomes the one scrolling region
27
+ * inside it, so a long menu stays fully reachable on a phone instead of running off the bottom of the screen.
28
+ *
20
29
  * @param props - Component props
21
30
  * @returns React.JSX.Element - Avatar, optionally wrapped in a Dropdown
22
31
  * @component
package/dist/index.cjs CHANGED
@@ -1182,17 +1182,23 @@ var __webpack_exports__ = {};
1182
1182
  trigger: "trigger-tlcps4",
1183
1183
  avatar: "avatar-Tmnqa_",
1184
1184
  caret: "caret-OWS8_0",
1185
+ popupRoot: "popupRoot-MxWPmX",
1185
1186
  panel: "panel-GCcfCo",
1187
+ divider: "divider-JJjJVy",
1186
1188
  userInfo: "userInfo-fDuDn1",
1187
1189
  userName: "userName-sIs1iq",
1188
1190
  userSubtitle: "userSubtitle-Fflhaw",
1189
- divider: "divider-JJjJVy",
1190
1191
  menu: "menu-cuORr_"
1191
1192
  };
1192
1193
  const OPEN_KEYS = new Set([
1193
1194
  'Enter',
1194
1195
  ' '
1195
1196
  ]);
1197
+ const COARSE_POINTER_QUERY = '(pointer: coarse)';
1198
+ const TRIGGER_TO_POPUP_OFFSET = 4;
1199
+ const VIEWPORT_GUTTER = 8;
1200
+ const MIN_POPUP_HEIGHT = 160;
1201
+ const MAX_HEIGHT_VAR = '--codebit-user-menu-max-height';
1196
1202
  const { Text } = external_antd_namespaceObject.Typography;
1197
1203
  function getInitials(name) {
1198
1204
  const parts = name?.trim().split(/\s+/).filter(Boolean) ?? [];
@@ -1206,6 +1212,23 @@ var __webpack_exports__ = {};
1206
1212
  ];
1207
1213
  return `${first[0]}${last[0]}`.toUpperCase();
1208
1214
  }
1215
+ function computeMaxHeight(trigger) {
1216
+ if (!trigger) return null;
1217
+ const viewportHeight = Math.min(document.documentElement.clientHeight, globalThis.visualViewport?.height ?? 1 / 0);
1218
+ return Math.max(MIN_POPUP_HEIGHT, viewportHeight - trigger.getBoundingClientRect().bottom - TRIGGER_TO_POPUP_OFFSET - VIEWPORT_GUTTER);
1219
+ }
1220
+ function toGroupItems(items) {
1221
+ return items.map((item)=>{
1222
+ if (!item || 'object' != typeof item) return item;
1223
+ if (!item.children || item.disabled) return item;
1224
+ return {
1225
+ key: item.key,
1226
+ label: item.label,
1227
+ type: 'group',
1228
+ children: toGroupItems(item.children)
1229
+ };
1230
+ });
1231
+ }
1209
1232
  function UserMenuPanel({ user, items, onItemClick }) {
1210
1233
  return /*#__PURE__*/ (0, jsx_runtime.jsxs)("div", {
1211
1234
  className: UserMenu_module.panel,
@@ -1251,10 +1274,48 @@ var __webpack_exports__ = {};
1251
1274
  onItemClick: onItemClick
1252
1275
  });
1253
1276
  }
1254
- function UserMenu({ user, items = [], onItemClick, caretIcon = /*#__PURE__*/ (0, jsx_runtime.jsx)(icons_namespaceObject.DownOutlined, {}) }) {
1277
+ function UserMenu({ user, items = [], onItemClick, caretIcon = /*#__PURE__*/ (0, jsx_runtime.jsx)(icons_namespaceObject.DownOutlined, {}), nestedItemsMode = 'auto' }) {
1255
1278
  const { t } = useCodebitWebAntdTranslation();
1256
1279
  const [open, setOpen] = (0, external_react_.useState)(false);
1280
+ const [maxHeight, setMaxHeight] = (0, external_react_.useState)(null);
1281
+ const [coarsePointer, setCoarsePointer] = (0, external_react_.useState)(false);
1282
+ const triggerRef = (0, external_react_.useRef)(null);
1257
1283
  const initials = getInitials(user?.name);
1284
+ (0, external_react_.useEffect)(()=>{
1285
+ const pointerQuery = globalThis.matchMedia?.(COARSE_POINTER_QUERY);
1286
+ if (!pointerQuery) return;
1287
+ const syncPointer = (event)=>setCoarsePointer(event.matches);
1288
+ setCoarsePointer(pointerQuery.matches);
1289
+ pointerQuery.addEventListener('change', syncPointer);
1290
+ return ()=>pointerQuery.removeEventListener('change', syncPointer);
1291
+ }, []);
1292
+ (0, external_react_.useEffect)(()=>{
1293
+ if (!open) return;
1294
+ const remeasure = ()=>setMaxHeight(computeMaxHeight(triggerRef.current));
1295
+ const visualViewport = globalThis.visualViewport;
1296
+ globalThis.addEventListener('resize', remeasure);
1297
+ globalThis.addEventListener('orientationchange', remeasure);
1298
+ visualViewport?.addEventListener('resize', remeasure);
1299
+ return ()=>{
1300
+ globalThis.removeEventListener('resize', remeasure);
1301
+ globalThis.removeEventListener('orientationchange', remeasure);
1302
+ visualViewport?.removeEventListener('resize', remeasure);
1303
+ };
1304
+ }, [
1305
+ open
1306
+ ]);
1307
+ const resolvedItems = (0, external_react_.useMemo)(()=>{
1308
+ const asGroups = 'group' === nestedItemsMode || 'auto' === nestedItemsMode && coarsePointer;
1309
+ return asGroups ? toGroupItems(items) : items;
1310
+ }, [
1311
+ items,
1312
+ nestedItemsMode,
1313
+ coarsePointer
1314
+ ]);
1315
+ const handleOpenChange = (nextOpen)=>{
1316
+ if (nextOpen) setMaxHeight(computeMaxHeight(triggerRef.current));
1317
+ setOpen(nextOpen);
1318
+ };
1258
1319
  const handleItemClick = (key)=>{
1259
1320
  setOpen(false);
1260
1321
  onItemClick?.(key);
@@ -1269,10 +1330,15 @@ var __webpack_exports__ = {};
1269
1330
  'click'
1270
1331
  ],
1271
1332
  open: open,
1272
- onOpenChange: setOpen,
1333
+ onOpenChange: handleOpenChange,
1273
1334
  placement: 'bottomRight',
1274
- popupRender: createPopupRender(user, items, handleItemClick),
1335
+ rootClassName: UserMenu_module.popupRoot,
1336
+ overlayStyle: null === maxHeight ? void 0 : {
1337
+ [MAX_HEIGHT_VAR]: `${maxHeight}px`
1338
+ },
1339
+ popupRender: createPopupRender(user, resolvedItems, handleItemClick),
1275
1340
  children: /*#__PURE__*/ (0, jsx_runtime.jsxs)(external_antd_namespaceObject.Space, {
1341
+ ref: triggerRef,
1276
1342
  className: UserMenu_module.trigger,
1277
1343
  role: 'button',
1278
1344
  tabIndex: 0,
@@ -1282,7 +1348,7 @@ var __webpack_exports__ = {};
1282
1348
  onKeyDown: (event)=>{
1283
1349
  if (!OPEN_KEYS.has(event.key)) return;
1284
1350
  event.preventDefault();
1285
- setOpen((wasOpen)=>!wasOpen);
1351
+ handleOpenChange(!open);
1286
1352
  },
1287
1353
  children: [
1288
1354
  avatar,
@@ -1304,7 +1370,12 @@ var __webpack_exports__ = {};
1304
1370
  }),
1305
1371
  items: external_prop_types_default().arrayOf(external_prop_types_default().object),
1306
1372
  onItemClick: external_prop_types_default().func,
1307
- caretIcon: external_prop_types_default().node
1373
+ caretIcon: external_prop_types_default().node,
1374
+ nestedItemsMode: external_prop_types_default().oneOf([
1375
+ 'auto',
1376
+ 'group',
1377
+ 'submenu'
1378
+ ])
1308
1379
  };
1309
1380
  function useErrorMessage() {
1310
1381
  const { t } = useCodebitWebAntdTranslation();
package/dist/index.css CHANGED
@@ -112,6 +112,16 @@ html.dark-theme {
112
112
  height: var(--antd-control-height-lg);
113
113
  }
114
114
 
115
+ .header-vgc67L .ant-space {
116
+ height: 100%;
117
+ }
118
+
119
+ .header-vgc67L .ant-space .ant-space-item {
120
+ align-items: center;
121
+ height: 100%;
122
+ display: inline-flex;
123
+ }
124
+
115
125
  .secondarySidebarTrigger-dD6WGB {
116
126
  margin-right: var(--antd-padding-xs);
117
127
  }
@@ -130,7 +140,7 @@ html.dark-theme {
130
140
  }
131
141
 
132
142
  .content-OZHp2h {
133
- padding: var(--antd-padding-xxs) var(--antd-padding) var(--antd-padding) var(--antd-padding);
143
+ padding: 0 var(--antd-padding-xs) var(--antd-padding-xs) var(--antd-padding-xs);
134
144
  position: relative;
135
145
  }
136
146
 
@@ -148,6 +158,10 @@ html.dark-theme {
148
158
  font-size: var(--antd-font-size-xs);
149
159
  }
150
160
 
161
+ .popupRoot-MxWPmX.ant-dropdown {
162
+ max-height: calc(var(--codebit-user-menu-max-height, calc(100vh - var(--antd-control-height-lg, 40px) - var(--antd-margin-xxs, 4px) - var(--antd-margin-xs, 8px))) - env(safe-area-inset-bottom, 0px));
163
+ }
164
+
151
165
  .panel-GCcfCo {
152
166
  background: var(--antd-color-bg-elevated);
153
167
  border: 1px solid var(--antd-color-border-secondary);
@@ -155,15 +169,22 @@ html.dark-theme {
155
169
  min-width: 200px;
156
170
  max-width: 280px;
157
171
  box-shadow: var(--antd-shadow-lg);
172
+ max-height: calc(var(--codebit-user-menu-max-height, calc(100vh - var(--antd-control-height-lg, 40px) - var(--antd-margin-xxs, 4px) - var(--antd-margin-xs, 8px))) - env(safe-area-inset-bottom, 0px));
158
173
  flex-direction: column;
159
174
  display: flex;
160
175
  overflow: hidden;
161
176
  }
162
177
 
178
+ .panel-GCcfCo > .divider-JJjJVy {
179
+ flex-shrink: 0;
180
+ margin: 0;
181
+ }
182
+
163
183
  .userInfo-fDuDn1 {
164
184
  padding: var(--antd-padding-xs) var(--antd-padding-sm);
165
185
  gap: var(--antd-margin-xxs);
166
186
  flex-direction: column;
187
+ flex-shrink: 0;
167
188
  display: flex;
168
189
  }
169
190
 
@@ -180,13 +201,13 @@ html.dark-theme {
180
201
  overflow: hidden;
181
202
  }
182
203
 
183
- .divider-JJjJVy {
184
- margin: 0;
185
- }
186
-
187
204
  .menu-cuORr_ {
205
+ overscroll-behavior: contain;
188
206
  background: none;
189
207
  border-inline-end: none;
208
+ flex: 0 auto;
209
+ min-height: 0;
210
+ overflow-y: auto;
190
211
  }
191
212
 
192
213
  .menu-cuORr_ .ant-dropdown-menu-title-content {
@@ -196,6 +217,31 @@ html.dark-theme {
196
217
  overflow: hidden;
197
218
  }
198
219
 
220
+ .menu-cuORr_ .ant-dropdown-menu-item-group-title {
221
+ text-overflow: ellipsis;
222
+ white-space: nowrap;
223
+ overflow: hidden;
224
+ }
225
+
226
+ @media (width <= 480px) {
227
+ .panel-GCcfCo {
228
+ max-width: min(360px, 100vw - var(--antd-margin-xs, 8px) - var(--antd-margin, 16px));
229
+ }
230
+ }
231
+
232
+ @media (pointer: coarse) {
233
+ .menu-cuORr_ .ant-dropdown-menu-item, .menu-cuORr_ .ant-dropdown-menu-submenu-title {
234
+ min-height: 44px;
235
+ }
236
+
237
+ .trigger-tlcps4 {
238
+ min-height: var(--antd-control-height-lg, 40px);
239
+ padding-inline: var(--antd-padding-xxs, 4px);
240
+ margin-inline: calc(var(--antd-padding-xxs, 4px) * -1);
241
+ align-items: center;
242
+ }
243
+ }
244
+
199
245
  .listCard-ISftFd {
200
246
  width: 100%;
201
247
  height: 100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebit-programando-solucoes/codebit-web-antd",
3
- "version": "1.1.50",
3
+ "version": "1.1.52",
4
4
  "main": "./dist/index.cjs",
5
5
  "type": "module",
6
6
  "types": "./src/index.d.ts",