@oat-sa/tao-core-ui 3.24.2 → 3.24.4

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.
@@ -95,7 +95,6 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
95
95
  const $error = $$1('.asset-search-error', $fileSelector);
96
96
  const $errorMessage = $$1('.asset-search-error-message', $error);
97
97
  const $retry = $$1('.asset-search-retry', $error);
98
- const $resultsRegion = $$1('.files-wrapper', $fileSelector);
99
98
  const $paginationContainer = $$1('.pagination-bottom', $container);
100
99
  const $uploadSwitcher = $$1('.upload-switcher', $fileSelector);
101
100
  const $modalTitle = $container.closest('.resourcemgr').find('> h2').first();
@@ -169,8 +168,9 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
169
168
  * Keeps the previous display name until a real label arrives (avoids URI flash).
170
169
  * @param {string} [folderLabel]
171
170
  * @param {Object} [content]
171
+ * @param {boolean} [includesSubfolders]
172
172
  */
173
- function updateScopeDisplay(folderLabel, content) {
173
+ function updateScopeDisplay(folderLabel, content, includesSubfolders) {
174
174
  if (isFolderDisplayLabel(folderLabel)) {
175
175
  scopeLabel = String(folderLabel).trim();
176
176
  }
@@ -181,7 +181,8 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
181
181
  } else {
182
182
  $scopeName.removeAttr('title');
183
183
  }
184
- if (folderHasSubfolders(content)) {
184
+ const showSubfolders = includesSubfolders !== false && folderHasSubfolders(content);
185
+ if (showSubfolders) {
185
186
  $scopeSubfolders.removeClass('hidden').removeAttr('hidden');
186
187
  } else {
187
188
  $scopeSubfolders.addClass('hidden').attr('hidden', 'hidden');
@@ -457,6 +458,7 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
457
458
  * Detach asset-search handlers and destroy Advanced Search.
458
459
  */
459
460
  function teardown() {
461
+ setResultsTableLoading(false);
460
462
  requestSeq += 1;
461
463
  searchMode = false;
462
464
  $fileSelector.removeClass('search-mode');
@@ -591,6 +593,7 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
591
593
  if ($modalTitle.length && browseModalTitle) {
592
594
  $modalTitle.text(browseModalTitle);
593
595
  }
596
+ setResultsTableLoading(false);
594
597
  hideLoading();
595
598
  hideError();
596
599
  $paginationContainer.empty();
@@ -601,9 +604,12 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
601
604
  }
602
605
 
603
606
  /**
604
- * Execute the scoped search request.
605
- * @param {{quiet?: boolean}} [runOptions]
607
+ * Toggle the file-table loading overlay during search (including pagination).
608
+ * @param {Boolean} loading
606
609
  */
610
+ function setResultsTableLoading(loading) {
611
+ $container.trigger(`searchtableloading.${ns}`, [!!loading]);
612
+ }
607
613
  function runSearch(runOptions) {
608
614
  if (!hasActiveSearch()) {
609
615
  exitSearchMode();
@@ -611,9 +617,8 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
611
617
  }
612
618
  const quiet = Boolean(runOptions && runOptions.quiet);
613
619
  const seq = ++requestSeq;
614
- if (quiet) {
615
- $resultsRegion.attr('aria-busy', 'true');
616
- } else {
620
+ setResultsTableLoading(true);
621
+ if (!quiet) {
617
622
  showLoading();
618
623
  }
619
624
  hideError();
@@ -637,9 +642,8 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
637
642
  if (seq !== requestSeq) {
638
643
  return;
639
644
  }
640
- if (quiet) {
641
- $resultsRegion.attr('aria-busy', 'false');
642
- } else {
645
+ setResultsTableLoading(false);
646
+ if (!quiet) {
643
647
  hideLoading();
644
648
  }
645
649
  let normalized = assetSearchContract.normalizeSearchResponse(response);
@@ -668,6 +672,7 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
668
672
  total = normalized.total;
669
673
  page = normalized.page;
670
674
  pageSize = normalized.pageSize;
675
+ updateScopeDisplay(scopeLabel, null, normalized.includesSubfolders);
671
676
  let emptyMessage = '';
672
677
  if (normalized.total === 0) {
673
678
  if (normalized.metadataUnsupported) {
@@ -693,19 +698,18 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
693
698
  emptyMessage
694
699
  }]);
695
700
  renderPagination();
696
- }).fail(function () {
701
+ }).fail(function (jqXHR) {
697
702
  if (seq !== requestSeq) {
698
703
  return;
699
704
  }
700
- if (quiet) {
701
- $resultsRegion.attr('aria-busy', 'false');
702
- } else {
705
+ setResultsTableLoading(false);
706
+ if (!quiet) {
703
707
  hideLoading();
704
708
  }
705
709
  page = 1;
706
710
  total = 0;
707
711
  $paginationContainer.empty();
708
- const message = __('Unable to search assets. Please try again.');
712
+ const message = assetSearchContract.resolveServiceUnavailableSearchMessage(jqXHR, __('Unable to search assets. Please try again.'));
709
713
  showError(message);
710
714
  $container.trigger(`searchresults.${ns}`, [{
711
715
  query,
@@ -755,7 +759,6 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
755
759
  $searchButton.prop('disabled', true).addClass('is-searching').attr('aria-busy', 'true');
756
760
  $searchButtonSpinner.removeClass('hidden');
757
761
  $searchButtonLabel.text(__('Searching'));
758
- $resultsRegion.attr('aria-busy', 'true');
759
762
  }
760
763
 
761
764
  /**
@@ -766,7 +769,6 @@ define(['jquery', 'i18n', 'ui/pagination', 'ui/searchModal/advancedSearch', 'han
766
769
  $searchButton.removeClass('is-searching').attr('aria-busy', 'false');
767
770
  $searchButtonSpinner.addClass('hidden');
768
771
  $searchButtonLabel.text(__('Search'));
769
- $resultsRegion.attr('aria-busy', 'false');
770
772
  updateSearchActionState();
771
773
  }
772
774
 
@@ -271,10 +271,36 @@ define(['exports'], function (exports) { 'use strict';
271
271
  items,
272
272
  total,
273
273
  page: Number.isFinite(page) && page > 0 ? page : 1,
274
- pageSize: Number.isFinite(pageSize) && pageSize > 0 ? pageSize : DEFAULT_PAGE_SIZE
274
+ pageSize: Number.isFinite(pageSize) && pageSize > 0 ? pageSize : DEFAULT_PAGE_SIZE,
275
+ includesSubfolders: body.includesSubfolders !== false
275
276
  };
276
277
  }
277
278
 
279
+ /**
280
+ * Prefer a server-provided 503 message when the search service is unavailable.
281
+ *
282
+ * @param {*} jqXHR
283
+ * @param {string} fallbackMessage
284
+ * @returns {string}
285
+ */
286
+ function resolveServiceUnavailableSearchMessage(jqXHR, fallbackMessage) {
287
+ if (!jqXHR || jqXHR.status !== 503) {
288
+ return fallbackMessage;
289
+ }
290
+ let body = jqXHR.responseJSON;
291
+ if (!body && jqXHR.responseText) {
292
+ try {
293
+ body = typeof jqXHR.responseText === 'string' ? JSON.parse(jqXHR.responseText) : jqXHR.responseText;
294
+ } catch (err) {
295
+ body = null;
296
+ }
297
+ }
298
+ if (body && body.message) {
299
+ return body.message;
300
+ }
301
+ return fallbackMessage;
302
+ }
303
+
278
304
  /**
279
305
  * Browse (`ItemContent/files`) responses use `children` and ignore `query`.
280
306
  * Real search responses expose `items`.
@@ -498,6 +524,7 @@ define(['exports'], function (exports) { 'use strict';
498
524
  exports.isBrowseShapedSearchPayload = isBrowseShapedSearchPayload;
499
525
  exports.isMetadataCriterionComplete = isMetadataCriterionComplete;
500
526
  exports.normalizeSearchResponse = normalizeSearchResponse;
527
+ exports.resolveServiceUnavailableSearchMessage = resolveServiceUnavailableSearchMessage;
501
528
  exports.sortAssetItems = sortAssetItems;
502
529
 
503
530
  Object.defineProperty(exports, '__esModule', { value: true });
@@ -325,7 +325,7 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
325
325
  });
326
326
 
327
327
  //load the content of the ROOT
328
- const showInitialListLoading = !searchMode && root === 'local';
328
+ const showInitialListLoading = !searchMode;
329
329
  if (showInitialListLoading) {
330
330
  setFilesLoading(true);
331
331
  }
@@ -775,6 +775,9 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
775
775
  const needTree = !existing || !existing.children && !existing.empty;
776
776
  const cacheKey = listCacheKey();
777
777
  const needList = !existing || existing.listCacheKey !== cacheKey;
778
+ if (needList && !searchMode) {
779
+ setFilesLoading(true);
780
+ }
778
781
  const treePromise = needTree ? (setTreeLoading(true), browseRequest(path, {
779
782
  part: 'tree',
780
783
  depth: 1,
@@ -803,6 +806,9 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
803
806
  }
804
807
  cb(getByExactPath(tree, path) || getByPath(tree, path));
805
808
  }).catch(function () {
809
+ if (!searchMode) {
810
+ setFilesLoading(false);
811
+ }
806
812
  cb(null);
807
813
  });
808
814
  return;
@@ -352,6 +352,9 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
352
352
  }
353
353
  applyFilesListLoading(loading);
354
354
  });
355
+ $container.on(`searchtableloading.${ns}`, function (e, loading) {
356
+ applyFilesListLoading(loading);
357
+ });
355
358
 
356
359
  //update current folder
357
360
  $container.on(`folderselect.${ns}`, function (e, fullPath, data, activePath, content) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oat-sa/tao-core-ui",
3
- "version": "3.24.2",
3
+ "version": "3.24.4",
4
4
  "displayName": "TAO Core UI",
5
5
  "description": "UI libraries of TAO",
6
6
  "scripts": {
@@ -40,7 +40,8 @@ import {
40
40
  buildSearchRequestParams,
41
41
  canSubmitScopedAssetSearch,
42
42
  isBrowseShapedSearchPayload,
43
- normalizeSearchResponse
43
+ normalizeSearchResponse,
44
+ resolveServiceUnavailableSearchMessage
44
45
  } from 'ui/resourcemgr/assetSearchContract';
45
46
  import 'ui/searchModal/css/advancedSearch.css';
46
47
 
@@ -99,7 +100,6 @@ export default function assetSearch(options) {
99
100
  const $error = $('.asset-search-error', $fileSelector);
100
101
  const $errorMessage = $('.asset-search-error-message', $error);
101
102
  const $retry = $('.asset-search-retry', $error);
102
- const $resultsRegion = $('.files-wrapper', $fileSelector);
103
103
  const $paginationContainer = $('.pagination-bottom', $container);
104
104
  const $uploadSwitcher = $('.upload-switcher', $fileSelector);
105
105
  const $modalTitle = $container.closest('.resourcemgr').find('> h2').first();
@@ -177,8 +177,9 @@ export default function assetSearch(options) {
177
177
  * Keeps the previous display name until a real label arrives (avoids URI flash).
178
178
  * @param {string} [folderLabel]
179
179
  * @param {Object} [content]
180
+ * @param {boolean} [includesSubfolders]
180
181
  */
181
- function updateScopeDisplay(folderLabel, content) {
182
+ function updateScopeDisplay(folderLabel, content, includesSubfolders) {
182
183
  if (isFolderDisplayLabel(folderLabel)) {
183
184
  scopeLabel = String(folderLabel).trim();
184
185
  }
@@ -189,7 +190,9 @@ export default function assetSearch(options) {
189
190
  } else {
190
191
  $scopeName.removeAttr('title');
191
192
  }
192
- if (folderHasSubfolders(content)) {
193
+ const showSubfolders =
194
+ includesSubfolders !== false && folderHasSubfolders(content);
195
+ if (showSubfolders) {
193
196
  $scopeSubfolders.removeClass('hidden').removeAttr('hidden');
194
197
  } else {
195
198
  $scopeSubfolders.addClass('hidden').attr('hidden', 'hidden');
@@ -500,6 +503,7 @@ export default function assetSearch(options) {
500
503
  * Detach asset-search handlers and destroy Advanced Search.
501
504
  */
502
505
  function teardown() {
506
+ setResultsTableLoading(false);
503
507
  requestSeq += 1;
504
508
  searchMode = false;
505
509
  $fileSelector.removeClass('search-mode');
@@ -637,6 +641,7 @@ export default function assetSearch(options) {
637
641
  if ($modalTitle.length && browseModalTitle) {
638
642
  $modalTitle.text(browseModalTitle);
639
643
  }
644
+ setResultsTableLoading(false);
640
645
  hideLoading();
641
646
  hideError();
642
647
  $paginationContainer.empty();
@@ -648,9 +653,13 @@ export default function assetSearch(options) {
648
653
  }
649
654
 
650
655
  /**
651
- * Execute the scoped search request.
652
- * @param {{quiet?: boolean}} [runOptions]
656
+ * Toggle the file-table loading overlay during search (including pagination).
657
+ * @param {Boolean} loading
653
658
  */
659
+ function setResultsTableLoading(loading) {
660
+ $container.trigger(`searchtableloading.${ns}`, [!!loading]);
661
+ }
662
+
654
663
  function runSearch(runOptions) {
655
664
  if (!hasActiveSearch()) {
656
665
  exitSearchMode();
@@ -659,9 +668,8 @@ export default function assetSearch(options) {
659
668
 
660
669
  const quiet = Boolean(runOptions && runOptions.quiet);
661
670
  const seq = ++requestSeq;
662
- if (quiet) {
663
- $resultsRegion.attr('aria-busy', 'true');
664
- } else {
671
+ setResultsTableLoading(true);
672
+ if (!quiet) {
665
673
  showLoading();
666
674
  }
667
675
  hideError();
@@ -688,9 +696,8 @@ export default function assetSearch(options) {
688
696
  if (seq !== requestSeq) {
689
697
  return;
690
698
  }
691
- if (quiet) {
692
- $resultsRegion.attr('aria-busy', 'false');
693
- } else {
699
+ setResultsTableLoading(false);
700
+ if (!quiet) {
694
701
  hideLoading();
695
702
  }
696
703
  let normalized = normalizeSearchResponse(response);
@@ -719,6 +726,7 @@ export default function assetSearch(options) {
719
726
  total = normalized.total;
720
727
  page = normalized.page;
721
728
  pageSize = normalized.pageSize;
729
+ updateScopeDisplay(scopeLabel, null, normalized.includesSubfolders);
722
730
 
723
731
  let emptyMessage = '';
724
732
  if (normalized.total === 0) {
@@ -749,19 +757,21 @@ export default function assetSearch(options) {
749
757
 
750
758
  renderPagination();
751
759
  })
752
- .fail(function () {
760
+ .fail(function (jqXHR) {
753
761
  if (seq !== requestSeq) {
754
762
  return;
755
763
  }
756
- if (quiet) {
757
- $resultsRegion.attr('aria-busy', 'false');
758
- } else {
764
+ setResultsTableLoading(false);
765
+ if (!quiet) {
759
766
  hideLoading();
760
767
  }
761
768
  page = 1;
762
769
  total = 0;
763
770
  $paginationContainer.empty();
764
- const message = __('Unable to search assets. Please try again.');
771
+ const message = resolveServiceUnavailableSearchMessage(
772
+ jqXHR,
773
+ __('Unable to search assets. Please try again.')
774
+ );
765
775
  showError(message);
766
776
  $container.trigger(`searchresults.${ns}`, [
767
777
  {
@@ -813,7 +823,6 @@ export default function assetSearch(options) {
813
823
  $searchButton.prop('disabled', true).addClass('is-searching').attr('aria-busy', 'true');
814
824
  $searchButtonSpinner.removeClass('hidden');
815
825
  $searchButtonLabel.text(__('Searching'));
816
- $resultsRegion.attr('aria-busy', 'true');
817
826
  }
818
827
 
819
828
  /**
@@ -824,7 +833,6 @@ export default function assetSearch(options) {
824
833
  $searchButton.removeClass('is-searching').attr('aria-busy', 'false');
825
834
  $searchButtonSpinner.addClass('hidden');
826
835
  $searchButtonLabel.text(__('Search'));
827
- $resultsRegion.attr('aria-busy', 'false');
828
836
  updateSearchActionState();
829
837
  }
830
838
 
@@ -279,10 +279,37 @@ export function normalizeSearchResponse(payload) {
279
279
  items,
280
280
  total,
281
281
  page: Number.isFinite(page) && page > 0 ? page : 1,
282
- pageSize: Number.isFinite(pageSize) && pageSize > 0 ? pageSize : DEFAULT_PAGE_SIZE
282
+ pageSize: Number.isFinite(pageSize) && pageSize > 0 ? pageSize : DEFAULT_PAGE_SIZE,
283
+ includesSubfolders: body.includesSubfolders !== false
283
284
  };
284
285
  }
285
286
 
287
+ /**
288
+ * Prefer a server-provided 503 message when the search service is unavailable.
289
+ *
290
+ * @param {*} jqXHR
291
+ * @param {string} fallbackMessage
292
+ * @returns {string}
293
+ */
294
+ export function resolveServiceUnavailableSearchMessage(jqXHR, fallbackMessage) {
295
+ if (!jqXHR || jqXHR.status !== 503) {
296
+ return fallbackMessage;
297
+ }
298
+ let body = jqXHR.responseJSON;
299
+ if (!body && jqXHR.responseText) {
300
+ try {
301
+ body =
302
+ typeof jqXHR.responseText === 'string' ? JSON.parse(jqXHR.responseText) : jqXHR.responseText;
303
+ } catch (err) {
304
+ body = null;
305
+ }
306
+ }
307
+ if (body && body.message) {
308
+ return body.message;
309
+ }
310
+ return fallbackMessage;
311
+ }
312
+
286
313
  /**
287
314
  * Browse (`ItemContent/files`) responses use `children` and ignore `query`.
288
315
  * Real search responses expose `items`.
@@ -154,7 +154,7 @@ export default function (options) {
154
154
  });
155
155
 
156
156
  //load the content of the ROOT
157
- const showInitialListLoading = !searchMode && root === 'local';
157
+ const showInitialListLoading = !searchMode;
158
158
  if (showInitialListLoading) {
159
159
  setFilesLoading(true);
160
160
  }
@@ -421,14 +421,14 @@ export default function (options) {
421
421
  $container.data('activeFileBrowserRoot', root);
422
422
  if (searchMode) {
423
423
  $container.trigger(`folderpath.${NS}`, [path, content.label]);
424
- $container.trigger(`folderselect.${NS}`, [
425
- content.label,
426
- getFilesForDisplay(content),
427
- path,
428
- content
429
- ]);
430
- return;
431
- }
424
+ $container.trigger(`folderselect.${NS}`, [
425
+ content.label,
426
+ getFilesForDisplay(content),
427
+ path,
428
+ content
429
+ ]);
430
+ return;
431
+ }
432
432
  updateSelectedClass(path, content.total, content.childrenLimit);
433
433
  $container.trigger(`folderpath.${NS}`, [path, content.label]);
434
434
  $container.trigger(`folderselect.${NS}`, [
@@ -657,6 +657,10 @@ export default function (options) {
657
657
  const cacheKey = listCacheKey();
658
658
  const needList = !existing || existing.listCacheKey !== cacheKey;
659
659
 
660
+ if (needList && !searchMode) {
661
+ setFilesLoading(true);
662
+ }
663
+
660
664
  const treePromise = needTree
661
665
  ? (setTreeLoading(true),
662
666
  browseRequest(path, {
@@ -697,6 +701,9 @@ export default function (options) {
697
701
  cb(getByExactPath(tree, path) || getByPath(tree, path));
698
702
  })
699
703
  .catch(function () {
704
+ if (!searchMode) {
705
+ setFilesLoading(false);
706
+ }
700
707
  cb(null);
701
708
  });
702
709
  return;
@@ -181,6 +181,10 @@ export default function (options) {
181
181
  applyFilesListLoading(loading);
182
182
  });
183
183
 
184
+ $container.on(`searchtableloading.${ns}`, function (e, loading) {
185
+ applyFilesListLoading(loading);
186
+ });
187
+
184
188
  //update current folder
185
189
  $container.on(`folderselect.${ns}`, function (e, fullPath, data, activePath, content) {
186
190
  let files;