jsswift 2.0.0 → 2.1.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/AI.md +38 -73
- package/README.md +2 -0
- package/dist/build/build-configured-runtime.d.mts +29 -0
- package/dist/build/build-configured-runtime.mjs +92 -0
- package/dist/build/runtime-layout.mjs +13 -0
- package/dist/build/select-ui.mjs +167 -0
- package/dist/build/vite-jss.d.mts +2 -0
- package/dist/build/vite-jss.mjs +73 -0
- package/dist/cms.js +3522 -0
- package/dist/documentions/AI.md +38 -73
- package/dist/documentions/README.md +4 -2
- package/dist/documentions/components/table.md +13 -0
- package/dist/documentions/configured-runtime.md +82 -0
- package/dist/documentions/examples/crud.md +18 -7
- package/dist/documentions/getting-started.md +2 -0
- package/dist/documentions/knowledge-map.json +20 -1
- package/dist/documentions/ui/data-feedback-overlays.md +2 -0
- package/dist/documentions/ui/forms.md +2 -0
- package/dist/ui-src/00-bootstrap.js +330 -0
- package/dist/ui-src/01-foundation-helpers.js +809 -0
- package/dist/ui-src/10-primitives-layout.js +5091 -0
- package/dist/ui-src/20-display-content.js +780 -0
- package/dist/ui-src/21-list-content.js +246 -0
- package/dist/ui-src/22-banner-content.js +376 -0
- package/dist/ui-src/23-tooltip.js +276 -0
- package/dist/ui-src/31-form-advanced.js +2784 -0
- package/dist/ui-src/40-navigation.js +1262 -0
- package/dist/ui-src/41-tab-panel.js +896 -0
- package/dist/ui-src/50-feedback.js +999 -0
- package/dist/ui-src/51-feedback-service.js +166 -0
- package/dist/ui-src/60-shell-app.js +1223 -0
- package/dist/ui-src/61-form-service.js +368 -0
- package/dist/ui-src/70-dialog.js +333 -0
- package/dist/ui-src/71-menu-overlays.js +1217 -0
- package/dist/ui-src/80-data.js +437 -0
- package/dist/ui-src/modules.json +212 -0
- package/dist/ui.js +12565 -0
- package/package.json +26 -2
|
@@ -0,0 +1,780 @@
|
|
|
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
|
+
|
|
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
|
+
const builtInSpriteIcons = {
|
|
48
|
+
"chevron-down": {
|
|
49
|
+
viewBox: "0 0 24 24",
|
|
50
|
+
paths: [{ d: "M6 9l6 6l6 -6", fill: "none", stroke: "currentColor", "stroke-width": 2, "stroke-linecap": "round", "stroke-linejoin": "round" }]
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const createBuiltInSpriteIcon = (id) => {
|
|
54
|
+
const def = builtInSpriteIcons[id];
|
|
55
|
+
if (!def) return null;
|
|
56
|
+
return _.svg(
|
|
57
|
+
{ width: "100%", height: "100%", viewBox: def.viewBox, fill: "none", "aria-hidden": "true", focusable: "false" },
|
|
58
|
+
...def.paths.map((path) => _.path(path))
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
const wrapIcon = (icon) => {
|
|
62
|
+
const tooltip = uiUnwrap(props.tooltip);
|
|
63
|
+
if (tooltip == null || tooltip === false || !UI.Tooltip) return icon;
|
|
64
|
+
const tooltipProps = isPlainObject(tooltip)
|
|
65
|
+
? { ...tooltip }
|
|
66
|
+
: { text: tooltip };
|
|
67
|
+
if (isPlainObject(props.tooltipProps)) Object.assign(tooltipProps, props.tooltipProps);
|
|
68
|
+
return UI.Tooltip({ ...tooltipProps, target: icon });
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const cls = uiClass([
|
|
72
|
+
"jss-icon",
|
|
73
|
+
"material-icons",
|
|
74
|
+
...(hasVisualSurface ? ["jss-clear-set", "jss-singularity"] : []),
|
|
75
|
+
sizeClass,
|
|
76
|
+
props.class
|
|
77
|
+
]);
|
|
78
|
+
const p = JSswift.omit(props, ["name", "size", "class", "style", "label", "border", "color", "icon", "iconRight", "removable", "onRemove", "dense", "flat", "glossy", "outline", "slots", "spriteUrl", "tooltip", "tooltipProps"]);
|
|
79
|
+
p.class = cls;
|
|
80
|
+
if (Object.keys(style).length) p.style = style;
|
|
81
|
+
|
|
82
|
+
if (typeof name === "function" || (name && typeof name === "object")) {
|
|
83
|
+
const customNode = JSswift.ui.renderSlot(slots, "default", {}, name);
|
|
84
|
+
const content = renderSlotToArray(null, "default", {}, customNode);
|
|
85
|
+
const icon = _.span({ ...p, "data-icon": "custom" }, ...content);
|
|
86
|
+
setPropertyProps(icon, props);
|
|
87
|
+
return wrapIcon(icon);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const nameStr = String(name);
|
|
91
|
+
const useHref = nameStr.includes("#") ? nameStr : "";
|
|
92
|
+
let icon = null;
|
|
93
|
+
if (useHref) {
|
|
94
|
+
const spriteUrl =
|
|
95
|
+
props.spriteUrl ||
|
|
96
|
+
JSswift.config?.iconSpriteUrl ||
|
|
97
|
+
(typeof globalThis !== "undefined" ? globalThis.JSswift_setting?.iconSpriteUrl : null);
|
|
98
|
+
const symbolId = useHref.slice(useHref.indexOf("#") + 1);
|
|
99
|
+
const svg = spriteUrl
|
|
100
|
+
? _.svg(
|
|
101
|
+
{ width: "100%", height: "100%" },
|
|
102
|
+
_.use({ href: spriteUrl + useHref })
|
|
103
|
+
)
|
|
104
|
+
: createBuiltInSpriteIcon(symbolId) || _.svg(
|
|
105
|
+
{ width: "100%", height: "100%" },
|
|
106
|
+
_.use({ href: useHref })
|
|
107
|
+
);
|
|
108
|
+
if (isFill) svg.style.fill = "currentColor";
|
|
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(--jss-icon-size-${size})`;
|
|
118
|
+
}
|
|
119
|
+
v = typeof v === "number" ? v + "px" : String(v);
|
|
120
|
+
icon.style.setProperty("--set-icon-size", v);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setPropertyProps(icon, props);
|
|
124
|
+
return wrapIcon(icon);
|
|
125
|
+
};
|
|
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]) ? `jss-size-${v}` : "";
|
|
135
|
+
});
|
|
136
|
+
const cls = uiClass([
|
|
137
|
+
"jss-clear-set",
|
|
138
|
+
"jss-badge",
|
|
139
|
+
"jss-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: `jss-badge-anchor jss-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: "jss-badge-content" },
|
|
226
|
+
_.span({ class: "jss-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: "jss-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 = "jss-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(["jss-avatar-status", fallbackState ? `jss-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(["jss-avatar-status", state ? `jss-state-${state}` : ""]) });
|
|
317
|
+
}
|
|
318
|
+
return avatarPrimitiveBadge(value, "jss-avatar-badge");
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const avatarRenderAnchor = (className, nodes) => {
|
|
322
|
+
if (!nodes || !nodes.length) return null;
|
|
323
|
+
return _.span({ class: uiClass(["jss-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)) ? `jss-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 ? `jss-state-${state}` : "";
|
|
337
|
+
});
|
|
338
|
+
const cls = uiClass([
|
|
339
|
+
"jss-clear-set",
|
|
340
|
+
"jss-avatar",
|
|
341
|
+
"jss-singularity",
|
|
342
|
+
sizeClass,
|
|
343
|
+
stateClass,
|
|
344
|
+
uiWhen(props.square, "jss-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(--jss-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: "jss-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 ? "jss-avatar-media" : "jss-avatar-fallback";
|
|
441
|
+
const body = _.span({ class: "jss-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
|
+
|
|
455
|
+
const wrap = _.div(
|
|
456
|
+
p,
|
|
457
|
+
body,
|
|
458
|
+
avatarRenderAnchor("jss-avatar-anchor-top-left", topLeftNodes),
|
|
459
|
+
avatarRenderAnchor("jss-avatar-anchor-top-right", topRightNodes),
|
|
460
|
+
avatarRenderAnchor("jss-avatar-anchor-bottom-left", bottomLeftNodes),
|
|
461
|
+
avatarRenderAnchor("jss-avatar-anchor-bottom-right", bottomRightNodes)
|
|
462
|
+
);
|
|
463
|
+
setPropertyProps(wrap, props);
|
|
464
|
+
return wrap;
|
|
465
|
+
};
|
|
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
|
+
"jss-clear-set",
|
|
478
|
+
"jss-chip",
|
|
479
|
+
"jss-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: "jss-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
|
+
|
|
517
|
+
// Esempio: JSswift.ui.Chip({ label: "Tag", removable: true })
|
|
518
|
+
|
|
519
|
+
UI.Stat = (...args) => {
|
|
520
|
+
const { props: rawProps, children } = JSswift.uiNormalizeArgs(args);
|
|
521
|
+
const slots = rawProps.slots || {};
|
|
522
|
+
const resolveStateValue = () => normalizeState(uiUnwrap(rawProps.state) || uiUnwrap(rawProps.color) || "");
|
|
523
|
+
const stateClass = uiComputed([rawProps.state, rawProps.color], () => {
|
|
524
|
+
const state = resolveStateValue();
|
|
525
|
+
return state ? `jss-state-${state}` : "";
|
|
526
|
+
});
|
|
527
|
+
const trendClass = uiComputed(rawProps.trend, () => {
|
|
528
|
+
const raw = String(uiUnwrap(rawProps.trend) || "").toLowerCase();
|
|
529
|
+
if (["up", "positive", "success", "gain", "increase"].includes(raw)) return "is-positive";
|
|
530
|
+
if (["down", "negative", "danger", "loss", "decrease"].includes(raw)) return "is-negative";
|
|
531
|
+
if (["flat", "neutral", "steady", "stable"].includes(raw)) return "is-neutral";
|
|
532
|
+
return "";
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
const props = { ...rawProps };
|
|
536
|
+
const p = JSswift.omit(props, [
|
|
537
|
+
"actions",
|
|
538
|
+
"aside",
|
|
539
|
+
"delta",
|
|
540
|
+
"eyebrow",
|
|
541
|
+
"footer",
|
|
542
|
+
"icon",
|
|
543
|
+
"iconSize",
|
|
544
|
+
"label",
|
|
545
|
+
"meta",
|
|
546
|
+
"note",
|
|
547
|
+
"slots",
|
|
548
|
+
"state",
|
|
549
|
+
"subtitle",
|
|
550
|
+
"title",
|
|
551
|
+
"trend",
|
|
552
|
+
"value"
|
|
553
|
+
]);
|
|
554
|
+
p.class = uiClass([
|
|
555
|
+
"jss-clear-set",
|
|
556
|
+
"jss-stat",
|
|
557
|
+
"jss-singularity",
|
|
558
|
+
stateClass,
|
|
559
|
+
trendClass,
|
|
560
|
+
props.class
|
|
561
|
+
]);
|
|
562
|
+
p.style = { ...(props.style || {}) };
|
|
563
|
+
|
|
564
|
+
const renderNamed = (slotName, fallback, ctx = {}) => renderSlotToArray(
|
|
565
|
+
slots,
|
|
566
|
+
slotName,
|
|
567
|
+
ctx,
|
|
568
|
+
fallback
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
const iconFallback = rawProps.icon == null
|
|
572
|
+
? null
|
|
573
|
+
: (typeof rawProps.icon === "string"
|
|
574
|
+
? UI.Icon({ name: rawProps.icon, size: rawProps.iconSize || "md" })
|
|
575
|
+
: JSswift.ui.slot(rawProps.icon, { as: "icon" }));
|
|
576
|
+
|
|
577
|
+
const eyebrowNodes = renderNamed("eyebrow", rawProps.eyebrow);
|
|
578
|
+
const labelNodes = renderNamed("label", rawProps.label ?? rawProps.title);
|
|
579
|
+
const valueNodes = renderNamed("value", rawProps.value);
|
|
580
|
+
const noteNodes = renderNamed("note", rawProps.note ?? rawProps.subtitle);
|
|
581
|
+
const metaNodes = renderNamed("meta", rawProps.meta);
|
|
582
|
+
const deltaNodes = renderNamed("delta", rawProps.delta, { trend: rawProps.trend });
|
|
583
|
+
const iconNodes = renderNamed("icon", iconFallback);
|
|
584
|
+
const asideNodes = renderNamed("aside", rawProps.aside);
|
|
585
|
+
const footerNodes = renderNamed("footer", rawProps.footer);
|
|
586
|
+
const actionsNodes = renderNamed("actions", rawProps.actions);
|
|
587
|
+
const bodyNodes = renderNamed("default", children);
|
|
588
|
+
|
|
589
|
+
const stat = _.article(
|
|
590
|
+
p,
|
|
591
|
+
_.div(
|
|
592
|
+
{ class: "jss-stat-head" },
|
|
593
|
+
_.div(
|
|
594
|
+
{ class: "jss-stat-lead" },
|
|
595
|
+
iconNodes.length ? _.div({ class: "jss-stat-icon" }, ...iconNodes) : null,
|
|
596
|
+
_.div(
|
|
597
|
+
{ class: "jss-stat-copy" },
|
|
598
|
+
eyebrowNodes.length ? _.div({ class: "jss-stat-eyebrow" }, ...eyebrowNodes) : null,
|
|
599
|
+
labelNodes.length ? _.div({ class: "jss-stat-label" }, ...labelNodes) : null
|
|
600
|
+
)
|
|
601
|
+
),
|
|
602
|
+
asideNodes.length ? _.div({ class: "jss-stat-aside" }, ...asideNodes) : null
|
|
603
|
+
),
|
|
604
|
+
_.div(
|
|
605
|
+
{ class: "jss-stat-body" },
|
|
606
|
+
valueNodes.length ? _.div({ class: "jss-stat-value" }, ...valueNodes) : null,
|
|
607
|
+
deltaNodes.length
|
|
608
|
+
? _.div({ class: "jss-stat-delta" }, ...deltaNodes)
|
|
609
|
+
: null,
|
|
610
|
+
noteNodes.length ? _.div({ class: "jss-stat-note" }, ...noteNodes) : null,
|
|
611
|
+
metaNodes.length ? _.div({ class: "jss-stat-meta" }, ...metaNodes) : null,
|
|
612
|
+
bodyNodes.length ? _.div({ class: "jss-stat-extra" }, ...bodyNodes) : null
|
|
613
|
+
),
|
|
614
|
+
(footerNodes.length || actionsNodes.length)
|
|
615
|
+
? _.div(
|
|
616
|
+
{ class: "jss-stat-footer" },
|
|
617
|
+
footerNodes.length ? _.div({ class: "jss-stat-footer-copy" }, ...footerNodes) : null,
|
|
618
|
+
actionsNodes.length ? _.div({ class: "jss-stat-actions" }, ...actionsNodes) : null
|
|
619
|
+
)
|
|
620
|
+
: null
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
setPropertyProps(stat, rawProps);
|
|
624
|
+
return stat;
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
// Esempio: JSswift.ui.Stat({ label: "Revenue", value: "€ 128k", delta: "+18%" })
|
|
628
|
+
|
|
629
|
+
UI.Kpi = (...args) => {
|
|
630
|
+
const { props: rawProps, children } = JSswift.uiNormalizeArgs(args);
|
|
631
|
+
const slots = rawProps.slots || {};
|
|
632
|
+
const resolveStateValue = () => normalizeState(uiUnwrap(rawProps.state) || uiUnwrap(rawProps.color) || "");
|
|
633
|
+
const stateClass = uiComputed([rawProps.state, rawProps.color], () => {
|
|
634
|
+
const state = resolveStateValue();
|
|
635
|
+
return state ? `jss-state-${state}` : "";
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
const props = { ...rawProps };
|
|
639
|
+
const p = JSswift.omit(props, [
|
|
640
|
+
"actions",
|
|
641
|
+
"aside",
|
|
642
|
+
"delta",
|
|
643
|
+
"eyebrow",
|
|
644
|
+
"footer",
|
|
645
|
+
"icon",
|
|
646
|
+
"iconSize",
|
|
647
|
+
"label",
|
|
648
|
+
"media",
|
|
649
|
+
"meta",
|
|
650
|
+
"note",
|
|
651
|
+
"slots",
|
|
652
|
+
"state",
|
|
653
|
+
"subtitle",
|
|
654
|
+
"title",
|
|
655
|
+
"trend",
|
|
656
|
+
"value"
|
|
657
|
+
]);
|
|
658
|
+
p.class = uiClass([
|
|
659
|
+
"jss-clear-set",
|
|
660
|
+
"jss-kpi",
|
|
661
|
+
"jss-singularity",
|
|
662
|
+
stateClass,
|
|
663
|
+
props.class
|
|
664
|
+
]);
|
|
665
|
+
p.style = { ...(props.style || {}) };
|
|
666
|
+
|
|
667
|
+
const renderNamed = (slotName, fallback, ctx = {}) => renderSlotToArray(
|
|
668
|
+
slots,
|
|
669
|
+
slotName,
|
|
670
|
+
ctx,
|
|
671
|
+
fallback
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
const iconFallback = rawProps.icon == null
|
|
675
|
+
? null
|
|
676
|
+
: (typeof rawProps.icon === "string"
|
|
677
|
+
? UI.Icon({ name: rawProps.icon, size: rawProps.iconSize || "lg" })
|
|
678
|
+
: JSswift.ui.slot(rawProps.icon, { as: "icon" }));
|
|
679
|
+
|
|
680
|
+
const eyebrowNodes = renderNamed("eyebrow", rawProps.eyebrow);
|
|
681
|
+
const titleNodes = renderNamed("title", rawProps.title ?? rawProps.label);
|
|
682
|
+
const valueNodes = renderNamed("value", rawProps.value);
|
|
683
|
+
const deltaNodes = renderNamed("delta", rawProps.delta, { trend: rawProps.trend });
|
|
684
|
+
const noteNodes = renderNamed("note", rawProps.note ?? rawProps.subtitle);
|
|
685
|
+
const metaNodes = renderNamed("meta", rawProps.meta);
|
|
686
|
+
const iconNodes = renderNamed("icon", iconFallback);
|
|
687
|
+
const asideNodes = renderNamed("aside", rawProps.aside);
|
|
688
|
+
const mediaNodes = renderNamed("media", rawProps.media);
|
|
689
|
+
const footerNodes = renderNamed("footer", rawProps.footer);
|
|
690
|
+
const actionsNodes = renderNamed("actions", rawProps.actions);
|
|
691
|
+
const bodyNodes = renderNamed("default", children);
|
|
692
|
+
|
|
693
|
+
const kpi = _.section(
|
|
694
|
+
p,
|
|
695
|
+
_.div(
|
|
696
|
+
{ class: "jss-kpi-head" },
|
|
697
|
+
_.div(
|
|
698
|
+
{ class: "jss-kpi-title-wrap" },
|
|
699
|
+
iconNodes.length ? _.div({ class: "jss-kpi-icon" }, ...iconNodes) : null,
|
|
700
|
+
_.div(
|
|
701
|
+
{ class: "jss-kpi-copy" },
|
|
702
|
+
eyebrowNodes.length ? _.div({ class: "jss-kpi-eyebrow" }, ...eyebrowNodes) : null,
|
|
703
|
+
titleNodes.length ? _.div({ class: "jss-kpi-title" }, ...titleNodes) : null
|
|
704
|
+
)
|
|
705
|
+
),
|
|
706
|
+
asideNodes.length ? _.div({ class: "jss-kpi-aside" }, ...asideNodes) : null
|
|
707
|
+
),
|
|
708
|
+
_.div(
|
|
709
|
+
{ class: "jss-kpi-core" },
|
|
710
|
+
_.div(
|
|
711
|
+
{ class: "jss-kpi-value-wrap" },
|
|
712
|
+
valueNodes.length ? _.div({ class: "jss-kpi-value" }, ...valueNodes) : null,
|
|
713
|
+
deltaNodes.length ? _.div({ class: "jss-kpi-delta" }, ...deltaNodes) : null
|
|
714
|
+
),
|
|
715
|
+
noteNodes.length ? _.div({ class: "jss-kpi-note" }, ...noteNodes) : null,
|
|
716
|
+
metaNodes.length ? _.div({ class: "jss-kpi-meta" }, ...metaNodes) : null,
|
|
717
|
+
mediaNodes.length ? _.div({ class: "jss-kpi-media" }, ...mediaNodes) : null,
|
|
718
|
+
bodyNodes.length ? _.div({ class: "jss-kpi-extra" }, ...bodyNodes) : null
|
|
719
|
+
),
|
|
720
|
+
(footerNodes.length || actionsNodes.length)
|
|
721
|
+
? _.div(
|
|
722
|
+
{ class: "jss-kpi-footer" },
|
|
723
|
+
footerNodes.length ? _.div({ class: "jss-kpi-footer-copy" }, ...footerNodes) : null,
|
|
724
|
+
actionsNodes.length ? _.div({ class: "jss-kpi-actions" }, ...actionsNodes) : null
|
|
725
|
+
)
|
|
726
|
+
: null
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
setPropertyProps(kpi, rawProps);
|
|
730
|
+
return kpi;
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// Esempio: JSswift.ui.Kpi({ title: "Orders", value: "342", delta: "+12%" })
|
|
734
|
+
|
|
735
|
+
const overlayAnimDuration = (el, fallback = 180) => {
|
|
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
|
+
|