@jsswift/ui 1.0.45 → 1.0.46

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.
@@ -1,48 +1,48 @@
1
1
  UI.Icon = (...args) => {
2
- let props = {};
3
- let children = [];
4
- let name = "home";
5
- let hasName = false;
6
- const a = args[0];
7
- const b = args[1];
8
- const isPlainObject = (v) => v && typeof v === "object" && !v.nodeType && !Array.isArray(v) && !(v instanceof Function);
9
-
10
- if (typeof a === "string") {
11
- name = a;
12
- hasName = true;
13
- if (isPlainObject(b)) {
14
- props = b;
15
- if (args.length > 2) children = args.slice(2);
16
- } else if (args.length > 1) {
17
- children = args.slice(1);
18
- }
19
- } else if (isPlainObject(a)) {
20
- props = a;
21
- if (a.name != null) {
22
- name = a.name;
23
- hasName = true;
24
- }
25
- if (args.length > 1) children = args.slice(1);
26
- } else if (args.length) {
27
- children = args;
28
- }
29
-
30
- if (!hasName && children.length) {
31
- name = children.length === 1 ? children[0] : children;
32
- }
33
- applyCommonProps(props);
34
-
35
- const slots = props.slots || {};
36
-
37
- const size = uiUnwrap(props.size);
38
- const color = uiUnwrap(props.color);
39
- const isFill = String(name).endsWith("-fill");
40
- const style = { ...(props.style || {}) };
41
- const sizeClass = uiComputed(props.size, () => {
42
- const v = uiUnwrap(props.size);
43
- return (typeof v === "string" && sizeMap[v]) ? v : "";
44
- });
45
-
2
+ let props = {};
3
+ let children = [];
4
+ let name = "home";
5
+ let hasName = false;
6
+ const a = args[0];
7
+ const b = args[1];
8
+ const isPlainObject = (v) => v && typeof v === "object" && !v.nodeType && !Array.isArray(v) && !(v instanceof Function);
9
+
10
+ if (typeof a === "string") {
11
+ name = a;
12
+ hasName = true;
13
+ if (isPlainObject(b)) {
14
+ props = b;
15
+ if (args.length > 2) children = args.slice(2);
16
+ } else if (args.length > 1) {
17
+ children = args.slice(1);
18
+ }
19
+ } else if (isPlainObject(a)) {
20
+ props = a;
21
+ if (a.name != null) {
22
+ name = a.name;
23
+ hasName = true;
24
+ }
25
+ if (args.length > 1) children = args.slice(1);
26
+ } else if (args.length) {
27
+ children = args;
28
+ }
29
+
30
+ if (!hasName && children.length) {
31
+ name = children.length === 1 ? children[0] : children;
32
+ }
33
+ applyCommonProps(props);
34
+
35
+ const slots = props.slots || {};
36
+
37
+ const size = uiUnwrap(props.size);
38
+ const color = uiUnwrap(props.color);
39
+ const isFill = String(name).endsWith("-fill");
40
+ const style = { ...(props.style || {}) };
41
+ const sizeClass = uiComputed(props.size, () => {
42
+ const v = uiUnwrap(props.size);
43
+ return (typeof v === "string" && sizeMap[v]) ? v : "";
44
+ });
45
+
46
46
  const hasVisualSurface = !!(props.color || props.clickable || props.border || props.glossy || props.glow || props.glass || props.shadow || props.outline || props.gradient || props.textGradient || props.lightShadow || props.radius);
47
47
  const builtInSpriteIcons = {
48
48
  "chevron-down": {
@@ -86,7 +86,7 @@
86
86
  setPropertyProps(icon, props);
87
87
  return wrapIcon(icon);
88
88
  }
89
-
89
+
90
90
  const nameStr = String(name);
91
91
  const useHref = nameStr.includes("#") ? nameStr : "";
92
92
  let icon = null;
@@ -107,422 +107,351 @@
107
107
  );
108
108
  if (isFill) svg.style.fill = "currentColor";
109
109
  icon = _.span({ ...p, "data-icon": nameStr }, svg, ...children);
110
- } else {
111
- icon = _.span({ ...p, "data-icon": nameStr }, nameStr, ...children);
112
- }
113
-
114
- if (size != null) {
115
- let v = size;
116
- if (JSswift.uiSizes.includes(size)) {
117
- v = `var(--cms-icon-size-${size})`;
118
- }
119
- v = typeof v === "number" ? v + "px" : String(v);
120
- icon.style.setProperty("--set-icon-size", v);
121
- }
110
+ } else {
111
+ icon = _.span({ ...p, "data-icon": nameStr }, nameStr, ...children);
112
+ }
113
+
114
+ if (size != null) {
115
+ let v = size;
116
+ if (JSswift.uiSizes.includes(size)) {
117
+ v = `var(--cms-icon-size-${size})`;
118
+ }
119
+ v = typeof v === "number" ? v + "px" : String(v);
120
+ icon.style.setProperty("--set-icon-size", v);
121
+ }
122
122
 
123
123
  setPropertyProps(icon, props);
124
124
  return wrapIcon(icon);
125
125
  };
126
- if (JSswift.isDev?.()) {
127
- UI.meta = UI.meta || {};
128
- UI.meta.Icon = {
129
- signature: "UI.Icon(name) | UI.Icon(props) | UI.Icon(props, ...children)",
130
- props: {
131
- name: "string|Node|Function",
132
- size: "number|string",
133
- color: "string",
134
- shadow: "boolean|string",
135
- lightShadow: "boolean",
136
- clickable: "boolean",
137
- border: "boolean",
138
- glossy: "boolean",
139
- glow: "boolean",
140
- glass: "boolean",
141
- gradient: "boolean|number",
142
- outline: "boolean",
143
- textGradient: "boolean",
144
- radius: "number|string",
145
- spriteUrl: "string",
146
- tooltip: "String|Node|Function|Object",
147
- tooltipProps: "object",
148
- slots: "{ default? }",
149
- class: "string",
150
- style: "object"
151
- },
152
- slots: {
153
- default: "Custom icon content"
154
- },
155
- returns: "HTMLSpanElement",
156
- description: "Sprite- or text-based icon with configurable size/color."
157
- };
158
- }
159
- // Esempio: JSswift.ui.Icon({ name: "home", size: 18 })
160
-
161
- UI.Badge = (...args) => {
162
- const { props, children } = JSswift.uiNormalizeArgs(args);
163
- const slots = props.slots || {};
164
- const sizeClass = uiComputed(props.size, () => {
165
- const v = uiUnwrap(props.size);
166
- return (typeof v === "string" && sizeMap[v]) ? `cms-size-${v}` : "";
167
- });
168
- const cls = uiClass([
169
- "cms-clear-set",
170
- "cms-badge",
171
- "cms-singularity",
172
- sizeClass,
173
- uiWhen(props.outline, "outline"),
174
- props.class
175
- ]);
176
- const p = JSswift.omit(props, [
177
- "label",
178
- "color",
179
- "outline",
180
- "size",
181
- "slots",
182
- "notification",
183
- "iconSize",
184
- "topLeft",
185
- "topRight",
186
- "bottomLeft",
187
- "bottomRight",
188
- "left",
189
- "right"
190
- ]);
191
- p.class = cls;
192
-
193
- const style = {
194
- ...(props.style || {})
195
- };
196
- if (props.size) {
197
- const size = uiUnwrap(props.size);
198
- if (!(typeof size === "string" && sizeMap[size])) {
199
- style.fontSize = toCssSize(props.size);
200
- }
201
- }
202
- p.style = style;
203
-
204
- const renderNamedSlotToArray = (names, ctx, fallback) => {
205
- const list = Array.isArray(names) ? names : [names];
206
- for (const name of list) {
207
- if (JSswift.ui.getSlot(slots, name) != null) {
208
- return renderSlotToArray(slots, name, ctx, fallback);
209
- }
210
- }
211
- return fallback == null || fallback === false || fallback === ""
212
- ? []
213
- : renderSlotToArray(null, "default", ctx, fallback);
214
- };
215
-
216
- const resolveIconSize = () => {
217
- const raw = uiUnwrap(props.iconSize ?? props.size);
218
- if (typeof raw === "number") return Math.max(10, Math.round(raw * 0.85));
219
- if (typeof raw === "string") {
220
- const map = {
221
- xxs: 10,
222
- xs: 11,
223
- sm: 12,
224
- md: 13,
225
- lg: 14,
226
- xl: 16,
227
- xxl: 18,
228
- xxxl: 20
229
- };
230
- return map[raw] || 14;
231
- }
232
- return 14;
233
- };
234
-
235
- const resolveIconFallback = (source, as) => {
236
- const raw = uiUnwrap(source);
237
- if (raw == null || raw === false || raw === "") return null;
238
- if (typeof raw === "string") return UI.Icon({ name: raw, size: resolveIconSize() });
239
- return JSswift.ui.slot(raw, { as });
240
- };
241
-
242
- const renderIconAnchor = (slotNames, propKey, position) => _.dynamic(() => {
243
- const iconFallback = resolveIconFallback(props[propKey], position);
244
- const nodes = renderNamedSlotToArray(slotNames, { position }, iconFallback);
245
- if (!nodes.length) return null;
246
- return _.span({
247
- class: `cms-badge-anchor cms-badge-anchor-${position}`,
248
- "data-badge-slot": position
249
- }, ...nodes);
250
- });
251
-
252
- const content = _.dynamic(() => {
253
- const labelFallback = uiUnwrap(props.label);
254
- let labelNodes = renderNamedSlotToArray(["label"], {}, labelFallback);
255
- if (!labelNodes.length) labelNodes = renderNamedSlotToArray(["default"], {}, children);
256
- return _.span(
257
- { class: "cms-badge-content" },
258
- _.span({ class: "cms-badge-label" }, ...(labelNodes.length ? labelNodes : [""]))
259
- );
260
- });
261
-
262
- const notification = _.dynamic(() => {
263
- const rawNotification = uiUnwrap(props.notification);
264
- const fallback = rawNotification == null || rawNotification === false || rawNotification === ""
265
- ? null
266
- : rawNotification;
267
- const nodes = renderNamedSlotToArray(["notification"], { notification: rawNotification }, fallback);
268
- if (!nodes.length) return null;
269
- return _.span({
270
- class: "cms-badge-notification",
271
- "data-badge-slot": "notification",
272
- "aria-label": typeof rawNotification === "number" ? `${rawNotification} notifications` : null
273
- }, ...nodes);
274
- });
275
-
276
- const wrap = _.span(
277
- renderIconAnchor(["left"], "left", "left"),
278
- renderIconAnchor(["centerLeft", "center-left"], "centerLeft", "center-left"),
279
- p,
280
- content,
281
- renderIconAnchor(["topLeft", "top-left"], "topLeft", "top-left"),
282
- renderIconAnchor(["topRight", "top-right"], "topRight", "top-right"),
283
- renderIconAnchor(["bottomLeft", "bottom-left"], "bottomLeft", "bottom-left"),
284
- renderIconAnchor(["bottomRight", "bottom-right"], "bottomRight", "bottom-right"),
285
- renderIconAnchor(["centerRight", "center-right"], "centerRight", "center-right"),
286
- renderIconAnchor(["right"], "right", "right"),
287
- notification
288
- );
289
- // se esiste un props con left o right
290
- if (props.topLeft || props.centerLeft || props.bottomLeft) {
291
- wrap.style.setProperty("padding-left", "15px");
292
- }
293
- if (props.topRight || props.centerRight || props.bottomRight) {
294
- wrap.style.setProperty("padding-right", "15px");
295
- }
296
-
297
- setPropertyProps(wrap, props);
298
- return wrap;
299
- };
300
- if (JSswift.isDev?.()) {
301
- UI.meta = UI.meta || {};
302
- UI.meta.Badge = {
303
- signature: "UI.Badge(...children) | UI.Badge(props, ...children)",
304
- props: {
305
- label: "String|Node|Function|Array",
306
- color: "string",
307
- size: "string|number",
308
- outline: "boolean",
309
- notification: "string|number|Node|Function|[get,set] signal",
310
- iconSize: "string|number",
311
- topLeft: "String|Node|Function|Array",
312
- topRight: "String|Node|Function|Array",
313
- bottomLeft: "String|Node|Function|Array",
314
- bottomRight: "String|Node|Function|Array",
315
- centerLeft: "String|Node|Function|Array",
316
- centerRight: "String|Node|Function|Array",
317
- left: "String|Node|Function|Array",
318
- right: "String|Node|Function|Array",
319
- slots: "{ label?, default?, notification?, topLeft?, topRight?, bottomLeft?, bottomRight?, left?, right? }",
320
- class: "string",
321
- style: "object"
322
- },
323
- slots: {
324
- label: "Badge label content",
325
- default: "Fallback content",
326
- notification: "Notification badge content",
327
- topLeft: "Icon anchored top-left",
328
- topRight: "Icon anchored top-right",
329
- bottomLeft: "Icon anchored bottom-left",
330
- bottomRight: "Icon anchored bottom-right",
331
- centerLeft: "Icon anchored center-left",
332
- centerRight: "Icon anchored center-right",
333
- left: "Icon anchored left",
334
- right: "Icon anchored right"
335
- },
336
- returns: "HTMLSpanElement",
337
- description: "Inline badge with reactive notification and 6 positionable icon slots."
338
- };
339
- }
340
- // Esempio: JSswift.ui.Badge({ label: "New" })
341
-
342
- const avatarGetInitials = (value) => {
343
- if (value == null || value === false) return "";
344
- const text = String(value)
345
- .replace(/[._-]+/g, " ")
346
- .replace(/\s+/g, " ")
347
- .trim();
348
- if (!text) return "";
349
- const words = text.split(" ").filter(Boolean);
350
- if (!words.length) return "";
351
- if (words.length === 1) {
352
- const token = words[0].replace(/[^a-z0-9]/gi, "");
353
- return token.slice(0, 2).toUpperCase();
354
- }
355
- return words.slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join("");
356
- };
357
-
358
- const avatarStatusState = (value, fallback) => {
359
- const raw = value == null || value === true ? fallback : value;
360
- if (raw == null || raw === false) return normalizeState(fallback);
361
- const token = String(raw).toLowerCase();
362
- if (token === "online" || token === "available" || token === "active") return "success";
363
- if (token === "away" || token === "pending") return "warning";
364
- if (token === "busy" || token === "dnd" || token === "blocked") return "danger";
365
- if (token === "offline" || token === "idle") return "secondary";
366
- return normalizeState(token) || normalizeState(fallback);
367
- };
368
-
369
- const avatarPrimitiveBadge = (value, className = "cms-avatar-badge") => {
370
- if (value == null || value === false) return null;
371
- if (value instanceof Node || Array.isArray(value)) return value;
372
- if (typeof value === "function") return value;
373
- if (value === true) return _.span({ class: className });
374
- return _.span({ class: className }, String(value));
375
- };
376
-
377
- const avatarPrimitiveStatus = (value, color) => {
378
- if (value == null || value === false) {
379
- if (!color) return null;
380
- const fallbackState = avatarStatusState(null, color);
381
- return _.span({ class: uiClass(["cms-avatar-status", fallbackState ? `cms-state-${fallbackState}` : ""]) });
382
- }
383
- if (value instanceof Node || Array.isArray(value)) return value;
384
- if (typeof value === "function") return value;
385
- const state = avatarStatusState(value, color);
386
- if (value === true || state) {
387
- return _.span({ class: uiClass(["cms-avatar-status", state ? `cms-state-${state}` : ""]) });
388
- }
389
- return avatarPrimitiveBadge(value, "cms-avatar-badge");
390
- };
391
-
392
- const avatarRenderAnchor = (className, nodes) => {
393
- if (!nodes || !nodes.length) return null;
394
- return _.span({ class: uiClass(["cms-avatar-anchor", className]) }, ...nodes);
395
- };
396
-
397
- UI.Avatar = (...args) => {
398
- const { props, children } = JSswift.uiNormalizeArgs(args);
399
- const slots = props.slots || {};
400
- const sizeClass = uiComputed(props.size, () => {
401
- const v = uiUnwrap(props.size);
402
- return (typeof v === "string" && JSswift.uiSizes?.includes(v)) ? `cms-size-${v}` : "";
403
- });
404
- const stateClass = uiComputed([props.color, props.state], () => {
405
- const v = uiUnwrap(props.color) || uiUnwrap(props.state) || "";
406
- const state = normalizeState(v);
407
- return state ? `cms-state-${state}` : "";
408
- });
409
- const cls = uiClass([
410
- "cms-clear-set",
411
- "cms-avatar",
412
- "cms-singularity",
413
- sizeClass,
414
- stateClass,
415
- uiWhen(props.square, "cms-avatar-square"),
416
- uiWhen(props.elevated, "elevated"),
417
- props.class
418
- ]);
419
-
420
- const p = JSswift.omit(props, [
421
- "src", "srcset", "srcSet", "sizes", "alt", "label", "name", "initials", "text",
422
- "size", "fontSize", "textSize", "radius", "rounded", "square", "elevated",
423
- "icon", "iconSize", "media", "fit", "badge", "notification", "status",
424
- "statusColor", "presence", "topLeft", "topRight", "bottomLeft", "bottomRight",
425
- "slots"
426
- ]);
427
- p.class = cls;
428
-
429
- const style = { ...(props.style || {}) };
430
- const sizeStyle = uiStyleValue(props.size, (value) => {
431
- if (typeof value === "string" && JSswift.uiSizes?.includes(value)) return "";
432
- return toCssSize(value);
433
- }, "");
434
- if (sizeStyle != null) {
435
- style.width = sizeStyle;
436
- style.height = sizeStyle;
437
- }
438
-
439
- const fontSizeValue = props.fontSize ?? props.textSize;
440
- const fontSizeStyle = fontSizeValue != null
441
- ? uiStyleValue(fontSizeValue, toCssSize)
442
- : uiStyleValue(props.size, (value) => {
443
- if (typeof value === "string" && JSswift.uiSizes?.includes(value)) return "";
444
- return `clamp(11px, calc(${toCssSize(value)} * 0.34), 28px)`;
445
- }, "");
446
- if (fontSizeStyle != null) style.fontSize = fontSizeStyle;
447
-
448
- const radiusStyle = uiStyleValue(props.radius, (value) => {
449
- const normalized = normalizeRadius(value);
450
- if (normalized === "xxxl") return "999px";
451
- if (normalized) return `var(--cms-r-${normalized})`;
452
- if (typeof value === "number") return `${value}px`;
453
- return String(value);
454
- });
455
- if (radiusStyle != null) style["--set-border-radius"] = radiusStyle;
456
- p.style = style;
457
-
458
- const ctx = { props };
459
- const altText = uiComputed([props.alt, props.label, props.name, props.text], () => {
460
- const raw = uiUnwrap(props.alt) || uiUnwrap(props.label) || uiUnwrap(props.name) || uiUnwrap(props.text) || "avatar";
461
- return String(raw);
462
- });
463
- const iconFallback = props.icon != null
464
- ? (typeof props.icon === "string"
465
- ? UI.Icon({ name: props.icon, size: props.iconSize || props.size || "sm" })
466
- : JSswift.ui.slot(props.icon, { as: "icon" }))
467
- : null;
468
- const labelFallback = uiComputed([props.initials, props.text, props.label, props.name], () => {
469
- const explicit = uiUnwrap(props.initials) ?? uiUnwrap(props.text);
470
- if (explicit != null) return explicit;
471
- const rawLabel = uiUnwrap(props.label) ?? uiUnwrap(props.name);
472
- if (typeof rawLabel === "string") return avatarGetInitials(rawLabel);
473
- return rawLabel;
474
- });
475
- const badgeFallback = avatarPrimitiveBadge(uiUnwrap(props.badge ?? props.notification));
476
- const statusFallback = avatarPrimitiveStatus(
477
- uiUnwrap(props.status ?? props.presence),
478
- uiUnwrap(props.statusColor)
479
- );
480
-
481
- let mediaNodes = renderSlotToArray(slots, "media", ctx, props.media);
482
- if (!mediaNodes.length && props.src) {
483
- const imgProps = {
484
- class: "cms-avatar-img",
485
- src: props.src,
486
- alt: altText,
487
- sizes: props.sizes,
488
- style: {}
489
- };
490
- if (props.srcset != null) imgProps.srcset = props.srcset;
491
- if (props.srcSet != null) imgProps.srcset = props.srcSet;
492
- const fitStyle = uiStyleValue(props.fit);
493
- if (fitStyle != null) imgProps.style.objectFit = fitStyle;
494
- mediaNodes = [_.img(imgProps)];
495
- }
496
-
497
- const defaultNodes = renderSlotToArray(slots, "default", ctx, children);
498
- const fallbackNodes = renderSlotToArray(slots, "fallback", ctx, null);
499
- const labelNodes = renderSlotToArray(slots, "label", ctx, labelFallback);
500
- const iconNodes = renderSlotToArray(slots, "icon", ctx, iconFallback);
501
- const mainNodes = mediaNodes.length
502
- ? mediaNodes
503
- : (defaultNodes.length
504
- ? defaultNodes
505
- : (fallbackNodes.length
506
- ? fallbackNodes
507
- : (labelNodes.length
508
- ? labelNodes
509
- : (iconNodes.length ? iconNodes : ["?"]))));
510
-
511
- const bodyClass = mediaNodes.length ? "cms-avatar-media" : "cms-avatar-fallback";
512
- const body = _.span({ class: "cms-avatar-body" },
513
- _.span({ class: bodyClass }, ...mainNodes)
514
- );
515
-
516
- let topLeftNodes = renderSlotToArray(slots, "topLeft", ctx, props.topLeft);
517
- let topRightNodes = renderSlotToArray(slots, "topRight", ctx, props.topRight);
518
- let bottomLeftNodes = renderSlotToArray(slots, "bottomLeft", ctx, props.bottomLeft);
519
- let bottomRightNodes = renderSlotToArray(slots, "bottomRight", ctx, props.bottomRight);
520
- const badgeNodes = renderSlotToArray(slots, "badge", ctx, badgeFallback);
521
- const statusNodes = renderSlotToArray(slots, "status", ctx, statusFallback);
522
-
523
- if (!topRightNodes.length && badgeNodes.length) topRightNodes = badgeNodes;
524
- if (!bottomRightNodes.length && statusNodes.length) bottomRightNodes = statusNodes;
525
-
126
+
127
+ // Esempio: JSswift.ui.Icon({ name: "home", size: 18 })
128
+
129
+ UI.Badge = (...args) => {
130
+ const { props, children } = JSswift.uiNormalizeArgs(args);
131
+ const slots = props.slots || {};
132
+ const sizeClass = uiComputed(props.size, () => {
133
+ const v = uiUnwrap(props.size);
134
+ return (typeof v === "string" && sizeMap[v]) ? `cms-size-${v}` : "";
135
+ });
136
+ const cls = uiClass([
137
+ "cms-clear-set",
138
+ "cms-badge",
139
+ "cms-singularity",
140
+ sizeClass,
141
+ uiWhen(props.outline, "outline"),
142
+ props.class
143
+ ]);
144
+ const p = JSswift.omit(props, [
145
+ "label",
146
+ "color",
147
+ "outline",
148
+ "size",
149
+ "slots",
150
+ "notification",
151
+ "iconSize",
152
+ "topLeft",
153
+ "topRight",
154
+ "bottomLeft",
155
+ "bottomRight",
156
+ "left",
157
+ "right"
158
+ ]);
159
+ p.class = cls;
160
+
161
+ const style = {
162
+ ...(props.style || {})
163
+ };
164
+ if (props.size) {
165
+ const size = uiUnwrap(props.size);
166
+ if (!(typeof size === "string" && sizeMap[size])) {
167
+ style.fontSize = toCssSize(props.size);
168
+ }
169
+ }
170
+ p.style = style;
171
+
172
+ const renderNamedSlotToArray = (names, ctx, fallback) => {
173
+ const list = Array.isArray(names) ? names : [names];
174
+ for (const name of list) {
175
+ if (JSswift.ui.getSlot(slots, name) != null) {
176
+ return renderSlotToArray(slots, name, ctx, fallback);
177
+ }
178
+ }
179
+ return fallback == null || fallback === false || fallback === ""
180
+ ? []
181
+ : renderSlotToArray(null, "default", ctx, fallback);
182
+ };
183
+
184
+ const resolveIconSize = () => {
185
+ const raw = uiUnwrap(props.iconSize ?? props.size);
186
+ if (typeof raw === "number") return Math.max(10, Math.round(raw * 0.85));
187
+ if (typeof raw === "string") {
188
+ const map = {
189
+ xxs: 10,
190
+ xs: 11,
191
+ sm: 12,
192
+ md: 13,
193
+ lg: 14,
194
+ xl: 16,
195
+ xxl: 18,
196
+ xxxl: 20
197
+ };
198
+ return map[raw] || 14;
199
+ }
200
+ return 14;
201
+ };
202
+
203
+ const resolveIconFallback = (source, as) => {
204
+ const raw = uiUnwrap(source);
205
+ if (raw == null || raw === false || raw === "") return null;
206
+ if (typeof raw === "string") return UI.Icon({ name: raw, size: resolveIconSize() });
207
+ return JSswift.ui.slot(raw, { as });
208
+ };
209
+
210
+ const renderIconAnchor = (slotNames, propKey, position) => _.dynamic(() => {
211
+ const iconFallback = resolveIconFallback(props[propKey], position);
212
+ const nodes = renderNamedSlotToArray(slotNames, { position }, iconFallback);
213
+ if (!nodes.length) return null;
214
+ return _.span({
215
+ class: `cms-badge-anchor cms-badge-anchor-${position}`,
216
+ "data-badge-slot": position
217
+ }, ...nodes);
218
+ });
219
+
220
+ const content = _.dynamic(() => {
221
+ const labelFallback = uiUnwrap(props.label);
222
+ let labelNodes = renderNamedSlotToArray(["label"], {}, labelFallback);
223
+ if (!labelNodes.length) labelNodes = renderNamedSlotToArray(["default"], {}, children);
224
+ return _.span(
225
+ { class: "cms-badge-content" },
226
+ _.span({ class: "cms-badge-label" }, ...(labelNodes.length ? labelNodes : [""]))
227
+ );
228
+ });
229
+
230
+ const notification = _.dynamic(() => {
231
+ const rawNotification = uiUnwrap(props.notification);
232
+ const fallback = rawNotification == null || rawNotification === false || rawNotification === ""
233
+ ? null
234
+ : rawNotification;
235
+ const nodes = renderNamedSlotToArray(["notification"], { notification: rawNotification }, fallback);
236
+ if (!nodes.length) return null;
237
+ return _.span({
238
+ class: "cms-badge-notification",
239
+ "data-badge-slot": "notification",
240
+ "aria-label": typeof rawNotification === "number" ? `${rawNotification} notifications` : null
241
+ }, ...nodes);
242
+ });
243
+
244
+ const wrap = _.span(
245
+ renderIconAnchor(["left"], "left", "left"),
246
+ renderIconAnchor(["centerLeft", "center-left"], "centerLeft", "center-left"),
247
+ p,
248
+ content,
249
+ renderIconAnchor(["topLeft", "top-left"], "topLeft", "top-left"),
250
+ renderIconAnchor(["topRight", "top-right"], "topRight", "top-right"),
251
+ renderIconAnchor(["bottomLeft", "bottom-left"], "bottomLeft", "bottom-left"),
252
+ renderIconAnchor(["bottomRight", "bottom-right"], "bottomRight", "bottom-right"),
253
+ renderIconAnchor(["centerRight", "center-right"], "centerRight", "center-right"),
254
+ renderIconAnchor(["right"], "right", "right"),
255
+ notification
256
+ );
257
+ // se esiste un props con left o right
258
+ if (props.topLeft || props.centerLeft || props.bottomLeft) {
259
+ wrap.style.setProperty("padding-left", "15px");
260
+ }
261
+ if (props.topRight || props.centerRight || props.bottomRight) {
262
+ wrap.style.setProperty("padding-right", "15px");
263
+ }
264
+
265
+ setPropertyProps(wrap, props);
266
+ return wrap;
267
+ };
268
+
269
+ // Esempio: JSswift.ui.Badge({ label: "New" })
270
+
271
+ const avatarGetInitials = (value) => {
272
+ if (value == null || value === false) return "";
273
+ const text = String(value)
274
+ .replace(/[._-]+/g, " ")
275
+ .replace(/\s+/g, " ")
276
+ .trim();
277
+ if (!text) return "";
278
+ const words = text.split(" ").filter(Boolean);
279
+ if (!words.length) return "";
280
+ if (words.length === 1) {
281
+ const token = words[0].replace(/[^a-z0-9]/gi, "");
282
+ return token.slice(0, 2).toUpperCase();
283
+ }
284
+ return words.slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join("");
285
+ };
286
+
287
+ const avatarStatusState = (value, fallback) => {
288
+ const raw = value == null || value === true ? fallback : value;
289
+ if (raw == null || raw === false) return normalizeState(fallback);
290
+ const token = String(raw).toLowerCase();
291
+ if (token === "online" || token === "available" || token === "active") return "success";
292
+ if (token === "away" || token === "pending") return "warning";
293
+ if (token === "busy" || token === "dnd" || token === "blocked") return "danger";
294
+ if (token === "offline" || token === "idle") return "secondary";
295
+ return normalizeState(token) || normalizeState(fallback);
296
+ };
297
+
298
+ const avatarPrimitiveBadge = (value, className = "cms-avatar-badge") => {
299
+ if (value == null || value === false) return null;
300
+ if (value instanceof Node || Array.isArray(value)) return value;
301
+ if (typeof value === "function") return value;
302
+ if (value === true) return _.span({ class: className });
303
+ return _.span({ class: className }, String(value));
304
+ };
305
+
306
+ const avatarPrimitiveStatus = (value, color) => {
307
+ if (value == null || value === false) {
308
+ if (!color) return null;
309
+ const fallbackState = avatarStatusState(null, color);
310
+ return _.span({ class: uiClass(["cms-avatar-status", fallbackState ? `cms-state-${fallbackState}` : ""]) });
311
+ }
312
+ if (value instanceof Node || Array.isArray(value)) return value;
313
+ if (typeof value === "function") return value;
314
+ const state = avatarStatusState(value, color);
315
+ if (value === true || state) {
316
+ return _.span({ class: uiClass(["cms-avatar-status", state ? `cms-state-${state}` : ""]) });
317
+ }
318
+ return avatarPrimitiveBadge(value, "cms-avatar-badge");
319
+ };
320
+
321
+ const avatarRenderAnchor = (className, nodes) => {
322
+ if (!nodes || !nodes.length) return null;
323
+ return _.span({ class: uiClass(["cms-avatar-anchor", className]) }, ...nodes);
324
+ };
325
+
326
+ UI.Avatar = (...args) => {
327
+ const { props, children } = JSswift.uiNormalizeArgs(args);
328
+ const slots = props.slots || {};
329
+ const sizeClass = uiComputed(props.size, () => {
330
+ const v = uiUnwrap(props.size);
331
+ return (typeof v === "string" && JSswift.uiSizes?.includes(v)) ? `cms-size-${v}` : "";
332
+ });
333
+ const stateClass = uiComputed([props.color, props.state], () => {
334
+ const v = uiUnwrap(props.color) || uiUnwrap(props.state) || "";
335
+ const state = normalizeState(v);
336
+ return state ? `cms-state-${state}` : "";
337
+ });
338
+ const cls = uiClass([
339
+ "cms-clear-set",
340
+ "cms-avatar",
341
+ "cms-singularity",
342
+ sizeClass,
343
+ stateClass,
344
+ uiWhen(props.square, "cms-avatar-square"),
345
+ uiWhen(props.elevated, "elevated"),
346
+ props.class
347
+ ]);
348
+
349
+ const p = JSswift.omit(props, [
350
+ "src", "srcset", "srcSet", "sizes", "alt", "label", "name", "initials", "text",
351
+ "size", "fontSize", "textSize", "radius", "rounded", "square", "elevated",
352
+ "icon", "iconSize", "media", "fit", "badge", "notification", "status",
353
+ "statusColor", "presence", "topLeft", "topRight", "bottomLeft", "bottomRight",
354
+ "slots"
355
+ ]);
356
+ p.class = cls;
357
+
358
+ const style = { ...(props.style || {}) };
359
+ const sizeStyle = uiStyleValue(props.size, (value) => {
360
+ if (typeof value === "string" && JSswift.uiSizes?.includes(value)) return "";
361
+ return toCssSize(value);
362
+ }, "");
363
+ if (sizeStyle != null) {
364
+ style.width = sizeStyle;
365
+ style.height = sizeStyle;
366
+ }
367
+
368
+ const fontSizeValue = props.fontSize ?? props.textSize;
369
+ const fontSizeStyle = fontSizeValue != null
370
+ ? uiStyleValue(fontSizeValue, toCssSize)
371
+ : uiStyleValue(props.size, (value) => {
372
+ if (typeof value === "string" && JSswift.uiSizes?.includes(value)) return "";
373
+ return `clamp(11px, calc(${toCssSize(value)} * 0.34), 28px)`;
374
+ }, "");
375
+ if (fontSizeStyle != null) style.fontSize = fontSizeStyle;
376
+
377
+ const radiusStyle = uiStyleValue(props.radius, (value) => {
378
+ const normalized = normalizeRadius(value);
379
+ if (normalized === "xxxl") return "999px";
380
+ if (normalized) return `var(--cms-r-${normalized})`;
381
+ if (typeof value === "number") return `${value}px`;
382
+ return String(value);
383
+ });
384
+ if (radiusStyle != null) style["--set-border-radius"] = radiusStyle;
385
+ p.style = style;
386
+
387
+ const ctx = { props };
388
+ const altText = uiComputed([props.alt, props.label, props.name, props.text], () => {
389
+ const raw = uiUnwrap(props.alt) || uiUnwrap(props.label) || uiUnwrap(props.name) || uiUnwrap(props.text) || "avatar";
390
+ return String(raw);
391
+ });
392
+ const iconFallback = props.icon != null
393
+ ? (typeof props.icon === "string"
394
+ ? UI.Icon({ name: props.icon, size: props.iconSize || props.size || "sm" })
395
+ : JSswift.ui.slot(props.icon, { as: "icon" }))
396
+ : null;
397
+ const labelFallback = uiComputed([props.initials, props.text, props.label, props.name], () => {
398
+ const explicit = uiUnwrap(props.initials) ?? uiUnwrap(props.text);
399
+ if (explicit != null) return explicit;
400
+ const rawLabel = uiUnwrap(props.label) ?? uiUnwrap(props.name);
401
+ if (typeof rawLabel === "string") return avatarGetInitials(rawLabel);
402
+ return rawLabel;
403
+ });
404
+ const badgeFallback = avatarPrimitiveBadge(uiUnwrap(props.badge ?? props.notification));
405
+ const statusFallback = avatarPrimitiveStatus(
406
+ uiUnwrap(props.status ?? props.presence),
407
+ uiUnwrap(props.statusColor)
408
+ );
409
+
410
+ let mediaNodes = renderSlotToArray(slots, "media", ctx, props.media);
411
+ if (!mediaNodes.length && props.src) {
412
+ const imgProps = {
413
+ class: "cms-avatar-img",
414
+ src: props.src,
415
+ alt: altText,
416
+ sizes: props.sizes,
417
+ style: {}
418
+ };
419
+ if (props.srcset != null) imgProps.srcset = props.srcset;
420
+ if (props.srcSet != null) imgProps.srcset = props.srcSet;
421
+ const fitStyle = uiStyleValue(props.fit);
422
+ if (fitStyle != null) imgProps.style.objectFit = fitStyle;
423
+ mediaNodes = [_.img(imgProps)];
424
+ }
425
+
426
+ const defaultNodes = renderSlotToArray(slots, "default", ctx, children);
427
+ const fallbackNodes = renderSlotToArray(slots, "fallback", ctx, null);
428
+ const labelNodes = renderSlotToArray(slots, "label", ctx, labelFallback);
429
+ const iconNodes = renderSlotToArray(slots, "icon", ctx, iconFallback);
430
+ const mainNodes = mediaNodes.length
431
+ ? mediaNodes
432
+ : (defaultNodes.length
433
+ ? defaultNodes
434
+ : (fallbackNodes.length
435
+ ? fallbackNodes
436
+ : (labelNodes.length
437
+ ? labelNodes
438
+ : (iconNodes.length ? iconNodes : ["?"]))));
439
+
440
+ const bodyClass = mediaNodes.length ? "cms-avatar-media" : "cms-avatar-fallback";
441
+ const body = _.span({ class: "cms-avatar-body" },
442
+ _.span({ class: bodyClass }, ...mainNodes)
443
+ );
444
+
445
+ let topLeftNodes = renderSlotToArray(slots, "topLeft", ctx, props.topLeft);
446
+ let topRightNodes = renderSlotToArray(slots, "topRight", ctx, props.topRight);
447
+ let bottomLeftNodes = renderSlotToArray(slots, "bottomLeft", ctx, props.bottomLeft);
448
+ let bottomRightNodes = renderSlotToArray(slots, "bottomRight", ctx, props.bottomRight);
449
+ const badgeNodes = renderSlotToArray(slots, "badge", ctx, badgeFallback);
450
+ const statusNodes = renderSlotToArray(slots, "status", ctx, statusFallback);
451
+
452
+ if (!topRightNodes.length && badgeNodes.length) topRightNodes = badgeNodes;
453
+ if (!bottomRightNodes.length && statusNodes.length) bottomRightNodes = statusNodes;
454
+
526
455
  const wrap = _.div(
527
456
  p,
528
457
  body,
@@ -534,140 +463,57 @@
534
463
  setPropertyProps(wrap, props);
535
464
  return wrap;
536
465
  };
537
- if (JSswift.isDev?.()) {
538
- UI.meta = UI.meta || {};
539
- UI.meta.Avatar = {
540
- signature: "UI.Avatar(...children) | UI.Avatar(props, ...children)",
541
- props: {
542
- src: "string",
543
- srcset: "string",
544
- sizes: "string",
545
- alt: "string",
546
- label: "String|Node|Function|Array",
547
- name: "string",
548
- initials: "string|Node|Function|Array",
549
- text: "string|Node|Function|Array",
550
- size: "number|string",
551
- fontSize: "number|string",
552
- radius: "number|string",
553
- square: "boolean",
554
- elevated: "boolean",
555
- color: "string",
556
- state: "string",
557
- icon: "string|Node|Function|Array",
558
- iconSize: "number|string",
559
- media: "Node|Function|Array",
560
- fit: "cover|contain|fill|scale-down|none",
561
- badge: "string|number|Node|Function|Array",
562
- notification: "string|number|Node|Function|Array",
563
- status: "boolean|string|number|Node|Function|Array",
564
- statusColor: "success|warning|danger|info|primary|secondary|dark|light|string",
565
- topLeft: "Node|Function|Array",
566
- topRight: "Node|Function|Array",
567
- bottomLeft: "Node|Function|Array",
568
- bottomRight: "Node|Function|Array",
569
- slots: "{ media?, default?, fallback?, label?, icon?, badge?, status?, topLeft?, topRight?, bottomLeft?, bottomRight? }",
570
- mobile: "{ width?, height?, minWidth?, maxWidth?, padding?, margin?, radius? }",
571
- tablet: "{ width?, height?, minWidth?, maxWidth?, padding?, margin?, radius? }",
572
- pc: "{ width?, height?, minWidth?, maxWidth?, padding?, margin?, radius? }",
573
- class: "string",
574
- style: "object"
575
- },
576
- slots: {
577
- media: "Custom main media",
578
- default: "Custom main content instead of image or fallback",
579
- fallback: "Fallback custom quando non c'e immagine",
580
- label: "Fallback testuale / initials",
581
- icon: "Fallback icon",
582
- badge: "Badge overlay, di default top-right",
583
- status: "Presence dot or overlay content, bottom-right by default",
584
- topLeft: "Top-left overlay content",
585
- topRight: "Top-right overlay content",
586
- bottomLeft: "Bottom-left overlay content",
587
- bottomRight: "Bottom-right overlay content"
588
- },
589
- returns: "HTMLDivElement",
590
- description: "Flexible avatar with image, smart fallbacks, states, badge, and overlay slots."
591
- };
592
- }
593
- // Esempio: JSswift.ui.Avatar({ label: "CM" })
594
-
595
- UI.Chip = (...args) => {
596
- const { props, children } = JSswift.uiNormalizeArgs(args);
597
- const slots = props.slots || {};
598
- const sizeClass = uiComputed(props.size, () => {
599
- const v = uiUnwrap(props.size);
600
- return (typeof v === "string" && sizeMap[v]) ? v : "";
601
- });
602
- const cls = uiClass([
603
- "cms-clear-set",
604
- "cms-chip",
605
- "cms-singularity",
606
- sizeClass,
607
- props.class
608
- ]);
609
- const p = JSswift.omit(props, ["label", "border", "color", "icon", "iconRight", "removable", "onRemove", "dense", "flat", "glossy", "outline", "slots"]);
610
- p.class = cls;
611
- p.style = {
612
- ...(props.style || {})
613
- };
614
-
615
- const iconFallback = props.icon
616
- ? (typeof props.icon === "string" ? UI.Icon({ name: props.icon, size: props?.size ?? null }) : JSswift.ui.slot(props.icon, { as: "icon" }))
617
- : null;
618
- const iconRightFallback = props.iconRight
619
- ? (typeof props.iconRight === "string" ? UI.Icon({ name: props.iconRight, size: props?.size ?? null }) : JSswift.ui.slot(props.iconRight, { as: "iconRight" }))
620
- : null;
621
- const iconNode = JSswift.ui.renderSlot(slots, "icon", {}, iconFallback);
622
- const iconRightNode = JSswift.ui.renderSlot(slots, "iconRight", {}, iconRightFallback);
623
- const labelNodes = renderSlotToArray(slots, "label", {}, props.label);
624
- const labelNode = labelNodes.length ? labelNodes : renderSlotToArray(slots, "default", {}, children);
625
-
626
- const iconNodes = renderSlotToArray(null, "default", {}, iconNode);
627
- const iconRightNodes = renderSlotToArray(null, "default", {}, iconRightNode);
628
- const wrap = _.span(p, ...iconNodes, ...(labelNode.length ? labelNode : [""]), ...iconRightNodes);
629
- if (props.removable) {
630
- const btn = UI.Btn({ class: "cms-chip-remove", onClick: props.onRemove, size: props?.size ?? null }, UI.Icon({ size: props?.size ?? null, name: "close" }));
631
- wrap.appendChild(btn);
632
- btn.onclick = () => {
633
- if (props.onRemove) {
634
- props.onRemove();
635
- }
636
- wrap.remove();
637
- }
638
- }
639
- setPropertyProps(wrap, props);
640
- return wrap;
641
- };
642
- if (JSswift.isDev?.()) {
643
- UI.meta = UI.meta || {};
644
- UI.meta.Chip = {
645
- signature: "UI.Chip(...children) | UI.Chip(props, ...children)",
646
- props: {
647
- label: "String|Node|Function|Array",
648
- icon: "String|Node|Function|Array",
649
- iconRight: "String|Node|Function|Array",
650
- removable: "boolean",
651
- onRemove: "function",
652
- dense: "boolean",
653
- outline: "boolean|string|number",
654
- slots: "{ icon?, label?, default? }",
655
- class: "string",
656
- style: "object"
657
- },
658
- slots: {
659
- icon: "Chip icon content",
660
- iconRight: "Chip right icon content",
661
- label: "Chip label content",
662
- default: "Fallback content"
663
- },
664
- events: {
665
- onRemove: "MouseEvent"
666
- },
667
- returns: "HTMLSpanElement",
668
- description: "Chip with optional icon and removal."
669
- };
670
- }
466
+
467
+ // Esempio: JSswift.ui.Avatar({ label: "CM" })
468
+
469
+ UI.Chip = (...args) => {
470
+ const { props, children } = JSswift.uiNormalizeArgs(args);
471
+ const slots = props.slots || {};
472
+ const sizeClass = uiComputed(props.size, () => {
473
+ const v = uiUnwrap(props.size);
474
+ return (typeof v === "string" && sizeMap[v]) ? v : "";
475
+ });
476
+ const cls = uiClass([
477
+ "cms-clear-set",
478
+ "cms-chip",
479
+ "cms-singularity",
480
+ sizeClass,
481
+ props.class
482
+ ]);
483
+ const p = JSswift.omit(props, ["label", "border", "color", "icon", "iconRight", "removable", "onRemove", "dense", "flat", "glossy", "outline", "slots"]);
484
+ p.class = cls;
485
+ p.style = {
486
+ ...(props.style || {})
487
+ };
488
+
489
+ const iconFallback = props.icon
490
+ ? (typeof props.icon === "string" ? UI.Icon({ name: props.icon, size: props?.size ?? null }) : JSswift.ui.slot(props.icon, { as: "icon" }))
491
+ : null;
492
+ const iconRightFallback = props.iconRight
493
+ ? (typeof props.iconRight === "string" ? UI.Icon({ name: props.iconRight, size: props?.size ?? null }) : JSswift.ui.slot(props.iconRight, { as: "iconRight" }))
494
+ : null;
495
+ const iconNode = JSswift.ui.renderSlot(slots, "icon", {}, iconFallback);
496
+ const iconRightNode = JSswift.ui.renderSlot(slots, "iconRight", {}, iconRightFallback);
497
+ const labelNodes = renderSlotToArray(slots, "label", {}, props.label);
498
+ const labelNode = labelNodes.length ? labelNodes : renderSlotToArray(slots, "default", {}, children);
499
+
500
+ const iconNodes = renderSlotToArray(null, "default", {}, iconNode);
501
+ const iconRightNodes = renderSlotToArray(null, "default", {}, iconRightNode);
502
+ const wrap = _.span(p, ...iconNodes, ...(labelNode.length ? labelNode : [""]), ...iconRightNodes);
503
+ if (props.removable) {
504
+ const btn = UI.Btn({ class: "cms-chip-remove", onClick: props.onRemove, size: props?.size ?? null }, UI.Icon({ size: props?.size ?? null, name: "close" }));
505
+ wrap.appendChild(btn);
506
+ btn.onclick = () => {
507
+ if (props.onRemove) {
508
+ props.onRemove();
509
+ }
510
+ wrap.remove();
511
+ }
512
+ }
513
+ setPropertyProps(wrap, props);
514
+ return wrap;
515
+ };
516
+
671
517
  // Esempio: JSswift.ui.Chip({ label: "Tag", removable: true })
672
518
 
673
519
  UI.Stat = (...args) => {
@@ -777,46 +623,7 @@
777
623
  setPropertyProps(stat, rawProps);
778
624
  return stat;
779
625
  };
780
- if (JSswift.isDev?.()) {
781
- UI.meta = UI.meta || {};
782
- UI.meta.Stat = {
783
- signature: "UI.Stat(...children) | UI.Stat(props, ...children)",
784
- props: {
785
- eyebrow: "String|Node|Function|Array",
786
- label: "String|Node|Function|Array",
787
- title: "Alias of label",
788
- value: "String|Number|Node|Function|Array",
789
- delta: "String|Node|Function|Array",
790
- trend: "up|down|flat|positive|negative|neutral",
791
- note: "String|Node|Function|Array",
792
- subtitle: "Alias of note",
793
- meta: "Node|Function|Array",
794
- icon: "String|Node|Function|Array",
795
- aside: "Node|Function|Array",
796
- footer: "Node|Function|Array",
797
- actions: "Node|Function|Array",
798
- state: "primary|secondary|success|warning|danger|info|light|dark",
799
- slots: "{ eyebrow?, label?, value?, delta?, note?, meta?, icon?, aside?, footer?, actions?, default? }",
800
- class: "string",
801
- style: "object"
802
- },
803
- slots: {
804
- eyebrow: "Small kicker above the stat label",
805
- label: "Main stat label",
806
- value: "Primary metric value",
807
- delta: "Trend / delta content",
808
- note: "Supporting note",
809
- meta: "Extra meta badges or info",
810
- icon: "Leading icon or avatar",
811
- aside: "Right-side support content",
812
- footer: "Footer/supporting bottom content",
813
- actions: "Footer actions cluster",
814
- default: "Extra body content"
815
- },
816
- returns: "HTMLElement",
817
- description: "Compact surface for single metrics, trends, and operational metadata."
818
- };
819
- }
626
+
820
627
  // Esempio: JSswift.ui.Stat({ label: "Revenue", value: "€ 128k", delta: "+18%" })
821
628
 
822
629
  UI.Kpi = (...args) => {
@@ -922,93 +729,52 @@
922
729
  setPropertyProps(kpi, rawProps);
923
730
  return kpi;
924
731
  };
925
- if (JSswift.isDev?.()) {
926
- UI.meta = UI.meta || {};
927
- UI.meta.Kpi = {
928
- signature: "UI.Kpi(...children) | UI.Kpi(props, ...children)",
929
- props: {
930
- eyebrow: "String|Node|Function|Array",
931
- title: "String|Node|Function|Array",
932
- label: "Alias of title",
933
- value: "String|Number|Node|Function|Array",
934
- delta: "String|Node|Function|Array",
935
- trend: "up|down|flat|positive|negative|neutral",
936
- note: "String|Node|Function|Array",
937
- subtitle: "Alias of note",
938
- meta: "Node|Function|Array",
939
- media: "Node|Function|Array",
940
- icon: "String|Node|Function|Array",
941
- aside: "Node|Function|Array",
942
- footer: "Node|Function|Array",
943
- actions: "Node|Function|Array",
944
- state: "primary|secondary|success|warning|danger|info|light|dark",
945
- slots: "{ eyebrow?, title?, value?, delta?, note?, meta?, media?, icon?, aside?, footer?, actions?, default? }",
946
- class: "string",
947
- style: "object"
948
- },
949
- slots: {
950
- eyebrow: "Kicker content",
951
- title: "KPI title content",
952
- value: "Primary KPI value",
953
- delta: "Delta/trend content",
954
- note: "Supporting note",
955
- meta: "Extra meta badges or inline info",
956
- media: "Secondary data view or mini-visual content",
957
- icon: "Leading icon or avatar",
958
- aside: "Top-right support content",
959
- footer: "Bottom support content",
960
- actions: "Footer actions cluster",
961
- default: "Extra body content"
962
- },
963
- returns: "HTMLElement",
964
- description: "Richer surface for KPIs, headline metrics, and mini dashboard summaries."
965
- };
966
- }
732
+
967
733
  // Esempio: JSswift.ui.Kpi({ title: "Orders", value: "342", delta: "+12%" })
968
734
 
969
735
  const overlayAnimDuration = (el, fallback = 180) => {
970
- const d = getComputedStyle(el).transitionDuration;
971
- if (!d) return fallback;
972
- const first = d.split(",")[0].trim();
973
- if (!first) return fallback;
974
- const ms = first.includes("ms") ? parseFloat(first) : parseFloat(first) * 1000;
975
- return Number.isFinite(ms) ? ms : fallback;
976
- };
977
-
978
- const overlayEnter = (entry) => {
979
- if (!entry?.overlay || !entry?.panel) return;
980
- entry.overlay.classList.add("enter");
981
- entry.panel.classList.add("enter");
982
- requestAnimationFrame(() => {
983
- if (!entry?.overlay || !entry?.panel) return;
984
- entry.overlay.classList.add("entered");
985
- entry.panel.classList.add("entered");
986
- entry.overlay.classList.remove("enter");
987
- entry.panel.classList.remove("enter");
988
- });
989
- };
990
-
991
- const overlayLeave = (entry, done) => {
992
- if (!entry?.overlay || !entry?.panel) {
993
- done?.();
994
- return;
995
- }
996
- if (entry._closing) return;
997
- entry._closing = true;
998
- entry.overlay.classList.add("leave");
999
- entry.panel.classList.add("leave");
1000
- const finish = () => {
1001
- if (entry._closed) return;
1002
- entry._closed = true;
1003
- done?.();
1004
- };
1005
- const onEnd = (e) => {
1006
- if (e.target !== entry.panel) return;
1007
- entry.panel.removeEventListener("transitionend", onEnd);
1008
- finish();
1009
- };
1010
- entry.panel.addEventListener("transitionend", onEnd);
1011
- const ms = overlayAnimDuration(entry.panel, 180);
1012
- setTimeout(finish, ms + 40);
1013
- };
1014
-
736
+ const d = getComputedStyle(el).transitionDuration;
737
+ if (!d) return fallback;
738
+ const first = d.split(",")[0].trim();
739
+ if (!first) return fallback;
740
+ const ms = first.includes("ms") ? parseFloat(first) : parseFloat(first) * 1000;
741
+ return Number.isFinite(ms) ? ms : fallback;
742
+ };
743
+
744
+ const overlayEnter = (entry) => {
745
+ if (!entry?.overlay || !entry?.panel) return;
746
+ entry.overlay.classList.add("enter");
747
+ entry.panel.classList.add("enter");
748
+ requestAnimationFrame(() => {
749
+ if (!entry?.overlay || !entry?.panel) return;
750
+ entry.overlay.classList.add("entered");
751
+ entry.panel.classList.add("entered");
752
+ entry.overlay.classList.remove("enter");
753
+ entry.panel.classList.remove("enter");
754
+ });
755
+ };
756
+
757
+ const overlayLeave = (entry, done) => {
758
+ if (!entry?.overlay || !entry?.panel) {
759
+ done?.();
760
+ return;
761
+ }
762
+ if (entry._closing) return;
763
+ entry._closing = true;
764
+ entry.overlay.classList.add("leave");
765
+ entry.panel.classList.add("leave");
766
+ const finish = () => {
767
+ if (entry._closed) return;
768
+ entry._closed = true;
769
+ done?.();
770
+ };
771
+ const onEnd = (e) => {
772
+ if (e.target !== entry.panel) return;
773
+ entry.panel.removeEventListener("transitionend", onEnd);
774
+ finish();
775
+ };
776
+ entry.panel.addEventListener("transitionend", onEnd);
777
+ const ms = overlayAnimDuration(entry.panel, 180);
778
+ setTimeout(finish, ms + 40);
779
+ };
780
+