@valtimo/components 13.32.0 → 13.33.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/fesm2022/valtimo-components.mjs +454 -6
- package/fesm2022/valtimo-components.mjs.map +1 -1
- package/lib/components/overflow-menu/overflow-menu.component.d.ts.map +1 -1
- package/lib/components/widget-layout-info/widget-layout-info.component.d.ts +11 -0
- package/lib/components/widget-layout-info/widget-layout-info.component.d.ts.map +1 -0
- package/lib/directives/muuri/muuri-gap-free-layout.d.ts +3 -0
- package/lib/directives/muuri/muuri-gap-free-layout.d.ts.map +1 -0
- package/lib/directives/muuri/muuri.directive.d.ts +3 -1
- package/lib/directives/muuri/muuri.directive.d.ts.map +1 -1
- package/lib/directives/muuri/widget-layout.d.ts +36 -0
- package/lib/directives/muuri/widget-layout.d.ts.map +1 -0
- package/package.json +1 -1
- package/public_api.d.ts +3 -0
- package/public_api.d.ts.map +1 -1
|
@@ -4498,6 +4498,10 @@ class OverflowMenuComponent {
|
|
|
4498
4498
|
return;
|
|
4499
4499
|
this._cleanupAutoUpdate = autoUpdate(reference, menu, () => {
|
|
4500
4500
|
computePosition(reference, menu, {
|
|
4501
|
+
// The pane is rendered with `position: fixed`, so positions must be
|
|
4502
|
+
// computed against the viewport. Without this the menu is offset by the
|
|
4503
|
+
// trigger's page position and lands far from lower rows (unclickable).
|
|
4504
|
+
strategy: 'fixed',
|
|
4501
4505
|
placement: this.placement,
|
|
4502
4506
|
middleware: [
|
|
4503
4507
|
offset({ mainAxis: this.offsetY, crossAxis: this.offsetX }),
|
|
@@ -17756,6 +17760,421 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
17756
17760
|
type: Input
|
|
17757
17761
|
}] } });
|
|
17758
17762
|
|
|
17763
|
+
/*
|
|
17764
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
17765
|
+
*
|
|
17766
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
17767
|
+
* you may not use this file except in compliance with the License.
|
|
17768
|
+
* You may obtain a copy of the License at
|
|
17769
|
+
*
|
|
17770
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
17771
|
+
*
|
|
17772
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17773
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
17774
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17775
|
+
* See the License for the specific language governing permissions and
|
|
17776
|
+
* limitations under the License.
|
|
17777
|
+
*/
|
|
17778
|
+
/**
|
|
17779
|
+
* Custom Muuri layout function — "beautiful" gap-free packing.
|
|
17780
|
+
*
|
|
17781
|
+
* Each widget occupies a whole number of grid columns (1-4) and a whole
|
|
17782
|
+
* number of row units (height scales with content). The grid column count is
|
|
17783
|
+
* derived from the actual item dimensions so the algorithm works identically
|
|
17784
|
+
* for case widgets (320px / 200px) and dashboard widgets (275px / 220px)
|
|
17785
|
+
* without hardcoding either constant.
|
|
17786
|
+
*
|
|
17787
|
+
* Goal (in priority order):
|
|
17788
|
+
* 1. Never leave a gap trapped in the middle (a hole with a widget below it).
|
|
17789
|
+
* 2. Keep the block compact (minimise height) and push any unavoidable
|
|
17790
|
+
* gaps to the bottom-right corner.
|
|
17791
|
+
* 3. Respect the source order as much as the above two allow.
|
|
17792
|
+
*
|
|
17793
|
+
* To achieve this we treat each section as a small 2D strip-packing problem
|
|
17794
|
+
* and search:
|
|
17795
|
+
* - over the number of columns to actually use (sometimes a narrower block
|
|
17796
|
+
* packs into a cleaner rectangle than spreading across every column);
|
|
17797
|
+
* - over widget orderings (reordering is allowed to fill gaps).
|
|
17798
|
+
* Every candidate layout is scored and the prettiest one wins. Because the
|
|
17799
|
+
* packing only depends on the column/row spans and the column count (not the
|
|
17800
|
+
* exact pixel width) results are memoised and reused across resizes.
|
|
17801
|
+
*
|
|
17802
|
+
* Compatible with Muuri's custom layout function API (v0.9.x):
|
|
17803
|
+
* layout(grid, layoutId, items, gridWidth, gridHeight, callback)
|
|
17804
|
+
*/
|
|
17805
|
+
/** Maximum number of grid columns to consider when detecting the column count. */
|
|
17806
|
+
const MAX_COLS = 20;
|
|
17807
|
+
/** Tolerance (in pixels) for floating-point width/height comparisons. */
|
|
17808
|
+
const SNAP_PX = 1;
|
|
17809
|
+
/**
|
|
17810
|
+
* How far (as a fraction of one column) an item width may deviate from a whole
|
|
17811
|
+
* number of columns and still be considered an integer span. Loose enough to
|
|
17812
|
+
* absorb sub-pixel column widths and widget margins, tight enough to reject a
|
|
17813
|
+
* grid that does not actually fit the widths.
|
|
17814
|
+
*/
|
|
17815
|
+
const COL_SNAP_RATIO = 0.12;
|
|
17816
|
+
/** Above this item count we stop trying every permutation and use heuristics. */
|
|
17817
|
+
const FULL_PERMUTATION_LIMIT = 7;
|
|
17818
|
+
/** Scoring weights — tuned so each rule dominates the ones below it. */
|
|
17819
|
+
const W_INTERNAL_GAP = 100000; // a hole with a widget below it: avoid at all costs
|
|
17820
|
+
const W_BOTTOM_GAP = 1000; // an empty cell inside the full-width bounding box
|
|
17821
|
+
const W_HEIGHT = 50; // prefer a compact (shorter) block
|
|
17822
|
+
const W_GAP_NOT_RIGHT = 80; // push the bottom gaps towards the right
|
|
17823
|
+
const W_INVERSION = 5; // respect the original order when everything else ties
|
|
17824
|
+
/** Memoised packings keyed by span signature + column count. */
|
|
17825
|
+
const layoutCache = new Map();
|
|
17826
|
+
const LAYOUT_CACHE_MAX = 256;
|
|
17827
|
+
function muuriGapFreeLayout(grid, layoutId, items, gridWidth, _gridHeight, callback) {
|
|
17828
|
+
const slots = new Float32Array(items.length * 2);
|
|
17829
|
+
if (items.length === 0) {
|
|
17830
|
+
callback({ id: layoutId, items, slots, width: gridWidth, height: 0, styles: { height: '0px' } });
|
|
17831
|
+
return;
|
|
17832
|
+
}
|
|
17833
|
+
// Pre-compute outer dimensions (pixels).
|
|
17834
|
+
const pxWidths = [];
|
|
17835
|
+
const pxHeights = [];
|
|
17836
|
+
for (let i = 0; i < items.length; i++) {
|
|
17837
|
+
const item = items[i];
|
|
17838
|
+
pxWidths.push(item._width + item._marginLeft + item._marginRight);
|
|
17839
|
+
pxHeights.push(item._height + item._marginTop + item._marginBottom);
|
|
17840
|
+
}
|
|
17841
|
+
// Detect the column count and row unit from the item dimensions.
|
|
17842
|
+
const totalCols = detectTotalCols(pxWidths, gridWidth);
|
|
17843
|
+
const colWidth = gridWidth / totalCols;
|
|
17844
|
+
const rowHeight = detectRowHeight(pxHeights);
|
|
17845
|
+
// Convert each item to column/row units.
|
|
17846
|
+
const colSpans = [];
|
|
17847
|
+
const rowSpans = [];
|
|
17848
|
+
for (let i = 0; i < items.length; i++) {
|
|
17849
|
+
colSpans.push(Math.min(totalCols, Math.max(1, Math.round(pxWidths[i] / colWidth))));
|
|
17850
|
+
rowSpans.push(Math.max(1, Math.round(pxHeights[i] / rowHeight)));
|
|
17851
|
+
}
|
|
17852
|
+
const placement = packSection(colSpans, rowSpans, totalCols);
|
|
17853
|
+
// Convert grid positions to pixel coordinates.
|
|
17854
|
+
for (let i = 0; i < items.length; i++) {
|
|
17855
|
+
slots[i * 2] = Math.round(placement.cols[i] * colWidth);
|
|
17856
|
+
slots[i * 2 + 1] = Math.round(placement.rows[i] * rowHeight);
|
|
17857
|
+
}
|
|
17858
|
+
const layoutHeight = Math.round(placement.height * rowHeight);
|
|
17859
|
+
const el = grid._element;
|
|
17860
|
+
const isBorderBox = el ? getComputedStyle(el).boxSizing === 'border-box' : false;
|
|
17861
|
+
const borderTop = grid._borderTop || 0;
|
|
17862
|
+
const borderBottom = grid._borderBottom || 0;
|
|
17863
|
+
const totalHeight = isBorderBox ? layoutHeight + borderTop + borderBottom : layoutHeight;
|
|
17864
|
+
callback({
|
|
17865
|
+
id: layoutId,
|
|
17866
|
+
items,
|
|
17867
|
+
slots,
|
|
17868
|
+
styles: { height: totalHeight + 'px' },
|
|
17869
|
+
width: gridWidth,
|
|
17870
|
+
height: layoutHeight,
|
|
17871
|
+
});
|
|
17872
|
+
}
|
|
17873
|
+
/**
|
|
17874
|
+
* Pack one section. Searches over the number of columns to use and over item
|
|
17875
|
+
* orderings, scoring every candidate, and returns the prettiest placement.
|
|
17876
|
+
*/
|
|
17877
|
+
function packSection(colSpans, rowSpans, maxCols) {
|
|
17878
|
+
const key = `${colSpans.join(',')}|${rowSpans.join(',')}|${maxCols}`;
|
|
17879
|
+
const cached = layoutCache.get(key);
|
|
17880
|
+
if (cached)
|
|
17881
|
+
return cached;
|
|
17882
|
+
const n = colSpans.length;
|
|
17883
|
+
let best = null;
|
|
17884
|
+
let bestScore = Infinity;
|
|
17885
|
+
for (let k = maxCols; k >= 1; k--) {
|
|
17886
|
+
// Widgets wider than the current column count become full-width.
|
|
17887
|
+
const cs = colSpans.map(c => Math.min(c, k));
|
|
17888
|
+
const orders = generateOrders(n, cs, rowSpans);
|
|
17889
|
+
for (const order of orders) {
|
|
17890
|
+
const placed = firstFitPack(order, cs, rowSpans, k);
|
|
17891
|
+
const score = scorePlacement(placed, cs, rowSpans, maxCols);
|
|
17892
|
+
if (score < bestScore) {
|
|
17893
|
+
bestScore = score;
|
|
17894
|
+
best = placed;
|
|
17895
|
+
}
|
|
17896
|
+
}
|
|
17897
|
+
}
|
|
17898
|
+
const result = best;
|
|
17899
|
+
if (layoutCache.size >= LAYOUT_CACHE_MAX)
|
|
17900
|
+
layoutCache.clear();
|
|
17901
|
+
layoutCache.set(key, result);
|
|
17902
|
+
return result;
|
|
17903
|
+
}
|
|
17904
|
+
/**
|
|
17905
|
+
* Place items (in the given order) into a `k`-column grid using top-left-first
|
|
17906
|
+
* first-fit. Returns the column/row of each item indexed by its original index.
|
|
17907
|
+
*/
|
|
17908
|
+
function firstFitPack(order, colSpans, rowSpans, k) {
|
|
17909
|
+
const occupied = [];
|
|
17910
|
+
let gridRows = 0;
|
|
17911
|
+
const cols = new Array(colSpans.length);
|
|
17912
|
+
const rows = new Array(colSpans.length);
|
|
17913
|
+
const ensureRows = (needed) => {
|
|
17914
|
+
while (gridRows < needed) {
|
|
17915
|
+
occupied.push(new Array(k).fill(false));
|
|
17916
|
+
gridRows++;
|
|
17917
|
+
}
|
|
17918
|
+
};
|
|
17919
|
+
for (const idx of order) {
|
|
17920
|
+
const cw = colSpans[idx];
|
|
17921
|
+
const rh = rowSpans[idx];
|
|
17922
|
+
let placed = false;
|
|
17923
|
+
for (let r = 0; !placed; r++) {
|
|
17924
|
+
ensureRows(r + rh);
|
|
17925
|
+
for (let c = 0; c <= k - cw; c++) {
|
|
17926
|
+
if (canPlace(occupied, r, c, rh, cw)) {
|
|
17927
|
+
markOccupied(occupied, r, c, rh, cw);
|
|
17928
|
+
cols[idx] = c;
|
|
17929
|
+
rows[idx] = r;
|
|
17930
|
+
placed = true;
|
|
17931
|
+
break;
|
|
17932
|
+
}
|
|
17933
|
+
}
|
|
17934
|
+
}
|
|
17935
|
+
}
|
|
17936
|
+
let height = 0;
|
|
17937
|
+
for (let i = 0; i < colSpans.length; i++) {
|
|
17938
|
+
height = Math.max(height, rows[i] + rowSpans[i]);
|
|
17939
|
+
}
|
|
17940
|
+
return { cols, rows, height };
|
|
17941
|
+
}
|
|
17942
|
+
/**
|
|
17943
|
+
* Score a placement — lower is prettier. Encodes the priority order:
|
|
17944
|
+
* no trapped gaps > compact (short) block > gaps bottom-right > original order.
|
|
17945
|
+
*
|
|
17946
|
+
* Waste is always measured against the full available width (`maxCols`), so a
|
|
17947
|
+
* tall narrow tower that leaves whole columns empty on the right is correctly
|
|
17948
|
+
* seen as gappy rather than "gap free". A narrower block is therefore only
|
|
17949
|
+
* chosen when it does not add height and produces a cleaner gap placement.
|
|
17950
|
+
*/
|
|
17951
|
+
function scorePlacement(placed, colSpans, rowSpans, maxCols) {
|
|
17952
|
+
const { cols, rows, height } = placed;
|
|
17953
|
+
const n = colSpans.length;
|
|
17954
|
+
// Build the occupancy grid across the full available width.
|
|
17955
|
+
const occ = [];
|
|
17956
|
+
for (let r = 0; r < height; r++)
|
|
17957
|
+
occ.push(new Array(maxCols).fill(false));
|
|
17958
|
+
for (let i = 0; i < n; i++) {
|
|
17959
|
+
for (let r = rows[i]; r < rows[i] + rowSpans[i]; r++) {
|
|
17960
|
+
for (let c = cols[i]; c < cols[i] + colSpans[i]; c++)
|
|
17961
|
+
occ[r][c] = true;
|
|
17962
|
+
}
|
|
17963
|
+
}
|
|
17964
|
+
let internalGaps = 0;
|
|
17965
|
+
let bottomGaps = 0;
|
|
17966
|
+
let gapNotRight = 0;
|
|
17967
|
+
for (let c = 0; c < maxCols; c++) {
|
|
17968
|
+
let lastFilled = -1;
|
|
17969
|
+
for (let r = 0; r < height; r++)
|
|
17970
|
+
if (occ[r][c])
|
|
17971
|
+
lastFilled = r;
|
|
17972
|
+
for (let r = 0; r < height; r++) {
|
|
17973
|
+
if (occ[r][c])
|
|
17974
|
+
continue;
|
|
17975
|
+
if (r < lastFilled) {
|
|
17976
|
+
internalGaps++;
|
|
17977
|
+
}
|
|
17978
|
+
else {
|
|
17979
|
+
bottomGaps++;
|
|
17980
|
+
gapNotRight += maxCols - 1 - c;
|
|
17981
|
+
}
|
|
17982
|
+
}
|
|
17983
|
+
}
|
|
17984
|
+
const inversions = countInversions(cols, rows, n);
|
|
17985
|
+
return (internalGaps * W_INTERNAL_GAP +
|
|
17986
|
+
bottomGaps * W_BOTTOM_GAP +
|
|
17987
|
+
height * W_HEIGHT +
|
|
17988
|
+
gapNotRight * W_GAP_NOT_RIGHT +
|
|
17989
|
+
inversions * W_INVERSION);
|
|
17990
|
+
}
|
|
17991
|
+
/**
|
|
17992
|
+
* Number of pairs whose reading order (top-to-bottom, left-to-right) differs
|
|
17993
|
+
* from their original order. Used as a tie-breaker to respect source order.
|
|
17994
|
+
*/
|
|
17995
|
+
function countInversions(cols, rows, n) {
|
|
17996
|
+
const order = Array.from({ length: n }, (_, i) => i).sort((a, b) => rows[a] !== rows[b] ? rows[a] - rows[b] : cols[a] - cols[b]);
|
|
17997
|
+
const rank = new Array(n);
|
|
17998
|
+
for (let pos = 0; pos < n; pos++)
|
|
17999
|
+
rank[order[pos]] = pos;
|
|
18000
|
+
let inversions = 0;
|
|
18001
|
+
for (let i = 0; i < n; i++) {
|
|
18002
|
+
for (let j = i + 1; j < n; j++)
|
|
18003
|
+
if (rank[i] > rank[j])
|
|
18004
|
+
inversions++;
|
|
18005
|
+
}
|
|
18006
|
+
return inversions;
|
|
18007
|
+
}
|
|
18008
|
+
/**
|
|
18009
|
+
* Candidate orderings to try. For small sections every permutation is tried;
|
|
18010
|
+
* for larger sections a handful of size-based heuristics keeps it fast.
|
|
18011
|
+
*/
|
|
18012
|
+
function generateOrders(n, colSpans, rowSpans) {
|
|
18013
|
+
const identity = Array.from({ length: n }, (_, i) => i);
|
|
18014
|
+
if (n <= 1)
|
|
18015
|
+
return [identity];
|
|
18016
|
+
if (n <= FULL_PERMUTATION_LIMIT)
|
|
18017
|
+
return permutations(identity);
|
|
18018
|
+
const byArea = [...identity].sort((a, b) => colSpans[b] * rowSpans[b] - colSpans[a] * rowSpans[a]);
|
|
18019
|
+
const byHeight = [...identity].sort((a, b) => rowSpans[b] - rowSpans[a] || colSpans[b] - colSpans[a]);
|
|
18020
|
+
const byWidth = [...identity].sort((a, b) => colSpans[b] - colSpans[a] || rowSpans[b] - rowSpans[a]);
|
|
18021
|
+
return dedupeOrders([identity, byArea, byHeight, byWidth]);
|
|
18022
|
+
}
|
|
18023
|
+
/** All permutations of the given index array (used only for small sections). */
|
|
18024
|
+
function permutations(arr) {
|
|
18025
|
+
if (arr.length <= 1)
|
|
18026
|
+
return [arr];
|
|
18027
|
+
const result = [];
|
|
18028
|
+
for (let i = 0; i < arr.length; i++) {
|
|
18029
|
+
const rest = [...arr.slice(0, i), ...arr.slice(i + 1)];
|
|
18030
|
+
for (const perm of permutations(rest))
|
|
18031
|
+
result.push([arr[i], ...perm]);
|
|
18032
|
+
}
|
|
18033
|
+
return result;
|
|
18034
|
+
}
|
|
18035
|
+
function dedupeOrders(orders) {
|
|
18036
|
+
const seen = new Set();
|
|
18037
|
+
const out = [];
|
|
18038
|
+
for (const o of orders) {
|
|
18039
|
+
const key = o.join(',');
|
|
18040
|
+
if (!seen.has(key)) {
|
|
18041
|
+
seen.add(key);
|
|
18042
|
+
out.push(o);
|
|
18043
|
+
}
|
|
18044
|
+
}
|
|
18045
|
+
return out;
|
|
18046
|
+
}
|
|
18047
|
+
/**
|
|
18048
|
+
* Detect the number of grid columns: the *coarsest* grid (smallest column
|
|
18049
|
+
* count, from 1 up to MAX_COLS) in which every item width is approximately a
|
|
18050
|
+
* whole number of columns. A coarser grid is always preferred — a finer grid
|
|
18051
|
+
* that merely happens to also divide the widths (e.g. describing 1x/2x widgets
|
|
18052
|
+
* as 6/12 columns) yields the exact same pixel layout but destabilises the
|
|
18053
|
+
* column spans and the packing search.
|
|
18054
|
+
*/
|
|
18055
|
+
function detectTotalCols(pxWidths, gridWidth) {
|
|
18056
|
+
for (let n = 1; n <= MAX_COLS; n++) {
|
|
18057
|
+
const unit = gridWidth / n;
|
|
18058
|
+
let valid = true;
|
|
18059
|
+
for (const w of pxWidths) {
|
|
18060
|
+
if (w < SNAP_PX)
|
|
18061
|
+
continue;
|
|
18062
|
+
const ratio = w / unit;
|
|
18063
|
+
if (Math.round(ratio) < 1 || Math.abs(ratio - Math.round(ratio)) > COL_SNAP_RATIO) {
|
|
18064
|
+
valid = false;
|
|
18065
|
+
break;
|
|
18066
|
+
}
|
|
18067
|
+
}
|
|
18068
|
+
if (valid)
|
|
18069
|
+
return n;
|
|
18070
|
+
}
|
|
18071
|
+
return 1;
|
|
18072
|
+
}
|
|
18073
|
+
/**
|
|
18074
|
+
* Detect the base row unit as the greatest common divisor of the item heights.
|
|
18075
|
+
* Heights are exact integer multiples of the base unit (e.g. 200 for case
|
|
18076
|
+
* widgets, 220 for dashboard widgets); the GCD recovers that base even when a
|
|
18077
|
+
* section happens to contain no single-row-tall widget.
|
|
18078
|
+
*/
|
|
18079
|
+
function detectRowHeight(pxHeights) {
|
|
18080
|
+
let unit = 0;
|
|
18081
|
+
for (const h of pxHeights) {
|
|
18082
|
+
const rounded = Math.round(h);
|
|
18083
|
+
if (rounded > SNAP_PX)
|
|
18084
|
+
unit = gcd(unit, rounded);
|
|
18085
|
+
}
|
|
18086
|
+
return unit > SNAP_PX ? unit : 200;
|
|
18087
|
+
}
|
|
18088
|
+
function gcd(a, b) {
|
|
18089
|
+
while (b > 0) {
|
|
18090
|
+
const t = a % b;
|
|
18091
|
+
a = b;
|
|
18092
|
+
b = t;
|
|
18093
|
+
}
|
|
18094
|
+
return a;
|
|
18095
|
+
}
|
|
18096
|
+
/** Check if an item of size (rowSpan × colSpan) can be placed at (row, col). */
|
|
18097
|
+
function canPlace(occupied, row, col, rowSpan, colSpan) {
|
|
18098
|
+
for (let r = row; r < row + rowSpan; r++) {
|
|
18099
|
+
for (let c = col; c < col + colSpan; c++) {
|
|
18100
|
+
if (occupied[r][c])
|
|
18101
|
+
return false;
|
|
18102
|
+
}
|
|
18103
|
+
}
|
|
18104
|
+
return true;
|
|
18105
|
+
}
|
|
18106
|
+
/** Mark cells as occupied for an item placed at (row, col). */
|
|
18107
|
+
function markOccupied(occupied, row, col, rowSpan, colSpan) {
|
|
18108
|
+
for (let r = row; r < row + rowSpan; r++) {
|
|
18109
|
+
for (let c = col; c < col + colSpan; c++)
|
|
18110
|
+
occupied[r][c] = true;
|
|
18111
|
+
}
|
|
18112
|
+
}
|
|
18113
|
+
|
|
18114
|
+
/*
|
|
18115
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
18116
|
+
*
|
|
18117
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
18118
|
+
* you may not use this file except in compliance with the License.
|
|
18119
|
+
* You may obtain a copy of the License at
|
|
18120
|
+
*
|
|
18121
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
18122
|
+
*
|
|
18123
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
18124
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
18125
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18126
|
+
* See the License for the specific language governing permissions and
|
|
18127
|
+
* limitations under the License.
|
|
18128
|
+
*/
|
|
18129
|
+
/**
|
|
18130
|
+
* Selectable widget layout algorithm. Persisted (optionally) per case widget
|
|
18131
|
+
* tab, IKO tab and dashboard; an absent value falls back to MUURI_GAP_FREE.
|
|
18132
|
+
*
|
|
18133
|
+
* - MUURI_GAP_FREE ("Default (less gaps)"): Muuri's built-in masonry with gap
|
|
18134
|
+
* filling — the behaviour from before the layout-algorithm work. Used when
|
|
18135
|
+
* nothing is configured.
|
|
18136
|
+
* - MUURI ("Default"): Muuri's plain masonry, without gap filling.
|
|
18137
|
+
* - BEAUTIFUL ("Beautiful (gap-free)"): the custom search-based gap-free packing.
|
|
18138
|
+
*/
|
|
18139
|
+
var WidgetLayout;
|
|
18140
|
+
(function (WidgetLayout) {
|
|
18141
|
+
WidgetLayout["MUURI_GAP_FREE"] = "MUURI_GAP_FREE";
|
|
18142
|
+
WidgetLayout["MUURI"] = "MUURI";
|
|
18143
|
+
WidgetLayout["BEAUTIFUL"] = "BEAUTIFUL";
|
|
18144
|
+
})(WidgetLayout || (WidgetLayout = {}));
|
|
18145
|
+
/** Row-unit height used by the default and plain Muuri algorithms. */
|
|
18146
|
+
const WIDGET_ROW_HEIGHT_DEFAULT = 200;
|
|
18147
|
+
/** Finer row-unit height the beautiful gap-free algorithm packs against. */
|
|
18148
|
+
const WIDGET_ROW_HEIGHT_GAP_FREE = 92;
|
|
18149
|
+
/**
|
|
18150
|
+
* Resolve a (possibly absent) layout choice to a concrete Muuri `layout` option
|
|
18151
|
+
* and the row-unit height widgets should snap to. Unknown/absent falls back to
|
|
18152
|
+
* MUURI_GAP_FREE (gap-filling Muuri — the pre-existing behaviour).
|
|
18153
|
+
*/
|
|
18154
|
+
function resolveWidgetLayout(layout) {
|
|
18155
|
+
switch (layout) {
|
|
18156
|
+
case WidgetLayout.BEAUTIFUL:
|
|
18157
|
+
return { muuriLayout: muuriGapFreeLayout, rowHeightUnit: WIDGET_ROW_HEIGHT_GAP_FREE };
|
|
18158
|
+
case WidgetLayout.MUURI:
|
|
18159
|
+
return { muuriLayout: { fillGaps: false }, rowHeightUnit: WIDGET_ROW_HEIGHT_DEFAULT };
|
|
18160
|
+
case WidgetLayout.MUURI_GAP_FREE:
|
|
18161
|
+
default:
|
|
18162
|
+
return { muuriLayout: { fillGaps: true }, rowHeightUnit: WIDGET_ROW_HEIGHT_DEFAULT };
|
|
18163
|
+
}
|
|
18164
|
+
}
|
|
18165
|
+
/** Selectable layout values in display order, for admin dropdowns. */
|
|
18166
|
+
const WIDGET_LAYOUT_VALUES = [
|
|
18167
|
+
WidgetLayout.MUURI_GAP_FREE,
|
|
18168
|
+
WidgetLayout.MUURI,
|
|
18169
|
+
WidgetLayout.BEAUTIFUL,
|
|
18170
|
+
];
|
|
18171
|
+
/** Translation keys for each layout value (under the shared `widgetLayout` namespace). */
|
|
18172
|
+
const WIDGET_LAYOUT_TRANSLATION_KEYS = {
|
|
18173
|
+
[WidgetLayout.MUURI_GAP_FREE]: 'widgetLayout.muuriGapFree',
|
|
18174
|
+
[WidgetLayout.MUURI]: 'widgetLayout.muuri',
|
|
18175
|
+
[WidgetLayout.BEAUTIFUL]: 'widgetLayout.beautiful',
|
|
18176
|
+
};
|
|
18177
|
+
|
|
17759
18178
|
/*
|
|
17760
18179
|
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
17761
18180
|
*
|
|
@@ -17828,9 +18247,7 @@ class MuuriDirective {
|
|
|
17828
18247
|
return;
|
|
17829
18248
|
}
|
|
17830
18249
|
this._muuriSubject$.next(new Muuri(nativeElement, {
|
|
17831
|
-
layout:
|
|
17832
|
-
fillGaps: true,
|
|
17833
|
-
},
|
|
18250
|
+
layout: resolveWidgetLayout(this.widgetLayout).muuriLayout,
|
|
17834
18251
|
layoutOnResize: false,
|
|
17835
18252
|
}));
|
|
17836
18253
|
}
|
|
@@ -17873,7 +18290,7 @@ class MuuriDirective {
|
|
|
17873
18290
|
this._muuri.refreshItems();
|
|
17874
18291
|
this._muuri.layout(true);
|
|
17875
18292
|
}
|
|
17876
|
-
},
|
|
18293
|
+
}, 200);
|
|
17877
18294
|
}
|
|
17878
18295
|
observeContainerWidthChanges() {
|
|
17879
18296
|
const nativeElement = this.elementRef.nativeElement;
|
|
@@ -17940,7 +18357,7 @@ class MuuriDirective {
|
|
|
17940
18357
|
this.renderer.setStyle(el, 'margin', '-8px');
|
|
17941
18358
|
}
|
|
17942
18359
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MuuriDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
17943
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.20", type: MuuriDirective, isStandalone: true, selector: "[muuri]", inputs: { columnMinWidth: "columnMinWidth" }, ngImport: i0 }); }
|
|
18360
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.20", type: MuuriDirective, isStandalone: true, selector: "[muuri]", inputs: { columnMinWidth: "columnMinWidth", widgetLayout: "widgetLayout" }, ngImport: i0 }); }
|
|
17944
18361
|
}
|
|
17945
18362
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: MuuriDirective, decorators: [{
|
|
17946
18363
|
type: Directive,
|
|
@@ -17950,6 +18367,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
17950
18367
|
}]
|
|
17951
18368
|
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }], propDecorators: { columnMinWidth: [{
|
|
17952
18369
|
type: Input
|
|
18370
|
+
}], widgetLayout: [{
|
|
18371
|
+
type: Input
|
|
17953
18372
|
}] } });
|
|
17954
18373
|
|
|
17955
18374
|
/*
|
|
@@ -18021,6 +18440,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
18021
18440
|
}]
|
|
18022
18441
|
}] });
|
|
18023
18442
|
|
|
18443
|
+
/*
|
|
18444
|
+
* Copyright 2015-2026 Ritense BV, the Netherlands.
|
|
18445
|
+
*
|
|
18446
|
+
* Licensed under EUPL, Version 1.2 (the "License");
|
|
18447
|
+
* you may not use this file except in compliance with the License.
|
|
18448
|
+
* You may obtain a copy of the License at
|
|
18449
|
+
*
|
|
18450
|
+
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
|
18451
|
+
*
|
|
18452
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
18453
|
+
* distributed under the License is distributed on an "AS IS" basis,
|
|
18454
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18455
|
+
* See the License for the specific language governing permissions and
|
|
18456
|
+
* limitations under the License.
|
|
18457
|
+
*/
|
|
18458
|
+
/**
|
|
18459
|
+
* Informational Carbon notification explaining the widget layout algorithms.
|
|
18460
|
+
* Rendered next to every layout-algorithm selector (case widget tab, IKO tab
|
|
18461
|
+
* and dashboard) so the trade-offs are explained consistently.
|
|
18462
|
+
*/
|
|
18463
|
+
class WidgetLayoutInfoComponent {
|
|
18464
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: WidgetLayoutInfoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
18465
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: WidgetLayoutInfoComponent, isStandalone: true, selector: "valtimo-widget-layout-info", ngImport: i0, template: "<cds-inline-notification\n [notificationObj]=\"{\n type: 'info',\n title: 'widgetLayout.infoTitle' | translate,\n message: 'widgetLayout.info' | translate,\n showClose: false,\n lowContrast: true\n }\"\n></cds-inline-notification>\n", dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: NotificationModule }, { kind: "component", type: i2$3.Notification, selector: "cds-notification, cds-inline-notification, ibm-notification, ibm-inline-notification", inputs: ["notificationObj"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
18466
|
+
}
|
|
18467
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: WidgetLayoutInfoComponent, decorators: [{
|
|
18468
|
+
type: Component,
|
|
18469
|
+
args: [{ selector: 'valtimo-widget-layout-info', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslateModule, NotificationModule], template: "<cds-inline-notification\n [notificationObj]=\"{\n type: 'info',\n title: 'widgetLayout.infoTitle' | translate,\n message: 'widgetLayout.info' | translate,\n showClose: false,\n lowContrast: true\n }\"\n></cds-inline-notification>\n" }]
|
|
18470
|
+
}] });
|
|
18471
|
+
|
|
18024
18472
|
/*
|
|
18025
18473
|
* Copyright 2015-2025 Ritense BV, the Netherlands.
|
|
18026
18474
|
*
|
|
@@ -18675,5 +19123,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
18675
19123
|
* Generated bundle index. Do not edit.
|
|
18676
19124
|
*/
|
|
18677
19125
|
|
|
18678
|
-
export { ARBITRARY_AMOUNT_VALUE_TEST_IDS, AUTO_KEY_INPUT_TEST_IDS, AdminSettingsService, Alert, AlertComponent, AlertModule, AlertService, AlertType, AlertsOptionsImpl, AssignmentComponent, AutoKeyInputComponent, BOOLEAN_CONVERTER_VALUES, BpmnJsDiagramComponent, BpmnJsDiagramModule, BreadcrumbNavigationComponent, BreadcrumbNavigationModule, BreadcrumbService, CARBON_CONSTANTS, CARBON_THEME, CASES_WITHOUT_STATUS_KEY, COLOR_PICKER_TEST_IDS, CONFIRMATION_MODAL_TEST_IDS, CamundaFormComponent, CamundaFormModule, CarbonListComponent, CarbonListFilterPipe, CarbonListModule, CarbonMultiInputComponent, CarbonMultiInputModule, CarbonNoResultsComponent, CaseCountPipe, CaseTagsSelectorComponent, CdsThemeService, ChoiceFieldService, ColorPickerComponent, ComponentsPipesModule, ConfirmationModalComponent, ConfirmationModalModule, ContextMenuDirective, CtrlClickDirective, CurrentCarbonTheme, DEFAULT_LIST_TRANSLATIONS, DEFAULT_PAGINATION, DEFAULT_PAGINATOR_CONFIG, DataListComponent, DataListModule, DatePickerComponent, DatePickerModule, DateTimePickerComponent, DigitOnlyDirective, DropzoneComponent, DropzoneModule, EditorComponent, EditorModule, EllipsisPipe, ExpansionPanelComponent, ExpansionPanelModule, FieldAutoFocusDirective, FieldAutoFocusModule, FileSizeModule, FileSizePipe, FilterSidebarComponent, FilterSidebarModule, FitPageDirective, FormComponent, FormIoCurrencyComponent, FormIoCurrentUserComponent, FormIoDomService, FormIoIbanComponent, FormIoModule, FormIoStateService, FormIoTagsService, FormIoUploaderComponent, FormModule, FormioBuilderComponent, FormioComponent, FormioDummyComponent, FormioOptionsImpl, FormioValueResolverSelectorComponent, InputComponent, InputLabelComponent, InputLabelModule, InputModule, IsArrayPipe, JSON_EDITOR_TEST_IDS, JsonEditorComponent, KEY_DROPDOWN_VALUE_TEST_IDS, KEY_VALUE_PATH_SELECTOR_TEST_IDS, KEY_VALUE_TEST_IDS, KeyGeneratorService, LeftSidebarComponent, LeftSidebarModule, ListColumnViewComponent, MdiIconSelectorComponent, MdiIconViewerComponent, MenuItemTextComponent, MenuItemTranslationPipe, MenuModule, MenuRoutingModule, MenuService, ModalComponent, ModalModule, ModalService, MonacoTheme, MoveRowDirection, MultiInputFormComponent, MultiInputFormModule, MultiselectDropdownComponent, MultiselectDropdownModule, MuuriDirective, MuuriDirectiveModule, MuuriItemComponent, ObserveSizeDirective, OverflowMenuComponent, OverflowMenuModule, OverflowMenuOptionComponent, OverflowMenuTriggerComponent, POPULAR_MDI_ICONS, PageHeaderComponent, PageHeaderModule, PageHeaderService, PageSubtitleService, PageTitleComponent, PageTitleModule, PageTitleService, ParagraphComponent, ParagraphModule, PendingChangesComponent, PendingChangesService, ProgressBarComponent, ProgressBarModule, PromptComponent, PromptModule, PromptService, QUICK_SEARCH_SERVICE, QuickSearchComponent, QuickSearchStateService, RIGHT_SIDEBAR_TEST_IDS, RadioComponent, RadioModule, ReadOnlyDirective, RemoveClassnamesDirective, RenderInBodyComponent, RenderInPageHeaderDirective, RenderPageHeaderDirective, RightSidebarComponent, RightSidebarModule, SINGLE_VALUE_TEST_IDS, STEPPER_FOOTER_STEP_TEST_IDS, SchemaEditorComponent, SearchFieldsComponent, SearchFieldsModule, SelectComponent, SelectModule, SelectableCarbonTheme, ShellService, SpinnerComponent, SpinnerModule, StatusSelectorComponent, StepperContainerComponent, StepperContentComponent, StepperFooterComponent, StepperFooterStepComponent, StepperHeaderComponent, StepperModule, StepperService, StepperStepComponent, TAG_ELLIPSIS_LIMIT, TableComponent, TableModule, TimelineComponent, TimelineItemImpl, TimelineModule, TooltipComponent, TooltipDirective, TooltipIconComponent, TooltipIconModule, TooltipModule, TopbarComponent, TopbarModule, UploaderComponent, UploaderDragDropDirective, UploaderModule, UserInterfaceService, VALUE_PATH_SELECTOR_DROPDOWN_VALUE_TEST_IDS, VALUE_PATH_SELECTOR_TEST_IDS, VALUE_PATH_SELECTOR_VALUE_TEST_IDS, VModalComponent, VModalModule, ValtimoCdsModalDirective, ValtimoModalService, ValuePathSelectorComponent, ValuePathSelectorInputMode, ValuePathSelectorPrefix, ValuePathSelectorService, ValuePathType, ViewContentService, ViewType, WebcamComponent, WebcamModule, WidgetComponent, WidgetModule, applyDataGridPatch, collectObjectLevels, createCustomFormioComponent, customUploaderType, enableCustomFormioComponents, menuInitializer, pendingChangesGuard, registerCustomFormioComponent, registerCustomTag, registerFormioCurrencyComponent, registerFormioCurrentUserComponent, registerFormioFileSelectorComponent, registerFormioIbanComponent, registerFormioUploadComponent, registerFormioValueResolverSelectorComponent, runAfterCarbonModalClosed, setRequiredOnSchema };
|
|
19126
|
+
export { ARBITRARY_AMOUNT_VALUE_TEST_IDS, AUTO_KEY_INPUT_TEST_IDS, AdminSettingsService, Alert, AlertComponent, AlertModule, AlertService, AlertType, AlertsOptionsImpl, AssignmentComponent, AutoKeyInputComponent, BOOLEAN_CONVERTER_VALUES, BpmnJsDiagramComponent, BpmnJsDiagramModule, BreadcrumbNavigationComponent, BreadcrumbNavigationModule, BreadcrumbService, CARBON_CONSTANTS, CARBON_THEME, CASES_WITHOUT_STATUS_KEY, COLOR_PICKER_TEST_IDS, CONFIRMATION_MODAL_TEST_IDS, CamundaFormComponent, CamundaFormModule, CarbonListComponent, CarbonListFilterPipe, CarbonListModule, CarbonMultiInputComponent, CarbonMultiInputModule, CarbonNoResultsComponent, CaseCountPipe, CaseTagsSelectorComponent, CdsThemeService, ChoiceFieldService, ColorPickerComponent, ComponentsPipesModule, ConfirmationModalComponent, ConfirmationModalModule, ContextMenuDirective, CtrlClickDirective, CurrentCarbonTheme, DEFAULT_LIST_TRANSLATIONS, DEFAULT_PAGINATION, DEFAULT_PAGINATOR_CONFIG, DataListComponent, DataListModule, DatePickerComponent, DatePickerModule, DateTimePickerComponent, DigitOnlyDirective, DropzoneComponent, DropzoneModule, EditorComponent, EditorModule, EllipsisPipe, ExpansionPanelComponent, ExpansionPanelModule, FieldAutoFocusDirective, FieldAutoFocusModule, FileSizeModule, FileSizePipe, FilterSidebarComponent, FilterSidebarModule, FitPageDirective, FormComponent, FormIoCurrencyComponent, FormIoCurrentUserComponent, FormIoDomService, FormIoIbanComponent, FormIoModule, FormIoStateService, FormIoTagsService, FormIoUploaderComponent, FormModule, FormioBuilderComponent, FormioComponent, FormioDummyComponent, FormioOptionsImpl, FormioValueResolverSelectorComponent, InputComponent, InputLabelComponent, InputLabelModule, InputModule, IsArrayPipe, JSON_EDITOR_TEST_IDS, JsonEditorComponent, KEY_DROPDOWN_VALUE_TEST_IDS, KEY_VALUE_PATH_SELECTOR_TEST_IDS, KEY_VALUE_TEST_IDS, KeyGeneratorService, LeftSidebarComponent, LeftSidebarModule, ListColumnViewComponent, MdiIconSelectorComponent, MdiIconViewerComponent, MenuItemTextComponent, MenuItemTranslationPipe, MenuModule, MenuRoutingModule, MenuService, ModalComponent, ModalModule, ModalService, MonacoTheme, MoveRowDirection, MultiInputFormComponent, MultiInputFormModule, MultiselectDropdownComponent, MultiselectDropdownModule, MuuriDirective, MuuriDirectiveModule, MuuriItemComponent, ObserveSizeDirective, OverflowMenuComponent, OverflowMenuModule, OverflowMenuOptionComponent, OverflowMenuTriggerComponent, POPULAR_MDI_ICONS, PageHeaderComponent, PageHeaderModule, PageHeaderService, PageSubtitleService, PageTitleComponent, PageTitleModule, PageTitleService, ParagraphComponent, ParagraphModule, PendingChangesComponent, PendingChangesService, ProgressBarComponent, ProgressBarModule, PromptComponent, PromptModule, PromptService, QUICK_SEARCH_SERVICE, QuickSearchComponent, QuickSearchStateService, RIGHT_SIDEBAR_TEST_IDS, RadioComponent, RadioModule, ReadOnlyDirective, RemoveClassnamesDirective, RenderInBodyComponent, RenderInPageHeaderDirective, RenderPageHeaderDirective, RightSidebarComponent, RightSidebarModule, SINGLE_VALUE_TEST_IDS, STEPPER_FOOTER_STEP_TEST_IDS, SchemaEditorComponent, SearchFieldsComponent, SearchFieldsModule, SelectComponent, SelectModule, SelectableCarbonTheme, ShellService, SpinnerComponent, SpinnerModule, StatusSelectorComponent, StepperContainerComponent, StepperContentComponent, StepperFooterComponent, StepperFooterStepComponent, StepperHeaderComponent, StepperModule, StepperService, StepperStepComponent, TAG_ELLIPSIS_LIMIT, TableComponent, TableModule, TimelineComponent, TimelineItemImpl, TimelineModule, TooltipComponent, TooltipDirective, TooltipIconComponent, TooltipIconModule, TooltipModule, TopbarComponent, TopbarModule, UploaderComponent, UploaderDragDropDirective, UploaderModule, UserInterfaceService, VALUE_PATH_SELECTOR_DROPDOWN_VALUE_TEST_IDS, VALUE_PATH_SELECTOR_TEST_IDS, VALUE_PATH_SELECTOR_VALUE_TEST_IDS, VModalComponent, VModalModule, ValtimoCdsModalDirective, ValtimoModalService, ValuePathSelectorComponent, ValuePathSelectorInputMode, ValuePathSelectorPrefix, ValuePathSelectorService, ValuePathType, ViewContentService, ViewType, WIDGET_LAYOUT_TRANSLATION_KEYS, WIDGET_LAYOUT_VALUES, WIDGET_ROW_HEIGHT_DEFAULT, WIDGET_ROW_HEIGHT_GAP_FREE, WebcamComponent, WebcamModule, WidgetComponent, WidgetLayout, WidgetLayoutInfoComponent, WidgetModule, applyDataGridPatch, collectObjectLevels, createCustomFormioComponent, customUploaderType, enableCustomFormioComponents, menuInitializer, muuriGapFreeLayout, pendingChangesGuard, registerCustomFormioComponent, registerCustomTag, registerFormioCurrencyComponent, registerFormioCurrentUserComponent, registerFormioFileSelectorComponent, registerFormioIbanComponent, registerFormioUploadComponent, registerFormioValueResolverSelectorComponent, resolveWidgetLayout, runAfterCarbonModalClosed, setRequiredOnSchema };
|
|
18679
19127
|
//# sourceMappingURL=valtimo-components.mjs.map
|