@oat-sa/tao-core-ui 3.23.0 → 3.24.0
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/dist/figure/FigureStateActive.js +64 -32
- package/dist/image/ImgStateActive/initHelper.js +64 -32
- package/dist/image/ImgStateActive.js +64 -32
- package/dist/previewer.js +1 -1
- package/dist/resourcemgr/assetSearch.js +793 -0
- package/dist/resourcemgr/assetSearchContract.js +477 -0
- package/dist/resourcemgr/css/resourcemgr.css +1137 -97
- package/dist/resourcemgr/css/resourcemgr.css.map +1 -1
- package/dist/resourcemgr/fileBrowser.js +456 -79
- package/dist/resourcemgr/filePreview.js +40 -4
- package/dist/resourcemgr/fileSelector.js +362 -92
- package/dist/resourcemgr.js +309 -57
- package/dist/searchModal/advancedSearch.js +233 -31
- package/package.json +15 -6
- package/src/image/ImgStateActive/initHelper.js +69 -31
- package/src/previewer.js +1 -1
- package/src/resourcemgr/assetSearch.js +847 -0
- package/src/resourcemgr/assetSearchContract.js +489 -0
- package/src/resourcemgr/css/resourcemgr.css +1137 -97
- package/src/resourcemgr/css/resourcemgr.css.map +1 -1
- package/src/resourcemgr/fileBrowser.js +470 -91
- package/src/resourcemgr/filePreview.js +46 -4
- package/src/resourcemgr/fileSelector.js +338 -46
- package/src/resourcemgr/scss/resourcemgr.scss +1351 -88
- package/src/resourcemgr/tpl/advanced-search.tpl +10 -0
- package/src/resourcemgr/tpl/fileSelect.tpl +25 -28
- package/src/resourcemgr/tpl/folder.tpl +4 -1
- package/src/resourcemgr/tpl/layout.tpl +113 -54
- package/src/resourcemgr/tpl/rootFolder.tpl +3 -1
- package/src/resourcemgr.js +289 -47
- package/src/searchModal/advancedSearch.js +242 -31
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/searchModal/css/advancedSearch.css', 'ui/component', 'ui/modal', 'ui/datatable', 'select2', 'core/dataProvider/request'], function ($$1, _, Handlebars, Helpers0, advancedSearch_css, component, modal, datatable, select2, request) { 'use strict';
|
|
1
|
+
define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/searchModal/css/advancedSearch.css', 'ui/component', 'ui/modal', 'ui/datatable', 'select2', 'core/dataProvider/request', 'i18n'], function ($$1, _, Handlebars, Helpers0, advancedSearch_css, component, modal, datatable, select2, request, __) { 'use strict';
|
|
2
2
|
|
|
3
3
|
$$1 = $$1 && Object.prototype.hasOwnProperty.call($$1, 'default') ? $$1['default'] : $$1;
|
|
4
4
|
_ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
|
|
@@ -6,6 +6,7 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
6
6
|
Helpers0 = Helpers0 && Object.prototype.hasOwnProperty.call(Helpers0, 'default') ? Helpers0['default'] : Helpers0;
|
|
7
7
|
component = component && Object.prototype.hasOwnProperty.call(component, 'default') ? component['default'] : component;
|
|
8
8
|
request = request && Object.prototype.hasOwnProperty.call(request, 'default') ? request['default'] : request;
|
|
9
|
+
__ = __ && Object.prototype.hasOwnProperty.call(__, 'default') ? __['default'] : __;
|
|
9
10
|
|
|
10
11
|
if (!Helpers0.__initialized) {
|
|
11
12
|
Helpers0(Handlebars);
|
|
@@ -410,6 +411,7 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
410
411
|
let $addCriteriaInput = null;
|
|
411
412
|
let $criteriaSelect = null;
|
|
412
413
|
let $advancedCriteriaContainer = null;
|
|
414
|
+
let $appliedFiltersSummary = null;
|
|
413
415
|
let criteriaState = null;
|
|
414
416
|
let criteriaMapping = {};
|
|
415
417
|
const criteriaTypes = {
|
|
@@ -422,7 +424,6 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
422
424
|
not: 'LOGIC_NOT'
|
|
423
425
|
};
|
|
424
426
|
let isAdvancedSearchStatusEnabled;
|
|
425
|
-
let isCriteriaListUpdated = false;
|
|
426
427
|
|
|
427
428
|
// Creates new component
|
|
428
429
|
const instance = component({
|
|
@@ -442,16 +443,20 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
442
443
|
if (!isAdvancedSearchStatusEnabled) {
|
|
443
444
|
return Promise.resolve();
|
|
444
445
|
}
|
|
445
|
-
const $criteriaIcon = $$1('
|
|
446
|
-
$criteriaIcon.
|
|
446
|
+
const $criteriaIcon = $addCriteria ? $$1('a span', $addCriteria).eq(0) : $$1();
|
|
447
|
+
const idleIconClass = $criteriaIcon.hasClass('icon-plus') ? 'icon-plus' : 'icon-add';
|
|
448
|
+
$criteriaIcon.removeClass('icon-add icon-plus').addClass('icon-loop');
|
|
447
449
|
return request(route).then(response => {
|
|
448
450
|
criteriaMapping = response.criteriaMapping || {};
|
|
449
451
|
const classTree = response.classDefinition ? response.classDefinition : response;
|
|
450
452
|
const criteria = formatCriteria(classTree);
|
|
451
453
|
updateCriteria(criteria);
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
454
|
+
$criteriaIcon.removeClass('icon-loop').addClass(idleIconClass);
|
|
455
|
+
}).catch(e => {
|
|
456
|
+
$criteriaIcon.removeClass('icon-loop').addClass(idleIconClass);
|
|
457
|
+
instance.trigger('error', e);
|
|
458
|
+
throw e;
|
|
459
|
+
});
|
|
455
460
|
},
|
|
456
461
|
/**
|
|
457
462
|
* Access to component state
|
|
@@ -467,10 +472,19 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
467
472
|
clear() {
|
|
468
473
|
$advancedCriteriaContainer.removeClass(['scrollable', 'scroll-separator-top', 'scroll-separator-bottom']);
|
|
469
474
|
$advancedCriteriaContainer.empty();
|
|
475
|
+
updateAppliedFiltersSummary(0);
|
|
476
|
+
// Rebuild selectable options: adding a criterion removes its <option>,
|
|
477
|
+
// so Clear must restore them (searchModal recovers via class re-select + updateCriteria).
|
|
478
|
+
if ($criteriaSelect && $criteriaSelect.length) {
|
|
479
|
+
$criteriaSelect.find('option:not(:first-child)').remove();
|
|
480
|
+
}
|
|
470
481
|
_.forEach(criteriaState, criterion => {
|
|
471
482
|
criterion.rendered = false;
|
|
472
483
|
criterion.value = null;
|
|
473
484
|
criterion.logic = null;
|
|
485
|
+
if ($criteriaSelect && $criteriaSelect.length) {
|
|
486
|
+
$criteriaSelect.append(createCriteriaOption(criterion));
|
|
487
|
+
}
|
|
474
488
|
});
|
|
475
489
|
},
|
|
476
490
|
/**
|
|
@@ -503,7 +517,7 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
503
517
|
});
|
|
504
518
|
return query;
|
|
505
519
|
}
|
|
506
|
-
}).setTemplate(advancedSearchTpl).on('render', () => {
|
|
520
|
+
}).setTemplate(config.layoutTemplate || advancedSearchTpl).on('render', () => {
|
|
507
521
|
initUiSelectors();
|
|
508
522
|
initAddCriteriaSelector().then(() => {
|
|
509
523
|
initCriteriaState();
|
|
@@ -521,6 +535,13 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
521
535
|
$addCriteriaInput = $$1('.add-criteria-container a', $container);
|
|
522
536
|
$criteriaSelect = $$1('.add-criteria-container select', $container);
|
|
523
537
|
$advancedCriteriaContainer = $$1('.advanced-criteria-container', $container);
|
|
538
|
+
$appliedFiltersSummary = $$1('.applied-filters-summary', $container);
|
|
539
|
+
if (config.collapsibleCriteria && $appliedFiltersSummary.length) {
|
|
540
|
+
$appliedFiltersSummary.off('click.advancedSearch').on('click.advancedSearch', function (e) {
|
|
541
|
+
e.preventDefault();
|
|
542
|
+
togglePreviousFilters();
|
|
543
|
+
});
|
|
544
|
+
}
|
|
524
545
|
$advancedCriteriaContainer.on('scroll', _.throttle(animateScroll, 100));
|
|
525
546
|
}
|
|
526
547
|
|
|
@@ -580,12 +601,23 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
580
601
|
});
|
|
581
602
|
|
|
582
603
|
// open dropdown when user clicks on add criteria input
|
|
583
|
-
$addCriteriaInput.on('click', ()
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
604
|
+
$addCriteriaInput.on('click', function (e) {
|
|
605
|
+
e.preventDefault();
|
|
606
|
+
e.stopPropagation();
|
|
607
|
+
const hasOptions = $criteriaSelect.find('option').filter(function () {
|
|
608
|
+
return Boolean(this.value);
|
|
609
|
+
}).length > 0;
|
|
610
|
+
// Never open an empty select2 (mask-only / no results). Wait for ClassMetadata options.
|
|
611
|
+
if (!hasOptions) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
$criteriaSelect.select2('open');
|
|
615
|
+
// if dropdown is opened above addCriteria input, top property is slightly decreased to avoid overlapping with addCriteria icon
|
|
616
|
+
const $dropdown = $$1('.criteria-dropdown-select2');
|
|
617
|
+
if ($dropdown.hasClass('select2-drop-above')) {
|
|
618
|
+
const top = parseFloat($dropdown.css('top'));
|
|
619
|
+
if (!Number.isNaN(top)) {
|
|
620
|
+
$dropdown.css('top', `${top - 10}px`);
|
|
589
621
|
}
|
|
590
622
|
}
|
|
591
623
|
});
|
|
@@ -646,9 +678,12 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
646
678
|
// render new criterion
|
|
647
679
|
const criterion = criteriaState[criterionToAdd];
|
|
648
680
|
const $criterionContainer = renderCriterion(criterion);
|
|
681
|
+
if (config.collapsibleCriteria) {
|
|
682
|
+
wrapCriterionAsSpoiler($criterionContainer, criterion);
|
|
683
|
+
}
|
|
649
684
|
|
|
650
685
|
// set logic to remove criterion
|
|
651
|
-
|
|
686
|
+
$criterionContainer.find('.icon-result-nok, .filter-spoiler-delete').on('click', {
|
|
652
687
|
criterion
|
|
653
688
|
}, removeCriterion);
|
|
654
689
|
|
|
@@ -660,6 +695,99 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
660
695
|
$advancedCriteriaContainer.addClass('scrollable');
|
|
661
696
|
}
|
|
662
697
|
criterion.rendered = true;
|
|
698
|
+
if (config.collapsibleCriteria) {
|
|
699
|
+
reorganizeFilterLayout();
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* @returns {jQuery}
|
|
705
|
+
*/
|
|
706
|
+
function getAllRenderedFilters() {
|
|
707
|
+
return $advancedCriteriaContainer.children('.filter-container').not('.invalid-criteria-warning-container');
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* @param {jQuery} $filter
|
|
712
|
+
* @param {boolean} open
|
|
713
|
+
*/
|
|
714
|
+
function setFilterSpoilerOpen($filter, open) {
|
|
715
|
+
const $spoiler = $filter.find('.filter-spoiler').first();
|
|
716
|
+
if (!$spoiler.length) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
$spoiler.toggleClass('is-open', open);
|
|
720
|
+
$filter.find('.filter-spoiler-toggle').attr('aria-expanded', open);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* @param {number} appliedCount
|
|
725
|
+
* @returns {string}
|
|
726
|
+
*/
|
|
727
|
+
function getAppliedFiltersSummaryText(appliedCount) {
|
|
728
|
+
if (appliedCount === 1) {
|
|
729
|
+
return __('1 filter applied');
|
|
730
|
+
}
|
|
731
|
+
return __('%s filters applied').replace('%s', String(appliedCount));
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Keeps each filter as its own spoiler card: newest expanded, previous collapsed (headers stay visible).
|
|
736
|
+
* With collapsibleCriteria, newest is prepended (index 0); otherwise newest is last.
|
|
737
|
+
*/
|
|
738
|
+
function reorganizeFilterLayout() {
|
|
739
|
+
const $filters = getAllRenderedFilters();
|
|
740
|
+
const count = $filters.length;
|
|
741
|
+
updateAppliedFiltersSummary(count);
|
|
742
|
+
if (count === 0) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
const openIndex = config.collapsibleCriteria ? 0 : count - 1;
|
|
746
|
+
$filters.each(function (index) {
|
|
747
|
+
setFilterSpoilerOpen($$1(this), index === openIndex);
|
|
748
|
+
});
|
|
749
|
+
if ($appliedFiltersSummary && $appliedFiltersSummary.length) {
|
|
750
|
+
$appliedFiltersSummary.attr('aria-expanded', 'false');
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Updates the "N filters applied" summary next to Add filter.
|
|
756
|
+
* @param {number} appliedCount
|
|
757
|
+
*/
|
|
758
|
+
function updateAppliedFiltersSummary(appliedCount) {
|
|
759
|
+
if ($appliedFiltersSummary && $appliedFiltersSummary.length) {
|
|
760
|
+
if (appliedCount > 0) {
|
|
761
|
+
$appliedFiltersSummary.text(getAppliedFiltersSummaryText(appliedCount)).removeClass('hidden').prop('hidden', false);
|
|
762
|
+
} else {
|
|
763
|
+
$appliedFiltersSummary.addClass('hidden').prop('hidden', true).text('').attr('aria-expanded', 'false');
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
instance.trigger('criteriachange', {
|
|
767
|
+
count: appliedCount
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Toggles expand/collapse for all filters except the newest one.
|
|
773
|
+
* With collapsibleCriteria (prepend), newest is first; otherwise newest is last.
|
|
774
|
+
*/
|
|
775
|
+
function togglePreviousFilters() {
|
|
776
|
+
const $filters = getAllRenderedFilters();
|
|
777
|
+
if ($filters.length <= 1) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
const $previous = config.collapsibleCriteria ? $filters.slice(1) : $filters.slice(0, -1);
|
|
781
|
+
const anyOpen = $previous.filter(function () {
|
|
782
|
+
return $$1(this).find('.filter-spoiler').first().hasClass('is-open');
|
|
783
|
+
}).length > 0;
|
|
784
|
+
const open = !anyOpen;
|
|
785
|
+
$previous.each(function () {
|
|
786
|
+
setFilterSpoilerOpen($$1(this), open);
|
|
787
|
+
});
|
|
788
|
+
if ($appliedFiltersSummary && $appliedFiltersSummary.length) {
|
|
789
|
+
$appliedFiltersSummary.attr('aria-expanded', open);
|
|
790
|
+
}
|
|
663
791
|
}
|
|
664
792
|
|
|
665
793
|
/**
|
|
@@ -677,9 +805,15 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
677
805
|
} else {
|
|
678
806
|
templateToUse = listCheckboxCriterionTpl;
|
|
679
807
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
808
|
+
if (config.collapsibleCriteria) {
|
|
809
|
+
$advancedCriteriaContainer.prepend(templateToUse({
|
|
810
|
+
criterion
|
|
811
|
+
}));
|
|
812
|
+
} else {
|
|
813
|
+
$advancedCriteriaContainer.append(templateToUse({
|
|
814
|
+
criterion
|
|
815
|
+
}));
|
|
816
|
+
}
|
|
683
817
|
const $criterionContainer = $$1(`.${criterion.id}-filter`, $container);
|
|
684
818
|
const valueMapping = criteriaMapping[criterion.type];
|
|
685
819
|
|
|
@@ -760,15 +894,17 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
760
894
|
* @param {object} $criterionContainer - rendered criterion
|
|
761
895
|
*/
|
|
762
896
|
function bindCriterionValue(criterion, $criterionContainer) {
|
|
897
|
+
if (criterion.type === criteriaTypes.text) {
|
|
898
|
+
// Bind synchronously so typing / Search right after Add filter is not lost
|
|
899
|
+
// (getInitialCriterionLabel resolves on a microtask).
|
|
900
|
+
$$1('input', $criterionContainer).val(criterion.value);
|
|
901
|
+
$$1('input', $criterionContainer).on('input change', function () {
|
|
902
|
+
criterion.value = $$1(this).val() || null;
|
|
903
|
+
});
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
763
906
|
getInitialCriterionLabel(criterion).then(initialCriterion => {
|
|
764
|
-
if (criterion.type === criteriaTypes.
|
|
765
|
-
// set initial value
|
|
766
|
-
$$1('input', $criterionContainer).val(criterion.value);
|
|
767
|
-
// set event to bind input value to critariaState
|
|
768
|
-
$$1('input', $criterionContainer).on('change', function () {
|
|
769
|
-
criterion.value = $$1(this).val() || null;
|
|
770
|
-
});
|
|
771
|
-
} else if (criterion.type === criteriaTypes.list && criterion.uri) {
|
|
907
|
+
if (criterion.type === criteriaTypes.list && criterion.uri) {
|
|
772
908
|
// set initial value
|
|
773
909
|
if (criterion.value) {
|
|
774
910
|
$$1(`input[name=${criterion.id}-select]`, $criterionContainer).select2('data', initialCriterion);
|
|
@@ -798,6 +934,58 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
798
934
|
});
|
|
799
935
|
}
|
|
800
936
|
|
|
937
|
+
/**
|
|
938
|
+
* Wraps a rendered criterion in a collapsible spoiler (Resource Manager UX).
|
|
939
|
+
* @param {jQuery} $criterionContainer
|
|
940
|
+
* @param {object} criterion
|
|
941
|
+
*/
|
|
942
|
+
function wrapCriterionAsSpoiler($criterionContainer, criterion) {
|
|
943
|
+
let $deleteBtn = $criterionContainer.find('.icon-result-nok').first();
|
|
944
|
+
if (!$deleteBtn.length) {
|
|
945
|
+
$deleteBtn = $$1('<button>', {
|
|
946
|
+
type: 'button',
|
|
947
|
+
class: 'filter-spoiler-delete icon-bin',
|
|
948
|
+
'aria-label': __('Remove criteria')
|
|
949
|
+
});
|
|
950
|
+
} else {
|
|
951
|
+
$deleteBtn.attr('type', 'button').removeClass('icon-result-nok').addClass('filter-spoiler-delete icon-bin').attr('aria-label', __('Remove criteria'));
|
|
952
|
+
}
|
|
953
|
+
const $title = $$1('<span>', {
|
|
954
|
+
class: 'filter-spoiler-title',
|
|
955
|
+
text: criterion.label
|
|
956
|
+
});
|
|
957
|
+
const $toggle = $$1('<button>', {
|
|
958
|
+
type: 'button',
|
|
959
|
+
class: 'filter-spoiler-toggle',
|
|
960
|
+
'aria-expanded': 'true'
|
|
961
|
+
}).append($title);
|
|
962
|
+
const $header = $$1('<div>', {
|
|
963
|
+
class: 'filter-spoiler-header'
|
|
964
|
+
}).append($toggle, $deleteBtn);
|
|
965
|
+
const $body = $$1('<div>', {
|
|
966
|
+
class: 'filter-spoiler-body'
|
|
967
|
+
});
|
|
968
|
+
$criterionContainer.children().appendTo($body);
|
|
969
|
+
const $spoiler = $$1('<div>', {
|
|
970
|
+
class: 'filter-spoiler is-open'
|
|
971
|
+
}).append($header, $body);
|
|
972
|
+
$criterionContainer.empty().append($spoiler);
|
|
973
|
+
$criterionContainer.addClass('filter-spoiler-wrapper');
|
|
974
|
+
$toggle.on('click', function (e) {
|
|
975
|
+
e.preventDefault();
|
|
976
|
+
const $spoilerEl = $criterionContainer.find('.filter-spoiler').first();
|
|
977
|
+
const willOpen = !$spoilerEl.hasClass('is-open');
|
|
978
|
+
if (willOpen && config.collapsibleCriteria) {
|
|
979
|
+
getAllRenderedFilters().each(function () {
|
|
980
|
+
if (this !== $criterionContainer.get(0)) {
|
|
981
|
+
setFilterSpoilerOpen($$1(this), false);
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
setFilterSpoilerOpen($criterionContainer, willOpen);
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
|
|
801
989
|
/**
|
|
802
990
|
* Removes a criterion from advanced criteria container when user clicks on the criterion close icon.
|
|
803
991
|
* It also adds the option element to criteria select so removed criterion can be rendered again
|
|
@@ -809,7 +997,7 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
809
997
|
const criterionKey = getCriterionStateId(criterion);
|
|
810
998
|
|
|
811
999
|
// remove criterion and append new criterion to select options
|
|
812
|
-
$$1(this).
|
|
1000
|
+
$$1(this).closest('.filter-container').remove();
|
|
813
1001
|
$criteriaSelect.append(newOption);
|
|
814
1002
|
|
|
815
1003
|
// reset criterion values on criteriaState
|
|
@@ -820,6 +1008,9 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
820
1008
|
if ($advancedCriteriaContainer.get(0).scrollHeight <= $advancedCriteriaContainer.outerHeight()) {
|
|
821
1009
|
$advancedCriteriaContainer.removeClass('scrollable');
|
|
822
1010
|
}
|
|
1011
|
+
if (config.collapsibleCriteria) {
|
|
1012
|
+
reorganizeFilterLayout();
|
|
1013
|
+
}
|
|
823
1014
|
}
|
|
824
1015
|
|
|
825
1016
|
/**
|
|
@@ -832,15 +1023,23 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
832
1023
|
function formatCriteria(classTree) {
|
|
833
1024
|
let criteria = [];
|
|
834
1025
|
_.forEach(classTree, classInstance => {
|
|
835
|
-
|
|
1026
|
+
const metadata = classInstance && classInstance.metadata;
|
|
1027
|
+
if (Array.isArray(metadata) && metadata.length) {
|
|
1028
|
+
criteria.push(...metadata);
|
|
1029
|
+
}
|
|
836
1030
|
});
|
|
837
1031
|
|
|
838
1032
|
// extends each criterion with an id that can be use as a valid css class
|
|
839
|
-
|
|
1033
|
+
criteria = criteria.filter(criterion => {
|
|
1034
|
+
const id = String(criterion && criterion.propertyUri || '').replace(/^[^a-zA-Z]*|[^a-zA-Z0-9]*/g, '');
|
|
1035
|
+
if (!id) {
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
840
1038
|
criterion.label = getCriterionLabel(criterion);
|
|
841
|
-
criterion.id =
|
|
1039
|
+
criterion.id = id;
|
|
1040
|
+
return true;
|
|
842
1041
|
});
|
|
843
|
-
return criteria;
|
|
1042
|
+
return _.uniqBy(criteria, 'propertyUri');
|
|
844
1043
|
}
|
|
845
1044
|
|
|
846
1045
|
/**
|
|
@@ -856,6 +1055,9 @@ define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/sear
|
|
|
856
1055
|
const invalidCriteria = deleteDeprecatedCriteria(criteria);
|
|
857
1056
|
extendCriteria(criteria);
|
|
858
1057
|
renderWarningMessage(invalidCriteria);
|
|
1058
|
+
if (config.collapsibleCriteria) {
|
|
1059
|
+
reorganizeFilterLayout();
|
|
1060
|
+
}
|
|
859
1061
|
}
|
|
860
1062
|
|
|
861
1063
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oat-sa/tao-core-ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.24.0",
|
|
4
4
|
"displayName": "TAO Core UI",
|
|
5
5
|
"description": "UI libraries of TAO",
|
|
6
6
|
"scripts": {
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"lint:src": "eslint -c .eslintrc.js src",
|
|
21
21
|
"lint:test": "eslint -c test/.eslintrc.js test",
|
|
22
22
|
"lint:report": "eslint -c .eslintrc.js --output-file eslint_report.json --format json src | true",
|
|
23
|
-
"prepare": "npm run build:all"
|
|
23
|
+
"prepare": "npm run build:all",
|
|
24
|
+
"audit:critical": "npm audit --audit-level=critical"
|
|
24
25
|
},
|
|
25
26
|
"keywords": [
|
|
26
27
|
"tao",
|
|
@@ -43,9 +44,9 @@
|
|
|
43
44
|
},
|
|
44
45
|
"license": "GPL-2.0",
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@babel/core": "^7.
|
|
47
|
-
"@babel/eslint-parser": "^7.
|
|
48
|
-
"@babel/preset-env": "^7.
|
|
47
|
+
"@babel/core": "^7.28.5",
|
|
48
|
+
"@babel/eslint-parser": "^7.28.5",
|
|
49
|
+
"@babel/preset-env": "^7.28.5",
|
|
49
50
|
"@csstools/postcss-sass": "^5.0.1",
|
|
50
51
|
"@oat-sa/browserslist-config-tao": "^1.0.1",
|
|
51
52
|
"@oat-sa/eslint-config-tao": "^2.0.0",
|
|
@@ -60,7 +61,7 @@
|
|
|
60
61
|
"autoprefixer": "^10.4.14",
|
|
61
62
|
"cross-env": "^7.0.2",
|
|
62
63
|
"devbridge-autocomplete": "^1.2.20",
|
|
63
|
-
"dompurify": "^2.
|
|
64
|
+
"dompurify": "^2.5.9",
|
|
64
65
|
"eslint": "^8.39.0",
|
|
65
66
|
"flatpickr": "4.5.7",
|
|
66
67
|
"fs-extra": "^11.1.1",
|
|
@@ -95,6 +96,14 @@
|
|
|
95
96
|
"text": "git+https://github.com/requirejs/text.git#2.0.16",
|
|
96
97
|
"tooltip.js": "^1.3.3"
|
|
97
98
|
},
|
|
99
|
+
"overrides": {
|
|
100
|
+
"shell-quote": "1.10.0",
|
|
101
|
+
"@babel/traverse": "^7.28.5",
|
|
102
|
+
"istanbul-lib-instrument": "^6.0.3",
|
|
103
|
+
"rollup-plugin-handlebars-plus": {
|
|
104
|
+
"handlebars": "~4.7.9"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
98
107
|
"browserslist": [
|
|
99
108
|
"extends @oat-sa/browserslist-config-tao"
|
|
100
109
|
],
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
*
|
|
12
12
|
* You should have received a copy of the GNU General Public License
|
|
13
13
|
* along with this program; if not, write to the Free Software
|
|
14
|
-
* Foundation, Inc.,
|
|
14
|
+
* Foundation, Inc., 31 Milk St # 960789 Boston, MA 02196 USA.
|
|
15
15
|
*
|
|
16
|
-
* Copyright (c) 2021 (original work) Open Assessment Technologies SA ;
|
|
16
|
+
* Copyright (c) 2021-2026 (original work) Open Assessment Technologies SA ;
|
|
17
17
|
*
|
|
18
18
|
*/
|
|
19
19
|
import $ from 'jquery';
|
|
@@ -38,11 +38,30 @@ export const initAdvanced = function (widget) {
|
|
|
38
38
|
export const initUpload = function (widget) {
|
|
39
39
|
const $form = widget.$form;
|
|
40
40
|
const options = widget.options;
|
|
41
|
-
const { img } = getImage(widget);
|
|
41
|
+
const { img, $img } = getImage(widget);
|
|
42
42
|
const $uploadTrigger = $form.find('[data-role="upload-trigger"]');
|
|
43
43
|
const $src = $form.find('input[name=src]');
|
|
44
44
|
const $alt = $form.find('input[name=alt]');
|
|
45
45
|
|
|
46
|
+
const commitSrc = function (srcValue) {
|
|
47
|
+
$src.val(srcValue);
|
|
48
|
+
// Keep QTI model + preview in sync immediately: the form src callback is
|
|
49
|
+
// throttled (1s), so a late trailing call must not leave a rejected pick.
|
|
50
|
+
if (img) {
|
|
51
|
+
img.attr('src', srcValue);
|
|
52
|
+
}
|
|
53
|
+
if ($img && $img.length) {
|
|
54
|
+
$img.attr('src', srcValue ? widget.getAssetManager().resolve(srcValue) : '');
|
|
55
|
+
$img.trigger('contentChange.qti-widget').change();
|
|
56
|
+
}
|
|
57
|
+
_.defer(function () {
|
|
58
|
+
if (img) {
|
|
59
|
+
img.attr('off-media-editor', 1);
|
|
60
|
+
}
|
|
61
|
+
$src.trigger('change');
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
46
65
|
const _openResourceMgr = function () {
|
|
47
66
|
$uploadTrigger.resourcemgr({
|
|
48
67
|
title: __(
|
|
@@ -51,10 +70,15 @@ export const initUpload = function (widget) {
|
|
|
51
70
|
appendContainer: options.mediaManager.appendContainer,
|
|
52
71
|
mediaSourcesUrl: options.mediaManager.mediaSourcesUrl,
|
|
53
72
|
browseUrl: options.mediaManager.browseUrl,
|
|
73
|
+
searchUrl: options.mediaManager.searchUrl,
|
|
54
74
|
uploadUrl: options.mediaManager.uploadUrl,
|
|
55
75
|
deleteUrl: options.mediaManager.deleteUrl,
|
|
56
76
|
downloadUrl: options.mediaManager.downloadUrl,
|
|
57
77
|
fileExistsUrl: options.mediaManager.fileExistsUrl,
|
|
78
|
+
rootClassUri: options.mediaManager.rootClassUri,
|
|
79
|
+
classMappingUrl: options.mediaManager.classMappingUrl,
|
|
80
|
+
statusUrl: options.mediaManager.statusUrl,
|
|
81
|
+
maxListSize: options.mediaManager.maxListSize,
|
|
58
82
|
params: {
|
|
59
83
|
uri: options.uri,
|
|
60
84
|
lang: options.lang,
|
|
@@ -69,44 +93,58 @@ export const initUpload = function (widget) {
|
|
|
69
93
|
pathParam: 'path',
|
|
70
94
|
path: options.mediaManager.path,
|
|
71
95
|
root: options.mediaManager.root,
|
|
96
|
+
currentAsset: $src.val() || null,
|
|
72
97
|
select: function (e, files) {
|
|
73
98
|
let file, alt;
|
|
74
99
|
let confirmBox, cancel, save;
|
|
75
|
-
if (files && files.length) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
if (!(files && files.length)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
file = files[0].file;
|
|
105
|
+
alt = files[0].alt;
|
|
106
|
+
|
|
107
|
+
// Apply the new file immediately; alt confirm only decides alt text.
|
|
108
|
+
commitSrc(file);
|
|
109
|
+
|
|
110
|
+
if ($.trim($alt.val()) === '') {
|
|
111
|
+
if (alt === '') {
|
|
112
|
+
alt = extractLabel(file);
|
|
113
|
+
}
|
|
114
|
+
if (img) {
|
|
83
115
|
img.attr('alt', alt);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
save = confirmBox.find('.save');
|
|
116
|
+
}
|
|
117
|
+
$alt.val(alt).trigger('change');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
89
120
|
|
|
90
|
-
|
|
121
|
+
// Existing alt: confirm replace. Cancel / overlay / Esc keeps the new image;
|
|
122
|
+
// only alt text stays unchanged unless the user confirms.
|
|
123
|
+
confirmBox = $('.change-alt-modal-feedback', $form);
|
|
124
|
+
cancel = confirmBox.find('.cancel');
|
|
125
|
+
save = confirmBox.find('.save');
|
|
91
126
|
|
|
92
|
-
|
|
127
|
+
const $altText = $('.alt-text', confirmBox).empty();
|
|
128
|
+
$altText
|
|
129
|
+
.append(document.createTextNode(`"${$alt.val()}"`))
|
|
130
|
+
.append($('<br>'))
|
|
131
|
+
.append(document.createTextNode(__('with')))
|
|
132
|
+
.append($('<br>'))
|
|
133
|
+
.append(document.createTextNode(`"${alt}" ?`));
|
|
93
134
|
|
|
94
|
-
|
|
95
|
-
img.attr('alt', alt);
|
|
96
|
-
$alt.val(alt).trigger('change');
|
|
97
|
-
confirmBox.modal('close');
|
|
98
|
-
});
|
|
135
|
+
confirmBox.modal({ width: 500 });
|
|
99
136
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
137
|
+
save.off('click').on('click', function () {
|
|
138
|
+
if (img) {
|
|
139
|
+
img.attr('alt', alt);
|
|
103
140
|
}
|
|
141
|
+
$alt.val(alt).trigger('change');
|
|
142
|
+
confirmBox.modal('close');
|
|
143
|
+
});
|
|
104
144
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
});
|
|
109
|
-
}
|
|
145
|
+
cancel.off('click').on('click', function () {
|
|
146
|
+
confirmBox.modal('close');
|
|
147
|
+
});
|
|
110
148
|
},
|
|
111
149
|
open: function () {
|
|
112
150
|
// hide tooltip if displayed
|
package/src/previewer.js
CHANGED
|
@@ -227,7 +227,7 @@ const previewer = {
|
|
|
227
227
|
// stop video, free the socket and remove player interface on video deletion
|
|
228
228
|
// stop video and free the socket on all other cases when video is selected or temporary hidden or modal window is closed
|
|
229
229
|
$controls = $(
|
|
230
|
-
'.actions a:nth-child(1), .actions a:nth-child(3), .icon-close, .upload-switcher, .select-action, .files
|
|
230
|
+
'.actions a:nth-child(1), .actions a:nth-child(3), .icon-close, .upload-switcher, .select-action, .files-list tr, .files .desc',
|
|
231
231
|
'#mediaManager'
|
|
232
232
|
);
|
|
233
233
|
$controls.off('mousedown.mediaelement').on('mousedown.mediaelement', function(event) {
|