@massimo-cassandro/minimo 1.30.0 → 1.31.1
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/charts/src/bars.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseStdAxisOpts } from './helpers/parse-std-axis-opts.js';
|
|
2
|
-
import { parseContainer } from './helpers/chart-utils.js';
|
|
2
|
+
import { parseContainer, waitForContainerSize } from './helpers/chart-utils.js';
|
|
3
3
|
import { cartesianAxis } from './helpers/cartesian-axis.js';
|
|
4
4
|
import { legenda } from './helpers/legenda.js';
|
|
5
5
|
|
|
@@ -185,13 +185,15 @@ export async function bars(params) {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
// container
|
|
188
|
-
|
|
188
|
+
let containerElement;
|
|
189
|
+
[containerElement, params.width, params.height] = parseContainer({ container: params.container, width: params.width, height: params.height });
|
|
189
190
|
|
|
190
|
-
|
|
191
|
-
if(!params.width || !params.height) {
|
|
191
|
+
if(!containerElement && (!params.width || !params.height)) {
|
|
192
192
|
throw `width e/o height mancanti: width: ${params.width}, height: ${params.height}`;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
({ width: params.width, height: params.height } = await waitForContainerSize(containerElement, { width: params.width, height: params.height }, params.debug));
|
|
196
|
+
|
|
195
197
|
// =>> legenda
|
|
196
198
|
// costruzione delle opzioni per la legenda
|
|
197
199
|
const legenda_params = {
|
package/charts/src/goal.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// mostra un triangolo rettangolo disposto orizzontalmente che mostra
|
|
2
2
|
// la porzione di raggiungimento di un obiettivo
|
|
3
3
|
|
|
4
|
-
import { parseContainer, getElementFromContainer } from './helpers/chart-utils.js';
|
|
4
|
+
import { parseContainer, getElementFromContainer, waitForContainerSize } from './helpers/chart-utils.js';
|
|
5
5
|
import { createTextEl } from './helpers/create-text-element.js';
|
|
6
6
|
|
|
7
7
|
export async function goal({
|
|
@@ -158,11 +158,12 @@ export async function goal({
|
|
|
158
158
|
// calcolo width
|
|
159
159
|
[, width] = parseContainer({ container: container, width: width, height: height });
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
if(!width || !height) {
|
|
161
|
+
if(!container && (!width || !height)) {
|
|
163
162
|
throw `width e/o height mancanti: width: ${width}, height: ${height} (chartHeight: ${chartHeight})`;
|
|
164
163
|
}
|
|
165
164
|
|
|
165
|
+
({ width, height } = await waitForContainerSize(container, { width, height }, debug));
|
|
166
|
+
|
|
166
167
|
// larghezza iniziale grafico
|
|
167
168
|
// al netto del padding orizzontale pari a 1/2 cursore, in modo che sia visibile interamente
|
|
168
169
|
// quando posizionato agli estremi (anche in presenta di etichetta laterale, nel caso quest'ultima sia più alta del grafico)
|
package/charts/src/hbars.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// categorie sull'asse verticale (a sinistra), valori sull'asse orizzontale (in basso)
|
|
3
3
|
|
|
4
4
|
import { parseStdAxisOpts } from './helpers/parse-std-axis-opts.js';
|
|
5
|
-
import { parseContainer } from './helpers/chart-utils.js';
|
|
5
|
+
import { parseContainer, waitForContainerSize } from './helpers/chart-utils.js';
|
|
6
6
|
import { cartesianAxis } from './helpers/cartesian-axis.js';
|
|
7
7
|
import { legenda } from './helpers/legenda.js';
|
|
8
8
|
|
|
@@ -186,13 +186,15 @@ export async function hbars(params) {
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
// container
|
|
189
|
-
|
|
189
|
+
let containerElement;
|
|
190
|
+
[containerElement, params.width, params.height] = parseContainer({ container: params.container, width: params.width, height: params.height });
|
|
190
191
|
|
|
191
|
-
|
|
192
|
-
if(!params.width || !params.height) {
|
|
192
|
+
if(!containerElement && (!params.width || !params.height)) {
|
|
193
193
|
throw `width e/o height mancanti: width: ${params.width}, height: ${params.height}`;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
({ width: params.width, height: params.height } = await waitForContainerSize(containerElement, { width: params.width, height: params.height }, params.debug));
|
|
197
|
+
|
|
196
198
|
// =>> legenda
|
|
197
199
|
// costruzione delle opzioni per la legenda
|
|
198
200
|
const legenda_params = {
|
|
@@ -155,3 +155,50 @@ export function parseContainer({ container = null, width = null, height = null }
|
|
|
155
155
|
|
|
156
156
|
return [containerElement, width, height];
|
|
157
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Attende che il container abbia dimensioni valide, utile quando è temporaneamente
|
|
161
|
+
* nascosto (es. `display: none` dentro un tab o un pannello non ancora attivo) al
|
|
162
|
+
* momento della creazione del grafico. Se `width`/`height` sono già entrambe valide
|
|
163
|
+
* risolve immediatamente, altrimenti osserva il container con un `ResizeObserver` ed
|
|
164
|
+
* attende che torni visibile. Non ha timeout: se il container non torna mai visibile
|
|
165
|
+
* la promise resta in sospeso.
|
|
166
|
+
* @param {Element|null} containerElement - L'elemento container (può essere null).
|
|
167
|
+
* @param {Object} size - Dimensioni già note.
|
|
168
|
+
* @param {number|null} size.width - Larghezza già nota (se valida, non si attende il container).
|
|
169
|
+
* @param {number|null} size.height - Altezza già nota (se valida, non si attende il container).
|
|
170
|
+
* @param {boolean} [debug=false] - Se true, logga in console quando l'attesa ha inizio. (default: false)
|
|
171
|
+
* @returns {Promise<{width: number, height: number}>} Le dimensioni effettive una volta disponibili.
|
|
172
|
+
*/
|
|
173
|
+
export function waitForContainerSize(containerElement, { width = null, height = null } = {}, debug = false) {
|
|
174
|
+
if (width && height) {
|
|
175
|
+
return Promise.resolve({ width, height });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!containerElement) {
|
|
179
|
+
return Promise.resolve({ width, height });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return new Promise(resolve => {
|
|
183
|
+
|
|
184
|
+
if (debug) {
|
|
185
|
+
// eslint-disable-next-line no-console
|
|
186
|
+
console.warn('MinimoCharts → in attesa che il container ottenga dimensioni valide (es. tab/pannello nascosto)', containerElement);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const observer = new ResizeObserver(entries => {
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
const observedWidth = width || entry.contentRect.width;
|
|
192
|
+
const observedHeight = height || entry.contentRect.height;
|
|
193
|
+
|
|
194
|
+
if (observedWidth && observedHeight) {
|
|
195
|
+
observer.disconnect();
|
|
196
|
+
resolve({ width: observedWidth, height: observedHeight });
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
observer.observe(containerElement);
|
|
203
|
+
});
|
|
204
|
+
}
|
package/charts/src/lines.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseStdAxisOpts } from './helpers/parse-std-axis-opts.js';
|
|
2
|
-
import { parseContainer } from './helpers/chart-utils.js';
|
|
2
|
+
import { parseContainer, waitForContainerSize } from './helpers/chart-utils.js';
|
|
3
3
|
import { cartesianAxis } from './helpers/cartesian-axis.js';
|
|
4
4
|
import { legenda } from './helpers/legenda.js';
|
|
5
5
|
|
|
@@ -196,12 +196,15 @@ export async function lines(params) {
|
|
|
196
196
|
|
|
197
197
|
|
|
198
198
|
// container
|
|
199
|
-
|
|
199
|
+
let containerElement;
|
|
200
|
+
[containerElement, params.width, params.height] = parseContainer({ container: params.container, width: params.width, height: params.height });
|
|
200
201
|
|
|
201
|
-
if(!params.width || !params.height) {
|
|
202
|
+
if(!containerElement && (!params.width || !params.height)) {
|
|
202
203
|
throw `width e/o height mancanti: width: ${params.width}, height: ${params.height}`;
|
|
203
204
|
}
|
|
204
205
|
|
|
206
|
+
({ width: params.width, height: params.height } = await waitForContainerSize(containerElement, { width: params.width, height: params.height }, params.debug));
|
|
207
|
+
|
|
205
208
|
// =>> ricalcolo data_lines_attrs (utilizzato anche per le linee della legenda)
|
|
206
209
|
const default_line_width = params.data_lines_attrs['stroke-width']?? 1;
|
|
207
210
|
|
package/package.json
CHANGED
|
@@ -27,6 +27,54 @@ function closeDialog(el) {
|
|
|
27
27
|
}, { once: true });
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Wraps a single domBuilder object (not an array, not a DOM Node) into an array,
|
|
32
|
+
* so `content`/`headerContent`/`footerContent` can be handled uniformly with domBuilder arrays.
|
|
33
|
+
* @param {*} value
|
|
34
|
+
* @returns {*}
|
|
35
|
+
*/
|
|
36
|
+
function normalizeContentParam(value) {
|
|
37
|
+
if(value != null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Node)) {
|
|
38
|
+
return [value];
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {*} value - a non-null `content`/`headerContent`/`footerContent` value
|
|
45
|
+
* @returns {'domBuilder' | 'node' | 'function' | 'html'}
|
|
46
|
+
*/
|
|
47
|
+
function getContentKind(value) {
|
|
48
|
+
if(Array.isArray(value)) {
|
|
49
|
+
return 'domBuilder';
|
|
50
|
+
}
|
|
51
|
+
if(value instanceof Node) {
|
|
52
|
+
return 'node';
|
|
53
|
+
}
|
|
54
|
+
if(typeof value === 'function') {
|
|
55
|
+
return 'function';
|
|
56
|
+
}
|
|
57
|
+
return 'html';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Resolves the `content`/`children` domBuilder props for a `headerContent`/`footerContent` value.
|
|
62
|
+
* The `function` kind is not handled here: it is invoked separately, from the item's `callback`,
|
|
63
|
+
* once the container element exists.
|
|
64
|
+
* @param {*} value
|
|
65
|
+
* @returns {{content: string | Node | null, children: DomBuilderItem[] | undefined}}
|
|
66
|
+
*/
|
|
67
|
+
function getDomBuilderContentProps(value) {
|
|
68
|
+
if(value == null) {
|
|
69
|
+
return { content: null, children: undefined };
|
|
70
|
+
}
|
|
71
|
+
const kind = getContentKind(value);
|
|
72
|
+
return {
|
|
73
|
+
content: (kind === 'html' || kind === 'node') ? value : null,
|
|
74
|
+
children: kind === 'domBuilder' ? value : undefined
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
30
78
|
/**
|
|
31
79
|
* modalPopup
|
|
32
80
|
* Opens a popup dialog with the given content.
|
|
@@ -40,7 +88,7 @@ function closeDialog(el) {
|
|
|
40
88
|
*
|
|
41
89
|
* @param {Object} params
|
|
42
90
|
* @param {string | null} [params.dialogExtraClassName=null] - Extra class added to the dialog element. (default: null)
|
|
43
|
-
* @param {string | DomBuilderItem | DomBuilderItem[] | null} [params.content=null] - Plain text, HTML, a domBuilder object, or a
|
|
91
|
+
* @param {string | DomBuilderItem | DomBuilderItem[] | Node | ((container: HTMLElement) => void) | null} [params.content=null] - Plain text, HTML, a domBuilder object, a domBuilder array, a DOM node (appended as-is), or a function invoked with the content container element to populate it directly (`content(dialogContentEl)`). (default: null)
|
|
44
92
|
* @param {string | null} [params.contentExtraClassName=null] - Extra class added to the content wrapper. (default: null)
|
|
45
93
|
* @param {HTMLElement | null} [params.triggerElement=null] - Optional element that triggered the popup; when set, `aria-haspopup`, `aria-controls` and `aria-expanded` are managed on it. (default: null)
|
|
46
94
|
* @param {boolean} [params.addFocus=true] - if true, dialog receives focus after opening (default: true)
|
|
@@ -58,8 +106,8 @@ function closeDialog(el) {
|
|
|
58
106
|
* @param {((el: HTMLDialogElement) => void) | null} [params.closeCallback=null] - Called with the dialog element just before it is removed, receives the dialog element (`closeCallback(dialogEl)`). NB: `dialogEl` is removed immediately after closing. (default: null)
|
|
59
107
|
* @param {((data: *, el: Element) => void) | null} [params.ajaxCallback=null] - Called with the Ajax response and the content element. (default: null)
|
|
60
108
|
* @param {boolean} [params.addScrollbarPadding=false] - Adds right padding to compensate for the scrollbar. (default: false)
|
|
61
|
-
* @param {string | DomBuilderItem | DomBuilderItem[] | null} [params.headerContent=null] - Header content: plain text, HTML, a domBuilder object, or a
|
|
62
|
-
* @param {string | DomBuilderItem | DomBuilderItem[] | null} [params.footerContent=null] - Footer content:
|
|
109
|
+
* @param {string | DomBuilderItem | DomBuilderItem[] | Node | ((container: HTMLElement) => void) | null} [params.headerContent=null] - Header content: plain text, HTML, a domBuilder object, a domBuilder array, a DOM node, or a function invoked with the header container element to populate it directly. (default: null)
|
|
110
|
+
* @param {string | DomBuilderItem | DomBuilderItem[] | Node | ((container: HTMLElement) => void) | null} [params.footerContent=null] - Footer content: same accepted types as `headerContent`, invoked with the footer container element. (default: null)
|
|
63
111
|
* @returns {HTMLDialogElement} The dialog element.
|
|
64
112
|
*/
|
|
65
113
|
|
|
@@ -84,7 +132,7 @@ export function modalPopup({
|
|
|
84
132
|
/** iframe url */
|
|
85
133
|
iframeUrl = null,
|
|
86
134
|
|
|
87
|
-
/** content: plain text, html, domBuilder object or
|
|
135
|
+
/** content: plain text, html, domBuilder object/array, DOM node, or function(container) */
|
|
88
136
|
content = null,
|
|
89
137
|
|
|
90
138
|
/** Ajax url and callback.
|
|
@@ -98,13 +146,13 @@ export function modalPopup({
|
|
|
98
146
|
closeCallback = null,
|
|
99
147
|
addScrollbarPadding = false, // adds extra right padding to compensate for the scrollbar
|
|
100
148
|
|
|
101
|
-
/** header content: plain text, HTML, domBuilder object or
|
|
149
|
+
/** header content: plain text, HTML, domBuilder object/array, DOM node, or function(container) */
|
|
102
150
|
headerContent = null,
|
|
103
151
|
|
|
104
152
|
/** extra classname added to header */
|
|
105
153
|
headerExtraClassName = null,
|
|
106
154
|
|
|
107
|
-
/** footer content: plain text, HTML, domBuilder object or
|
|
155
|
+
/** footer content: plain text, HTML, domBuilder object/array, DOM node, or function(container) */
|
|
108
156
|
footerContent = null,
|
|
109
157
|
|
|
110
158
|
/** extra classname added to footer */
|
|
@@ -121,22 +169,13 @@ export function modalPopup({
|
|
|
121
169
|
}
|
|
122
170
|
|
|
123
171
|
// allow a single domBuilder object (in addition to a domBuilder array) by wrapping it in an array
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if(headerContent != null && typeof headerContent === 'object' && !Array.isArray(headerContent)) {
|
|
128
|
-
headerContent = [headerContent];
|
|
129
|
-
}
|
|
130
|
-
if(footerContent != null && typeof footerContent === 'object' && !Array.isArray(footerContent)) {
|
|
131
|
-
footerContent = [footerContent];
|
|
132
|
-
}
|
|
172
|
+
content = normalizeContentParam(content);
|
|
173
|
+
headerContent = normalizeContentParam(headerContent);
|
|
174
|
+
footerContent = normalizeContentParam(footerContent);
|
|
133
175
|
|
|
134
176
|
let mode;
|
|
135
|
-
if(content != null
|
|
136
|
-
mode =
|
|
137
|
-
|
|
138
|
-
} else if (content != null) {
|
|
139
|
-
mode = 'html';
|
|
177
|
+
if(content != null) {
|
|
178
|
+
mode = getContentKind(content);
|
|
140
179
|
|
|
141
180
|
} else if (ajaxUrl != null && ajaxCallback != null) {
|
|
142
181
|
mode = 'ajax';
|
|
@@ -210,13 +249,17 @@ export function modalPopup({
|
|
|
210
249
|
id: headerId,
|
|
211
250
|
className: classnames(styles.header, headerExtraClassName),
|
|
212
251
|
condition: headerContent != null,
|
|
213
|
-
|
|
214
|
-
|
|
252
|
+
...getDomBuilderContentProps(headerContent),
|
|
253
|
+
callback: el => {
|
|
254
|
+
if(headerContent != null && getContentKind(headerContent) === 'function') {
|
|
255
|
+
/** @type {(container: HTMLElement) => void} */ (headerContent)(/** @type {HTMLElement} */ (el));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
215
258
|
},
|
|
216
259
|
{
|
|
217
260
|
className: classnames(styles.content, contentExtraClassName),
|
|
218
|
-
content: mode === 'html'
|
|
219
|
-
? content
|
|
261
|
+
content: (mode === 'html' || mode === 'node')
|
|
262
|
+
? /** @type {string | Node} */ (content)
|
|
220
263
|
: mode === 'ajax'
|
|
221
264
|
? spinner()
|
|
222
265
|
: null,
|
|
@@ -233,7 +276,11 @@ export function modalPopup({
|
|
|
233
276
|
}
|
|
234
277
|
],
|
|
235
278
|
callback: el => {
|
|
236
|
-
dialogContentEl = el;
|
|
279
|
+
dialogContentEl = /** @type {HTMLElement} */ (el);
|
|
280
|
+
|
|
281
|
+
if(mode === 'function') {
|
|
282
|
+
/** @type {(container: HTMLElement) => void} */ (content)(dialogContentEl);
|
|
283
|
+
}
|
|
237
284
|
|
|
238
285
|
if(mode === 'ajax') {
|
|
239
286
|
try {
|
|
@@ -241,7 +288,7 @@ export function modalPopup({
|
|
|
241
288
|
(async () => {
|
|
242
289
|
const response = await fetch(safeAjaxUrl),
|
|
243
290
|
data = await response.json();
|
|
244
|
-
|
|
291
|
+
dialogContentEl.innerHTML = '';
|
|
245
292
|
ajaxCallback?.(data, dialogContentEl);
|
|
246
293
|
})();
|
|
247
294
|
|
|
@@ -258,8 +305,12 @@ export function modalPopup({
|
|
|
258
305
|
{
|
|
259
306
|
className: classnames(styles.footer, footerExtraClassName),
|
|
260
307
|
condition: footerContent != null,
|
|
261
|
-
|
|
262
|
-
|
|
308
|
+
...getDomBuilderContentProps(footerContent),
|
|
309
|
+
callback: el => {
|
|
310
|
+
if(footerContent != null && getContentKind(footerContent) === 'function') {
|
|
311
|
+
/** @type {(container: HTMLElement) => void} */ (footerContent)(/** @type {HTMLElement} */ (el));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
263
314
|
},
|
|
264
315
|
]
|
|
265
316
|
}
|