@topconsultnpm/sdkui-react-beta 6.16.70 → 6.16.72

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.
@@ -15,6 +15,7 @@ interface ITMMessageBox extends ITMPopup {
15
15
  onButtonClick?: (e: ButtonNames) => void;
16
16
  parentId?: string;
17
17
  onClose?: () => void;
18
+ confirmOnEnter?: boolean;
18
19
  }
19
20
  interface ITMExceptionBox extends ITMPopup {
20
21
  exception?: any;
@@ -23,6 +24,6 @@ declare class TMExceptionBoxManager {
23
24
  static show({ title, exception }: ITMExceptionBox): void;
24
25
  }
25
26
  declare class TMMessageBoxManager {
26
- static show({ title, buttons, onButtonClick, message, parentId, resizable, onClose }: ITMMessageBox): void;
27
+ static show({ title, buttons, onButtonClick, message, parentId, resizable, onClose, confirmOnEnter }: ITMMessageBox): void;
27
28
  }
28
29
  export { TMExceptionBoxManager, TMMessageBoxManager };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useEffect, useMemo, useRef, useState } from 'react';
2
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import TMButton from './TMButton';
4
4
  import { SDKUI_Localizator, calcResponsiveSizes, getExceptionMessage } from '../../helper';
5
5
  import styled from 'styled-components';
@@ -174,7 +174,7 @@ const ResponsiveButton = styled(TMButton) `
174
174
  const ResponsiveMessageBody = ({ message, isMobile, MessageToolbar }) => {
175
175
  return (_jsxs(ResponsiveMessageContainer, { children: [_jsxs(ResponsiveMessageContent, { "$isMobile": isMobile, children: [_jsx(ResponsiveToppyImage, { "$isMobile": isMobile, src: toppy, alt: "Toppy" }), _jsx(ResponsiveMessageText, { "$isMobile": isMobile, children: typeof message === 'string' ? _jsx(Message, { msg: message }) : message })] }), _jsx(MessageToolbar, {})] }));
176
176
  };
177
- const TMMessageBox = ({ resizable = false, onButtonClick, title = 'TopMedia', message = '', buttons, onClose }) => {
177
+ const TMMessageBox = ({ resizable = false, onButtonClick, title = 'TopMedia', message = '', buttons, onClose, confirmOnEnter = false }) => {
178
178
  let deviceType = useDeviceType();
179
179
  const isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
180
180
  const [initialWidth, setInitialWidth] = useState('clamp(100px, 90vw, 400px)');
@@ -190,6 +190,21 @@ const TMMessageBox = ({ resizable = false, onButtonClick, title = 'TopMedia', me
190
190
  setBtns(arr);
191
191
  }
192
192
  }, [buttons]);
193
+ const handleKeyDown = useCallback((e) => {
194
+ if (confirmOnEnter && e.key === 'Enter') {
195
+ e.preventDefault();
196
+ if (btns.includes(ButtonNames.YES)) {
197
+ onButtonClick?.(ButtonNames.YES);
198
+ setIsVisible(false);
199
+ }
200
+ }
201
+ }, [confirmOnEnter, btns, onButtonClick]);
202
+ useEffect(() => {
203
+ if (!confirmOnEnter)
204
+ return;
205
+ window.addEventListener('keydown', handleKeyDown);
206
+ return () => window.removeEventListener('keydown', handleKeyDown);
207
+ }, [confirmOnEnter, handleKeyDown]);
193
208
  const MessageToolbar = () => {
194
209
  return (_jsxs(StyledMessageToolbar, { children: [(btns.includes(ButtonNames.OK) || btns.includes(ButtonNames.YES)) &&
195
210
  _jsx(ResponsiveButton, { fontSize: 'clamp(12px, 2vw, 1.1rem)', color: 'primaryOutline', btnStyle: 'text', onClick: () => {
@@ -245,7 +260,7 @@ class TMExceptionBoxManager {
245
260
  }
246
261
  }
247
262
  class TMMessageBoxManager {
248
- static show({ title, buttons, onButtonClick, message, parentId, resizable, onClose }) {
263
+ static show({ title, buttons, onButtonClick, message, parentId, resizable, onClose, confirmOnEnter = false }) {
249
264
  let container = document.createElement('div');
250
265
  if (parentId) {
251
266
  const parent = document.getElementById(parentId);
@@ -260,7 +275,7 @@ class TMMessageBoxManager {
260
275
  document.body.appendChild(container);
261
276
  }
262
277
  const root = ReactDOM.createRoot(container);
263
- root.render(_jsx(TMDeviceProvider, { children: React.createElement(TMMessageBox, { title, buttons, onButtonClick, message, resizable, onClose }) }));
278
+ root.render(_jsx(TMDeviceProvider, { children: React.createElement(TMMessageBox, { title, buttons, onButtonClick, message, resizable, onClose, confirmOnEnter }) }));
264
279
  }
265
280
  }
266
281
  export { TMExceptionBoxManager, TMMessageBoxManager };
@@ -124,10 +124,27 @@ export const TMFileViewer = ({ fileBlob, isResizingActive }) => {
124
124
  useEffect(() => {
125
125
  const checkIsMobile = () => {
126
126
  const userAgent = navigator.userAgent || navigator.vendor || window.opera;
127
- const isMobileDevice = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
127
+ // Only detect actual mobile/tablet devices, NOT desktop browsers
128
+ const isMobileDevice =
129
+ // Traditional mobile detection (phones and tablets)
130
+ /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent) ||
131
+ // Additional Android tablet detection (covers tablets in landscape)
132
+ /android.*tablet|android.*mobile/i.test(userAgent) ||
133
+ // Touch-only devices (excludes laptops with touchscreen)
134
+ (('ontouchstart' in window || navigator.maxTouchPoints > 0) &&
135
+ !/Windows NT|Macintosh|Linux/.test(userAgent)) ||
136
+ // Small screen mobile devices only
137
+ (window.screen.width <= 768 && /Mobi|Android/i.test(userAgent));
128
138
  setIsMobile(isMobileDevice);
129
139
  };
130
140
  checkIsMobile();
141
+ // Listen for orientation changes (important for tablets)
142
+ window.addEventListener('orientationchange', checkIsMobile);
143
+ window.addEventListener('resize', checkIsMobile);
144
+ return () => {
145
+ window.removeEventListener('orientationchange', checkIsMobile);
146
+ window.removeEventListener('resize', checkIsMobile);
147
+ };
131
148
  }, []);
132
149
  useEffect(() => {
133
150
  if (fileBlob) {
@@ -170,7 +187,17 @@ export const TMFileViewer = ({ fileBlob, isResizingActive }) => {
170
187
  if (fileBlob.type.includes('image')) {
171
188
  return (_jsx(ImageViewer, { fileBlob: fileBlob, alt: '' }));
172
189
  }
173
- if (fileType === 'application/pdf' && isMobile) {
190
+ // Check if device has limited PDF support (only for mobile/tablet devices)
191
+ const hasLimitedPDFSupport = () => {
192
+ const userAgent = navigator.userAgent || navigator.vendor || window.opera;
193
+ // Only check for actual mobile/tablet devices, NOT desktop browsers
194
+ const isAndroidMobile = /android/i.test(userAgent) && (/mobile|tablet/i.test(userAgent) || window.screen.width <= 1024);
195
+ const isIOSMobile = /iphone|ipad|ipod/i.test(userAgent);
196
+ const isOtherMobile = /webos|blackberry|iemobile|opera mini/i.test(userAgent);
197
+ // Return true only for actual mobile devices, not desktop browsers
198
+ return isMobile && (isAndroidMobile || isIOSMobile || isOtherMobile);
199
+ };
200
+ if (fileType === 'application/pdf' && hasLimitedPDFSupport()) {
174
201
  return (_jsxs("div", { style: {
175
202
  padding: '40px',
176
203
  textAlign: 'center',
@@ -37,6 +37,7 @@ import TMAccordion from '../../base/TMAccordion';
37
37
  import TMDataGridExportForm from '../../base/TMDataGridExportForm';
38
38
  import TMSearchResultFloatingActionButton from './TMSearchResultFloatingActionButton';
39
39
  import ShowAlert from '../../base/TMAlert';
40
+ import TMSpinner from '../../base/TMSpinner';
40
41
  //#region Helper Methods
41
42
  export const getSearchResultCountersSingleCategory = (searchResults) => {
42
43
  // let totDcmtTypes = searchResults.length;
@@ -500,14 +501,62 @@ const TMSearchResultGrid = ({ openInOffice, inputFocusedItem, showSearch, allowM
500
501
  setSelectedRowKeys(inputSelectedItemsRowIndex);
501
502
  }, [inputSelectedItems]);
502
503
  const onKeyDown = useCallback((e) => {
503
- if (e.event?.key === 'Delete') {
504
- if (selectedRowKeys.length === 0)
504
+ // Check if the pressed key is the "Delete" key.
505
+ if (e.event?.key === 'Delete' && !showExportForm) {
506
+ // Get the selected or focused documents/items from the grid.
507
+ const dcmts = getSelectedDcmtsOrFocused(inputSelectedItems, inputFocusedItem);
508
+ // If there are no selected or focused items, do nothing.
509
+ if (dcmts === undefined || dcmts.length === 0)
505
510
  return;
506
- setDataSource((prevDataSource = []) => prevDataSource.filter((data) => !selectedRowKeys.includes(data.rowIndex)));
507
- setSelectedRowKeys([]);
508
- setFocusedItem(undefined);
511
+ // --- CASE 1: Only one item selected ---
512
+ if (dcmts.length === 1) {
513
+ const current = dcmts[0];
514
+ // If the selected item has a row index (valid entry)
515
+ if (current.rowIndex !== undefined) {
516
+ TMSpinner.show({ description: SDKUI_Localizator.RemovingFromList });
517
+ const currentDataSource = dataSource ?? [];
518
+ setTimeout(() => {
519
+ // Remove the selected item from the data source by filtering it out.
520
+ setDataSource(currentDataSource.filter((data) => data.rowIndex !== current.rowIndex));
521
+ // Clear any selection, focused item, and update callbacks.
522
+ onSelectionChanged?.([]);
523
+ setSelectedRowKeys([]);
524
+ onFocusedItemChanged?.(undefined);
525
+ setFocusedItem(undefined);
526
+ TMSpinner.hide();
527
+ }, 500);
528
+ }
529
+ return;
530
+ }
531
+ // --- CASE 2: Multiple items selected ---
532
+ if (dcmts.length > 1) {
533
+ // Show a confirmation message box before deleting multiple items.
534
+ TMMessageBoxManager.show({
535
+ buttons: [ButtonNames.YES, ButtonNames.NO],
536
+ title: SDKUI_Localizator.RemoveFromList,
537
+ message: `${SDKUI_Localizator.ConfirmSelectedDocumentsMessage.replaceParams(dcmts.length)}`,
538
+ confirmOnEnter: true,
539
+ onButtonClick(e) {
540
+ // Only proceed if user clicks "YES"
541
+ if (e !== ButtonNames.YES)
542
+ return;
543
+ TMSpinner.show({ description: SDKUI_Localizator.RemovingFromList });
544
+ const currentDataSource = dataSource ?? [];
545
+ setTimeout(() => {
546
+ // Remove all selected items from the data source.
547
+ setDataSource(currentDataSource.filter((data) => !dcmts.some((d) => d.rowIndex === data.rowIndex)));
548
+ // Clear selections and focus after deletion.
549
+ onSelectionChanged?.([]);
550
+ setSelectedRowKeys([]);
551
+ onFocusedItemChanged?.(undefined);
552
+ setFocusedItem(undefined);
553
+ TMSpinner.hide();
554
+ }, 500);
555
+ },
556
+ });
557
+ }
509
558
  }
510
- }, [selectedRowKeys]);
559
+ }, [inputSelectedItems, inputFocusedItem, dataSource]);
511
560
  const cellRender = useCallback((cellData, dataDomain, dataListID, dataListViewMode) => {
512
561
  if (!cellData || cellData.value === undefined)
513
562
  return null;
@@ -17,10 +17,10 @@ export const getSelectedDcmtsOrFocused = (selectedItems, focusedItem, fileFormat
17
17
  if (selectedItems.length <= 0 && !focusedItem)
18
18
  return [];
19
19
  if (selectedItems.length > 0) {
20
- return selectedItems.map((item) => { return { TID: item.TID, DID: item.DID, FILEEXT: item.FILEEXT, fileFormat: fileFormat }; });
20
+ return selectedItems.map((item) => { return { TID: item.TID, DID: item.DID, FILEEXT: item.FILEEXT, fileFormat: fileFormat, rowIndex: item.rowIndex }; });
21
21
  }
22
22
  else if (focusedItem !== undefined) {
23
- return [{ TID: focusedItem.TID, DID: focusedItem.DID, FILEEXT: focusedItem.FILEEXT, fileFormat: fileFormat }];
23
+ return [{ TID: focusedItem.TID, DID: focusedItem.DID, FILEEXT: focusedItem.FILEEXT, fileFormat: fileFormat, rowIndex: focusedItem.rowIndex }];
24
24
  }
25
25
  return [];
26
26
  };
@@ -292,7 +292,7 @@ const TMBlogs = (props) => {
292
292
  disabled: false,
293
293
  },
294
294
  ];
295
- return menuItemsElements;
295
+ return menuItemsElements.filter(item => item.visible);
296
296
  }, [isHeaderHidden, localShowId, setLocalShowId, focusedBlog, focusedAttachment, showDcmtForm]);
297
297
  useEffect(() => {
298
298
  setUiId(genUniqueId());
@@ -403,8 +403,10 @@ export declare class SDKUI_Localizator {
403
403
  static get RelationManyToMany(): "Folge viele mit vielen" | "Relation many to many" | "Correlación muchos a muchos" | "Corrélation plusieurs à plusieurs" | "Muitos para muitos relação" | "Correlazione molti a molti";
404
404
  static get RelationType(): "Art der Beziehung" | "Relation type" | "Tipo de relación" | "Type de relation" | "Tipo de relacionamento" | "Tipo di relazione";
405
405
  static get RemoveFromCache(): string;
406
+ static get RemoveFromList(): string;
406
407
  static get RemoveFromWorkgroup(): string;
407
408
  static get RemoveNamedPreferredCredentials(): "Möchten Sie die Anmeldedaten '{{0}}' entfernen, die als bevorzugt festgelegt wurden?" | "Do you want to remove the '{{0}}' credentials set as preferred?" | "¿Quieres eliminar las credenciales '{{0}}' configuradas como preferidas?" | "Voulez-vous supprimer les identifiants '{{0}}' définis comme préférés ?" | "Deseja remover as credenciais '{{0}}' definidas como preferidas?" | "Vuoi rimuovere le credenziali '{{0}}' impostate come preferite?";
409
+ static get RemovingFromList(): string;
408
410
  static get RememberCredentials(): "Anmeldedaten merken" | "Remember credentials" | "Recordar credenciales" | "Se souvenir des identifiants" | "Lembrar credenciais" | "Ricorda credenziali";
409
411
  static get ReopenDocument(): string;
410
412
  static get ReplaceDocument(): "Dokument ersetzen" | "Replace Document" | "Reemplazar Documento" | "Remplacer le Document" | "Substituir Documento" | "Sostituisci Documento";
@@ -3988,6 +3988,16 @@ export class SDKUI_Localizator {
3988
3988
  default: return "Rimuovi elemento dalla cache";
3989
3989
  }
3990
3990
  }
3991
+ static get RemoveFromList() {
3992
+ switch (this._cultureID) {
3993
+ case CultureIDs.De_DE: return "Aus der Liste entfernen";
3994
+ case CultureIDs.En_US: return "Remove from list";
3995
+ case CultureIDs.Es_ES: return "Eliminar de la lista";
3996
+ case CultureIDs.Fr_FR: return "Supprimer de la liste";
3997
+ case CultureIDs.Pt_PT: return "Remover da lista";
3998
+ default: return "Rimuovere dalla lista";
3999
+ }
4000
+ }
3991
4001
  static get RemoveFromWorkgroup() {
3992
4002
  switch (this._cultureID) {
3993
4003
  case CultureIDs.De_DE: return "Aus der Arbeitsgruppe entfernen";
@@ -4014,6 +4024,16 @@ export class SDKUI_Localizator {
4014
4024
  return "Vuoi rimuovere le credenziali '{{0}}' impostate come preferite?";
4015
4025
  }
4016
4026
  }
4027
+ static get RemovingFromList() {
4028
+ switch (this._cultureID) {
4029
+ case CultureIDs.De_DE: return "Wird aus der Liste entfernt";
4030
+ case CultureIDs.En_US: return "Removing from list";
4031
+ case CultureIDs.Es_ES: return "Eliminando de la lista";
4032
+ case CultureIDs.Fr_FR: return "Suppression de la liste en cours";
4033
+ case CultureIDs.Pt_PT: return "Removendo da lista";
4034
+ default: return "Rimozione dalla lista in corso";
4035
+ }
4036
+ }
4017
4037
  static get RememberCredentials() {
4018
4038
  switch (this._cultureID) {
4019
4039
  case CultureIDs.De_DE: return "Anmeldedaten merken";
package/lib/ts/types.d.ts CHANGED
@@ -76,6 +76,7 @@ export type DcmtInfo = {
76
76
  metadataValues?: MetadataValueDescriptorEx[];
77
77
  fileName?: string;
78
78
  workingGroupId?: number;
79
+ rowIndex?: number;
79
80
  };
80
81
  export declare class MetadataValueDescriptorEx extends MetadataValueDescriptor {
81
82
  tid?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react-beta",
3
- "version": "6.16.70",
3
+ "version": "6.16.72",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",