@codebit-programando-solucoes/codebit-web-antd 1.1.49 → 1.1.51

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
@@ -779,6 +779,7 @@ var __webpack_exports__ = {};
779
779
  document.documentElement.classList.remove('dark-theme');
780
780
  target.classList.remove('dark-theme');
781
781
  }
782
+ document.documentElement.style.colorScheme = isDarkMode ? 'dark' : 'light';
782
783
  document.documentElement.style.setProperty('--color-primary', resolvedColors.primary[isDarkMode ? 'dark' : 'light']);
783
784
  }, [
784
785
  isDarkMode,
@@ -1169,11 +1170,6 @@ var __webpack_exports__ = {};
1169
1170
  const ThemeToggle = ()=>{
1170
1171
  const config = (0, external_react_.useContext)(CodebitThemeContext);
1171
1172
  if (!config) throw new Error('ThemeToggle must be used within CodebitThemeContext');
1172
- (0, external_react_.useEffect)(()=>{
1173
- document.documentElement.style.colorScheme = config.isDarkMode ? 'dark' : 'light';
1174
- }, [
1175
- config.isDarkMode
1176
- ]);
1177
1173
  return /*#__PURE__*/ (0, jsx_runtime.jsx)(external_antd_namespaceObject.Switch, {
1178
1174
  theme: config.isDarkMode ? 'dark' : 'light',
1179
1175
  checked: config.isDarkMode,
@@ -1186,17 +1182,23 @@ var __webpack_exports__ = {};
1186
1182
  trigger: "trigger-tlcps4",
1187
1183
  avatar: "avatar-Tmnqa_",
1188
1184
  caret: "caret-OWS8_0",
1185
+ popupRoot: "popupRoot-MxWPmX",
1189
1186
  panel: "panel-GCcfCo",
1187
+ divider: "divider-JJjJVy",
1190
1188
  userInfo: "userInfo-fDuDn1",
1191
1189
  userName: "userName-sIs1iq",
1192
1190
  userSubtitle: "userSubtitle-Fflhaw",
1193
- divider: "divider-JJjJVy",
1194
1191
  menu: "menu-cuORr_"
1195
1192
  };
1196
1193
  const OPEN_KEYS = new Set([
1197
1194
  'Enter',
1198
1195
  ' '
1199
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';
1200
1202
  const { Text } = external_antd_namespaceObject.Typography;
1201
1203
  function getInitials(name) {
1202
1204
  const parts = name?.trim().split(/\s+/).filter(Boolean) ?? [];
@@ -1210,6 +1212,23 @@ var __webpack_exports__ = {};
1210
1212
  ];
1211
1213
  return `${first[0]}${last[0]}`.toUpperCase();
1212
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
+ }
1213
1232
  function UserMenuPanel({ user, items, onItemClick }) {
1214
1233
  return /*#__PURE__*/ (0, jsx_runtime.jsxs)("div", {
1215
1234
  className: UserMenu_module.panel,
@@ -1255,10 +1274,48 @@ var __webpack_exports__ = {};
1255
1274
  onItemClick: onItemClick
1256
1275
  });
1257
1276
  }
1258
- 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' }) {
1259
1278
  const { t } = useCodebitWebAntdTranslation();
1260
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);
1261
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
+ };
1262
1319
  const handleItemClick = (key)=>{
1263
1320
  setOpen(false);
1264
1321
  onItemClick?.(key);
@@ -1273,10 +1330,15 @@ var __webpack_exports__ = {};
1273
1330
  'click'
1274
1331
  ],
1275
1332
  open: open,
1276
- onOpenChange: setOpen,
1333
+ onOpenChange: handleOpenChange,
1277
1334
  placement: 'bottomRight',
1278
- 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),
1279
1340
  children: /*#__PURE__*/ (0, jsx_runtime.jsxs)(external_antd_namespaceObject.Space, {
1341
+ ref: triggerRef,
1280
1342
  className: UserMenu_module.trigger,
1281
1343
  role: 'button',
1282
1344
  tabIndex: 0,
@@ -1286,7 +1348,7 @@ var __webpack_exports__ = {};
1286
1348
  onKeyDown: (event)=>{
1287
1349
  if (!OPEN_KEYS.has(event.key)) return;
1288
1350
  event.preventDefault();
1289
- setOpen((wasOpen)=>!wasOpen);
1351
+ handleOpenChange(!open);
1290
1352
  },
1291
1353
  children: [
1292
1354
  avatar,
@@ -1308,7 +1370,12 @@ var __webpack_exports__ = {};
1308
1370
  }),
1309
1371
  items: external_prop_types_default().arrayOf(external_prop_types_default().object),
1310
1372
  onItemClick: external_prop_types_default().func,
1311
- 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
+ ])
1312
1379
  };
1313
1380
  function useErrorMessage() {
1314
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
+ vertical-align: middle;
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.49",
3
+ "version": "1.1.51",
4
4
  "main": "./dist/index.cjs",
5
5
  "type": "module",
6
6
  "types": "./src/index.d.ts",