@massimo-cassandro/minimo 1.30.0 → 1.31.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massimo-cassandro/minimo",
3
- "version": "1.30.0",
3
+ "version": "1.31.0",
4
4
  "description": "Light css framework & utilities",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -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 domBuilder array. (default: null)
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 domBuilder array. (default: null)
62
- * @param {string | DomBuilderItem | DomBuilderItem[] | null} [params.footerContent=null] - Footer content: plain text, HTML, a domBuilder object, or a domBuilder array. (default: null)
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 domBuilder array */
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 domBuilder array */
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 domBuilder array */
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
- if(content != null && typeof content === 'object' && !Array.isArray(content)) {
125
- content = [content];
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 && Array.isArray(content)) {
136
- mode = 'domBuilder';
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
- content: Array.isArray(headerContent) ? null : headerContent,
214
- children: Array.isArray(headerContent) ? headerContent : undefined
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
- el.innerHTML = '';
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
- content: Array.isArray(footerContent) ? null : footerContent,
262
- children: Array.isArray(footerContent) ? footerContent : undefined
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
  }