emilsoftware-utilities 2.0.0-dev.64 → 2.0.0-dev.65
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.
|
@@ -177,6 +177,154 @@
|
|
|
177
177
|
return groups;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Appiattisce sia i grant Accessi sia le strutture gia raggruppate
|
|
182
|
+
* (`mnuAbil`, `{ codGrp, desGrp, menu }`) in una lista uniforme.
|
|
183
|
+
*/
|
|
184
|
+
function flattenGrants(input) {
|
|
185
|
+
if (!Array.isArray(input)) return [];
|
|
186
|
+
|
|
187
|
+
const grouped = input.filter(
|
|
188
|
+
(item) =>
|
|
189
|
+
item &&
|
|
190
|
+
(Array.isArray(item.menu) || Array.isArray(item.menus)),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
if (grouped.length > 0) {
|
|
194
|
+
const flat = [];
|
|
195
|
+
for (const group of grouped) {
|
|
196
|
+
const menus = group.menu || group.menus || [];
|
|
197
|
+
for (const menu of menus) {
|
|
198
|
+
flat.push({
|
|
199
|
+
codiceMenu: menu.codMnu ?? menu.codiceMenu ?? menu.code,
|
|
200
|
+
descrizioneMenu:
|
|
201
|
+
menu.desMnu ?? menu.descrizioneMenu ?? menu.label,
|
|
202
|
+
codiceGruppo:
|
|
203
|
+
group.codGrp ?? group.codiceGruppo ?? menu.codiceGruppo,
|
|
204
|
+
descrizioneGruppo:
|
|
205
|
+
group.desGrp ??
|
|
206
|
+
group.descrizioneGruppo ??
|
|
207
|
+
menu.descrizioneGruppo,
|
|
208
|
+
icona: menu.icon ?? menu.icona,
|
|
209
|
+
pagina: menu.pagina ?? menu.page,
|
|
210
|
+
tipo: menu.tipo ?? 'M',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return flat;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return input;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function asNumberMap(value) {
|
|
221
|
+
return value && typeof value === 'object' ? value : {};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function buildModel(input, options) {
|
|
225
|
+
const config = options || {};
|
|
226
|
+
const routeMap = config.routeMap || {};
|
|
227
|
+
const menuOrder = asNumberMap(config.menuOrder);
|
|
228
|
+
const groupOrder = asNumberMap(config.groupOrder);
|
|
229
|
+
const availableRoutes = new Set(
|
|
230
|
+
Array.isArray(config.availableRoutes)
|
|
231
|
+
? config.availableRoutes.map(normalizePage)
|
|
232
|
+
: [],
|
|
233
|
+
);
|
|
234
|
+
const isRouteAvailable =
|
|
235
|
+
typeof config.isRouteAvailable === 'function'
|
|
236
|
+
? config.isRouteAvailable
|
|
237
|
+
: (path) => availableRoutes.has(normalizePage(path));
|
|
238
|
+
|
|
239
|
+
const seen = new Set();
|
|
240
|
+
const areas = [];
|
|
241
|
+
|
|
242
|
+
flattenGrants(input).forEach((grant, index) => {
|
|
243
|
+
if (!grant || typeof grant !== 'object') return;
|
|
244
|
+
const type = String(grant.tipo || 'M').toUpperCase();
|
|
245
|
+
if (type !== 'M') return;
|
|
246
|
+
|
|
247
|
+
const code = String(
|
|
248
|
+
grant.codiceMenu || grant.codMnu || '',
|
|
249
|
+
)
|
|
250
|
+
.trim()
|
|
251
|
+
.toUpperCase();
|
|
252
|
+
if (!code) return;
|
|
253
|
+
|
|
254
|
+
const page = normalizePage(grant.pagina || grant.page);
|
|
255
|
+
if (!page) return;
|
|
256
|
+
|
|
257
|
+
const mappedRoute = String(routeMap[code] || '').trim();
|
|
258
|
+
const known = Boolean(mappedRoute) || isRouteAvailable(page);
|
|
259
|
+
const route = mappedRoute || (known ? page : '');
|
|
260
|
+
const available = Boolean(route);
|
|
261
|
+
|
|
262
|
+
const key = available ? `route:${route}` : `menu:${code}`;
|
|
263
|
+
if (seen.has(key)) return;
|
|
264
|
+
seen.add(key);
|
|
265
|
+
|
|
266
|
+
const groupName = String(
|
|
267
|
+
grant.descrizioneGruppo ||
|
|
268
|
+
grant.desGrp ||
|
|
269
|
+
grant.codiceGruppo ||
|
|
270
|
+
grant.codGrp ||
|
|
271
|
+
'',
|
|
272
|
+
).trim();
|
|
273
|
+
const groupKey =
|
|
274
|
+
String(grant.codiceGruppo || grant.codGrp || '').trim() ||
|
|
275
|
+
groupName ||
|
|
276
|
+
'ALTRE';
|
|
277
|
+
|
|
278
|
+
areas.push({
|
|
279
|
+
code,
|
|
280
|
+
label: String(
|
|
281
|
+
grant.descrizioneMenu ||
|
|
282
|
+
grant.desMnu ||
|
|
283
|
+
grant.label ||
|
|
284
|
+
code,
|
|
285
|
+
),
|
|
286
|
+
group: groupName,
|
|
287
|
+
groupKey,
|
|
288
|
+
groupOrder: Number(groupOrder[groupKey] ?? 999),
|
|
289
|
+
order: Number(menuOrder[code] ?? index),
|
|
290
|
+
icon: String(grant.icona || grant.icon || 'grid-outline'),
|
|
291
|
+
route,
|
|
292
|
+
page,
|
|
293
|
+
url: '',
|
|
294
|
+
available,
|
|
295
|
+
reason: available
|
|
296
|
+
? ''
|
|
297
|
+
: typeof config.reasonFor === 'function'
|
|
298
|
+
? String(config.reasonFor(page) || '')
|
|
299
|
+
: '',
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const grouped = new Map();
|
|
304
|
+
for (const area of areas) {
|
|
305
|
+
let group = grouped.get(area.groupKey);
|
|
306
|
+
if (!group) {
|
|
307
|
+
group = {
|
|
308
|
+
name: area.group || 'Altre aree',
|
|
309
|
+
order: area.groupOrder,
|
|
310
|
+
areas: [],
|
|
311
|
+
};
|
|
312
|
+
grouped.set(area.groupKey, group);
|
|
313
|
+
}
|
|
314
|
+
group.areas.push(area);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const groups = [...grouped.values()].sort(
|
|
318
|
+
(a, b) => a.order - b.order || a.name.localeCompare(b.name),
|
|
319
|
+
);
|
|
320
|
+
for (const group of groups) {
|
|
321
|
+
group.areas.sort(
|
|
322
|
+
(a, b) => a.order - b.order || a.label.localeCompare(b.label),
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
return groups;
|
|
326
|
+
}
|
|
327
|
+
|
|
180
328
|
const STYLES = `
|
|
181
329
|
:host {
|
|
182
330
|
--es-font: var(--bs-body-font-family, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
|
|
@@ -300,6 +448,110 @@
|
|
|
300
448
|
font-size: 13px;
|
|
301
449
|
}
|
|
302
450
|
|
|
451
|
+
.default-page {
|
|
452
|
+
display: flex;
|
|
453
|
+
align-items: center;
|
|
454
|
+
gap: 14px;
|
|
455
|
+
margin-top: 16px;
|
|
456
|
+
padding: 14px 16px;
|
|
457
|
+
border: 1px solid var(--es-border);
|
|
458
|
+
border-left: 4px solid var(--es-primary);
|
|
459
|
+
border-radius: var(--es-radius);
|
|
460
|
+
background: var(--es-surface);
|
|
461
|
+
box-shadow: var(--es-shadow);
|
|
462
|
+
color: inherit;
|
|
463
|
+
text-decoration: none;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.default-page:hover,
|
|
467
|
+
.default-page:focus-visible {
|
|
468
|
+
border-color: var(--es-primary);
|
|
469
|
+
border-left-color: var(--es-primary);
|
|
470
|
+
box-shadow: var(--es-shadow-hover);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.default-page:focus-visible {
|
|
474
|
+
outline: 2px solid var(--es-primary);
|
|
475
|
+
outline-offset: 2px;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.default-page__icon {
|
|
479
|
+
display: grid;
|
|
480
|
+
place-items: center;
|
|
481
|
+
flex: 0 0 auto;
|
|
482
|
+
width: 44px;
|
|
483
|
+
height: 44px;
|
|
484
|
+
border-radius: 12px;
|
|
485
|
+
background: var(--es-primary-soft);
|
|
486
|
+
color: var(--es-primary);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.default-page__icon svg {
|
|
490
|
+
width: 22px;
|
|
491
|
+
height: 22px;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.default-page__copy {
|
|
495
|
+
display: flex;
|
|
496
|
+
flex: 1 1 auto;
|
|
497
|
+
flex-direction: column;
|
|
498
|
+
gap: 2px;
|
|
499
|
+
min-width: 0;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.default-page__eyebrow {
|
|
503
|
+
color: var(--es-muted);
|
|
504
|
+
font-size: 11px;
|
|
505
|
+
font-weight: 600;
|
|
506
|
+
letter-spacing: 0.08em;
|
|
507
|
+
text-transform: uppercase;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.default-page__label {
|
|
511
|
+
overflow: hidden;
|
|
512
|
+
color: var(--es-text);
|
|
513
|
+
font-size: 15px;
|
|
514
|
+
font-weight: 600;
|
|
515
|
+
text-overflow: ellipsis;
|
|
516
|
+
white-space: nowrap;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.default-page__url {
|
|
520
|
+
overflow: hidden;
|
|
521
|
+
color: var(--es-muted);
|
|
522
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
523
|
+
font-size: 11px;
|
|
524
|
+
text-overflow: ellipsis;
|
|
525
|
+
white-space: nowrap;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.default-page__action {
|
|
529
|
+
flex: 0 0 auto;
|
|
530
|
+
color: var(--es-primary);
|
|
531
|
+
font-size: 13px;
|
|
532
|
+
font-weight: 600;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.default-page--blocked {
|
|
536
|
+
border-left-color: var(--es-disabled-text);
|
|
537
|
+
background: var(--es-disabled-bg);
|
|
538
|
+
box-shadow: none;
|
|
539
|
+
cursor: not-allowed;
|
|
540
|
+
opacity: 0.8;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.default-page--blocked .default-page__icon,
|
|
544
|
+
.default-page--blocked .default-page__label,
|
|
545
|
+
.default-page--blocked .default-page__action {
|
|
546
|
+
color: var(--es-disabled-text);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.default-page__reason {
|
|
550
|
+
color: var(--es-muted);
|
|
551
|
+
font-size: 12px;
|
|
552
|
+
font-style: italic;
|
|
553
|
+
}
|
|
554
|
+
|
|
303
555
|
.groups {
|
|
304
556
|
display: grid;
|
|
305
557
|
gap: 26px;
|
|
@@ -519,10 +771,18 @@
|
|
|
519
771
|
'empty-text',
|
|
520
772
|
'unavailable-template',
|
|
521
773
|
'href-mode',
|
|
774
|
+
'default-page',
|
|
522
775
|
];
|
|
523
776
|
}
|
|
524
777
|
|
|
525
778
|
#groups = [];
|
|
779
|
+
#grants = [];
|
|
780
|
+
#useGrants = false;
|
|
781
|
+
#routeMap = {};
|
|
782
|
+
#menuOrder = {};
|
|
783
|
+
#groupOrder = {};
|
|
784
|
+
#availableRoutes = [];
|
|
785
|
+
#isRouteAvailable = null;
|
|
526
786
|
#root;
|
|
527
787
|
#media;
|
|
528
788
|
#observer;
|
|
@@ -571,6 +831,92 @@
|
|
|
571
831
|
|
|
572
832
|
set groups(value) {
|
|
573
833
|
this.#groups = normalizeGroups(value);
|
|
834
|
+
this.#useGrants = false;
|
|
835
|
+
this.render();
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Grant Accessi (anche raggruppati per `menu`/`menus`). Il componente
|
|
840
|
+
* costruisce da solo gruppi, rotte e stato disponibile/bloccato.
|
|
841
|
+
*/
|
|
842
|
+
get grants() {
|
|
843
|
+
return this.#grants;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
set grants(value) {
|
|
847
|
+
this.#grants = Array.isArray(value) ? value : [];
|
|
848
|
+
this.#useGrants = true;
|
|
849
|
+
this.#rebuildFromGrants();
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
get routeMap() {
|
|
853
|
+
return this.#routeMap;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
set routeMap(value) {
|
|
857
|
+
this.#routeMap = value && typeof value === 'object' ? value : {};
|
|
858
|
+
this.#refreshIfGrants();
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
get menuOrder() {
|
|
862
|
+
return this.#menuOrder;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
set menuOrder(value) {
|
|
866
|
+
this.#menuOrder = value && typeof value === 'object' ? value : {};
|
|
867
|
+
this.#refreshIfGrants();
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
get groupOrder() {
|
|
871
|
+
return this.#groupOrder;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
set groupOrder(value) {
|
|
875
|
+
this.#groupOrder = value && typeof value === 'object' ? value : {};
|
|
876
|
+
this.#refreshIfGrants();
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
get availableRoutes() {
|
|
880
|
+
return this.#availableRoutes;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
set availableRoutes(value) {
|
|
884
|
+
this.#availableRoutes = Array.isArray(value) ? value : [];
|
|
885
|
+
this.#refreshIfGrants();
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
get isRouteAvailable() {
|
|
889
|
+
return this.#isRouteAvailable;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
set isRouteAvailable(value) {
|
|
893
|
+
this.#isRouteAvailable = typeof value === 'function' ? value : null;
|
|
894
|
+
this.#refreshIfGrants();
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
get defaultPage() {
|
|
898
|
+
return this.getAttribute('default-page') || '';
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
set defaultPage(value) {
|
|
902
|
+
this.#setOptionalAttribute('default-page', value);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
#refreshIfGrants() {
|
|
906
|
+
if (!this.#useGrants) return;
|
|
907
|
+
this.#rebuildFromGrants();
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
#rebuildFromGrants() {
|
|
911
|
+
this.#groups = buildModel(this.#grants, {
|
|
912
|
+
routeMap: this.#routeMap,
|
|
913
|
+
menuOrder: this.#menuOrder,
|
|
914
|
+
groupOrder: this.#groupOrder,
|
|
915
|
+
availableRoutes: this.#availableRoutes,
|
|
916
|
+
isRouteAvailable: this.#isRouteAvailable,
|
|
917
|
+
reasonFor: (page) =>
|
|
918
|
+
this.unavailableTemplate.replace('{page}', page),
|
|
919
|
+
});
|
|
574
920
|
this.render();
|
|
575
921
|
}
|
|
576
922
|
|
|
@@ -635,7 +981,7 @@
|
|
|
635
981
|
}
|
|
636
982
|
|
|
637
983
|
get theme() {
|
|
638
|
-
return this.getAttribute('theme') || '
|
|
984
|
+
return this.getAttribute('theme') || 'light';
|
|
639
985
|
}
|
|
640
986
|
|
|
641
987
|
set theme(value) {
|
|
@@ -672,8 +1018,9 @@
|
|
|
672
1018
|
}
|
|
673
1019
|
|
|
674
1020
|
#resolveTheme() {
|
|
675
|
-
const explicit = String(this.getAttribute('theme') || '
|
|
1021
|
+
const explicit = String(this.getAttribute('theme') || 'light').toLowerCase();
|
|
676
1022
|
if (explicit === 'light' || explicit === 'dark') return explicit;
|
|
1023
|
+
if (explicit !== 'auto') return 'light';
|
|
677
1024
|
|
|
678
1025
|
const root = document.documentElement;
|
|
679
1026
|
if (
|
|
@@ -799,7 +1146,92 @@
|
|
|
799
1146
|
: '';
|
|
800
1147
|
|
|
801
1148
|
this.#root.innerHTML =
|
|
802
|
-
'<main class="shell">' +
|
|
1149
|
+
'<main class="shell">' +
|
|
1150
|
+
header +
|
|
1151
|
+
this.#renderDefaultPage() +
|
|
1152
|
+
body +
|
|
1153
|
+
legend +
|
|
1154
|
+
'</main>';
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
#renderDefaultPage() {
|
|
1158
|
+
const page = normalizePage(this.defaultPage);
|
|
1159
|
+
if (!page) return '';
|
|
1160
|
+
|
|
1161
|
+
const areas = this.#groups.reduce(
|
|
1162
|
+
(all, group) => all.concat(group.areas),
|
|
1163
|
+
[],
|
|
1164
|
+
);
|
|
1165
|
+
const match = areas.find(
|
|
1166
|
+
(area) => area.route === page || area.page === page,
|
|
1167
|
+
);
|
|
1168
|
+
|
|
1169
|
+
let route = '';
|
|
1170
|
+
if (match) {
|
|
1171
|
+
route = match.available ? match.route : '';
|
|
1172
|
+
} else {
|
|
1173
|
+
const availableRoutes = new Set(
|
|
1174
|
+
this.#availableRoutes.map(normalizePage),
|
|
1175
|
+
);
|
|
1176
|
+
const known = this.#isRouteAvailable
|
|
1177
|
+
? this.#isRouteAvailable(page)
|
|
1178
|
+
: availableRoutes.has(page);
|
|
1179
|
+
route = known ? page : '';
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const available = Boolean(route);
|
|
1183
|
+
const href = available
|
|
1184
|
+
? this.#hrefFor({ available: true, route })
|
|
1185
|
+
: '';
|
|
1186
|
+
const url =
|
|
1187
|
+
(match && match.url) ||
|
|
1188
|
+
(href ? new URL(href, document.baseURI).href : page);
|
|
1189
|
+
const label = match ? match.label : page;
|
|
1190
|
+
const iconName = match ? match.icon : 'home-outline';
|
|
1191
|
+
const reason = available
|
|
1192
|
+
? ''
|
|
1193
|
+
: (match && match.reason) || this.#defaultReason({ page });
|
|
1194
|
+
|
|
1195
|
+
const content =
|
|
1196
|
+
'<span class="default-page__icon">' +
|
|
1197
|
+
icon(iconName) +
|
|
1198
|
+
'</span>' +
|
|
1199
|
+
'<span class="default-page__copy">' +
|
|
1200
|
+
'<span class="default-page__eyebrow">Pagina di default</span>' +
|
|
1201
|
+
'<span class="default-page__label">' +
|
|
1202
|
+
escapeHtml(label) +
|
|
1203
|
+
'</span>' +
|
|
1204
|
+
(available
|
|
1205
|
+
? '<span class="default-page__url" title="' +
|
|
1206
|
+
escapeHtml(url) +
|
|
1207
|
+
'">' +
|
|
1208
|
+
escapeHtml(url) +
|
|
1209
|
+
'</span>'
|
|
1210
|
+
: '<span class="default-page__reason">' +
|
|
1211
|
+
escapeHtml(reason) +
|
|
1212
|
+
'</span>') +
|
|
1213
|
+
'</span>';
|
|
1214
|
+
|
|
1215
|
+
if (available) {
|
|
1216
|
+
return (
|
|
1217
|
+
'<a class="default-page" href="' +
|
|
1218
|
+
escapeHtml(href) +
|
|
1219
|
+
'">' +
|
|
1220
|
+
content +
|
|
1221
|
+
'<span class="default-page__action">Apri</span>' +
|
|
1222
|
+
'</a>'
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
return (
|
|
1227
|
+
'<div class="default-page default-page--blocked" role="link" ' +
|
|
1228
|
+
'aria-disabled="true" tabindex="0" title="' +
|
|
1229
|
+
escapeHtml(reason) +
|
|
1230
|
+
'">' +
|
|
1231
|
+
content +
|
|
1232
|
+
'<span class="default-page__action">Bloccata</span>' +
|
|
1233
|
+
'</div>'
|
|
1234
|
+
);
|
|
803
1235
|
}
|
|
804
1236
|
|
|
805
1237
|
#renderGroup(group, groupIndex) {
|
|
@@ -892,7 +1324,13 @@
|
|
|
892
1324
|
customElements.define(TAG, AreeDisponibiliElement);
|
|
893
1325
|
}
|
|
894
1326
|
|
|
895
|
-
// Esposto per integrazioni che preferiscono costruire
|
|
1327
|
+
// Esposto per integrazioni che preferiscono costruire elemento e modello
|
|
1328
|
+
// da codice, senza duplicare la logica di raggruppamento nelle applicazioni.
|
|
896
1329
|
window.EmilSoftware = window.EmilSoftware || {};
|
|
897
1330
|
window.EmilSoftware.AreeDisponibiliElement = AreeDisponibiliElement;
|
|
1331
|
+
window.EmilSoftware.AreeDisponibili = {
|
|
1332
|
+
Element: AreeDisponibiliElement,
|
|
1333
|
+
buildModel,
|
|
1334
|
+
version: '1.1.0',
|
|
1335
|
+
};
|
|
898
1336
|
})();
|
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|