@alikhalilll/a-skeleton 1.0.0 → 1.2.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/.media/hero.png +0 -0
- package/.media/hero.svg +232 -0
- package/README.md +459 -170
- package/dist/index.cjs +3696 -828
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +534 -43
- package/dist/index.d.ts +534 -43
- package/dist/index.js +3677 -830
- package/dist/index.js.map +1 -1
- package/dist/nuxt/index.cjs +16 -1
- package/dist/nuxt/index.cjs.map +1 -1
- package/dist/nuxt/index.js +16 -1
- package/dist/nuxt/index.js.map +1 -1
- package/dist/resolver/index.cjs +16 -1
- package/dist/resolver/index.cjs.map +1 -1
- package/dist/resolver/index.js +16 -1
- package/dist/resolver/index.js.map +1 -1
- package/dist/styles.css +56 -11
- package/package.json +8 -2
- package/src/components/ASkeleton.vue +216 -98
- package/src/components/ASkeletonClone.vue +106 -0
- package/src/components/ASkeletonLayer.vue +20 -32
- package/src/components/CloneNode.ts +161 -0
- package/src/components/StructuralLayerNode.ts +157 -0
- package/src/components/icons.ts +45 -0
- package/src/components/variants/ASkeletonArticle.vue +33 -0
- package/src/components/variants/ASkeletonAvatar.vue +42 -0
- package/src/components/variants/ASkeletonButton.vue +37 -0
- package/src/components/variants/ASkeletonCard.vue +47 -0
- package/src/components/variants/ASkeletonChart.vue +56 -0
- package/src/components/variants/ASkeletonChip.vue +32 -0
- package/src/components/variants/ASkeletonDivider.vue +26 -0
- package/src/components/variants/ASkeletonForm.vue +32 -0
- package/src/components/variants/ASkeletonHeading.vue +47 -0
- package/src/components/variants/ASkeletonImage.vue +57 -0
- package/src/components/variants/ASkeletonInput.vue +33 -0
- package/src/components/variants/ASkeletonListItem.vue +40 -0
- package/src/components/variants/ASkeletonTable.vue +49 -0
- package/src/components/variants/ASkeletonText.vue +49 -0
- package/src/components/variants/ASkeletonVideo.vue +55 -0
- package/src/composables/useShapeProbe.ts +33 -9
- package/src/composables/useSkeleton.ts +33 -21
- package/src/composables/useSkeletonCache.ts +282 -24
- package/src/index.ts +48 -2
- package/src/nuxt/index.ts +16 -0
- package/src/resolver/index.ts +16 -0
- package/src/types.ts +124 -5
- package/src/utils/buildStructuralSkeleton.ts +400 -103
- package/src/utils/captureStyles.ts +378 -0
- package/src/utils/domRead.ts +143 -0
- package/src/utils/walkDom.ts +261 -16
- package/src/utils/walkStructural.ts +418 -0
- package/web-types.json +10 -4
package/dist/index.cjs
CHANGED
|
@@ -3399,689 +3399,1118 @@ function cn(...inputs) {
|
|
|
3399
3399
|
return twMerge(clsx(inputs));
|
|
3400
3400
|
}
|
|
3401
3401
|
//#endregion
|
|
3402
|
-
//#region src/utils/
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
"
|
|
3402
|
+
//#region src/utils/buildStructuralSkeleton.ts
|
|
3403
|
+
/**
|
|
3404
|
+
* Atomic tags — rendered as a single shimmer block. Their internal rendering
|
|
3405
|
+
* is opaque to vnode walking (no meaningful child structure for us to mirror).
|
|
3406
|
+
* Their own `class` + `style` are preserved so Tailwind utilities like
|
|
3407
|
+
* `size-16` and `rounded-full` still drive the dimensions.
|
|
3408
|
+
*
|
|
3409
|
+
* Note: `button`, `a`, `label` are deliberately NOT atomic — they're treated
|
|
3410
|
+
* as containers so their real `background-color` / `border` / shadow survive
|
|
3411
|
+
* (we add `.a-skel-block` to atomics, which paints a skeleton gradient that
|
|
3412
|
+
* would override Tailwind's `bg-emerald-600` and friends). Their text content
|
|
3413
|
+
* is recursed and replaced with a shimmer span inside the real button shape.
|
|
3414
|
+
*/
|
|
3415
|
+
const ATOMIC_TAGS$2 = new Set([
|
|
3416
|
+
"img",
|
|
3417
|
+
"svg",
|
|
3418
|
+
"canvas",
|
|
3419
|
+
"video",
|
|
3420
|
+
"audio",
|
|
3421
|
+
"input",
|
|
3422
|
+
"textarea",
|
|
3423
|
+
"select",
|
|
3424
|
+
"progress",
|
|
3425
|
+
"meter",
|
|
3426
|
+
"hr",
|
|
3427
|
+
"iframe",
|
|
3428
|
+
"object",
|
|
3429
|
+
"embed",
|
|
3430
|
+
"picture"
|
|
3417
3431
|
]);
|
|
3418
3432
|
/**
|
|
3419
|
-
*
|
|
3420
|
-
*
|
|
3421
|
-
*
|
|
3433
|
+
* SVG child tags that should never be walked by the structural skeleton —
|
|
3434
|
+
* SVG interior elements use a different coordinate space (`x`/`y` are
|
|
3435
|
+
* attributes, not CSS), so emitting `.a-skel-block` divs at their tag would
|
|
3436
|
+
* yield non-rendering elements. When we see one of these, the parent `<svg>`
|
|
3437
|
+
* has already been treated as an atomic block — we just return null/skip.
|
|
3438
|
+
*/
|
|
3439
|
+
const SVG_INTERIOR_TAGS = new Set([
|
|
3440
|
+
"circle",
|
|
3441
|
+
"rect",
|
|
3442
|
+
"path",
|
|
3443
|
+
"line",
|
|
3444
|
+
"polyline",
|
|
3445
|
+
"polygon",
|
|
3446
|
+
"ellipse",
|
|
3447
|
+
"g",
|
|
3448
|
+
"defs",
|
|
3449
|
+
"clippath",
|
|
3450
|
+
"mask",
|
|
3451
|
+
"pattern",
|
|
3452
|
+
"lineargradient",
|
|
3453
|
+
"radialgradient",
|
|
3454
|
+
"stop",
|
|
3455
|
+
"use",
|
|
3456
|
+
"symbol",
|
|
3457
|
+
"foreignobject",
|
|
3458
|
+
"text",
|
|
3459
|
+
"tspan",
|
|
3460
|
+
"textpath",
|
|
3461
|
+
"marker",
|
|
3462
|
+
"filter",
|
|
3463
|
+
"feblend",
|
|
3464
|
+
"fecolormatrix",
|
|
3465
|
+
"fegaussianblur",
|
|
3466
|
+
"feoffset",
|
|
3467
|
+
"fedropshadow",
|
|
3468
|
+
"femerge",
|
|
3469
|
+
"femergenode"
|
|
3470
|
+
]);
|
|
3471
|
+
/**
|
|
3472
|
+
* Table-structural tags — preserve them as-is. Their semantics (`display:
|
|
3473
|
+
* table-*`) are critical for layout, and replacing them with `<div>` would
|
|
3474
|
+
* break the grid. Children are recursed normally.
|
|
3475
|
+
*/
|
|
3476
|
+
const TABLE_TAGS = new Set([
|
|
3477
|
+
"table",
|
|
3478
|
+
"thead",
|
|
3479
|
+
"tbody",
|
|
3480
|
+
"tfoot",
|
|
3481
|
+
"tr",
|
|
3482
|
+
"td",
|
|
3483
|
+
"th",
|
|
3484
|
+
"caption",
|
|
3485
|
+
"colgroup",
|
|
3486
|
+
"col"
|
|
3487
|
+
]);
|
|
3488
|
+
/**
|
|
3489
|
+
* List-structural tags — `<ul>`, `<ol>`, `<li>`, `<dl>`, `<dt>`, `<dd>`.
|
|
3490
|
+
* Preserved as containers; `<li>` recurses into its text/children so each
|
|
3491
|
+
* list item gets the right shimmer treatment.
|
|
3492
|
+
*/
|
|
3493
|
+
const LIST_TAGS = new Set([
|
|
3494
|
+
"ul",
|
|
3495
|
+
"ol",
|
|
3496
|
+
"li",
|
|
3497
|
+
"dl",
|
|
3498
|
+
"dt",
|
|
3499
|
+
"dd"
|
|
3500
|
+
]);
|
|
3501
|
+
/**
|
|
3502
|
+
* Tags whose content is conventionally text. When the author writes
|
|
3503
|
+
* `<h3>{{ data?.name }}</h3>` and `data` is null during loading, the
|
|
3504
|
+
* interpolation produces no children — the walker would otherwise emit an
|
|
3505
|
+
* empty `<h3></h3>` and no skeleton bar shows. For these tags we emit a
|
|
3506
|
+
* synthetic placeholder text-content span so the bar renders at the tag's
|
|
3507
|
+
* natural rendered width (Tailwind sizing on the tag still drives height).
|
|
3508
|
+
*/
|
|
3509
|
+
const TEXT_OWNER_TAGS = new Set([
|
|
3510
|
+
"h1",
|
|
3511
|
+
"h2",
|
|
3512
|
+
"h3",
|
|
3513
|
+
"h4",
|
|
3514
|
+
"h5",
|
|
3515
|
+
"h6",
|
|
3516
|
+
"p",
|
|
3517
|
+
"span",
|
|
3518
|
+
"a",
|
|
3519
|
+
"em",
|
|
3520
|
+
"strong",
|
|
3521
|
+
"small",
|
|
3522
|
+
"code",
|
|
3523
|
+
"b",
|
|
3524
|
+
"i",
|
|
3525
|
+
"mark",
|
|
3526
|
+
"label",
|
|
3527
|
+
"caption",
|
|
3528
|
+
"time",
|
|
3529
|
+
"dt",
|
|
3530
|
+
"dd",
|
|
3531
|
+
"li",
|
|
3532
|
+
"th",
|
|
3533
|
+
"td",
|
|
3534
|
+
"figcaption",
|
|
3535
|
+
"blockquote",
|
|
3536
|
+
"cite",
|
|
3537
|
+
"q"
|
|
3538
|
+
]);
|
|
3539
|
+
/**
|
|
3540
|
+
* Non-breaking-space placeholder for empty text-owners. ~24 chars wide is
|
|
3541
|
+
* enough to read as "a line of text" in most fonts while still being short
|
|
3542
|
+
* enough that the rendered bar doesn't wrap in narrow containers.
|
|
3543
|
+
*/
|
|
3544
|
+
const TEXT_PLACEHOLDER = "\xA0".repeat(24);
|
|
3545
|
+
const DEFAULT_MAX_DEPTH$3 = 16;
|
|
3546
|
+
const DEFAULT_MAX_NODES$3 = 600;
|
|
3547
|
+
/**
|
|
3548
|
+
* Walk a slot's vnode tree and produce a **DOM-mirror skeleton**: every element
|
|
3549
|
+
* is preserved (same tag, same `class`, same inline `style`), and only the
|
|
3550
|
+
* content is replaced. The output is structurally identical to what the real
|
|
3551
|
+
* component would render — Tailwind utilities for layout, spacing, sizing,
|
|
3552
|
+
* backgrounds and shadows all carry through. The CSS does the work of making
|
|
3553
|
+
* it *look* like a skeleton.
|
|
3422
3554
|
*
|
|
3423
|
-
*
|
|
3424
|
-
*
|
|
3425
|
-
*
|
|
3426
|
-
*
|
|
3427
|
-
*
|
|
3428
|
-
*
|
|
3429
|
-
*
|
|
3430
|
-
*
|
|
3431
|
-
*
|
|
3555
|
+
* Replacement rules:
|
|
3556
|
+
* - **Raw text** (e.g. interpolations, static strings) is wrapped in
|
|
3557
|
+
* `<span class="a-skel-text-content">…the real text…</span>`. The text is
|
|
3558
|
+
* kept as the span's children so the inline layout reserves its real
|
|
3559
|
+
* rendered width — but the glyphs are painted transparent and a skeleton
|
|
3560
|
+
* gradient is painted in their place. Multi-line text wraps naturally,
|
|
3561
|
+
* producing one shimmer rect per visual line at the exact rendered position.
|
|
3562
|
+
* - **Atomic / interactive tags** (`img`, `svg`, `button`, `input`, …) are
|
|
3563
|
+
* replaced by a `<div class="a-skel-block">` carrying the original element's
|
|
3564
|
+
* `class` and `style`, so dimensions and shapes (size, border-radius, etc.)
|
|
3565
|
+
* still drive the layout.
|
|
3566
|
+
* - **Container elements** (`div`, `section`, `h1`, `p`, `a`, `span`, …) are
|
|
3567
|
+
* preserved as the same tag with the same `class` and `style`; we recurse
|
|
3568
|
+
* into their children.
|
|
3569
|
+
* - **Component vnodes** can't be introspected at render time, so we emit a
|
|
3570
|
+
* single `<div class="a-skel-block">` carrying their outer `class` / `style`.
|
|
3432
3571
|
*/
|
|
3433
|
-
function
|
|
3434
|
-
const
|
|
3435
|
-
const
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3572
|
+
function buildStructuralSkeleton(vnodes, opts) {
|
|
3573
|
+
const maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH$3;
|
|
3574
|
+
const state = {
|
|
3575
|
+
emitted: 0,
|
|
3576
|
+
cap: opts.maxNodes ?? DEFAULT_MAX_NODES$3
|
|
3577
|
+
};
|
|
3578
|
+
const out = [];
|
|
3579
|
+
walk(vnodes, opts, 0, maxDepth, state, out);
|
|
3580
|
+
return out;
|
|
3581
|
+
}
|
|
3582
|
+
function walk(input, opts, depth, max, state, out) {
|
|
3583
|
+
if (state.emitted >= state.cap) return;
|
|
3584
|
+
if (input == null || typeof input === "boolean") return;
|
|
3585
|
+
if (Array.isArray(input)) {
|
|
3586
|
+
for (let i = 0; i < input.length; i++) {
|
|
3587
|
+
if (state.emitted >= state.cap) return;
|
|
3588
|
+
walk(input[i], opts, depth, max, state, out);
|
|
3589
|
+
}
|
|
3590
|
+
return;
|
|
3591
|
+
}
|
|
3592
|
+
if (typeof input === "string" || typeof input === "number") {
|
|
3593
|
+
const str = String(input);
|
|
3594
|
+
if (str.trim()) push(out, textContentSpan(str, opts.animationClass), state);
|
|
3595
|
+
return;
|
|
3596
|
+
}
|
|
3597
|
+
const v = input;
|
|
3598
|
+
const type = v.type;
|
|
3599
|
+
if (type === vue.Comment) return;
|
|
3600
|
+
if (type === vue.Text) {
|
|
3601
|
+
const text = typeof v.children === "string" ? v.children : "";
|
|
3602
|
+
if (text.trim()) push(out, textContentSpan(text, opts.animationClass), state);
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
if (type === vue.Fragment) {
|
|
3606
|
+
walk(v.children, opts, depth, max, state, out);
|
|
3607
|
+
return;
|
|
3608
|
+
}
|
|
3609
|
+
if (typeof type === "string") {
|
|
3610
|
+
const tag = type.toLowerCase();
|
|
3611
|
+
if (SVG_INTERIOR_TAGS.has(tag)) return;
|
|
3612
|
+
const props = v.props ?? {};
|
|
3613
|
+
if (props["data-skeleton-ignore"] !== void 0) {
|
|
3614
|
+
push(out, (0, vue.cloneVNode)(v), state);
|
|
3442
3615
|
return;
|
|
3443
3616
|
}
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
const c = el.children[i];
|
|
3456
|
-
if (c.dataset?.skeletonIgnore === void 0) childElements.push(c);
|
|
3617
|
+
if (props["data-skeleton-stop"] !== void 0) {
|
|
3618
|
+
push(out, (0, vue.h)("div", {
|
|
3619
|
+
class: [
|
|
3620
|
+
"a-skel-block",
|
|
3621
|
+
v.props?.class,
|
|
3622
|
+
opts.animationClass
|
|
3623
|
+
],
|
|
3624
|
+
style: v.props?.style,
|
|
3625
|
+
"aria-hidden": "true"
|
|
3626
|
+
}), state);
|
|
3627
|
+
return;
|
|
3457
3628
|
}
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
if (isLeafTag || hasStop || reachedDepth || childElements.length === 0) {
|
|
3461
|
-
const node = elementToShape(tag, cs, rect, rootRect, hasOwnText);
|
|
3462
|
-
if (node) nodes.push(node);
|
|
3629
|
+
if (props["data-skeleton-text"] !== void 0) {
|
|
3630
|
+
push(out, textContentSpan(" ", opts.animationClass), state);
|
|
3463
3631
|
return;
|
|
3464
3632
|
}
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3633
|
+
if (props["data-skeleton-block"] !== void 0) {
|
|
3634
|
+
push(out, (0, vue.h)("div", {
|
|
3635
|
+
class: [
|
|
3636
|
+
"a-skel-block",
|
|
3637
|
+
v.props?.class,
|
|
3638
|
+
opts.animationClass
|
|
3639
|
+
],
|
|
3640
|
+
style: v.props?.style,
|
|
3641
|
+
"aria-hidden": "true"
|
|
3642
|
+
}), state);
|
|
3643
|
+
return;
|
|
3471
3644
|
}
|
|
3645
|
+
push(out, transformElement(v, tag, opts, depth, max, state), state);
|
|
3646
|
+
return;
|
|
3472
3647
|
}
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3648
|
+
if (typeof type === "object" || typeof type === "function") push(out, (0, vue.h)("div", {
|
|
3649
|
+
class: [
|
|
3650
|
+
"a-skel-block",
|
|
3651
|
+
v.props?.class,
|
|
3652
|
+
opts.animationClass
|
|
3653
|
+
],
|
|
3654
|
+
style: v.props?.style,
|
|
3655
|
+
"aria-hidden": "true"
|
|
3656
|
+
}), state);
|
|
3657
|
+
}
|
|
3658
|
+
function push(out, vn, state) {
|
|
3659
|
+
if (state.emitted >= state.cap) return;
|
|
3660
|
+
out.push(vn);
|
|
3661
|
+
state.emitted++;
|
|
3662
|
+
}
|
|
3663
|
+
/**
|
|
3664
|
+
* Surface-bearing tags — when these are encountered without their own explicit
|
|
3665
|
+
* background, the skeleton paints the default fill so they still read as a
|
|
3666
|
+
* solid element (a button without `bg-*` shouldn't look invisible).
|
|
3667
|
+
*
|
|
3668
|
+
* Pure layout containers (`div`, `section`, `main`, `article`, `header`,
|
|
3669
|
+
* `footer`, `nav`, `aside`) deliberately don't get the fallback — they're
|
|
3670
|
+
* transparent in the real DOM and should stay that way in the skeleton.
|
|
3671
|
+
*/
|
|
3672
|
+
const SURFACE_TAGS = new Set([
|
|
3673
|
+
"button",
|
|
3674
|
+
"a",
|
|
3675
|
+
"label",
|
|
3676
|
+
"summary",
|
|
3677
|
+
"fieldset",
|
|
3678
|
+
"legend"
|
|
3679
|
+
]);
|
|
3680
|
+
function transformElement(v, tag, opts, depth, max, state) {
|
|
3681
|
+
const cls = v.props?.class;
|
|
3682
|
+
const styl = v.props?.style;
|
|
3683
|
+
if (ATOMIC_TAGS$2.has(tag)) {
|
|
3684
|
+
const dimStyle = atomicDimensionStyle(v.props);
|
|
3685
|
+
return (0, vue.h)("div", {
|
|
3686
|
+
class: [
|
|
3687
|
+
"a-skel-block",
|
|
3688
|
+
cls,
|
|
3689
|
+
opts.animationClass
|
|
3690
|
+
],
|
|
3691
|
+
style: dimStyle ? mergeStyle(dimStyle, styl) : styl,
|
|
3692
|
+
"aria-hidden": "true"
|
|
3693
|
+
});
|
|
3479
3694
|
}
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3695
|
+
if (TABLE_TAGS.has(tag)) {
|
|
3696
|
+
if (depth >= max) return (0, vue.h)(tag, {
|
|
3697
|
+
class: cls,
|
|
3698
|
+
style: styl
|
|
3699
|
+
});
|
|
3700
|
+
const recursed = [];
|
|
3701
|
+
walk(v.children, opts, depth + 1, max, state, recursed);
|
|
3702
|
+
if (recursed.length === 0 && TEXT_OWNER_TAGS.has(tag)) return (0, vue.h)(tag, {
|
|
3703
|
+
class: cls,
|
|
3704
|
+
style: styl
|
|
3705
|
+
}, [textContentSpan(TEXT_PLACEHOLDER, opts.animationClass)]);
|
|
3706
|
+
return (0, vue.h)(tag, {
|
|
3707
|
+
class: cls,
|
|
3708
|
+
style: styl
|
|
3709
|
+
}, recursed.length > 0 ? recursed : void 0);
|
|
3710
|
+
}
|
|
3711
|
+
if (LIST_TAGS.has(tag)) {
|
|
3712
|
+
if (depth >= max) return (0, vue.h)(tag, {
|
|
3713
|
+
class: cls,
|
|
3714
|
+
style: styl
|
|
3715
|
+
});
|
|
3716
|
+
const recursed = [];
|
|
3717
|
+
walk(v.children, opts, depth + 1, max, state, recursed);
|
|
3718
|
+
if (recursed.length === 0 && TEXT_OWNER_TAGS.has(tag)) return (0, vue.h)(tag, {
|
|
3719
|
+
class: cls,
|
|
3720
|
+
style: styl
|
|
3721
|
+
}, [textContentSpan(TEXT_PLACEHOLDER, opts.animationClass)]);
|
|
3722
|
+
return (0, vue.h)(tag, {
|
|
3723
|
+
class: cls,
|
|
3724
|
+
style: styl
|
|
3725
|
+
}, recursed.length > 0 ? recursed : void 0);
|
|
3726
|
+
}
|
|
3727
|
+
if (depth >= max) return (0, vue.h)("div", {
|
|
3728
|
+
class: [
|
|
3729
|
+
"a-skel-block",
|
|
3730
|
+
cls,
|
|
3731
|
+
opts.animationClass
|
|
3732
|
+
],
|
|
3733
|
+
style: styl,
|
|
3734
|
+
"aria-hidden": "true"
|
|
3735
|
+
});
|
|
3736
|
+
const recursed = [];
|
|
3737
|
+
walk(v.children, opts, depth + 1, max, state, recursed);
|
|
3738
|
+
if (recursed.length === 0 && TEXT_OWNER_TAGS.has(tag)) return (0, vue.h)(tag, {
|
|
3739
|
+
class: cls,
|
|
3740
|
+
style: styl
|
|
3741
|
+
}, [textContentSpan(TEXT_PLACEHOLDER, opts.animationClass)]);
|
|
3742
|
+
if (recursed.length === 0) return (0, vue.h)(tag, {
|
|
3743
|
+
class: cls,
|
|
3744
|
+
style: styl
|
|
3485
3745
|
});
|
|
3746
|
+
if (SURFACE_TAGS.has(tag) && !hasExplicitBackground(cls, styl)) return (0, vue.h)(tag, {
|
|
3747
|
+
class: [
|
|
3748
|
+
"a-skel-block",
|
|
3749
|
+
cls,
|
|
3750
|
+
opts.animationClass
|
|
3751
|
+
],
|
|
3752
|
+
style: styl
|
|
3753
|
+
}, recursed);
|
|
3754
|
+
return cloneTag(tag, cls, styl, recursed);
|
|
3486
3755
|
}
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3756
|
+
/**
|
|
3757
|
+
* Extract width/height from HTML attributes that affect layout (`<svg
|
|
3758
|
+
* width="408">`, `<img width="…" height="…">`) and project them into inline
|
|
3759
|
+
* style. Without this, replacing an attribute-sized atomic element with a
|
|
3760
|
+
* `<div>` would drop the size — divs don't honour those attributes.
|
|
3761
|
+
*/
|
|
3762
|
+
function atomicDimensionStyle(props) {
|
|
3763
|
+
if (!props) return null;
|
|
3764
|
+
const out = {};
|
|
3765
|
+
const w = props.width;
|
|
3766
|
+
const h = props.height;
|
|
3767
|
+
if (w !== void 0 && w !== null && w !== "") out.width = typeof w === "number" ? `${w}px` : /^\d+$/.test(String(w)) ? `${w}px` : String(w);
|
|
3768
|
+
if (h !== void 0 && h !== null && h !== "") out.height = typeof h === "number" ? `${h}px` : /^\d+$/.test(String(h)) ? `${h}px` : String(h);
|
|
3769
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
3770
|
+
}
|
|
3771
|
+
/**
|
|
3772
|
+
* Detect whether the element has an explicit background — either via a
|
|
3773
|
+
* Tailwind / DaisyUI / CSS-modules `bg-*` class or via an inline
|
|
3774
|
+
* `background` / `background-color` / `background-image` style. When false,
|
|
3775
|
+
* surface-bearing elements (button, a, label, …) fall back to the default
|
|
3776
|
+
* skeleton fill so they don't render as a transparent gap during loading.
|
|
3777
|
+
*/
|
|
3778
|
+
function hasExplicitBackground(cls, styl) {
|
|
3779
|
+
if (hasBackgroundInStyle(styl)) return true;
|
|
3780
|
+
if (hasBackgroundInClass(cls)) return true;
|
|
3781
|
+
return false;
|
|
3782
|
+
}
|
|
3783
|
+
const BG_CLASS_RE = /(?:^|\s|:)bg-(?!skel)(?:\[|[a-z])/i;
|
|
3784
|
+
function hasBackgroundInClass(cls) {
|
|
3785
|
+
if (cls == null || cls === false) return false;
|
|
3786
|
+
if (typeof cls === "string") return BG_CLASS_RE.test(cls);
|
|
3787
|
+
if (Array.isArray(cls)) {
|
|
3788
|
+
for (const item of cls) if (hasBackgroundInClass(item)) return true;
|
|
3789
|
+
return false;
|
|
3790
|
+
}
|
|
3791
|
+
if (typeof cls === "object") {
|
|
3792
|
+
for (const k of Object.keys(cls)) if (cls[k] && BG_CLASS_RE.test(k)) return true;
|
|
3491
3793
|
}
|
|
3492
3794
|
return false;
|
|
3493
3795
|
}
|
|
3494
|
-
function
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
let type;
|
|
3503
|
-
let resolvedRadius = radius;
|
|
3504
|
-
let lines;
|
|
3505
|
-
let lineHeight;
|
|
3506
|
-
if (tag === "IMG" || tag === "SVG" || tag === "VIDEO" || tag === "CANVAS") type = "image";
|
|
3507
|
-
else if (isCircle) {
|
|
3508
|
-
type = "circle";
|
|
3509
|
-
resolvedRadius = Math.floor(minDim / 2);
|
|
3510
|
-
} else if (hasText) {
|
|
3511
|
-
type = "text";
|
|
3512
|
-
lineHeight = Math.round(parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.4 || 16);
|
|
3513
|
-
lines = Math.max(1, Math.round(h / lineHeight));
|
|
3514
|
-
resolvedRadius = Math.min(radius, 4);
|
|
3515
|
-
} else type = "block";
|
|
3516
|
-
return freezeShape({
|
|
3517
|
-
type,
|
|
3518
|
-
x,
|
|
3519
|
-
y,
|
|
3520
|
-
w,
|
|
3521
|
-
h,
|
|
3522
|
-
radius: resolvedRadius,
|
|
3523
|
-
lines,
|
|
3524
|
-
lineHeight
|
|
3525
|
-
});
|
|
3796
|
+
function hasBackgroundInStyle(styl) {
|
|
3797
|
+
if (!styl) return false;
|
|
3798
|
+
if (typeof styl === "string") return /background(?:-color|-image)?\s*:/i.test(styl);
|
|
3799
|
+
if (typeof styl === "object") {
|
|
3800
|
+
const s = styl;
|
|
3801
|
+
return "background" in s || "backgroundColor" in s || "backgroundImage" in s || "background-color" in s || "background-image" in s;
|
|
3802
|
+
}
|
|
3803
|
+
return false;
|
|
3526
3804
|
}
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3805
|
+
function mergeStyle(a, b) {
|
|
3806
|
+
if (!b) return a;
|
|
3807
|
+
if (typeof b === "string") return `${Object.entries(a).map(([k, v]) => `${k}: ${v}`).join("; ")}; ${b}`;
|
|
3808
|
+
return {
|
|
3809
|
+
...a,
|
|
3810
|
+
...b
|
|
3811
|
+
};
|
|
3812
|
+
}
|
|
3813
|
+
function cloneTag(tag, cls, styl, children) {
|
|
3814
|
+
return (0, vue.h)(tag, {
|
|
3815
|
+
class: cls,
|
|
3816
|
+
style: styl
|
|
3817
|
+
}, children);
|
|
3818
|
+
}
|
|
3819
|
+
function textContentSpan(text, animationClass) {
|
|
3820
|
+
return (0, vue.h)("span", {
|
|
3821
|
+
class: ["a-skel-text-content", animationClass],
|
|
3822
|
+
"aria-hidden": "true"
|
|
3823
|
+
}, text);
|
|
3824
|
+
}
|
|
3825
|
+
//#endregion
|
|
3826
|
+
//#region src/components/StructuralSkeleton.ts
|
|
3827
|
+
/**
|
|
3828
|
+
* Renders a structural skeleton derived from a slot's vnode tree. Pure render
|
|
3829
|
+
* function — no template, no scoped styles — so the parent's class strings
|
|
3830
|
+
* pass through unchanged and Tailwind utilities continue to drive layout.
|
|
3831
|
+
*
|
|
3832
|
+
* `maxNodes` is forwarded to the walker; cap defaults to 300 (see
|
|
3833
|
+
* `buildStructuralSkeleton`). Beyond that the walk stops emitting and the cap
|
|
3834
|
+
* propagates back as a clipped tree, keeping first-paint bounded.
|
|
3530
3835
|
*/
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
const isLast = i === node.lines;
|
|
3550
|
-
arr.push(Object.freeze({
|
|
3551
|
-
left: `${node.x}px`,
|
|
3552
|
-
top: `${node.y + (i - 1) * lh}px`,
|
|
3553
|
-
width: isLast ? widthLast : widthFull,
|
|
3554
|
-
height: heightStr,
|
|
3555
|
-
borderRadius: radiusStr
|
|
3556
|
-
}));
|
|
3836
|
+
const StructuralSkeleton = (0, vue.defineComponent)({
|
|
3837
|
+
name: "StructuralSkeleton",
|
|
3838
|
+
props: {
|
|
3839
|
+
vnodes: {
|
|
3840
|
+
type: Array,
|
|
3841
|
+
required: true
|
|
3842
|
+
},
|
|
3843
|
+
animation: {
|
|
3844
|
+
type: String,
|
|
3845
|
+
default: null
|
|
3846
|
+
},
|
|
3847
|
+
maxDepth: {
|
|
3848
|
+
type: Number,
|
|
3849
|
+
default: 8
|
|
3850
|
+
},
|
|
3851
|
+
maxNodes: {
|
|
3852
|
+
type: Number,
|
|
3853
|
+
default: 300
|
|
3557
3854
|
}
|
|
3558
|
-
|
|
3855
|
+
},
|
|
3856
|
+
setup(props) {
|
|
3857
|
+
return () => buildStructuralSkeleton(props.vnodes, {
|
|
3858
|
+
animationClass: props.animation,
|
|
3859
|
+
maxDepth: props.maxDepth,
|
|
3860
|
+
maxNodes: props.maxNodes
|
|
3861
|
+
});
|
|
3559
3862
|
}
|
|
3560
|
-
|
|
3561
|
-
|
|
3863
|
+
});
|
|
3864
|
+
//#endregion
|
|
3865
|
+
//#region src/components/icons.ts
|
|
3866
|
+
/**
|
|
3867
|
+
* Shared placeholder icon renderers for leaf nodes (image / video). Lives at
|
|
3868
|
+
* one site so both `CloneNode.ts` (clone mode) and `StructuralLayerNode.ts`
|
|
3869
|
+
* (Recipe 3 structural mode) render identical glyphs.
|
|
3870
|
+
*/
|
|
3871
|
+
function renderImageIcon() {
|
|
3872
|
+
return (0, vue.h)("svg", {
|
|
3873
|
+
class: "a-skel-clone-icon",
|
|
3874
|
+
viewBox: "0 0 24 24",
|
|
3875
|
+
"aria-hidden": "true"
|
|
3876
|
+
}, [(0, vue.h)("path", {
|
|
3877
|
+
d: "M19 5H5a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2Zm-3.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM19 17H5l3.5-4.5 2.5 3 3.5-4.5L19 17Z",
|
|
3878
|
+
fill: "currentColor"
|
|
3879
|
+
})]);
|
|
3880
|
+
}
|
|
3881
|
+
function renderPlayIcon() {
|
|
3882
|
+
return (0, vue.h)("svg", {
|
|
3883
|
+
class: "a-skel-clone-icon",
|
|
3884
|
+
viewBox: "0 0 24 24",
|
|
3885
|
+
"aria-hidden": "true"
|
|
3886
|
+
}, [(0, vue.h)("circle", {
|
|
3887
|
+
cx: 12,
|
|
3888
|
+
cy: 12,
|
|
3889
|
+
r: 10,
|
|
3890
|
+
stroke: "currentColor",
|
|
3891
|
+
"stroke-width": 1.5,
|
|
3892
|
+
fill: "none"
|
|
3893
|
+
}), (0, vue.h)("path", {
|
|
3894
|
+
d: "M10 8l6 4-6 4V8Z",
|
|
3895
|
+
fill: "currentColor"
|
|
3896
|
+
})]);
|
|
3897
|
+
}
|
|
3898
|
+
//#endregion
|
|
3899
|
+
//#region src/components/CloneNode.ts
|
|
3900
|
+
/**
|
|
3901
|
+
* `CloneNode` — recursive renderer for one node of a `CaptureSnapshot`.
|
|
3902
|
+
*
|
|
3903
|
+
* Design:
|
|
3904
|
+
* - **Strategy table by `CapturedNode.kind`**. Each kind ('container' |
|
|
3905
|
+
* 'text' | 'image' | 'video' | 'media' | 'block') has its own renderer
|
|
3906
|
+
* function. Adding a new kind is one entry in `RENDERERS` — no edits
|
|
3907
|
+
* elsewhere (Open/Closed).
|
|
3908
|
+
* - **Pure render function**. No reactive state of its own; everything
|
|
3909
|
+
* flows from props.
|
|
3910
|
+
* - **Composition**: per-line text bars share the same shape as block
|
|
3911
|
+
* leaves so there's no behaviour duplication.
|
|
3912
|
+
*/
|
|
3913
|
+
const RENDERERS$1 = {
|
|
3914
|
+
container: renderContainer$1,
|
|
3915
|
+
text: renderText,
|
|
3916
|
+
image: (ctx) => renderLeafWithIcon$1(ctx, "image"),
|
|
3917
|
+
video: (ctx) => renderLeafWithIcon$1(ctx, "video"),
|
|
3918
|
+
media: (ctx) => renderLeaf(ctx),
|
|
3919
|
+
block: (ctx) => renderLeaf(ctx)
|
|
3920
|
+
};
|
|
3921
|
+
/**
|
|
3922
|
+
* Container — positioned wrapper with captured background / border / shadow.
|
|
3923
|
+
* No `.a-skel` class so the surface shows through exactly as in the real DOM.
|
|
3924
|
+
* Recurse into children via the same `CloneNode`.
|
|
3925
|
+
*
|
|
3926
|
+
* `CapturedNode.{x,y}` is always root-relative, but children render inside
|
|
3927
|
+
* this container — itself `position: absolute`. If we hand children the
|
|
3928
|
+
* root-relative coords as-is, their `left` / `top` resolve against the
|
|
3929
|
+
* container (their new offset parent), doubling the offset. Pass a
|
|
3930
|
+
* `blockStyle` closure that subtracts this container's offset so each
|
|
3931
|
+
* descendant lands at its captured root-relative position regardless of
|
|
3932
|
+
* how deeply it's nested.
|
|
3933
|
+
*/
|
|
3934
|
+
function renderContainer$1(ctx) {
|
|
3935
|
+
const { node } = ctx;
|
|
3936
|
+
const childBlockStyle = (n) => ({
|
|
3937
|
+
position: "absolute",
|
|
3938
|
+
left: `${n.x - node.x}px`,
|
|
3939
|
+
top: `${n.y - node.y}px`,
|
|
3940
|
+
width: `${n.w}px`,
|
|
3941
|
+
height: `${n.h}px`,
|
|
3942
|
+
...n.style
|
|
3943
|
+
});
|
|
3944
|
+
return (0, vue.h)("div", {
|
|
3945
|
+
class: "a-skel-clone-container",
|
|
3946
|
+
style: ctx.blockStyle(node),
|
|
3947
|
+
"aria-hidden": "true"
|
|
3948
|
+
}, (node.children ?? []).map((child) => (0, vue.h)(CloneNode, {
|
|
3949
|
+
node: child,
|
|
3950
|
+
animClass: ctx.animClass,
|
|
3951
|
+
blockStyle: childBlockStyle,
|
|
3952
|
+
lineStyle: ctx.lineStyle
|
|
3953
|
+
})));
|
|
3954
|
+
}
|
|
3955
|
+
/**
|
|
3956
|
+
* Text — container carrying captured background/border, with per-line
|
|
3957
|
+
* shimmer bars positioned absolutely inside. Each text line becomes one bar
|
|
3958
|
+
* at the exact rendered text rect (multi-line / wrapped / RTL all replay 1:1).
|
|
3959
|
+
*/
|
|
3960
|
+
function renderText(ctx) {
|
|
3961
|
+
const { node, animClass } = ctx;
|
|
3962
|
+
const lines = node.textLines ?? [{
|
|
3562
3963
|
x: node.x,
|
|
3563
3964
|
y: node.y,
|
|
3564
3965
|
w: node.w,
|
|
3565
|
-
h: node.h
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
style,
|
|
3570
|
-
|
|
3966
|
+
h: node.h
|
|
3967
|
+
}];
|
|
3968
|
+
return (0, vue.h)("div", {
|
|
3969
|
+
class: "a-skel-clone-container a-skel-clone-text",
|
|
3970
|
+
style: ctx.blockStyle(node),
|
|
3971
|
+
"aria-hidden": "true"
|
|
3972
|
+
}, lines.map((line) => (0, vue.h)("div", {
|
|
3973
|
+
class: ["a-skel-clone-textbar", animClass],
|
|
3974
|
+
style: ctx.lineStyle({
|
|
3975
|
+
x: line.x - node.x,
|
|
3976
|
+
y: line.y - node.y,
|
|
3977
|
+
w: line.w,
|
|
3978
|
+
h: line.h
|
|
3979
|
+
}, node.style)
|
|
3980
|
+
})));
|
|
3981
|
+
}
|
|
3982
|
+
/** Plain shimmer leaf (block / media). */
|
|
3983
|
+
function renderLeaf(ctx) {
|
|
3984
|
+
return (0, vue.h)("div", {
|
|
3985
|
+
class: ["a-skel a-skel-clone-leaf", ctx.animClass],
|
|
3986
|
+
style: ctx.blockStyle(ctx.node),
|
|
3987
|
+
"aria-hidden": "true"
|
|
3571
3988
|
});
|
|
3572
3989
|
}
|
|
3990
|
+
/** Shimmer leaf with a centered placeholder icon (image / video). */
|
|
3991
|
+
function renderLeafWithIcon$1(ctx, kind) {
|
|
3992
|
+
return (0, vue.h)("div", {
|
|
3993
|
+
class: ["a-skel a-skel-clone-leaf a-skel-clone-leaf--with-icon", ctx.animClass],
|
|
3994
|
+
style: ctx.blockStyle(ctx.node),
|
|
3995
|
+
"aria-hidden": "true"
|
|
3996
|
+
}, [kind === "image" ? renderImageIcon() : renderPlayIcon()]);
|
|
3997
|
+
}
|
|
3998
|
+
const CloneNode = (0, vue.defineComponent)({
|
|
3999
|
+
name: "CloneNode",
|
|
4000
|
+
props: {
|
|
4001
|
+
node: {
|
|
4002
|
+
type: Object,
|
|
4003
|
+
required: true
|
|
4004
|
+
},
|
|
4005
|
+
animClass: {
|
|
4006
|
+
type: [String, null],
|
|
4007
|
+
default: null
|
|
4008
|
+
},
|
|
4009
|
+
blockStyle: {
|
|
4010
|
+
type: Function,
|
|
4011
|
+
required: true
|
|
4012
|
+
},
|
|
4013
|
+
lineStyle: {
|
|
4014
|
+
type: Function,
|
|
4015
|
+
required: true
|
|
4016
|
+
}
|
|
4017
|
+
},
|
|
4018
|
+
setup(props) {
|
|
4019
|
+
return () => {
|
|
4020
|
+
const renderer = RENDERERS$1[props.node.kind];
|
|
4021
|
+
return renderer({
|
|
4022
|
+
node: props.node,
|
|
4023
|
+
animClass: props.animClass,
|
|
4024
|
+
blockStyle: props.blockStyle,
|
|
4025
|
+
lineStyle: props.lineStyle
|
|
4026
|
+
});
|
|
4027
|
+
};
|
|
4028
|
+
}
|
|
4029
|
+
});
|
|
3573
4030
|
//#endregion
|
|
3574
|
-
//#region
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
4031
|
+
//#region \0/plugin-vue/export-helper
|
|
4032
|
+
var export_helper_default = (sfc, props) => {
|
|
4033
|
+
const target = sfc.__vccOpts || sfc;
|
|
4034
|
+
for (const [key, val] of props) target[key] = val;
|
|
4035
|
+
return target;
|
|
4036
|
+
};
|
|
4037
|
+
//#endregion
|
|
4038
|
+
//#region src/components/ASkeletonClone.vue
|
|
4039
|
+
const _sfc_main$18 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
4040
|
+
__name: "ASkeletonClone",
|
|
4041
|
+
props: {
|
|
4042
|
+
shape: {
|
|
4043
|
+
type: Object,
|
|
4044
|
+
required: true
|
|
4045
|
+
},
|
|
4046
|
+
animation: {
|
|
4047
|
+
type: String,
|
|
4048
|
+
required: false,
|
|
4049
|
+
default: "pulse"
|
|
4050
|
+
},
|
|
4051
|
+
class: {
|
|
4052
|
+
type: [
|
|
4053
|
+
String,
|
|
4054
|
+
Array,
|
|
4055
|
+
Object
|
|
4056
|
+
],
|
|
4057
|
+
required: false
|
|
3599
4058
|
}
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
4059
|
+
},
|
|
4060
|
+
setup(__props, { expose: __expose }) {
|
|
4061
|
+
__expose();
|
|
4062
|
+
const props = __props;
|
|
4063
|
+
const animClass = (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`);
|
|
4064
|
+
const layerStyle = (0, vue.computed)(() => ({
|
|
4065
|
+
position: "relative",
|
|
4066
|
+
width: `${props.shape.width}px`,
|
|
4067
|
+
height: `${props.shape.height}px`
|
|
4068
|
+
}));
|
|
4069
|
+
function blockStyle(n) {
|
|
4070
|
+
return {
|
|
4071
|
+
position: "absolute",
|
|
4072
|
+
left: `${n.x}px`,
|
|
4073
|
+
top: `${n.y}px`,
|
|
4074
|
+
width: `${n.w}px`,
|
|
4075
|
+
height: `${n.h}px`,
|
|
4076
|
+
...n.style
|
|
4077
|
+
};
|
|
3603
4078
|
}
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
4079
|
+
function lineStyle(line, parentStyle) {
|
|
4080
|
+
const radius = parentStyle.borderTopLeftRadius ?? "var(--ak-skel-radius-sm)";
|
|
4081
|
+
return {
|
|
4082
|
+
position: "absolute",
|
|
4083
|
+
left: `${line.x}px`,
|
|
4084
|
+
top: `${line.y}px`,
|
|
4085
|
+
width: `${line.w}px`,
|
|
4086
|
+
height: `${line.h}px`,
|
|
4087
|
+
backgroundColor: "var(--ak-skel-base)",
|
|
4088
|
+
borderRadius: radius
|
|
4089
|
+
};
|
|
3607
4090
|
}
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
4091
|
+
const __returned__ = {
|
|
4092
|
+
props,
|
|
4093
|
+
animClass,
|
|
4094
|
+
layerStyle,
|
|
4095
|
+
blockStyle,
|
|
4096
|
+
lineStyle,
|
|
4097
|
+
get cn() {
|
|
4098
|
+
return cn;
|
|
4099
|
+
},
|
|
4100
|
+
get CloneNode() {
|
|
4101
|
+
return CloneNode;
|
|
4102
|
+
}
|
|
4103
|
+
};
|
|
4104
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
4105
|
+
enumerable: false,
|
|
4106
|
+
value: true
|
|
3614
4107
|
});
|
|
3615
|
-
|
|
3616
|
-
hasCaptured = true;
|
|
3617
|
-
options.onCapture(result);
|
|
3618
|
-
}
|
|
4108
|
+
return __returned__;
|
|
3619
4109
|
}
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
4110
|
+
});
|
|
4111
|
+
function _sfc_render$18(_ctx, _cache, $props, $setup, $data, $options) {
|
|
4112
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
4113
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skeleton__clone", $setup.props.class)),
|
|
4114
|
+
style: (0, vue.normalizeStyle)($setup.layerStyle),
|
|
4115
|
+
role: "status",
|
|
4116
|
+
"aria-busy": "true",
|
|
4117
|
+
"aria-live": "polite"
|
|
4118
|
+
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)($setup.props.shape.nodes, (node, idx) => {
|
|
4119
|
+
return (0, vue.openBlock)(), (0, vue.createBlock)($setup["CloneNode"], {
|
|
4120
|
+
key: idx,
|
|
4121
|
+
node,
|
|
4122
|
+
"anim-class": $setup.animClass,
|
|
4123
|
+
"block-style": $setup.blockStyle,
|
|
4124
|
+
"line-style": $setup.lineStyle
|
|
4125
|
+
}, null, 8, ["node", "anim-class"]);
|
|
4126
|
+
}), 128)), _cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading…", -1))], 6);
|
|
4127
|
+
}
|
|
4128
|
+
var ASkeletonClone_default = /* @__PURE__ */ export_helper_default(_sfc_main$18, [
|
|
4129
|
+
["render", _sfc_render$18],
|
|
4130
|
+
["__scopeId", "data-v-1554ff36"],
|
|
4131
|
+
["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/ASkeletonClone.vue"]
|
|
4132
|
+
]);
|
|
4133
|
+
//#endregion
|
|
4134
|
+
//#region src/utils/domRead.ts
|
|
4135
|
+
/**
|
|
4136
|
+
* Computed values that count as "default" and shouldn't be persisted. Stripped
|
|
4137
|
+
* before a captured `style` object lands on a node so a plain unstyled element
|
|
4138
|
+
* produces an empty style — keeps snapshots small and the rendered DOM clean.
|
|
4139
|
+
*/
|
|
4140
|
+
const SKIP_VALUES = new Set([
|
|
4141
|
+
"none",
|
|
4142
|
+
"normal",
|
|
4143
|
+
"auto",
|
|
4144
|
+
"0px",
|
|
4145
|
+
"0",
|
|
4146
|
+
"0 0",
|
|
4147
|
+
"0% 0%",
|
|
4148
|
+
"0px 0px",
|
|
4149
|
+
"0deg",
|
|
4150
|
+
"0s",
|
|
4151
|
+
"visible",
|
|
4152
|
+
"static",
|
|
4153
|
+
"transparent",
|
|
4154
|
+
"rgba(0, 0, 0, 0)",
|
|
4155
|
+
"rgb(0, 0, 0, 0)",
|
|
4156
|
+
"initial",
|
|
4157
|
+
"inherit",
|
|
4158
|
+
"unset",
|
|
4159
|
+
"currentcolor"
|
|
4160
|
+
]);
|
|
4161
|
+
/**
|
|
4162
|
+
* Read the subset of `props` from `cs` and return a frozen camelCased style
|
|
4163
|
+
* map with the SKIP_VALUES entries omitted. `opacity: 1` is treated as default
|
|
4164
|
+
* (matches CSS' initial value).
|
|
4165
|
+
*/
|
|
4166
|
+
function readComputedStyles(cs, props) {
|
|
4167
|
+
const out = {};
|
|
4168
|
+
for (const prop of props) {
|
|
4169
|
+
const val = cs.getPropertyValue(prop).trim();
|
|
4170
|
+
if (!val) continue;
|
|
4171
|
+
if (SKIP_VALUES.has(val.toLowerCase())) continue;
|
|
4172
|
+
if (prop === "opacity" && (val === "1" || parseFloat(val) === 1)) continue;
|
|
4173
|
+
out[camelCase(prop)] = val;
|
|
3626
4174
|
}
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
4175
|
+
return Object.freeze(out);
|
|
4176
|
+
}
|
|
4177
|
+
function camelCase(prop) {
|
|
4178
|
+
return prop.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
4179
|
+
}
|
|
4180
|
+
/** True when the element has at least one non-whitespace direct text-node child. */
|
|
4181
|
+
function hasDirectText(el) {
|
|
4182
|
+
for (let i = 0; i < el.childNodes.length; i++) {
|
|
4183
|
+
const node = el.childNodes[i];
|
|
4184
|
+
if (node.nodeType === Node.TEXT_NODE && (node.textContent ?? "").trim().length > 0) return true;
|
|
3633
4185
|
}
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
}, {
|
|
3646
|
-
immediate: true,
|
|
3647
|
-
flush: "post"
|
|
3648
|
-
});
|
|
3649
|
-
(0, vue.onBeforeUnmount)(cleanup);
|
|
4186
|
+
return false;
|
|
4187
|
+
}
|
|
4188
|
+
/** Element children, with `data-skeleton-ignore` descendants filtered out. */
|
|
4189
|
+
function collectVisibleChildren(el) {
|
|
4190
|
+
const out = [];
|
|
4191
|
+
for (let i = 0; i < el.children.length; i++) {
|
|
4192
|
+
const c = el.children[i];
|
|
4193
|
+
if (c.dataset?.skeletonIgnore !== void 0) continue;
|
|
4194
|
+
out.push(c);
|
|
4195
|
+
}
|
|
4196
|
+
return out;
|
|
3650
4197
|
}
|
|
3651
|
-
//#endregion
|
|
3652
|
-
//#region src/composables/useSkeletonCache.ts
|
|
3653
|
-
const memory = /* @__PURE__ */ new Map();
|
|
3654
|
-
const STORAGE_PREFIX = "a-skeleton:";
|
|
3655
4198
|
/**
|
|
3656
|
-
*
|
|
3657
|
-
*
|
|
3658
|
-
*
|
|
3659
|
-
*
|
|
4199
|
+
* Per-line text rects via `Range.getClientRects()`. Returns one rect per visual
|
|
4200
|
+
* line — exact left/width for each line so wrapped paragraphs, RTL last-line
|
|
4201
|
+
* positions, centered headings replay 1:1 without heuristics. Returns
|
|
4202
|
+
* `undefined` if the element has no direct text or the Range API isn't usable.
|
|
4203
|
+
*
|
|
4204
|
+
* Two rects on the same baseline AND horizontally touching (gap ≤ 2 px) are
|
|
4205
|
+
* merged so inline spans on the same line collapse to one bar. Same-baseline
|
|
4206
|
+
* rects on different visual lines (rare; float-into-paragraph layouts) won't
|
|
4207
|
+
* touch horizontally and stay separate.
|
|
3660
4208
|
*/
|
|
3661
|
-
function
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
if (!persist || typeof window === "undefined") return void 0;
|
|
4209
|
+
function captureTextLines(el, origin) {
|
|
4210
|
+
if (typeof document === "undefined" || typeof document.createRange !== "function") return void 0;
|
|
4211
|
+
let range;
|
|
3665
4212
|
try {
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
const rehydrated = rehydrateShape(JSON.parse(raw));
|
|
3669
|
-
memory.set(key, rehydrated);
|
|
3670
|
-
return rehydrated;
|
|
4213
|
+
range = document.createRange();
|
|
4214
|
+
range.selectNodeContents(el);
|
|
3671
4215
|
} catch {
|
|
3672
4216
|
return;
|
|
3673
4217
|
}
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
const
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
} catch {}
|
|
3696
|
-
return;
|
|
3697
|
-
}
|
|
3698
|
-
memory.delete(key);
|
|
3699
|
-
if (typeof window === "undefined") return;
|
|
3700
|
-
try {
|
|
3701
|
-
window.localStorage.removeItem(STORAGE_PREFIX + key);
|
|
3702
|
-
} catch {}
|
|
3703
|
-
}
|
|
3704
|
-
/**
|
|
3705
|
-
* Rebuild `style` + `lineStyles` for nodes that lost them via serialization.
|
|
3706
|
-
* Walks the array in-place where possible and freezes the result so the render
|
|
3707
|
-
* path stays allocation-free.
|
|
3708
|
-
*/
|
|
3709
|
-
function rehydrateShape(shape) {
|
|
3710
|
-
const nodes = shape.nodes.map((n) => n.style ? n : freezeNodeStyles(n));
|
|
3711
|
-
return Object.freeze({
|
|
3712
|
-
nodes: Object.freeze(nodes),
|
|
3713
|
-
width: shape.width,
|
|
3714
|
-
height: shape.height,
|
|
3715
|
-
truncated: shape.truncated
|
|
3716
|
-
});
|
|
3717
|
-
}
|
|
3718
|
-
function leanNodes(nodes) {
|
|
3719
|
-
return nodes.map((n) => ({
|
|
3720
|
-
type: n.type,
|
|
3721
|
-
x: n.x,
|
|
3722
|
-
y: n.y,
|
|
3723
|
-
w: n.w,
|
|
3724
|
-
h: n.h,
|
|
3725
|
-
radius: n.radius,
|
|
3726
|
-
lines: n.lines,
|
|
3727
|
-
lineHeight: n.lineHeight
|
|
3728
|
-
}));
|
|
3729
|
-
}
|
|
3730
|
-
function freezeNodeStyles(node) {
|
|
3731
|
-
const style = Object.freeze({
|
|
3732
|
-
left: `${node.x}px`,
|
|
3733
|
-
top: `${node.y}px`,
|
|
3734
|
-
width: `${node.w}px`,
|
|
3735
|
-
height: `${node.h}px`,
|
|
3736
|
-
borderRadius: `${node.radius}px`
|
|
3737
|
-
});
|
|
3738
|
-
let lineStyles;
|
|
3739
|
-
if (node.type === "text" && node.lines && node.lines > 1) {
|
|
3740
|
-
const lh = node.lineHeight ?? Math.round(node.h / node.lines);
|
|
3741
|
-
const barHeight = Math.max(8, Math.round(lh * .7));
|
|
3742
|
-
const widthFull = `${node.w}px`;
|
|
3743
|
-
const widthLast = `${Math.max(40, Math.round(node.w * .7))}px`;
|
|
3744
|
-
const heightStr = `${barHeight}px`;
|
|
3745
|
-
const radiusStr = `${node.radius}px`;
|
|
3746
|
-
const arr = [];
|
|
3747
|
-
for (let i = 1; i <= node.lines; i++) {
|
|
3748
|
-
const isLast = i === node.lines;
|
|
3749
|
-
arr.push(Object.freeze({
|
|
3750
|
-
left: `${node.x}px`,
|
|
3751
|
-
top: `${node.y + (i - 1) * lh}px`,
|
|
3752
|
-
width: isLast ? widthLast : widthFull,
|
|
3753
|
-
height: heightStr,
|
|
3754
|
-
borderRadius: radiusStr
|
|
3755
|
-
}));
|
|
3756
|
-
}
|
|
3757
|
-
lineStyles = Object.freeze(arr);
|
|
3758
|
-
}
|
|
3759
|
-
return Object.freeze({
|
|
3760
|
-
type: node.type,
|
|
3761
|
-
x: node.x,
|
|
3762
|
-
y: node.y,
|
|
3763
|
-
w: node.w,
|
|
3764
|
-
h: node.h,
|
|
3765
|
-
radius: node.radius,
|
|
3766
|
-
lines: node.lines,
|
|
3767
|
-
lineHeight: node.lineHeight,
|
|
3768
|
-
style,
|
|
3769
|
-
lineStyles
|
|
3770
|
-
});
|
|
3771
|
-
}
|
|
3772
|
-
//#endregion
|
|
3773
|
-
//#region src/utils/fingerprint.ts
|
|
3774
|
-
/**
|
|
3775
|
-
* Derive a default cache key from a slot's vnode tree. Returns the first
|
|
3776
|
-
* non-comment child's component name (or HTML tag). When the slot only contains
|
|
3777
|
-
* text / comments / unknown content, returns `'anonymous'` so the caller can
|
|
3778
|
-
* still cache, but with no useful identity — encourage an explicit `cacheKey`.
|
|
3779
|
-
*/
|
|
3780
|
-
function fingerprintSlot(vnodes) {
|
|
3781
|
-
if (!vnodes) return "anonymous";
|
|
3782
|
-
for (const vnode of vnodes) {
|
|
3783
|
-
const tag = describeVNode(vnode);
|
|
3784
|
-
if (tag) return tag;
|
|
3785
|
-
}
|
|
3786
|
-
return "anonymous";
|
|
3787
|
-
}
|
|
3788
|
-
function describeVNode(vnode) {
|
|
3789
|
-
const t = vnode.type;
|
|
3790
|
-
if (t === vue.Comment || t === vue.Text) return void 0;
|
|
3791
|
-
if (t === vue.Fragment) {
|
|
3792
|
-
const children = vnode.children;
|
|
3793
|
-
if (Array.isArray(children)) {
|
|
3794
|
-
for (const child of children) if (child && typeof child === "object" && "type" in child) {
|
|
3795
|
-
const found = describeVNode(child);
|
|
3796
|
-
if (found) return found;
|
|
3797
|
-
}
|
|
3798
|
-
}
|
|
3799
|
-
return;
|
|
3800
|
-
}
|
|
3801
|
-
if (typeof t === "string") return t;
|
|
3802
|
-
if (typeof t === "object" && t !== null) {
|
|
3803
|
-
const named = t.name ?? t.__name ?? t.displayName;
|
|
3804
|
-
if (named) return named;
|
|
3805
|
-
}
|
|
4218
|
+
const rects = range.getClientRects();
|
|
4219
|
+
if (!rects || rects.length === 0) return void 0;
|
|
4220
|
+
const merged = [];
|
|
4221
|
+
for (let i = 0; i < rects.length; i++) {
|
|
4222
|
+
const r = rects[i];
|
|
4223
|
+
if (r.width <= 0 || r.height <= 0) continue;
|
|
4224
|
+
const lr = {
|
|
4225
|
+
x: Math.round(r.left - origin.left),
|
|
4226
|
+
y: Math.round(r.top - origin.top),
|
|
4227
|
+
w: Math.round(r.width),
|
|
4228
|
+
h: Math.round(r.height)
|
|
4229
|
+
};
|
|
4230
|
+
const last = merged[merged.length - 1];
|
|
4231
|
+
if (last && Math.abs(last.y - lr.y) <= 1 && Math.abs(last.h - lr.h) <= 1 && Math.max(last.x, lr.x) - Math.min(last.x + last.w, lr.x + lr.w) <= 2) {
|
|
4232
|
+
const leftEdge = Math.min(last.x, lr.x);
|
|
4233
|
+
const rightEdge = Math.max(last.x + last.w, lr.x + lr.w);
|
|
4234
|
+
last.x = leftEdge;
|
|
4235
|
+
last.w = rightEdge - leftEdge;
|
|
4236
|
+
} else merged.push(lr);
|
|
4237
|
+
}
|
|
4238
|
+
return merged.length > 0 ? merged : void 0;
|
|
3806
4239
|
}
|
|
3807
4240
|
//#endregion
|
|
3808
|
-
//#region src/utils/
|
|
4241
|
+
//#region src/utils/captureStyles.ts
|
|
3809
4242
|
/**
|
|
3810
|
-
*
|
|
3811
|
-
*
|
|
3812
|
-
*
|
|
4243
|
+
* Comprehensive style-capture engine.
|
|
4244
|
+
*
|
|
4245
|
+
* Walks the slot's rendered DOM after mount and captures **every visible CSS
|
|
4246
|
+
* property** of every element it encounters, plus geometry, into a frozen
|
|
4247
|
+
* snapshot tree. The snapshot is replayed by `<ASkeletonClone>` as a tree
|
|
4248
|
+
* of positioned divs each carrying its captured inline style.
|
|
4249
|
+
*
|
|
4250
|
+
* Why this exists (vs the DOM-mirror walker in `buildStructuralSkeleton.ts`):
|
|
4251
|
+
*
|
|
4252
|
+
* - DOM-mirror preserves the slot's vnode tree and inherits styling from the
|
|
4253
|
+
* consumer's `class` / inline `style` attributes. That works for *static*
|
|
4254
|
+
* styles, but it can't see styles applied via:
|
|
4255
|
+
* - JavaScript-set inline style (refs, watchers, animation libs)
|
|
4256
|
+
* - CSS-in-JS runtimes that hash class names per instance
|
|
4257
|
+
* - DaisyUI / shadcn / custom CSS where the computed result differs
|
|
4258
|
+
* from what the class string implies
|
|
4259
|
+
* - Scoped styles compiled with content-hash data attributes
|
|
4260
|
+
*
|
|
4261
|
+
* - `captureSnapshot` reads `getComputedStyle()` for each element — the
|
|
4262
|
+
* **final** computed style after the cascade has resolved. Replaying that
|
|
4263
|
+
* produces a pixel-identical surface no matter what styling system the
|
|
4264
|
+
* consumer uses.
|
|
4265
|
+
*
|
|
4266
|
+
* Cost is bounded by the same `maxNodes` / `maxDepth` / `minSize` filters
|
|
4267
|
+
* as the legacy `walkDom` capture. The whole pass is a single top-down read
|
|
4268
|
+
* with no DOM writes between, so the browser does one layout up front.
|
|
3813
4269
|
*/
|
|
3814
|
-
const
|
|
4270
|
+
const DEFAULT_MAX_DEPTH$2 = 12;
|
|
4271
|
+
const DEFAULT_MAX_NODES$2 = 800;
|
|
4272
|
+
const DEFAULT_MIN_SIZE$2 = 4;
|
|
4273
|
+
/** Tags treated as atomic leaves — no recursion into their contents. */
|
|
4274
|
+
const ATOMIC_TAGS$1 = new Set([
|
|
3815
4275
|
"img",
|
|
3816
4276
|
"svg",
|
|
3817
4277
|
"canvas",
|
|
3818
4278
|
"video",
|
|
4279
|
+
"audio",
|
|
3819
4280
|
"input",
|
|
3820
4281
|
"textarea",
|
|
3821
4282
|
"select",
|
|
3822
|
-
"button",
|
|
3823
4283
|
"progress",
|
|
3824
4284
|
"meter",
|
|
3825
|
-
"hr"
|
|
4285
|
+
"hr",
|
|
4286
|
+
"iframe",
|
|
4287
|
+
"object",
|
|
4288
|
+
"embed",
|
|
4289
|
+
"picture",
|
|
4290
|
+
"br"
|
|
3826
4291
|
]);
|
|
3827
|
-
/**
|
|
3828
|
-
const
|
|
4292
|
+
/** Tags whose content is text — captured as text bars (per-line via Range). */
|
|
4293
|
+
const TEXT_OWNERS$1 = new Set([
|
|
4294
|
+
"p",
|
|
3829
4295
|
"h1",
|
|
3830
4296
|
"h2",
|
|
3831
4297
|
"h3",
|
|
3832
4298
|
"h4",
|
|
3833
4299
|
"h5",
|
|
3834
|
-
"h6"
|
|
3835
|
-
]);
|
|
3836
|
-
/** Multi-line text containers — produce N bars with a shortened last line. */
|
|
3837
|
-
const PARAGRAPH_TAGS = new Set(["p", "blockquote"]);
|
|
3838
|
-
/** Inline text — single bar, but inherits parent font sizing. */
|
|
3839
|
-
const INLINE_TEXT_TAGS = new Set([
|
|
4300
|
+
"h6",
|
|
3840
4301
|
"span",
|
|
3841
4302
|
"a",
|
|
3842
|
-
"small",
|
|
3843
|
-
"strong",
|
|
3844
4303
|
"em",
|
|
4304
|
+
"strong",
|
|
4305
|
+
"small",
|
|
3845
4306
|
"code",
|
|
3846
|
-
"time",
|
|
3847
|
-
"label",
|
|
3848
4307
|
"b",
|
|
3849
4308
|
"i",
|
|
3850
|
-
"mark"
|
|
4309
|
+
"mark",
|
|
4310
|
+
"label",
|
|
4311
|
+
"caption",
|
|
4312
|
+
"time",
|
|
4313
|
+
"dt",
|
|
4314
|
+
"dd",
|
|
4315
|
+
"li",
|
|
4316
|
+
"th",
|
|
4317
|
+
"td",
|
|
4318
|
+
"figcaption",
|
|
4319
|
+
"blockquote",
|
|
4320
|
+
"cite",
|
|
4321
|
+
"q"
|
|
3851
4322
|
]);
|
|
3852
|
-
const DEFAULT_MAX_DEPTH = 8;
|
|
3853
|
-
const DEFAULT_MAX_NODES = 300;
|
|
3854
4323
|
/**
|
|
3855
|
-
*
|
|
3856
|
-
*
|
|
3857
|
-
*
|
|
3858
|
-
* blocks. The result renders correctly on the FIRST paint without any DOM
|
|
3859
|
-
* measurement, as long as the slot's template renders structure even when its
|
|
3860
|
-
* data is empty (use `v-if`/`v-else` to swap content, not to gate the wrapper).
|
|
4324
|
+
* Visual CSS properties read from `getComputedStyle()`. Property listed here
|
|
4325
|
+
* is included in the captured snapshot **only when its value differs from the
|
|
4326
|
+
* skip set** — so a plain unstyled `<div>` produces an empty style object.
|
|
3861
4327
|
*
|
|
3862
|
-
*
|
|
3863
|
-
*
|
|
3864
|
-
* row list renders ~300 skeleton rows on first paint and then the measured cache
|
|
3865
|
-
* takes over for subsequent loads.
|
|
4328
|
+
* Listed in the order they're written into the snapshot's `style` object so
|
|
4329
|
+
* the replay is deterministic across captures.
|
|
3866
4330
|
*/
|
|
3867
|
-
|
|
3868
|
-
|
|
4331
|
+
const VISUAL_PROPS$1 = [
|
|
4332
|
+
"padding-top",
|
|
4333
|
+
"padding-right",
|
|
4334
|
+
"padding-bottom",
|
|
4335
|
+
"padding-left",
|
|
4336
|
+
"border-top-width",
|
|
4337
|
+
"border-right-width",
|
|
4338
|
+
"border-bottom-width",
|
|
4339
|
+
"border-left-width",
|
|
4340
|
+
"border-top-style",
|
|
4341
|
+
"border-right-style",
|
|
4342
|
+
"border-bottom-style",
|
|
4343
|
+
"border-left-style",
|
|
4344
|
+
"border-top-color",
|
|
4345
|
+
"border-right-color",
|
|
4346
|
+
"border-bottom-color",
|
|
4347
|
+
"border-left-color",
|
|
4348
|
+
"border-top-left-radius",
|
|
4349
|
+
"border-top-right-radius",
|
|
4350
|
+
"border-bottom-right-radius",
|
|
4351
|
+
"border-bottom-left-radius",
|
|
4352
|
+
"background-color",
|
|
4353
|
+
"background-image",
|
|
4354
|
+
"background-position",
|
|
4355
|
+
"background-size",
|
|
4356
|
+
"background-repeat",
|
|
4357
|
+
"background-origin",
|
|
4358
|
+
"background-clip",
|
|
4359
|
+
"box-shadow",
|
|
4360
|
+
"opacity",
|
|
4361
|
+
"filter",
|
|
4362
|
+
"backdrop-filter",
|
|
4363
|
+
"transform",
|
|
4364
|
+
"transform-origin",
|
|
4365
|
+
"mix-blend-mode",
|
|
4366
|
+
"font-family",
|
|
4367
|
+
"font-size",
|
|
4368
|
+
"font-weight",
|
|
4369
|
+
"font-style",
|
|
4370
|
+
"line-height",
|
|
4371
|
+
"letter-spacing",
|
|
4372
|
+
"text-align",
|
|
4373
|
+
"text-transform",
|
|
4374
|
+
"text-decoration-line",
|
|
4375
|
+
"text-decoration-color"
|
|
4376
|
+
];
|
|
4377
|
+
/**
|
|
4378
|
+
* Snapshot the rendered DOM under `root`, returning a frozen tree of every
|
|
4379
|
+
* visible element + its computed visual styles. Replaying the snapshot
|
|
4380
|
+
* produces a surface visually identical to `root`.
|
|
4381
|
+
*/
|
|
4382
|
+
function captureSnapshot(root, options = {}) {
|
|
4383
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH$2;
|
|
4384
|
+
const maxNodes = options.maxNodes ?? DEFAULT_MAX_NODES$2;
|
|
4385
|
+
const minSize = options.minSize ?? DEFAULT_MIN_SIZE$2;
|
|
4386
|
+
const walkAtomic = options.walkAtomic ?? false;
|
|
4387
|
+
const rootRect = root.getBoundingClientRect();
|
|
3869
4388
|
const state = {
|
|
3870
|
-
|
|
3871
|
-
|
|
4389
|
+
count: 0,
|
|
4390
|
+
truncated: false
|
|
3872
4391
|
};
|
|
3873
|
-
const
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
if (state.emitted >= state.cap) return;
|
|
3879
|
-
if (input == null || typeof input === "boolean") return;
|
|
3880
|
-
if (Array.isArray(input)) {
|
|
3881
|
-
for (let i = 0; i < input.length; i++) {
|
|
3882
|
-
if (state.emitted >= state.cap) return;
|
|
3883
|
-
walk(input[i], opts, depth, max, state, out);
|
|
4392
|
+
const nodes = [];
|
|
4393
|
+
for (let i = 0; i < root.children.length; i++) {
|
|
4394
|
+
if (state.count >= maxNodes) {
|
|
4395
|
+
state.truncated = true;
|
|
4396
|
+
break;
|
|
3884
4397
|
}
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
if (type === vue.Comment) return;
|
|
3894
|
-
if (type === vue.Text) {
|
|
3895
|
-
if (typeof v.children === "string" ? v.children.trim() : "") push(out, textBar(opts.animationClass), state);
|
|
3896
|
-
return;
|
|
3897
|
-
}
|
|
3898
|
-
if (type === vue.Fragment) {
|
|
3899
|
-
walk(v.children, opts, depth, max, state, out);
|
|
3900
|
-
return;
|
|
3901
|
-
}
|
|
3902
|
-
if (typeof type === "string") {
|
|
3903
|
-
push(out, transformElement(v, type.toLowerCase(), opts, depth, max, state), state);
|
|
3904
|
-
return;
|
|
4398
|
+
const node = capture$1(root.children[i], rootRect, 1, {
|
|
4399
|
+
maxDepth,
|
|
4400
|
+
maxNodes,
|
|
4401
|
+
minSize,
|
|
4402
|
+
walkAtomic,
|
|
4403
|
+
state
|
|
4404
|
+
});
|
|
4405
|
+
if (node) nodes.push(node);
|
|
3905
4406
|
}
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
style: v.props?.style
|
|
3913
|
-
}), state);
|
|
3914
|
-
}
|
|
3915
|
-
function push(out, vn, state) {
|
|
3916
|
-
if (state.emitted >= state.cap) return;
|
|
3917
|
-
out.push(vn);
|
|
3918
|
-
state.emitted++;
|
|
3919
|
-
}
|
|
3920
|
-
function transformElement(v, tag, opts, depth, max, state) {
|
|
3921
|
-
const cls = v.props?.class;
|
|
3922
|
-
const styl = v.props?.style;
|
|
3923
|
-
if (ATOMIC_TAGS.has(tag)) return (0, vue.h)("div", {
|
|
3924
|
-
class: [
|
|
3925
|
-
"a-skel-block",
|
|
3926
|
-
cls,
|
|
3927
|
-
opts.animationClass
|
|
3928
|
-
],
|
|
3929
|
-
style: styl
|
|
4407
|
+
return Object.freeze({
|
|
4408
|
+
width: Math.round(rootRect.width),
|
|
4409
|
+
height: Math.round(rootRect.height),
|
|
4410
|
+
nodes: Object.freeze(nodes),
|
|
4411
|
+
truncated: state.truncated,
|
|
4412
|
+
capturedAt: Date.now()
|
|
3930
4413
|
});
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
const children = v.children;
|
|
3937
|
-
const recursedChildren = [];
|
|
3938
|
-
walk(children, opts, depth + 1, max, state, recursedChildren);
|
|
3939
|
-
if (recursedChildren.length > 0) return (0, vue.h)(tag, {
|
|
3940
|
-
class: cls,
|
|
3941
|
-
style: styl
|
|
3942
|
-
}, recursedChildren);
|
|
3943
|
-
const lines = estimateLines(children, 3);
|
|
3944
|
-
return (0, vue.h)(tag, {
|
|
3945
|
-
class: cls,
|
|
3946
|
-
style: styl
|
|
3947
|
-
}, multiLineBars(lines, opts.animationClass));
|
|
4414
|
+
}
|
|
4415
|
+
function capture$1(el, origin, depth, ctx) {
|
|
4416
|
+
if (ctx.state.count >= ctx.maxNodes) {
|
|
4417
|
+
ctx.state.truncated = true;
|
|
4418
|
+
return null;
|
|
3948
4419
|
}
|
|
3949
|
-
if (
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
4420
|
+
if (el.dataset?.skeletonIgnore !== void 0) return null;
|
|
4421
|
+
const cs = window.getComputedStyle(el);
|
|
4422
|
+
if (cs.display === "none" || cs.visibility === "hidden") return null;
|
|
4423
|
+
const o = parseFloat(cs.opacity);
|
|
4424
|
+
if (Number.isFinite(o) && o === 0) return null;
|
|
4425
|
+
const rect = el.getBoundingClientRect();
|
|
4426
|
+
const tag = el.tagName.toLowerCase();
|
|
4427
|
+
const isTextOwnerTag = TEXT_OWNERS$1.has(tag);
|
|
4428
|
+
let effectiveHeight = rect.height;
|
|
4429
|
+
if (isTextOwnerTag && effectiveHeight < ctx.minSize) {
|
|
4430
|
+
const lh = parseFloat(cs.lineHeight);
|
|
4431
|
+
const fs = parseFloat(cs.fontSize);
|
|
4432
|
+
if (Number.isFinite(lh) && lh > 0) effectiveHeight = lh;
|
|
4433
|
+
else if (Number.isFinite(fs) && fs > 0) effectiveHeight = fs * 1.4;
|
|
3961
4434
|
}
|
|
3962
|
-
if (
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
const
|
|
3971
|
-
|
|
3972
|
-
if (
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
const key = Math.round(widthFraction * 10) / 10;
|
|
4008
|
-
const hit = PARTIAL_BAR_CACHE.get(key);
|
|
4009
|
-
if (hit) return hit;
|
|
4010
|
-
const made = Object.freeze({
|
|
4011
|
-
display: "inline-block",
|
|
4012
|
-
width: `${Math.round(key * 100)}%`,
|
|
4013
|
-
height: "0.75em",
|
|
4014
|
-
verticalAlign: "middle",
|
|
4015
|
-
borderRadius: "4px"
|
|
4016
|
-
});
|
|
4017
|
-
PARTIAL_BAR_CACHE.set(key, made);
|
|
4018
|
-
return made;
|
|
4019
|
-
}
|
|
4020
|
-
function textBar(animationClass, widthFraction = 1) {
|
|
4021
|
-
return (0, vue.h)("span", {
|
|
4022
|
-
class: [
|
|
4023
|
-
"a-skel-block",
|
|
4024
|
-
"a-skel-block--text",
|
|
4025
|
-
animationClass
|
|
4026
|
-
],
|
|
4027
|
-
style: widthFraction === 1 ? BAR_STYLE_FULL : partialBarStyle(widthFraction)
|
|
4435
|
+
if (rect.width < ctx.minSize || effectiveHeight < ctx.minSize) return null;
|
|
4436
|
+
const x = Math.round(rect.left - origin.left);
|
|
4437
|
+
const y = Math.round(rect.top - origin.top);
|
|
4438
|
+
const w = Math.round(rect.width);
|
|
4439
|
+
const h = Math.round(effectiveHeight);
|
|
4440
|
+
const style = readComputedStyles(cs, VISUAL_PROPS$1);
|
|
4441
|
+
const isAtomic = ATOMIC_TAGS$1.has(tag);
|
|
4442
|
+
const hasOwnText = hasDirectText(el);
|
|
4443
|
+
const childrenEls = collectVisibleChildren(el);
|
|
4444
|
+
let kind;
|
|
4445
|
+
if (tag === "img" || tag === "picture") kind = "image";
|
|
4446
|
+
else if (tag === "video") kind = "video";
|
|
4447
|
+
else if (isAtomic) kind = "media";
|
|
4448
|
+
else if (childrenEls.length === 0 && (hasOwnText || isTextOwnerTag)) kind = "text";
|
|
4449
|
+
else if (childrenEls.length === 0) kind = "block";
|
|
4450
|
+
else kind = "container";
|
|
4451
|
+
let textLines;
|
|
4452
|
+
if (kind === "text") textLines = captureTextLines(el, origin);
|
|
4453
|
+
let children;
|
|
4454
|
+
if (kind === "container" && depth < ctx.maxDepth) {
|
|
4455
|
+
children = [];
|
|
4456
|
+
for (const c of childrenEls) {
|
|
4457
|
+
if (ctx.state.count >= ctx.maxNodes) {
|
|
4458
|
+
ctx.state.truncated = true;
|
|
4459
|
+
break;
|
|
4460
|
+
}
|
|
4461
|
+
const childNode = capture$1(c, origin, depth + 1, ctx);
|
|
4462
|
+
if (childNode) children.push(childNode);
|
|
4463
|
+
}
|
|
4464
|
+
if (children.length === 0) {
|
|
4465
|
+
children = void 0;
|
|
4466
|
+
kind = style && Object.keys(style).length > 0 ? "block" : "container";
|
|
4467
|
+
}
|
|
4468
|
+
} else if (kind === "container" && depth >= ctx.maxDepth || isAtomic && ctx.walkAtomic) kind = "block";
|
|
4469
|
+
ctx.state.count++;
|
|
4470
|
+
return Object.freeze({
|
|
4471
|
+
tag,
|
|
4472
|
+
x,
|
|
4473
|
+
y,
|
|
4474
|
+
w,
|
|
4475
|
+
h,
|
|
4476
|
+
style,
|
|
4477
|
+
kind,
|
|
4478
|
+
textLines: textLines ? Object.freeze(textLines) : void 0,
|
|
4479
|
+
children: children ? Object.freeze(children) : void 0
|
|
4028
4480
|
});
|
|
4029
4481
|
}
|
|
4030
4482
|
//#endregion
|
|
4031
|
-
//#region src/components/
|
|
4483
|
+
//#region src/components/ASkeleton.vue
|
|
4032
4484
|
/**
|
|
4033
|
-
*
|
|
4034
|
-
* function — no template, no scoped styles — so the parent's class strings
|
|
4035
|
-
* pass through unchanged and Tailwind utilities continue to drive layout.
|
|
4485
|
+
* `<ASkeleton>` — the package's headline wrapper.
|
|
4036
4486
|
*
|
|
4037
|
-
*
|
|
4038
|
-
* `
|
|
4039
|
-
*
|
|
4487
|
+
* Two rendering strategies, selected via `mode`:
|
|
4488
|
+
* - `mirror` (default): walks the slot's vnode tree (`buildStructuralSkeleton`)
|
|
4489
|
+
* and preserves every element with its real `class` / inline `style`. Pure
|
|
4490
|
+
* Vue, SSR-safe, no DOM read.
|
|
4491
|
+
* - `clone`: mounts the slot off-screen once, snapshots every leaf's
|
|
4492
|
+
* `getComputedStyle()` (`captureSnapshot`), then replays the snapshot via
|
|
4493
|
+
* `<ASkeletonClone>` as positioned divs carrying every captured CSS
|
|
4494
|
+
* property. Pixel-identical regardless of styling system.
|
|
4495
|
+
*
|
|
4496
|
+
* The strategies are exclusive — picking one decides the entire loading-state
|
|
4497
|
+
* render path. The wrapper itself only orchestrates: it doesn't decide
|
|
4498
|
+
* per-element treatment (that's the strategies' job). Single Responsibility:
|
|
4499
|
+
* orchestration + a11y + containment.
|
|
4040
4500
|
*/
|
|
4041
|
-
const
|
|
4042
|
-
|
|
4043
|
-
props: {
|
|
4044
|
-
vnodes: {
|
|
4045
|
-
type: Array,
|
|
4046
|
-
required: true
|
|
4047
|
-
},
|
|
4048
|
-
animation: {
|
|
4049
|
-
type: String,
|
|
4050
|
-
default: null
|
|
4051
|
-
},
|
|
4052
|
-
maxDepth: {
|
|
4053
|
-
type: Number,
|
|
4054
|
-
default: 8
|
|
4055
|
-
},
|
|
4056
|
-
maxNodes: {
|
|
4057
|
-
type: Number,
|
|
4058
|
-
default: 300
|
|
4059
|
-
}
|
|
4060
|
-
},
|
|
4061
|
-
setup(props) {
|
|
4062
|
-
return () => buildStructuralSkeleton(props.vnodes, {
|
|
4063
|
-
animationClass: props.animation,
|
|
4064
|
-
maxDepth: props.maxDepth,
|
|
4065
|
-
maxNodes: props.maxNodes
|
|
4066
|
-
});
|
|
4067
|
-
}
|
|
4068
|
-
});
|
|
4069
|
-
//#endregion
|
|
4070
|
-
//#region \0/plugin-vue/export-helper
|
|
4071
|
-
var export_helper_default = (sfc, props) => {
|
|
4072
|
-
const target = sfc.__vccOpts || sfc;
|
|
4073
|
-
for (const [key, val] of props) target[key] = val;
|
|
4074
|
-
return target;
|
|
4075
|
-
};
|
|
4076
|
-
//#endregion
|
|
4077
|
-
//#region src/components/ASkeleton.vue
|
|
4078
|
-
const _sfc_main$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
4501
|
+
const MAX_RETRY_ATTEMPTS = 5;
|
|
4502
|
+
const _sfc_main$17 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
4079
4503
|
__name: "ASkeleton",
|
|
4080
4504
|
props: {
|
|
4081
4505
|
loading: {
|
|
4082
4506
|
type: Boolean,
|
|
4083
4507
|
required: true
|
|
4084
4508
|
},
|
|
4509
|
+
mode: {
|
|
4510
|
+
type: String,
|
|
4511
|
+
required: false,
|
|
4512
|
+
default: "clone"
|
|
4513
|
+
},
|
|
4085
4514
|
cacheKey: {
|
|
4086
4515
|
type: String,
|
|
4087
4516
|
required: false
|
|
@@ -4089,12 +4518,12 @@ const _sfc_main$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4089
4518
|
maxDepth: {
|
|
4090
4519
|
type: Number,
|
|
4091
4520
|
required: false,
|
|
4092
|
-
default:
|
|
4521
|
+
default: 16
|
|
4093
4522
|
},
|
|
4094
4523
|
maxNodes: {
|
|
4095
4524
|
type: Number,
|
|
4096
4525
|
required: false,
|
|
4097
|
-
default:
|
|
4526
|
+
default: 600
|
|
4098
4527
|
},
|
|
4099
4528
|
minNodeSize: {
|
|
4100
4529
|
type: Number,
|
|
@@ -4132,54 +4561,91 @@ const _sfc_main$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4132
4561
|
__expose();
|
|
4133
4562
|
const props = __props;
|
|
4134
4563
|
const slots = (0, vue.useSlots)();
|
|
4135
|
-
const
|
|
4136
|
-
const cached = (0, vue.shallowRef)(getCached(resolvedKey.value, props.persist));
|
|
4137
|
-
(0, vue.watch)(resolvedKey, (key) => {
|
|
4138
|
-
cached.value = getCached(key, props.persist);
|
|
4139
|
-
});
|
|
4140
|
-
const wrapperRef = (0, vue.ref)(null);
|
|
4141
|
-
useShapeProbe(() => props.loading ? null : wrapperRef.value, {
|
|
4142
|
-
maxDepth: props.maxDepth,
|
|
4143
|
-
maxNodes: props.maxNodes,
|
|
4144
|
-
minSize: props.minNodeSize,
|
|
4145
|
-
onCapture: (shape) => {
|
|
4146
|
-
setCached(resolvedKey.value, shape, props.persist);
|
|
4147
|
-
cached.value = shape;
|
|
4148
|
-
}
|
|
4149
|
-
});
|
|
4564
|
+
const instanceId = (0, vue.useId)();
|
|
4150
4565
|
const animationClass = (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-block--anim-${props.animation}`);
|
|
4151
|
-
const
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
const
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4566
|
+
const cloneAnimation = (0, vue.computed)(() => props.animation);
|
|
4567
|
+
const mirrorVNodes = (0, vue.computed)(() => props.loading ? slots.default?.() ?? [] : []);
|
|
4568
|
+
const hasContent = (0, vue.computed)(() => mirrorVNodes.value.length > 0);
|
|
4569
|
+
const captureRef = (0, vue.ref)(null);
|
|
4570
|
+
const snapshot = (0, vue.shallowRef)(void 0);
|
|
4571
|
+
const snapshotValid = (0, vue.computed)(() => !!snapshot.value && snapshot.value.width > 0 && snapshot.value.height > 0);
|
|
4572
|
+
const snapshotRenderable = (0, vue.computed)(() => snapshotValid.value && snapshot.value.nodes.length > 0);
|
|
4573
|
+
let captureFrame;
|
|
4574
|
+
let retryAttempts = 0;
|
|
4575
|
+
function takeSnapshot() {
|
|
4576
|
+
if (captureFrame !== void 0) cancelAnimationFrame(captureFrame);
|
|
4577
|
+
retryAttempts = 0;
|
|
4578
|
+
scheduleCapture();
|
|
4579
|
+
}
|
|
4580
|
+
function scheduleCapture() {
|
|
4581
|
+
captureFrame = requestAnimationFrame(() => {
|
|
4582
|
+
captureFrame = requestAnimationFrame(() => {
|
|
4583
|
+
captureFrame = void 0;
|
|
4584
|
+
if (!captureRef.value) return;
|
|
4585
|
+
const result = captureSnapshot(captureRef.value, {
|
|
4586
|
+
maxDepth: props.maxDepth,
|
|
4587
|
+
maxNodes: props.maxNodes,
|
|
4588
|
+
minSize: props.minNodeSize
|
|
4589
|
+
});
|
|
4590
|
+
if (result.width > 0 && result.height > 0) {
|
|
4591
|
+
snapshot.value = result;
|
|
4592
|
+
return;
|
|
4593
|
+
}
|
|
4594
|
+
if (retryAttempts < MAX_RETRY_ATTEMPTS) {
|
|
4595
|
+
retryAttempts++;
|
|
4596
|
+
scheduleCapture();
|
|
4597
|
+
}
|
|
4598
|
+
});
|
|
4163
4599
|
});
|
|
4600
|
+
}
|
|
4601
|
+
(0, vue.watch)(captureRef, (el) => {
|
|
4602
|
+
if (props.mode !== "clone") return;
|
|
4603
|
+
if (el) takeSnapshot();
|
|
4604
|
+
}, { flush: "post" });
|
|
4605
|
+
(0, vue.watch)(() => props.mode, (mode) => {
|
|
4606
|
+
if (mode === "clone" && captureRef.value) takeSnapshot();
|
|
4607
|
+
});
|
|
4608
|
+
(0, vue.watch)(() => props.loading, (next, prev) => {
|
|
4609
|
+
if (props.mode !== "clone") return;
|
|
4610
|
+
if (next && !prev && captureRef.value) takeSnapshot();
|
|
4611
|
+
});
|
|
4612
|
+
(0, vue.onBeforeUnmount)(() => {
|
|
4613
|
+
if (captureFrame !== void 0) cancelAnimationFrame(captureFrame);
|
|
4164
4614
|
});
|
|
4165
|
-
const structuralVNodes = (0, vue.computed)(() => props.loading ? slots.default?.() ?? [] : []);
|
|
4166
4615
|
const __returned__ = {
|
|
4167
4616
|
props,
|
|
4168
4617
|
slots,
|
|
4169
|
-
|
|
4170
|
-
cached,
|
|
4171
|
-
wrapperRef,
|
|
4618
|
+
instanceId,
|
|
4172
4619
|
animationClass,
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4620
|
+
cloneAnimation,
|
|
4621
|
+
mirrorVNodes,
|
|
4622
|
+
hasContent,
|
|
4623
|
+
captureRef,
|
|
4624
|
+
snapshot,
|
|
4625
|
+
snapshotValid,
|
|
4626
|
+
snapshotRenderable,
|
|
4627
|
+
get captureFrame() {
|
|
4628
|
+
return captureFrame;
|
|
4629
|
+
},
|
|
4630
|
+
set captureFrame(v) {
|
|
4631
|
+
captureFrame = v;
|
|
4632
|
+
},
|
|
4633
|
+
get retryAttempts() {
|
|
4634
|
+
return retryAttempts;
|
|
4635
|
+
},
|
|
4636
|
+
set retryAttempts(v) {
|
|
4637
|
+
retryAttempts = v;
|
|
4638
|
+
},
|
|
4639
|
+
MAX_RETRY_ATTEMPTS,
|
|
4640
|
+
takeSnapshot,
|
|
4641
|
+
scheduleCapture,
|
|
4177
4642
|
get cn() {
|
|
4178
4643
|
return cn;
|
|
4179
4644
|
},
|
|
4180
4645
|
get StructuralSkeleton() {
|
|
4181
4646
|
return StructuralSkeleton;
|
|
4182
|
-
}
|
|
4647
|
+
},
|
|
4648
|
+
ASkeletonClone: ASkeletonClone_default
|
|
4183
4649
|
};
|
|
4184
4650
|
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
4185
4651
|
enumerable: false,
|
|
@@ -4188,44 +4654,61 @@ const _sfc_main$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4188
4654
|
return __returned__;
|
|
4189
4655
|
}
|
|
4190
4656
|
});
|
|
4191
|
-
const _hoisted_1 = [
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4657
|
+
const _hoisted_1$5 = [
|
|
4658
|
+
"data-loading",
|
|
4659
|
+
"aria-busy",
|
|
4660
|
+
"aria-live"
|
|
4661
|
+
];
|
|
4662
|
+
const _hoisted_2$1 = ["aria-hidden"];
|
|
4663
|
+
const _hoisted_3$1 = {
|
|
4664
|
+
key: 0,
|
|
4665
|
+
class: "a-skeleton__mirror a-skeleton__mirror--fallback"
|
|
4197
4666
|
};
|
|
4198
|
-
const
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
"aria-busy": "true"
|
|
4667
|
+
const _hoisted_4 = {
|
|
4668
|
+
key: 1,
|
|
4669
|
+
class: "a-skeleton__replay"
|
|
4202
4670
|
};
|
|
4203
|
-
|
|
4671
|
+
const _hoisted_5 = {
|
|
4672
|
+
key: 0,
|
|
4673
|
+
class: "a-skeleton__mirror"
|
|
4674
|
+
};
|
|
4675
|
+
const _hoisted_6 = {
|
|
4676
|
+
key: 1,
|
|
4677
|
+
class: "a-skeleton__fallback"
|
|
4678
|
+
};
|
|
4679
|
+
function _sfc_render$17(_ctx, _cache, $props, $setup, $data, $options) {
|
|
4204
4680
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
"data-loading": $setup.props.loading ? "" : void 0
|
|
4208
|
-
}, [$setup.props.loading ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [(0, vue.createCommentVNode)(" Cache hit: pixel-aligned positioned blocks from a previous measurement.\n Styles are pre-computed during capture so the loop below never calls\n a function or allocates a style object per node. "), $setup.cached ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
4209
|
-
key: 0,
|
|
4210
|
-
class: "a-skeleton__layer",
|
|
4211
|
-
style: (0, vue.normalizeStyle)($setup.layerStyle),
|
|
4681
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skeleton", `a-skeleton--mode-${$setup.props.mode}`, $setup.props.class)),
|
|
4682
|
+
"data-loading": $setup.props.loading ? "" : void 0,
|
|
4212
4683
|
role: "status",
|
|
4213
|
-
"aria-
|
|
4214
|
-
"aria-
|
|
4215
|
-
}, [((0, vue.openBlock)(
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4684
|
+
"aria-busy": $setup.props.loading ? "true" : void 0,
|
|
4685
|
+
"aria-live": $setup.props.loading ? "polite" : void 0
|
|
4686
|
+
}, [$setup.props.mode === "clone" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [
|
|
4687
|
+
(0, vue.createCommentVNode)(" Off-screen capture mount: the slot is always rendered (so we have a\n live target to snapshot from) but visually suppressed while the\n skeleton is showing. `aria-hidden` keeps it out of AT trees too. "),
|
|
4688
|
+
(0, vue.createElementVNode)("div", {
|
|
4689
|
+
ref: "captureRef",
|
|
4690
|
+
class: (0, vue.normalizeClass)(["a-skeleton__capture", $setup.props.loading ? "a-skeleton__capture--hidden" : null]),
|
|
4691
|
+
"aria-hidden": $setup.props.loading ? "true" : void 0
|
|
4692
|
+
}, [(0, vue.renderSlot)(_ctx.$slots, "default", {}, void 0, true)], 10, _hoisted_2$1),
|
|
4693
|
+
(0, vue.createCommentVNode)(" Mirror fallback — **always rendered as a backdrop** while loading,\n regardless of snapshot state. The replay layer below sits on top of\n it (higher z-index). If the snapshot turns out to be empty / zero-\n sized / otherwise unrenderable, the mirror underneath still gives the\n user a structural skeleton instead of a blank wrapper. "),
|
|
4694
|
+
$setup.props.loading && $setup.hasContent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_3$1, [(0, vue.createVNode)($setup["StructuralSkeleton"], {
|
|
4695
|
+
vnodes: $setup.mirrorVNodes,
|
|
4696
|
+
animation: $setup.animationClass,
|
|
4697
|
+
"max-depth": $props.maxDepth,
|
|
4698
|
+
"max-nodes": $props.maxNodes
|
|
4699
|
+
}, null, 8, [
|
|
4700
|
+
"vnodes",
|
|
4701
|
+
"animation",
|
|
4702
|
+
"max-depth",
|
|
4703
|
+
"max-nodes"
|
|
4704
|
+
])])) : (0, vue.createCommentVNode)("v-if", true),
|
|
4705
|
+
(0, vue.createCommentVNode)(" Replay layer — wrapped in a positioning div whose scoped styles\n are GUARANTEED to apply (a child-component root only inherits the\n parent's scope when no inner div is present; placing the absolute-\n positioning on a regular div in the parent template removes that\n ambiguity). Gated on `snapshotValid` (width AND height > 0) AND\n snapshot.nodes.length > 0 so an empty capture doesn't render a\n transparent overlay that masks the mirror underneath. "),
|
|
4706
|
+
$setup.props.loading && $setup.snapshotRenderable ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4, [(0, vue.createVNode)($setup["ASkeletonClone"], {
|
|
4707
|
+
shape: $setup.snapshot,
|
|
4708
|
+
animation: $setup.cloneAnimation
|
|
4709
|
+
}, null, 8, ["shape", "animation"])])) : (0, vue.createCommentVNode)("v-if", true)
|
|
4710
|
+
], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [$setup.props.loading ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [$setup.hasContent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_5, [(0, vue.createVNode)($setup["StructuralSkeleton"], {
|
|
4711
|
+
vnodes: $setup.mirrorVNodes,
|
|
4229
4712
|
animation: $setup.animationClass,
|
|
4230
4713
|
"max-depth": $props.maxDepth,
|
|
4231
4714
|
"max-nodes": $props.maxNodes
|
|
@@ -4234,16 +4717,153 @@ function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
4234
4717
|
"animation",
|
|
4235
4718
|
"max-depth",
|
|
4236
4719
|
"max-nodes"
|
|
4237
|
-
])])
|
|
4720
|
+
])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6, [(0, vue.renderSlot)(_ctx.$slots, "fallback", {}, () => [(0, vue.createElementVNode)("div", { class: (0, vue.normalizeClass)(["a-skel-block a-skel-block--block a-skel-fallback-default", $setup.animationClass]) }, null, 2)], true)]))], 64)) : (0, vue.renderSlot)(_ctx.$slots, "default", { key: 1 }, void 0, true)], 64))], 10, _hoisted_1$5);
|
|
4238
4721
|
}
|
|
4239
|
-
var ASkeleton_default = /* @__PURE__ */ export_helper_default(_sfc_main$
|
|
4240
|
-
["render", _sfc_render$
|
|
4722
|
+
var ASkeleton_default = /* @__PURE__ */ export_helper_default(_sfc_main$17, [
|
|
4723
|
+
["render", _sfc_render$17],
|
|
4241
4724
|
["__scopeId", "data-v-16717541"],
|
|
4242
4725
|
["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/ASkeleton.vue"]
|
|
4243
4726
|
]);
|
|
4244
4727
|
//#endregion
|
|
4728
|
+
//#region src/components/StructuralLayerNode.ts
|
|
4729
|
+
/**
|
|
4730
|
+
* `StructuralLayerNode` — recursive renderer for a `StructuralNode` from a
|
|
4731
|
+
* `StructuralShape` captured by `walkStructural`. Used internally by
|
|
4732
|
+
* `<ASkeletonLayer>`.
|
|
4733
|
+
*
|
|
4734
|
+
* Design:
|
|
4735
|
+
* - **Strategy table** keyed by `node.kind` + `node.leafKind`. Each kind has
|
|
4736
|
+
* its own renderer function. Adding a new kind is one entry in `RENDERERS`
|
|
4737
|
+
* (Open/Closed) — no edits elsewhere.
|
|
4738
|
+
* - **Pure render function**: no reactive state of its own; everything flows
|
|
4739
|
+
* from props.
|
|
4740
|
+
* - **Container preservation**: container nodes emit `<{node.tag}
|
|
4741
|
+
* :class="node.className" :style="node.style">…children…</…>`. The
|
|
4742
|
+
* captured `style` carries comprehensive layout + visual CSS so the
|
|
4743
|
+
* skeleton reads correctly even when the consumer's stylesheet isn't
|
|
4744
|
+
* loaded at the mount point.
|
|
4745
|
+
* - **Leaves**: render `<div class="a-skel + originalClass" :style="style">`.
|
|
4746
|
+
* The original `class` is preserved so utility-first CSS (`mt-4`,
|
|
4747
|
+
* `flex-1`, `inline-block`, …) survives; the captured style carries the
|
|
4748
|
+
* same layout + visual properties used on containers plus `width` +
|
|
4749
|
+
* `height` so the placeholder claims the right space.
|
|
4750
|
+
*/
|
|
4751
|
+
const LEAF_RENDERERS = {
|
|
4752
|
+
block: renderLeafBlock,
|
|
4753
|
+
media: renderLeafBlock,
|
|
4754
|
+
text: renderLeafText,
|
|
4755
|
+
image: (node, animClass) => renderLeafWithIcon(node, animClass, "image")
|
|
4756
|
+
};
|
|
4757
|
+
function renderContainer(node, animClass) {
|
|
4758
|
+
return (0, vue.h)(node.tag, {
|
|
4759
|
+
class: node.className || void 0,
|
|
4760
|
+
style: node.style,
|
|
4761
|
+
"aria-hidden": "true"
|
|
4762
|
+
}, node.children.map((child) => (0, vue.h)(StructuralLayerNode, {
|
|
4763
|
+
node: child,
|
|
4764
|
+
animClass
|
|
4765
|
+
})));
|
|
4766
|
+
}
|
|
4767
|
+
/**
|
|
4768
|
+
* Build the leaf's `class` payload. Always includes `a-skel` (skeleton
|
|
4769
|
+
* surface) + the animation class; the original consumer class trails so it
|
|
4770
|
+
* survives the cascade alongside utility-first frameworks.
|
|
4771
|
+
*/
|
|
4772
|
+
function leafClass(node, animClass, extra) {
|
|
4773
|
+
const classes = ["a-skel"];
|
|
4774
|
+
if (extra) classes.push(extra);
|
|
4775
|
+
if (animClass) classes.push(animClass);
|
|
4776
|
+
if (node.className) classes.push(node.className);
|
|
4777
|
+
return classes;
|
|
4778
|
+
}
|
|
4779
|
+
function renderLeafBlock(node, animClass) {
|
|
4780
|
+
return (0, vue.h)("div", {
|
|
4781
|
+
class: leafClass(node, animClass),
|
|
4782
|
+
style: node.style,
|
|
4783
|
+
"aria-hidden": "true"
|
|
4784
|
+
});
|
|
4785
|
+
}
|
|
4786
|
+
/**
|
|
4787
|
+
* Text leaf — host carries the captured layout + visual surface (including
|
|
4788
|
+
* the leaf's `width` / `height` so it claims the right space in its parent's
|
|
4789
|
+
* layout). Each captured per-line rect renders as one bar inside the host,
|
|
4790
|
+
* absolutely positioned at the captured rect (rects are leaf-relative; the
|
|
4791
|
+
* host's captured style includes `position: relative` via the `.a-skel-text-host`
|
|
4792
|
+
* default — see styles.src.css).
|
|
4793
|
+
*
|
|
4794
|
+
* When `textLines` is absent (Range API unusable), the leaf renders as a
|
|
4795
|
+
* single shimmer bar at the host's bounds — same as a leaf-block.
|
|
4796
|
+
*/
|
|
4797
|
+
function renderLeafText(node, animClass) {
|
|
4798
|
+
const lines = node.textLines;
|
|
4799
|
+
if (!lines || lines.length === 0) return (0, vue.h)("div", {
|
|
4800
|
+
class: leafClass(node, animClass, "a-skel--text"),
|
|
4801
|
+
style: node.style,
|
|
4802
|
+
"aria-hidden": "true"
|
|
4803
|
+
});
|
|
4804
|
+
return (0, vue.h)("div", {
|
|
4805
|
+
class: ["a-skel-text-host", node.className || void 0],
|
|
4806
|
+
style: node.style,
|
|
4807
|
+
"aria-hidden": "true"
|
|
4808
|
+
}, lines.map((line) => (0, vue.h)("div", {
|
|
4809
|
+
class: [
|
|
4810
|
+
"a-skel",
|
|
4811
|
+
"a-skel--text-line",
|
|
4812
|
+
animClass
|
|
4813
|
+
],
|
|
4814
|
+
style: {
|
|
4815
|
+
position: "absolute",
|
|
4816
|
+
left: `${line.x}px`,
|
|
4817
|
+
top: `${line.y}px`,
|
|
4818
|
+
width: `${line.w}px`,
|
|
4819
|
+
height: `${line.h}px`
|
|
4820
|
+
}
|
|
4821
|
+
})));
|
|
4822
|
+
}
|
|
4823
|
+
function renderLeafWithIcon(node, animClass, kind) {
|
|
4824
|
+
return (0, vue.h)("div", {
|
|
4825
|
+
class: leafClass(node, animClass, "a-skel--with-icon"),
|
|
4826
|
+
style: node.style,
|
|
4827
|
+
"aria-hidden": "true"
|
|
4828
|
+
}, [kind === "image" ? renderImageIcon() : renderPlayIcon()]);
|
|
4829
|
+
}
|
|
4830
|
+
const RENDERERS = {
|
|
4831
|
+
container: (ctx) => renderContainer(ctx.node, ctx.animClass),
|
|
4832
|
+
leaf: (ctx) => {
|
|
4833
|
+
const leaf = ctx.node;
|
|
4834
|
+
return LEAF_RENDERERS[leaf.leafKind](leaf, ctx.animClass);
|
|
4835
|
+
}
|
|
4836
|
+
};
|
|
4837
|
+
const StructuralLayerNode = (0, vue.defineComponent)({
|
|
4838
|
+
name: "StructuralLayerNode",
|
|
4839
|
+
props: {
|
|
4840
|
+
node: {
|
|
4841
|
+
type: Object,
|
|
4842
|
+
required: true
|
|
4843
|
+
},
|
|
4844
|
+
animClass: {
|
|
4845
|
+
type: [String, null],
|
|
4846
|
+
default: null
|
|
4847
|
+
}
|
|
4848
|
+
},
|
|
4849
|
+
setup(props) {
|
|
4850
|
+
return () => RENDERERS[props.node.kind]({
|
|
4851
|
+
node: props.node,
|
|
4852
|
+
animClass: props.animClass
|
|
4853
|
+
});
|
|
4854
|
+
}
|
|
4855
|
+
});
|
|
4856
|
+
//#endregion
|
|
4245
4857
|
//#region src/components/ASkeletonLayer.vue
|
|
4246
|
-
|
|
4858
|
+
/**
|
|
4859
|
+
* `<ASkeletonLayer>` — replays a `StructuralShape` produced by
|
|
4860
|
+
* `walkStructural` / `useSkeleton()` as a tree of preserved containers +
|
|
4861
|
+
* a-skel leaf placeholders. The layer is a transparent shell with no
|
|
4862
|
+
* width / height / position constraints, so it drops into the consumer's
|
|
4863
|
+
* own container and lets the captured tree's flex/grid/spacing dictate
|
|
4864
|
+
* how the skeleton lays itself out.
|
|
4865
|
+
*/
|
|
4866
|
+
const _sfc_main$16 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
4247
4867
|
__name: "ASkeletonLayer",
|
|
4248
4868
|
props: {
|
|
4249
4869
|
shape: {
|
|
@@ -4267,176 +4887,2366 @@ const _sfc_main$1 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4267
4887
|
skipCheck: true
|
|
4268
4888
|
}
|
|
4269
4889
|
},
|
|
4890
|
+
setup(__props, { expose: __expose }) {
|
|
4891
|
+
__expose();
|
|
4892
|
+
const props = __props;
|
|
4893
|
+
const __returned__ = {
|
|
4894
|
+
props,
|
|
4895
|
+
animationClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
4896
|
+
get cn() {
|
|
4897
|
+
return cn;
|
|
4898
|
+
},
|
|
4899
|
+
get StructuralLayerNode() {
|
|
4900
|
+
return StructuralLayerNode;
|
|
4901
|
+
}
|
|
4902
|
+
};
|
|
4903
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
4904
|
+
enumerable: false,
|
|
4905
|
+
value: true
|
|
4906
|
+
});
|
|
4907
|
+
return __returned__;
|
|
4908
|
+
}
|
|
4909
|
+
});
|
|
4910
|
+
function _sfc_render$16(_ctx, _cache, $props, $setup, $data, $options) {
|
|
4911
|
+
return $props.shape ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
4912
|
+
key: 0,
|
|
4913
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skeleton__layer", $setup.props.class)),
|
|
4914
|
+
role: "status",
|
|
4915
|
+
"aria-live": "polite",
|
|
4916
|
+
"aria-busy": "true"
|
|
4917
|
+
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)($props.shape.nodes, (node, idx) => {
|
|
4918
|
+
return (0, vue.openBlock)(), (0, vue.createBlock)($setup["StructuralLayerNode"], {
|
|
4919
|
+
key: idx,
|
|
4920
|
+
node,
|
|
4921
|
+
"anim-class": $setup.animationClass
|
|
4922
|
+
}, null, 8, ["node", "anim-class"]);
|
|
4923
|
+
}), 128)), _cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading…", -1))], 2)) : (0, vue.createCommentVNode)("v-if", true);
|
|
4924
|
+
}
|
|
4925
|
+
var ASkeletonLayer_default = /* @__PURE__ */ export_helper_default(_sfc_main$16, [["render", _sfc_render$16], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/ASkeletonLayer.vue"]]);
|
|
4926
|
+
//#endregion
|
|
4927
|
+
//#region src/components/ASkeletonBlock.vue
|
|
4928
|
+
const _sfc_main$15 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
4929
|
+
__name: "ASkeletonBlock",
|
|
4930
|
+
props: {
|
|
4931
|
+
type: {
|
|
4932
|
+
type: String,
|
|
4933
|
+
required: false,
|
|
4934
|
+
default: "block"
|
|
4935
|
+
},
|
|
4936
|
+
w: {
|
|
4937
|
+
type: [Number, String],
|
|
4938
|
+
required: false
|
|
4939
|
+
},
|
|
4940
|
+
h: {
|
|
4941
|
+
type: [Number, String],
|
|
4942
|
+
required: false
|
|
4943
|
+
},
|
|
4944
|
+
radius: {
|
|
4945
|
+
type: [Number, String],
|
|
4946
|
+
required: false
|
|
4947
|
+
},
|
|
4948
|
+
lines: {
|
|
4949
|
+
type: Number,
|
|
4950
|
+
required: false,
|
|
4951
|
+
default: 1
|
|
4952
|
+
},
|
|
4953
|
+
animation: {
|
|
4954
|
+
type: String,
|
|
4955
|
+
required: false,
|
|
4956
|
+
default: "shimmer"
|
|
4957
|
+
},
|
|
4958
|
+
class: {
|
|
4959
|
+
type: [
|
|
4960
|
+
Boolean,
|
|
4961
|
+
null,
|
|
4962
|
+
String,
|
|
4963
|
+
Object,
|
|
4964
|
+
Array
|
|
4965
|
+
],
|
|
4966
|
+
required: false,
|
|
4967
|
+
skipCheck: true
|
|
4968
|
+
}
|
|
4969
|
+
},
|
|
4270
4970
|
setup(__props, { expose: __expose }) {
|
|
4271
4971
|
__expose();
|
|
4272
4972
|
const props = __props;
|
|
4273
4973
|
const animationClass = (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-block--anim-${props.animation}`);
|
|
4974
|
+
const blockClass = (0, vue.computed)(() => [
|
|
4975
|
+
"a-skel-block",
|
|
4976
|
+
`a-skel-block--${props.type}`,
|
|
4977
|
+
animationClass.value
|
|
4978
|
+
]);
|
|
4979
|
+
function toLength(v) {
|
|
4980
|
+
if (v === void 0) return void 0;
|
|
4981
|
+
return typeof v === "number" ? `${v}px` : v;
|
|
4982
|
+
}
|
|
4983
|
+
const radiusValue = (0, vue.computed)(() => props.type === "circle" && props.radius === void 0 ? "50%" : toLength(props.radius));
|
|
4274
4984
|
const __returned__ = {
|
|
4275
4985
|
props,
|
|
4276
4986
|
animationClass,
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
text: `a-skel-block a-skel-block--text${suffix}`,
|
|
4287
|
-
image: `a-skel-block a-skel-block--image${suffix}`,
|
|
4288
|
-
circle: `a-skel-block a-skel-block--circle${suffix}`
|
|
4289
|
-
});
|
|
4290
|
-
}),
|
|
4987
|
+
blockClass,
|
|
4988
|
+
toLength,
|
|
4989
|
+
radiusValue,
|
|
4990
|
+
blockStyle: (0, vue.computed)(() => ({
|
|
4991
|
+
width: toLength(props.w),
|
|
4992
|
+
height: toLength(props.h),
|
|
4993
|
+
borderRadius: radiusValue.value
|
|
4994
|
+
})),
|
|
4995
|
+
isMultiLineText: (0, vue.computed)(() => props.type === "text" && props.lines > 1),
|
|
4291
4996
|
get cn() {
|
|
4292
4997
|
return cn;
|
|
4293
4998
|
}
|
|
4294
|
-
};
|
|
4295
|
-
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
4296
|
-
enumerable: false,
|
|
4297
|
-
value: true
|
|
4298
|
-
});
|
|
4299
|
-
return __returned__;
|
|
4999
|
+
};
|
|
5000
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5001
|
+
enumerable: false,
|
|
5002
|
+
value: true
|
|
5003
|
+
});
|
|
5004
|
+
return __returned__;
|
|
5005
|
+
}
|
|
5006
|
+
});
|
|
5007
|
+
function _sfc_render$15(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5008
|
+
return $setup.isMultiLineText ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5009
|
+
key: 0,
|
|
5010
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel-block-stack", $setup.props.class)),
|
|
5011
|
+
role: "status",
|
|
5012
|
+
"aria-busy": "true"
|
|
5013
|
+
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)($setup.props.lines, (i) => {
|
|
5014
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5015
|
+
key: i,
|
|
5016
|
+
class: (0, vue.normalizeClass)($setup.blockClass),
|
|
5017
|
+
style: (0, vue.normalizeStyle)({
|
|
5018
|
+
height: $setup.blockStyle.height ?? "0.75em",
|
|
5019
|
+
width: i === $setup.props.lines ? "70%" : "100%",
|
|
5020
|
+
borderRadius: $setup.blockStyle.borderRadius ?? "4px"
|
|
5021
|
+
})
|
|
5022
|
+
}, null, 6);
|
|
5023
|
+
}), 128))], 2)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5024
|
+
key: 1,
|
|
5025
|
+
class: (0, vue.normalizeClass)($setup.cn($setup.blockClass, $setup.props.class)),
|
|
5026
|
+
style: (0, vue.normalizeStyle)($setup.blockStyle),
|
|
5027
|
+
role: "status",
|
|
5028
|
+
"aria-busy": "true"
|
|
5029
|
+
}, null, 6));
|
|
5030
|
+
}
|
|
5031
|
+
var ASkeletonBlock_default = /* @__PURE__ */ export_helper_default(_sfc_main$15, [
|
|
5032
|
+
["render", _sfc_render$15],
|
|
5033
|
+
["__scopeId", "data-v-bdfba69a"],
|
|
5034
|
+
["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/ASkeletonBlock.vue"]
|
|
5035
|
+
]);
|
|
5036
|
+
//#endregion
|
|
5037
|
+
//#region src/components/variants/ASkeletonText.vue
|
|
5038
|
+
const _sfc_main$14 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5039
|
+
__name: "ASkeletonText",
|
|
5040
|
+
props: {
|
|
5041
|
+
lines: {
|
|
5042
|
+
type: Number,
|
|
5043
|
+
required: false,
|
|
5044
|
+
default: 1
|
|
5045
|
+
},
|
|
5046
|
+
width: {
|
|
5047
|
+
type: [Number, String],
|
|
5048
|
+
required: false
|
|
5049
|
+
},
|
|
5050
|
+
animation: {
|
|
5051
|
+
type: String,
|
|
5052
|
+
required: false,
|
|
5053
|
+
default: "pulse"
|
|
5054
|
+
},
|
|
5055
|
+
class: {
|
|
5056
|
+
type: [
|
|
5057
|
+
Boolean,
|
|
5058
|
+
null,
|
|
5059
|
+
String,
|
|
5060
|
+
Object,
|
|
5061
|
+
Array
|
|
5062
|
+
],
|
|
5063
|
+
required: false,
|
|
5064
|
+
skipCheck: true
|
|
5065
|
+
}
|
|
5066
|
+
},
|
|
5067
|
+
setup(__props, { expose: __expose }) {
|
|
5068
|
+
__expose();
|
|
5069
|
+
const props = __props;
|
|
5070
|
+
const animClass = (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`);
|
|
5071
|
+
const rootStyle = (0, vue.computed)(() => props.width !== void 0 ? { width: typeof props.width === "number" ? `${props.width}px` : String(props.width) } : void 0);
|
|
5072
|
+
function widthForLine(i) {
|
|
5073
|
+
if (i === props.lines - 1 && props.lines > 1) return "65%";
|
|
5074
|
+
const palette = [
|
|
5075
|
+
"100%",
|
|
5076
|
+
"92%",
|
|
5077
|
+
"88%",
|
|
5078
|
+
"95%"
|
|
5079
|
+
];
|
|
5080
|
+
return palette[i % palette.length];
|
|
5081
|
+
}
|
|
5082
|
+
const __returned__ = {
|
|
5083
|
+
props,
|
|
5084
|
+
animClass,
|
|
5085
|
+
rootStyle,
|
|
5086
|
+
widthForLine,
|
|
5087
|
+
get cn() {
|
|
5088
|
+
return cn;
|
|
5089
|
+
}
|
|
5090
|
+
};
|
|
5091
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5092
|
+
enumerable: false,
|
|
5093
|
+
value: true
|
|
5094
|
+
});
|
|
5095
|
+
return __returned__;
|
|
5096
|
+
}
|
|
5097
|
+
});
|
|
5098
|
+
function _sfc_render$14(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5099
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5100
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel-variants-text", $setup.props.class)),
|
|
5101
|
+
style: (0, vue.normalizeStyle)($setup.rootStyle),
|
|
5102
|
+
role: "status",
|
|
5103
|
+
"aria-live": "polite",
|
|
5104
|
+
"aria-busy": "true"
|
|
5105
|
+
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)($setup.props.lines, (i) => {
|
|
5106
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5107
|
+
key: i,
|
|
5108
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-text", $setup.animClass)),
|
|
5109
|
+
style: (0, vue.normalizeStyle)({
|
|
5110
|
+
width: $setup.widthForLine(i - 1),
|
|
5111
|
+
marginTop: i > 1 ? "0.5em" : void 0
|
|
5112
|
+
})
|
|
5113
|
+
}, null, 6);
|
|
5114
|
+
}), 128)), _cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading…", -1))], 6);
|
|
5115
|
+
}
|
|
5116
|
+
var ASkeletonText_default = /* @__PURE__ */ export_helper_default(_sfc_main$14, [["render", _sfc_render$14], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonText.vue"]]);
|
|
5117
|
+
//#endregion
|
|
5118
|
+
//#region src/components/variants/ASkeletonHeading.vue
|
|
5119
|
+
const _sfc_main$13 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5120
|
+
__name: "ASkeletonHeading",
|
|
5121
|
+
props: {
|
|
5122
|
+
level: {
|
|
5123
|
+
type: Number,
|
|
5124
|
+
required: false,
|
|
5125
|
+
default: 2
|
|
5126
|
+
},
|
|
5127
|
+
width: {
|
|
5128
|
+
type: [Number, String],
|
|
5129
|
+
required: false
|
|
5130
|
+
},
|
|
5131
|
+
animation: {
|
|
5132
|
+
type: String,
|
|
5133
|
+
required: false,
|
|
5134
|
+
default: "pulse"
|
|
5135
|
+
},
|
|
5136
|
+
class: {
|
|
5137
|
+
type: [
|
|
5138
|
+
Boolean,
|
|
5139
|
+
null,
|
|
5140
|
+
String,
|
|
5141
|
+
Object,
|
|
5142
|
+
Array
|
|
5143
|
+
],
|
|
5144
|
+
required: false,
|
|
5145
|
+
skipCheck: true
|
|
5146
|
+
}
|
|
5147
|
+
},
|
|
5148
|
+
setup(__props, { expose: __expose }) {
|
|
5149
|
+
__expose();
|
|
5150
|
+
const props = __props;
|
|
5151
|
+
const animClass = (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`);
|
|
5152
|
+
const HEIGHT_BY_LEVEL = {
|
|
5153
|
+
1: "2.25rem",
|
|
5154
|
+
2: "1.75rem",
|
|
5155
|
+
3: "1.5rem",
|
|
5156
|
+
4: "1.25rem",
|
|
5157
|
+
5: "1.125rem",
|
|
5158
|
+
6: "1rem"
|
|
5159
|
+
};
|
|
5160
|
+
const __returned__ = {
|
|
5161
|
+
props,
|
|
5162
|
+
animClass,
|
|
5163
|
+
HEIGHT_BY_LEVEL,
|
|
5164
|
+
rootStyle: (0, vue.computed)(() => ({
|
|
5165
|
+
width: props.width !== void 0 ? typeof props.width === "number" ? `${props.width}px` : String(props.width) : "60%",
|
|
5166
|
+
height: HEIGHT_BY_LEVEL[props.level]
|
|
5167
|
+
})),
|
|
5168
|
+
get cn() {
|
|
5169
|
+
return cn;
|
|
5170
|
+
}
|
|
5171
|
+
};
|
|
5172
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5173
|
+
enumerable: false,
|
|
5174
|
+
value: true
|
|
5175
|
+
});
|
|
5176
|
+
return __returned__;
|
|
5177
|
+
}
|
|
5178
|
+
});
|
|
5179
|
+
function _sfc_render$13(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5180
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5181
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-heading", $setup.animClass, $setup.props.class)),
|
|
5182
|
+
style: (0, vue.normalizeStyle)($setup.rootStyle),
|
|
5183
|
+
role: "status",
|
|
5184
|
+
"aria-busy": "true"
|
|
5185
|
+
}, [..._cache[0] || (_cache[0] = [(0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading heading…", -1)])], 6);
|
|
5186
|
+
}
|
|
5187
|
+
var ASkeletonHeading_default = /* @__PURE__ */ export_helper_default(_sfc_main$13, [["render", _sfc_render$13], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonHeading.vue"]]);
|
|
5188
|
+
//#endregion
|
|
5189
|
+
//#region src/components/variants/ASkeletonAvatar.vue
|
|
5190
|
+
const _sfc_main$12 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5191
|
+
__name: "ASkeletonAvatar",
|
|
5192
|
+
props: {
|
|
5193
|
+
size: {
|
|
5194
|
+
type: [Number, String],
|
|
5195
|
+
required: false,
|
|
5196
|
+
default: 48
|
|
5197
|
+
},
|
|
5198
|
+
shape: {
|
|
5199
|
+
type: String,
|
|
5200
|
+
required: false,
|
|
5201
|
+
default: "circle"
|
|
5202
|
+
},
|
|
5203
|
+
animation: {
|
|
5204
|
+
type: String,
|
|
5205
|
+
required: false,
|
|
5206
|
+
default: "pulse"
|
|
5207
|
+
},
|
|
5208
|
+
class: {
|
|
5209
|
+
type: [
|
|
5210
|
+
Boolean,
|
|
5211
|
+
null,
|
|
5212
|
+
String,
|
|
5213
|
+
Object,
|
|
5214
|
+
Array
|
|
5215
|
+
],
|
|
5216
|
+
required: false,
|
|
5217
|
+
skipCheck: true
|
|
5218
|
+
}
|
|
5219
|
+
},
|
|
5220
|
+
setup(__props, { expose: __expose }) {
|
|
5221
|
+
__expose();
|
|
5222
|
+
const props = __props;
|
|
5223
|
+
const __returned__ = {
|
|
5224
|
+
props,
|
|
5225
|
+
animClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
5226
|
+
sizeStyle: (0, vue.computed)(() => {
|
|
5227
|
+
const s = typeof props.size === "number" ? `${props.size}px` : String(props.size);
|
|
5228
|
+
return {
|
|
5229
|
+
width: s,
|
|
5230
|
+
height: s,
|
|
5231
|
+
borderRadius: props.shape === "circle" ? "9999px" : props.shape === "rounded" ? "var(--ak-skel-radius)" : "0"
|
|
5232
|
+
};
|
|
5233
|
+
}),
|
|
5234
|
+
get cn() {
|
|
5235
|
+
return cn;
|
|
5236
|
+
}
|
|
5237
|
+
};
|
|
5238
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5239
|
+
enumerable: false,
|
|
5240
|
+
value: true
|
|
5241
|
+
});
|
|
5242
|
+
return __returned__;
|
|
5243
|
+
}
|
|
5244
|
+
});
|
|
5245
|
+
function _sfc_render$12(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5246
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5247
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-avatar", $setup.animClass, $setup.props.class)),
|
|
5248
|
+
style: (0, vue.normalizeStyle)($setup.sizeStyle),
|
|
5249
|
+
role: "status",
|
|
5250
|
+
"aria-busy": "true"
|
|
5251
|
+
}, [..._cache[0] || (_cache[0] = [(0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading avatar…", -1)])], 6);
|
|
5252
|
+
}
|
|
5253
|
+
var ASkeletonAvatar_default = /* @__PURE__ */ export_helper_default(_sfc_main$12, [["render", _sfc_render$12], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonAvatar.vue"]]);
|
|
5254
|
+
//#endregion
|
|
5255
|
+
//#region src/components/variants/ASkeletonImage.vue
|
|
5256
|
+
const _sfc_main$11 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5257
|
+
__name: "ASkeletonImage",
|
|
5258
|
+
props: {
|
|
5259
|
+
ratio: {
|
|
5260
|
+
type: String,
|
|
5261
|
+
required: false,
|
|
5262
|
+
default: "16 / 9"
|
|
5263
|
+
},
|
|
5264
|
+
width: {
|
|
5265
|
+
type: [Number, String],
|
|
5266
|
+
required: false
|
|
5267
|
+
},
|
|
5268
|
+
height: {
|
|
5269
|
+
type: [Number, String],
|
|
5270
|
+
required: false
|
|
5271
|
+
},
|
|
5272
|
+
showIcon: {
|
|
5273
|
+
type: Boolean,
|
|
5274
|
+
required: false,
|
|
5275
|
+
default: true
|
|
5276
|
+
},
|
|
5277
|
+
animation: {
|
|
5278
|
+
type: String,
|
|
5279
|
+
required: false,
|
|
5280
|
+
default: "pulse"
|
|
5281
|
+
},
|
|
5282
|
+
class: {
|
|
5283
|
+
type: [
|
|
5284
|
+
Boolean,
|
|
5285
|
+
null,
|
|
5286
|
+
String,
|
|
5287
|
+
Object,
|
|
5288
|
+
Array
|
|
5289
|
+
],
|
|
5290
|
+
required: false,
|
|
5291
|
+
skipCheck: true
|
|
5292
|
+
}
|
|
5293
|
+
},
|
|
5294
|
+
setup(__props, { expose: __expose }) {
|
|
5295
|
+
__expose();
|
|
5296
|
+
const props = __props;
|
|
5297
|
+
const __returned__ = {
|
|
5298
|
+
props,
|
|
5299
|
+
animClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
5300
|
+
rootStyle: (0, vue.computed)(() => {
|
|
5301
|
+
const s = {};
|
|
5302
|
+
if (props.ratio) s.aspectRatio = props.ratio;
|
|
5303
|
+
if (props.width !== void 0) s.width = typeof props.width === "number" ? `${props.width}px` : String(props.width);
|
|
5304
|
+
if (props.height !== void 0) s.height = typeof props.height === "number" ? `${props.height}px` : String(props.height);
|
|
5305
|
+
return s;
|
|
5306
|
+
}),
|
|
5307
|
+
get cn() {
|
|
5308
|
+
return cn;
|
|
5309
|
+
}
|
|
5310
|
+
};
|
|
5311
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5312
|
+
enumerable: false,
|
|
5313
|
+
value: true
|
|
5314
|
+
});
|
|
5315
|
+
return __returned__;
|
|
5316
|
+
}
|
|
5317
|
+
});
|
|
5318
|
+
const _hoisted_1$4 = {
|
|
5319
|
+
key: 0,
|
|
5320
|
+
class: "size-10",
|
|
5321
|
+
viewBox: "0 0 24 24",
|
|
5322
|
+
fill: "none",
|
|
5323
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
5324
|
+
"aria-hidden": "true"
|
|
5325
|
+
};
|
|
5326
|
+
function _sfc_render$11(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5327
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5328
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-image", $setup.animClass, $setup.props.class)),
|
|
5329
|
+
style: (0, vue.normalizeStyle)($setup.rootStyle),
|
|
5330
|
+
role: "status",
|
|
5331
|
+
"aria-busy": "true"
|
|
5332
|
+
}, [$setup.props.showIcon ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_1$4, [..._cache[0] || (_cache[0] = [(0, vue.createElementVNode)("path", {
|
|
5333
|
+
d: "M19 5H5a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2Zm-3.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM19 17H5l3.5-4.5 2.5 3 3.5-4.5L19 17Z",
|
|
5334
|
+
fill: "currentColor"
|
|
5335
|
+
}, null, -1)])])) : (0, vue.createCommentVNode)("v-if", true), _cache[1] || (_cache[1] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading image…", -1))], 6);
|
|
5336
|
+
}
|
|
5337
|
+
var ASkeletonImage_default = /* @__PURE__ */ export_helper_default(_sfc_main$11, [["render", _sfc_render$11], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonImage.vue"]]);
|
|
5338
|
+
//#endregion
|
|
5339
|
+
//#region src/components/variants/ASkeletonVideo.vue
|
|
5340
|
+
const _sfc_main$10 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5341
|
+
__name: "ASkeletonVideo",
|
|
5342
|
+
props: {
|
|
5343
|
+
ratio: {
|
|
5344
|
+
type: String,
|
|
5345
|
+
required: false,
|
|
5346
|
+
default: "16 / 9"
|
|
5347
|
+
},
|
|
5348
|
+
width: {
|
|
5349
|
+
type: [Number, String],
|
|
5350
|
+
required: false
|
|
5351
|
+
},
|
|
5352
|
+
height: {
|
|
5353
|
+
type: [Number, String],
|
|
5354
|
+
required: false
|
|
5355
|
+
},
|
|
5356
|
+
showIcon: {
|
|
5357
|
+
type: Boolean,
|
|
5358
|
+
required: false,
|
|
5359
|
+
default: true
|
|
5360
|
+
},
|
|
5361
|
+
animation: {
|
|
5362
|
+
type: String,
|
|
5363
|
+
required: false,
|
|
5364
|
+
default: "pulse"
|
|
5365
|
+
},
|
|
5366
|
+
class: {
|
|
5367
|
+
type: [
|
|
5368
|
+
Boolean,
|
|
5369
|
+
null,
|
|
5370
|
+
String,
|
|
5371
|
+
Object,
|
|
5372
|
+
Array
|
|
5373
|
+
],
|
|
5374
|
+
required: false,
|
|
5375
|
+
skipCheck: true
|
|
5376
|
+
}
|
|
5377
|
+
},
|
|
5378
|
+
setup(__props, { expose: __expose }) {
|
|
5379
|
+
__expose();
|
|
5380
|
+
const props = __props;
|
|
5381
|
+
const __returned__ = {
|
|
5382
|
+
props,
|
|
5383
|
+
animClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
5384
|
+
rootStyle: (0, vue.computed)(() => {
|
|
5385
|
+
const s = {};
|
|
5386
|
+
if (props.ratio) s.aspectRatio = props.ratio;
|
|
5387
|
+
if (props.width !== void 0) s.width = typeof props.width === "number" ? `${props.width}px` : String(props.width);
|
|
5388
|
+
if (props.height !== void 0) s.height = typeof props.height === "number" ? `${props.height}px` : String(props.height);
|
|
5389
|
+
return s;
|
|
5390
|
+
}),
|
|
5391
|
+
get cn() {
|
|
5392
|
+
return cn;
|
|
5393
|
+
}
|
|
5394
|
+
};
|
|
5395
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5396
|
+
enumerable: false,
|
|
5397
|
+
value: true
|
|
5398
|
+
});
|
|
5399
|
+
return __returned__;
|
|
5400
|
+
}
|
|
5401
|
+
});
|
|
5402
|
+
const _hoisted_1$3 = {
|
|
5403
|
+
key: 0,
|
|
5404
|
+
class: "size-12",
|
|
5405
|
+
viewBox: "0 0 24 24",
|
|
5406
|
+
fill: "none",
|
|
5407
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
5408
|
+
"aria-hidden": "true"
|
|
5409
|
+
};
|
|
5410
|
+
function _sfc_render$10(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5411
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5412
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-video", $setup.animClass, $setup.props.class)),
|
|
5413
|
+
style: (0, vue.normalizeStyle)($setup.rootStyle),
|
|
5414
|
+
role: "status",
|
|
5415
|
+
"aria-busy": "true"
|
|
5416
|
+
}, [$setup.props.showIcon ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_1$3, [..._cache[0] || (_cache[0] = [(0, vue.createElementVNode)("circle", {
|
|
5417
|
+
cx: "12",
|
|
5418
|
+
cy: "12",
|
|
5419
|
+
r: "10",
|
|
5420
|
+
stroke: "currentColor",
|
|
5421
|
+
"stroke-width": "1.5"
|
|
5422
|
+
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
5423
|
+
d: "M10 8l6 4-6 4V8Z",
|
|
5424
|
+
fill: "currentColor"
|
|
5425
|
+
}, null, -1)])])) : (0, vue.createCommentVNode)("v-if", true), _cache[1] || (_cache[1] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading video…", -1))], 6);
|
|
5426
|
+
}
|
|
5427
|
+
var ASkeletonVideo_default = /* @__PURE__ */ export_helper_default(_sfc_main$10, [["render", _sfc_render$10], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonVideo.vue"]]);
|
|
5428
|
+
//#endregion
|
|
5429
|
+
//#region src/components/variants/ASkeletonButton.vue
|
|
5430
|
+
const _sfc_main$9 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5431
|
+
__name: "ASkeletonButton",
|
|
5432
|
+
props: {
|
|
5433
|
+
width: {
|
|
5434
|
+
type: [Number, String],
|
|
5435
|
+
required: false,
|
|
5436
|
+
default: 120
|
|
5437
|
+
},
|
|
5438
|
+
height: {
|
|
5439
|
+
type: [Number, String],
|
|
5440
|
+
required: false,
|
|
5441
|
+
default: 40
|
|
5442
|
+
},
|
|
5443
|
+
outlined: {
|
|
5444
|
+
type: Boolean,
|
|
5445
|
+
required: false
|
|
5446
|
+
},
|
|
5447
|
+
animation: {
|
|
5448
|
+
type: String,
|
|
5449
|
+
required: false,
|
|
5450
|
+
default: "pulse"
|
|
5451
|
+
},
|
|
5452
|
+
class: {
|
|
5453
|
+
type: [
|
|
5454
|
+
Boolean,
|
|
5455
|
+
null,
|
|
5456
|
+
String,
|
|
5457
|
+
Object,
|
|
5458
|
+
Array
|
|
5459
|
+
],
|
|
5460
|
+
required: false,
|
|
5461
|
+
skipCheck: true
|
|
5462
|
+
}
|
|
5463
|
+
},
|
|
5464
|
+
setup(__props, { expose: __expose }) {
|
|
5465
|
+
__expose();
|
|
5466
|
+
const props = __props;
|
|
5467
|
+
const __returned__ = {
|
|
5468
|
+
props,
|
|
5469
|
+
animClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
5470
|
+
rootStyle: (0, vue.computed)(() => ({
|
|
5471
|
+
width: typeof props.width === "number" ? `${props.width}px` : String(props.width),
|
|
5472
|
+
height: typeof props.height === "number" ? `${props.height}px` : String(props.height),
|
|
5473
|
+
...props.outlined ? {
|
|
5474
|
+
backgroundColor: "transparent",
|
|
5475
|
+
border: "1px solid var(--ak-skel-ring)"
|
|
5476
|
+
} : {}
|
|
5477
|
+
})),
|
|
5478
|
+
get cn() {
|
|
5479
|
+
return cn;
|
|
5480
|
+
}
|
|
5481
|
+
};
|
|
5482
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5483
|
+
enumerable: false,
|
|
5484
|
+
value: true
|
|
5485
|
+
});
|
|
5486
|
+
return __returned__;
|
|
5487
|
+
}
|
|
5488
|
+
});
|
|
5489
|
+
function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5490
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5491
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-button", $setup.animClass, $setup.props.class)),
|
|
5492
|
+
style: (0, vue.normalizeStyle)($setup.rootStyle),
|
|
5493
|
+
role: "status",
|
|
5494
|
+
"aria-busy": "true"
|
|
5495
|
+
}, [..._cache[0] || (_cache[0] = [(0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading button…", -1)])], 6);
|
|
5496
|
+
}
|
|
5497
|
+
var ASkeletonButton_default = /* @__PURE__ */ export_helper_default(_sfc_main$9, [["render", _sfc_render$9], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonButton.vue"]]);
|
|
5498
|
+
//#endregion
|
|
5499
|
+
//#region src/components/variants/ASkeletonInput.vue
|
|
5500
|
+
const _sfc_main$8 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5501
|
+
__name: "ASkeletonInput",
|
|
5502
|
+
props: {
|
|
5503
|
+
width: {
|
|
5504
|
+
type: [Number, String],
|
|
5505
|
+
required: false,
|
|
5506
|
+
default: "100%"
|
|
5507
|
+
},
|
|
5508
|
+
height: {
|
|
5509
|
+
type: [Number, String],
|
|
5510
|
+
required: false,
|
|
5511
|
+
default: 40
|
|
5512
|
+
},
|
|
5513
|
+
animation: {
|
|
5514
|
+
type: String,
|
|
5515
|
+
required: false,
|
|
5516
|
+
default: "pulse"
|
|
5517
|
+
},
|
|
5518
|
+
class: {
|
|
5519
|
+
type: [
|
|
5520
|
+
Boolean,
|
|
5521
|
+
null,
|
|
5522
|
+
String,
|
|
5523
|
+
Object,
|
|
5524
|
+
Array
|
|
5525
|
+
],
|
|
5526
|
+
required: false,
|
|
5527
|
+
skipCheck: true
|
|
5528
|
+
}
|
|
5529
|
+
},
|
|
5530
|
+
setup(__props, { expose: __expose }) {
|
|
5531
|
+
__expose();
|
|
5532
|
+
const props = __props;
|
|
5533
|
+
const __returned__ = {
|
|
5534
|
+
props,
|
|
5535
|
+
animClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
5536
|
+
rootStyle: (0, vue.computed)(() => ({
|
|
5537
|
+
width: typeof props.width === "number" ? `${props.width}px` : String(props.width),
|
|
5538
|
+
height: typeof props.height === "number" ? `${props.height}px` : String(props.height)
|
|
5539
|
+
})),
|
|
5540
|
+
get cn() {
|
|
5541
|
+
return cn;
|
|
5542
|
+
}
|
|
5543
|
+
};
|
|
5544
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5545
|
+
enumerable: false,
|
|
5546
|
+
value: true
|
|
5547
|
+
});
|
|
5548
|
+
return __returned__;
|
|
5549
|
+
}
|
|
5550
|
+
});
|
|
5551
|
+
function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5552
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5553
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-input", $setup.animClass, $setup.props.class)),
|
|
5554
|
+
style: (0, vue.normalizeStyle)($setup.rootStyle),
|
|
5555
|
+
role: "status",
|
|
5556
|
+
"aria-busy": "true"
|
|
5557
|
+
}, [..._cache[0] || (_cache[0] = [(0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading input…", -1)])], 6);
|
|
5558
|
+
}
|
|
5559
|
+
var ASkeletonInput_default = /* @__PURE__ */ export_helper_default(_sfc_main$8, [["render", _sfc_render$8], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonInput.vue"]]);
|
|
5560
|
+
//#endregion
|
|
5561
|
+
//#region src/components/variants/ASkeletonListItem.vue
|
|
5562
|
+
const _sfc_main$7 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5563
|
+
__name: "ASkeletonListItem",
|
|
5564
|
+
props: {
|
|
5565
|
+
avatar: {
|
|
5566
|
+
type: Boolean,
|
|
5567
|
+
required: false,
|
|
5568
|
+
default: true
|
|
5569
|
+
},
|
|
5570
|
+
lines: {
|
|
5571
|
+
type: Number,
|
|
5572
|
+
required: false,
|
|
5573
|
+
default: 2
|
|
5574
|
+
},
|
|
5575
|
+
trailing: {
|
|
5576
|
+
type: Boolean,
|
|
5577
|
+
required: false,
|
|
5578
|
+
default: false
|
|
5579
|
+
},
|
|
5580
|
+
animation: {
|
|
5581
|
+
type: String,
|
|
5582
|
+
required: false,
|
|
5583
|
+
default: "pulse"
|
|
5584
|
+
},
|
|
5585
|
+
class: {
|
|
5586
|
+
type: [
|
|
5587
|
+
Boolean,
|
|
5588
|
+
null,
|
|
5589
|
+
String,
|
|
5590
|
+
Object,
|
|
5591
|
+
Array
|
|
5592
|
+
],
|
|
5593
|
+
required: false,
|
|
5594
|
+
skipCheck: true
|
|
5595
|
+
}
|
|
5596
|
+
},
|
|
5597
|
+
setup(__props, { expose: __expose }) {
|
|
5598
|
+
__expose();
|
|
5599
|
+
const props = __props;
|
|
5600
|
+
const __returned__ = {
|
|
5601
|
+
props,
|
|
5602
|
+
animClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
5603
|
+
get cn() {
|
|
5604
|
+
return cn;
|
|
5605
|
+
},
|
|
5606
|
+
ASkeletonAvatar: ASkeletonAvatar_default,
|
|
5607
|
+
ASkeletonText: ASkeletonText_default
|
|
5608
|
+
};
|
|
5609
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5610
|
+
enumerable: false,
|
|
5611
|
+
value: true
|
|
5612
|
+
});
|
|
5613
|
+
return __returned__;
|
|
5614
|
+
}
|
|
5615
|
+
});
|
|
5616
|
+
const _hoisted_1$2 = { class: "a-skel-variant-list-item__body" };
|
|
5617
|
+
function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5618
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5619
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel-variant-list-item", $setup.props.class)),
|
|
5620
|
+
role: "status",
|
|
5621
|
+
"aria-busy": "true"
|
|
5622
|
+
}, [
|
|
5623
|
+
$setup.props.avatar ? ((0, vue.openBlock)(), (0, vue.createBlock)($setup["ASkeletonAvatar"], {
|
|
5624
|
+
key: 0,
|
|
5625
|
+
size: 40,
|
|
5626
|
+
animation: $setup.props.animation
|
|
5627
|
+
}, null, 8, ["animation"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
5628
|
+
(0, vue.createElementVNode)("div", _hoisted_1$2, [(0, vue.createVNode)($setup["ASkeletonText"], {
|
|
5629
|
+
lines: $setup.props.lines,
|
|
5630
|
+
animation: $setup.props.animation
|
|
5631
|
+
}, null, 8, ["lines", "animation"])]),
|
|
5632
|
+
$setup.props.trailing ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5633
|
+
key: 1,
|
|
5634
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-button", $setup.animClass)),
|
|
5635
|
+
style: {
|
|
5636
|
+
width: "40px",
|
|
5637
|
+
height: "24px"
|
|
5638
|
+
}
|
|
5639
|
+
}, null, 2)) : (0, vue.createCommentVNode)("v-if", true),
|
|
5640
|
+
_cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading list item…", -1))
|
|
5641
|
+
], 2);
|
|
5642
|
+
}
|
|
5643
|
+
var ASkeletonListItem_default = /* @__PURE__ */ export_helper_default(_sfc_main$7, [["render", _sfc_render$7], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonListItem.vue"]]);
|
|
5644
|
+
//#endregion
|
|
5645
|
+
//#region src/components/variants/ASkeletonCard.vue
|
|
5646
|
+
const _sfc_main$6 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5647
|
+
__name: "ASkeletonCard",
|
|
5648
|
+
props: {
|
|
5649
|
+
media: {
|
|
5650
|
+
type: Boolean,
|
|
5651
|
+
required: false,
|
|
5652
|
+
default: true
|
|
5653
|
+
},
|
|
5654
|
+
heading: {
|
|
5655
|
+
type: Boolean,
|
|
5656
|
+
required: false,
|
|
5657
|
+
default: true
|
|
5658
|
+
},
|
|
5659
|
+
lines: {
|
|
5660
|
+
type: Number,
|
|
5661
|
+
required: false,
|
|
5662
|
+
default: 3
|
|
5663
|
+
},
|
|
5664
|
+
actions: {
|
|
5665
|
+
type: Boolean,
|
|
5666
|
+
required: false,
|
|
5667
|
+
default: true
|
|
5668
|
+
},
|
|
5669
|
+
footerAvatar: {
|
|
5670
|
+
type: Boolean,
|
|
5671
|
+
required: false,
|
|
5672
|
+
default: false
|
|
5673
|
+
},
|
|
5674
|
+
animation: {
|
|
5675
|
+
type: String,
|
|
5676
|
+
required: false,
|
|
5677
|
+
default: "pulse"
|
|
5678
|
+
},
|
|
5679
|
+
class: {
|
|
5680
|
+
type: [
|
|
5681
|
+
Boolean,
|
|
5682
|
+
null,
|
|
5683
|
+
String,
|
|
5684
|
+
Object,
|
|
5685
|
+
Array
|
|
5686
|
+
],
|
|
5687
|
+
required: false,
|
|
5688
|
+
skipCheck: true
|
|
5689
|
+
}
|
|
5690
|
+
},
|
|
5691
|
+
setup(__props, { expose: __expose }) {
|
|
5692
|
+
__expose();
|
|
5693
|
+
const __returned__ = {
|
|
5694
|
+
get cn() {
|
|
5695
|
+
return cn;
|
|
5696
|
+
},
|
|
5697
|
+
ASkeletonImage: ASkeletonImage_default,
|
|
5698
|
+
ASkeletonHeading: ASkeletonHeading_default,
|
|
5699
|
+
ASkeletonText: ASkeletonText_default,
|
|
5700
|
+
ASkeletonButton: ASkeletonButton_default,
|
|
5701
|
+
ASkeletonAvatar: ASkeletonAvatar_default
|
|
5702
|
+
};
|
|
5703
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5704
|
+
enumerable: false,
|
|
5705
|
+
value: true
|
|
5706
|
+
});
|
|
5707
|
+
return __returned__;
|
|
5708
|
+
}
|
|
5709
|
+
});
|
|
5710
|
+
const _hoisted_1$1 = {
|
|
5711
|
+
key: 2,
|
|
5712
|
+
class: "mt-2 flex gap-2"
|
|
5713
|
+
};
|
|
5714
|
+
const _hoisted_2 = {
|
|
5715
|
+
key: 3,
|
|
5716
|
+
class: "mt-3 flex items-center gap-3"
|
|
5717
|
+
};
|
|
5718
|
+
const _hoisted_3 = { style: { "flex": "1" } };
|
|
5719
|
+
function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5720
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5721
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel-variant-card", _ctx.$props.class)),
|
|
5722
|
+
role: "status",
|
|
5723
|
+
"aria-busy": "true"
|
|
5724
|
+
}, [
|
|
5725
|
+
_ctx.$props.media ? ((0, vue.openBlock)(), (0, vue.createBlock)($setup["ASkeletonImage"], {
|
|
5726
|
+
key: 0,
|
|
5727
|
+
animation: _ctx.$props.animation
|
|
5728
|
+
}, null, 8, ["animation"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
5729
|
+
_ctx.$props.heading ? ((0, vue.openBlock)(), (0, vue.createBlock)($setup["ASkeletonHeading"], {
|
|
5730
|
+
key: 1,
|
|
5731
|
+
level: 3,
|
|
5732
|
+
animation: _ctx.$props.animation
|
|
5733
|
+
}, null, 8, ["animation"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
5734
|
+
(0, vue.createVNode)($setup["ASkeletonText"], {
|
|
5735
|
+
lines: _ctx.$props.lines,
|
|
5736
|
+
animation: _ctx.$props.animation
|
|
5737
|
+
}, null, 8, ["lines", "animation"]),
|
|
5738
|
+
_ctx.$props.actions ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$1, [(0, vue.createVNode)($setup["ASkeletonButton"], {
|
|
5739
|
+
width: 96,
|
|
5740
|
+
height: 36,
|
|
5741
|
+
animation: _ctx.$props.animation
|
|
5742
|
+
}, null, 8, ["animation"]), (0, vue.createVNode)($setup["ASkeletonButton"], {
|
|
5743
|
+
width: 96,
|
|
5744
|
+
height: 36,
|
|
5745
|
+
outlined: "",
|
|
5746
|
+
animation: _ctx.$props.animation
|
|
5747
|
+
}, null, 8, ["animation"])])) : (0, vue.createCommentVNode)("v-if", true),
|
|
5748
|
+
_ctx.$props.footerAvatar ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2, [(0, vue.createVNode)($setup["ASkeletonAvatar"], {
|
|
5749
|
+
size: 36,
|
|
5750
|
+
animation: _ctx.$props.animation
|
|
5751
|
+
}, null, 8, ["animation"]), (0, vue.createElementVNode)("div", _hoisted_3, [(0, vue.createVNode)($setup["ASkeletonText"], {
|
|
5752
|
+
lines: 2,
|
|
5753
|
+
animation: _ctx.$props.animation
|
|
5754
|
+
}, null, 8, ["animation"])])])) : (0, vue.createCommentVNode)("v-if", true),
|
|
5755
|
+
_cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading card…", -1))
|
|
5756
|
+
], 2);
|
|
5757
|
+
}
|
|
5758
|
+
var ASkeletonCard_default = /* @__PURE__ */ export_helper_default(_sfc_main$6, [["render", _sfc_render$6], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonCard.vue"]]);
|
|
5759
|
+
//#endregion
|
|
5760
|
+
//#region src/components/variants/ASkeletonTable.vue
|
|
5761
|
+
const _sfc_main$5 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5762
|
+
__name: "ASkeletonTable",
|
|
5763
|
+
props: {
|
|
5764
|
+
rows: {
|
|
5765
|
+
type: Number,
|
|
5766
|
+
required: false,
|
|
5767
|
+
default: 5
|
|
5768
|
+
},
|
|
5769
|
+
columns: {
|
|
5770
|
+
type: Number,
|
|
5771
|
+
required: false,
|
|
5772
|
+
default: 4
|
|
5773
|
+
},
|
|
5774
|
+
showHeader: {
|
|
5775
|
+
type: Boolean,
|
|
5776
|
+
required: false,
|
|
5777
|
+
default: true
|
|
5778
|
+
},
|
|
5779
|
+
animation: {
|
|
5780
|
+
type: String,
|
|
5781
|
+
required: false,
|
|
5782
|
+
default: "pulse"
|
|
5783
|
+
},
|
|
5784
|
+
class: {
|
|
5785
|
+
type: [
|
|
5786
|
+
Boolean,
|
|
5787
|
+
null,
|
|
5788
|
+
String,
|
|
5789
|
+
Object,
|
|
5790
|
+
Array
|
|
5791
|
+
],
|
|
5792
|
+
required: false,
|
|
5793
|
+
skipCheck: true
|
|
5794
|
+
}
|
|
5795
|
+
},
|
|
5796
|
+
setup(__props, { expose: __expose }) {
|
|
5797
|
+
__expose();
|
|
5798
|
+
const props = __props;
|
|
5799
|
+
const __returned__ = {
|
|
5800
|
+
props,
|
|
5801
|
+
animClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
5802
|
+
rowStyle: (0, vue.computed)(() => ({ gridTemplateColumns: `repeat(${props.columns}, minmax(0, 1fr))` })),
|
|
5803
|
+
get cn() {
|
|
5804
|
+
return cn;
|
|
5805
|
+
}
|
|
5806
|
+
};
|
|
5807
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5808
|
+
enumerable: false,
|
|
5809
|
+
value: true
|
|
5810
|
+
});
|
|
5811
|
+
return __returned__;
|
|
5812
|
+
}
|
|
5813
|
+
});
|
|
5814
|
+
function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5815
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5816
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel-variant-table", $setup.props.class)),
|
|
5817
|
+
role: "status",
|
|
5818
|
+
"aria-busy": "true"
|
|
5819
|
+
}, [
|
|
5820
|
+
$setup.props.showHeader ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5821
|
+
key: 0,
|
|
5822
|
+
class: "a-skel-variant-table__row",
|
|
5823
|
+
style: (0, vue.normalizeStyle)($setup.rowStyle)
|
|
5824
|
+
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)($setup.props.columns, (c) => {
|
|
5825
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5826
|
+
key: `h-${c}`,
|
|
5827
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-heading", $setup.animClass)),
|
|
5828
|
+
style: {
|
|
5829
|
+
height: "1.5rem",
|
|
5830
|
+
width: "70%"
|
|
5831
|
+
}
|
|
5832
|
+
}, null, 2);
|
|
5833
|
+
}), 128))], 4)) : (0, vue.createCommentVNode)("v-if", true),
|
|
5834
|
+
((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)($setup.props.rows, (r) => {
|
|
5835
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5836
|
+
key: r,
|
|
5837
|
+
class: "a-skel-variant-table__row",
|
|
5838
|
+
style: (0, vue.normalizeStyle)($setup.rowStyle)
|
|
5839
|
+
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)($setup.props.columns, (c) => {
|
|
5840
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5841
|
+
key: `${r}-${c}`,
|
|
5842
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-text", $setup.animClass)),
|
|
5843
|
+
style: (0, vue.normalizeStyle)({ width: c === 1 ? "85%" : c === $setup.props.columns ? "50%" : "70%" })
|
|
5844
|
+
}, null, 6);
|
|
5845
|
+
}), 128))], 4);
|
|
5846
|
+
}), 128)),
|
|
5847
|
+
_cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading table…", -1))
|
|
5848
|
+
], 2);
|
|
5849
|
+
}
|
|
5850
|
+
var ASkeletonTable_default = /* @__PURE__ */ export_helper_default(_sfc_main$5, [["render", _sfc_render$5], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonTable.vue"]]);
|
|
5851
|
+
//#endregion
|
|
5852
|
+
//#region src/components/variants/ASkeletonChart.vue
|
|
5853
|
+
const _sfc_main$4 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5854
|
+
__name: "ASkeletonChart",
|
|
5855
|
+
props: {
|
|
5856
|
+
bars: {
|
|
5857
|
+
type: Number,
|
|
5858
|
+
required: false,
|
|
5859
|
+
default: 7
|
|
5860
|
+
},
|
|
5861
|
+
height: {
|
|
5862
|
+
type: [Number, String],
|
|
5863
|
+
required: false,
|
|
5864
|
+
default: "8rem"
|
|
5865
|
+
},
|
|
5866
|
+
showHeader: {
|
|
5867
|
+
type: Boolean,
|
|
5868
|
+
required: false,
|
|
5869
|
+
default: true
|
|
5870
|
+
},
|
|
5871
|
+
animation: {
|
|
5872
|
+
type: String,
|
|
5873
|
+
required: false,
|
|
5874
|
+
default: "pulse"
|
|
5875
|
+
},
|
|
5876
|
+
class: {
|
|
5877
|
+
type: [
|
|
5878
|
+
Boolean,
|
|
5879
|
+
null,
|
|
5880
|
+
String,
|
|
5881
|
+
Object,
|
|
5882
|
+
Array
|
|
5883
|
+
],
|
|
5884
|
+
required: false,
|
|
5885
|
+
skipCheck: true
|
|
5886
|
+
}
|
|
5887
|
+
},
|
|
5888
|
+
setup(__props, { expose: __expose }) {
|
|
5889
|
+
__expose();
|
|
5890
|
+
const props = __props;
|
|
5891
|
+
const animClass = (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`);
|
|
5892
|
+
const chartStyle = (0, vue.computed)(() => ({ height: typeof props.height === "number" ? `${props.height}px` : String(props.height) }));
|
|
5893
|
+
const HEIGHTS = [
|
|
5894
|
+
62,
|
|
5895
|
+
78,
|
|
5896
|
+
45,
|
|
5897
|
+
91,
|
|
5898
|
+
68,
|
|
5899
|
+
82,
|
|
5900
|
+
55,
|
|
5901
|
+
73,
|
|
5902
|
+
39,
|
|
5903
|
+
88,
|
|
5904
|
+
60,
|
|
5905
|
+
74
|
|
5906
|
+
];
|
|
5907
|
+
function barHeight(i) {
|
|
5908
|
+
return `${HEIGHTS[i % HEIGHTS.length]}%`;
|
|
5909
|
+
}
|
|
5910
|
+
const __returned__ = {
|
|
5911
|
+
props,
|
|
5912
|
+
animClass,
|
|
5913
|
+
chartStyle,
|
|
5914
|
+
HEIGHTS,
|
|
5915
|
+
barHeight,
|
|
5916
|
+
get cn() {
|
|
5917
|
+
return cn;
|
|
5918
|
+
},
|
|
5919
|
+
ASkeletonHeading: ASkeletonHeading_default,
|
|
5920
|
+
ASkeletonText: ASkeletonText_default
|
|
5921
|
+
};
|
|
5922
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
5923
|
+
enumerable: false,
|
|
5924
|
+
value: true
|
|
5925
|
+
});
|
|
5926
|
+
return __returned__;
|
|
5927
|
+
}
|
|
5928
|
+
});
|
|
5929
|
+
const _hoisted_1 = { style: { "margin-top": "0.4rem" } };
|
|
5930
|
+
function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
|
|
5931
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5932
|
+
class: (0, vue.normalizeClass)($setup.cn($setup.props.class)),
|
|
5933
|
+
role: "status",
|
|
5934
|
+
"aria-busy": "true"
|
|
5935
|
+
}, [
|
|
5936
|
+
$setup.props.showHeader ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [(0, vue.createVNode)($setup["ASkeletonHeading"], {
|
|
5937
|
+
level: 4,
|
|
5938
|
+
animation: $setup.props.animation,
|
|
5939
|
+
width: "45%"
|
|
5940
|
+
}, null, 8, ["animation"]), (0, vue.createElementVNode)("div", _hoisted_1, [(0, vue.createVNode)($setup["ASkeletonText"], {
|
|
5941
|
+
lines: 1,
|
|
5942
|
+
animation: $setup.props.animation,
|
|
5943
|
+
width: "60%"
|
|
5944
|
+
}, null, 8, ["animation"])])], 64)) : (0, vue.createCommentVNode)("v-if", true),
|
|
5945
|
+
(0, vue.createElementVNode)("div", {
|
|
5946
|
+
class: "a-skel-variant-chart",
|
|
5947
|
+
style: (0, vue.normalizeStyle)([$setup.chartStyle, { "margin-top": "1rem" }])
|
|
5948
|
+
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)($setup.props.bars, (i) => {
|
|
5949
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
5950
|
+
key: i,
|
|
5951
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel a-skel-variant-chart__bar", $setup.animClass)),
|
|
5952
|
+
style: (0, vue.normalizeStyle)({ height: $setup.barHeight(i - 1) })
|
|
5953
|
+
}, null, 6);
|
|
5954
|
+
}), 128))], 4),
|
|
5955
|
+
_cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading chart…", -1))
|
|
5956
|
+
], 2);
|
|
5957
|
+
}
|
|
5958
|
+
var ASkeletonChart_default = /* @__PURE__ */ export_helper_default(_sfc_main$4, [["render", _sfc_render$4], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonChart.vue"]]);
|
|
5959
|
+
//#endregion
|
|
5960
|
+
//#region src/components/variants/ASkeletonForm.vue
|
|
5961
|
+
const _sfc_main$3 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
5962
|
+
__name: "ASkeletonForm",
|
|
5963
|
+
props: {
|
|
5964
|
+
fields: {
|
|
5965
|
+
type: Number,
|
|
5966
|
+
required: false,
|
|
5967
|
+
default: 3
|
|
5968
|
+
},
|
|
5969
|
+
showSubmit: {
|
|
5970
|
+
type: Boolean,
|
|
5971
|
+
required: false,
|
|
5972
|
+
default: true
|
|
5973
|
+
},
|
|
5974
|
+
animation: {
|
|
5975
|
+
type: String,
|
|
5976
|
+
required: false,
|
|
5977
|
+
default: "pulse"
|
|
5978
|
+
},
|
|
5979
|
+
class: {
|
|
5980
|
+
type: [
|
|
5981
|
+
Boolean,
|
|
5982
|
+
null,
|
|
5983
|
+
String,
|
|
5984
|
+
Object,
|
|
5985
|
+
Array
|
|
5986
|
+
],
|
|
5987
|
+
required: false,
|
|
5988
|
+
skipCheck: true
|
|
5989
|
+
}
|
|
5990
|
+
},
|
|
5991
|
+
setup(__props, { expose: __expose }) {
|
|
5992
|
+
__expose();
|
|
5993
|
+
const __returned__ = {
|
|
5994
|
+
get cn() {
|
|
5995
|
+
return cn;
|
|
5996
|
+
},
|
|
5997
|
+
ASkeletonText: ASkeletonText_default,
|
|
5998
|
+
ASkeletonInput: ASkeletonInput_default,
|
|
5999
|
+
ASkeletonButton: ASkeletonButton_default
|
|
6000
|
+
};
|
|
6001
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
6002
|
+
enumerable: false,
|
|
6003
|
+
value: true
|
|
6004
|
+
});
|
|
6005
|
+
return __returned__;
|
|
6006
|
+
}
|
|
6007
|
+
});
|
|
6008
|
+
function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
|
|
6009
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
6010
|
+
class: (0, vue.normalizeClass)($setup.cn("flex flex-col gap-4", _ctx.$props.class)),
|
|
6011
|
+
role: "status",
|
|
6012
|
+
"aria-busy": "true"
|
|
6013
|
+
}, [
|
|
6014
|
+
((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(_ctx.$props.fields, (i) => {
|
|
6015
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
6016
|
+
key: i,
|
|
6017
|
+
class: "flex flex-col gap-2"
|
|
6018
|
+
}, [(0, vue.createVNode)($setup["ASkeletonText"], {
|
|
6019
|
+
lines: 1,
|
|
6020
|
+
width: "30%",
|
|
6021
|
+
animation: _ctx.$props.animation
|
|
6022
|
+
}, null, 8, ["animation"]), (0, vue.createVNode)($setup["ASkeletonInput"], { animation: _ctx.$props.animation }, null, 8, ["animation"])]);
|
|
6023
|
+
}), 128)),
|
|
6024
|
+
_ctx.$props.showSubmit ? ((0, vue.openBlock)(), (0, vue.createBlock)($setup["ASkeletonButton"], {
|
|
6025
|
+
key: 0,
|
|
6026
|
+
width: 120,
|
|
6027
|
+
height: 40,
|
|
6028
|
+
animation: _ctx.$props.animation
|
|
6029
|
+
}, null, 8, ["animation"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
6030
|
+
_cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading form…", -1))
|
|
6031
|
+
], 2);
|
|
6032
|
+
}
|
|
6033
|
+
var ASkeletonForm_default = /* @__PURE__ */ export_helper_default(_sfc_main$3, [["render", _sfc_render$3], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonForm.vue"]]);
|
|
6034
|
+
//#endregion
|
|
6035
|
+
//#region src/components/variants/ASkeletonArticle.vue
|
|
6036
|
+
const _sfc_main$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
6037
|
+
__name: "ASkeletonArticle",
|
|
6038
|
+
props: {
|
|
6039
|
+
media: {
|
|
6040
|
+
type: Boolean,
|
|
6041
|
+
required: false,
|
|
6042
|
+
default: true
|
|
6043
|
+
},
|
|
6044
|
+
paragraphs: {
|
|
6045
|
+
type: Number,
|
|
6046
|
+
required: false,
|
|
6047
|
+
default: 3
|
|
6048
|
+
},
|
|
6049
|
+
linesPerParagraph: {
|
|
6050
|
+
type: Number,
|
|
6051
|
+
required: false,
|
|
6052
|
+
default: 4
|
|
6053
|
+
},
|
|
6054
|
+
animation: {
|
|
6055
|
+
type: String,
|
|
6056
|
+
required: false,
|
|
6057
|
+
default: "pulse"
|
|
6058
|
+
},
|
|
6059
|
+
class: {
|
|
6060
|
+
type: [
|
|
6061
|
+
Boolean,
|
|
6062
|
+
null,
|
|
6063
|
+
String,
|
|
6064
|
+
Object,
|
|
6065
|
+
Array
|
|
6066
|
+
],
|
|
6067
|
+
required: false,
|
|
6068
|
+
skipCheck: true
|
|
6069
|
+
}
|
|
6070
|
+
},
|
|
6071
|
+
setup(__props, { expose: __expose }) {
|
|
6072
|
+
__expose();
|
|
6073
|
+
const __returned__ = {
|
|
6074
|
+
get cn() {
|
|
6075
|
+
return cn;
|
|
6076
|
+
},
|
|
6077
|
+
ASkeletonHeading: ASkeletonHeading_default,
|
|
6078
|
+
ASkeletonText: ASkeletonText_default,
|
|
6079
|
+
ASkeletonImage: ASkeletonImage_default
|
|
6080
|
+
};
|
|
6081
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
6082
|
+
enumerable: false,
|
|
6083
|
+
value: true
|
|
6084
|
+
});
|
|
6085
|
+
return __returned__;
|
|
6086
|
+
}
|
|
6087
|
+
});
|
|
6088
|
+
function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
|
|
6089
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("article", {
|
|
6090
|
+
class: (0, vue.normalizeClass)($setup.cn("flex flex-col gap-4", _ctx.$props.class)),
|
|
6091
|
+
role: "status",
|
|
6092
|
+
"aria-busy": "true"
|
|
6093
|
+
}, [
|
|
6094
|
+
(0, vue.createVNode)($setup["ASkeletonHeading"], {
|
|
6095
|
+
level: 1,
|
|
6096
|
+
animation: _ctx.$props.animation
|
|
6097
|
+
}, null, 8, ["animation"]),
|
|
6098
|
+
_ctx.$props.media ? ((0, vue.openBlock)(), (0, vue.createBlock)($setup["ASkeletonImage"], {
|
|
6099
|
+
key: 0,
|
|
6100
|
+
animation: _ctx.$props.animation
|
|
6101
|
+
}, null, 8, ["animation"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
6102
|
+
((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(_ctx.$props.paragraphs, (i) => {
|
|
6103
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", { key: i }, [(0, vue.createVNode)($setup["ASkeletonText"], {
|
|
6104
|
+
lines: _ctx.$props.linesPerParagraph,
|
|
6105
|
+
animation: _ctx.$props.animation
|
|
6106
|
+
}, null, 8, ["lines", "animation"])]);
|
|
6107
|
+
}), 128)),
|
|
6108
|
+
_cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "a-skel-sr-only" }, "Loading article…", -1))
|
|
6109
|
+
], 2);
|
|
6110
|
+
}
|
|
6111
|
+
var ASkeletonArticle_default = /* @__PURE__ */ export_helper_default(_sfc_main$2, [["render", _sfc_render$2], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonArticle.vue"]]);
|
|
6112
|
+
//#endregion
|
|
6113
|
+
//#region src/components/variants/ASkeletonDivider.vue
|
|
6114
|
+
const _sfc_main$1 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
6115
|
+
__name: "ASkeletonDivider",
|
|
6116
|
+
props: {
|
|
6117
|
+
thickness: {
|
|
6118
|
+
type: Number,
|
|
6119
|
+
required: false,
|
|
6120
|
+
default: 1
|
|
6121
|
+
},
|
|
6122
|
+
animation: {
|
|
6123
|
+
type: String,
|
|
6124
|
+
required: false,
|
|
6125
|
+
default: "pulse"
|
|
6126
|
+
},
|
|
6127
|
+
class: {
|
|
6128
|
+
type: [
|
|
6129
|
+
Boolean,
|
|
6130
|
+
null,
|
|
6131
|
+
String,
|
|
6132
|
+
Object,
|
|
6133
|
+
Array
|
|
6134
|
+
],
|
|
6135
|
+
required: false,
|
|
6136
|
+
skipCheck: true
|
|
6137
|
+
}
|
|
6138
|
+
},
|
|
6139
|
+
setup(__props, { expose: __expose }) {
|
|
6140
|
+
__expose();
|
|
6141
|
+
const props = __props;
|
|
6142
|
+
const __returned__ = {
|
|
6143
|
+
props,
|
|
6144
|
+
animClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
6145
|
+
rootStyle: (0, vue.computed)(() => ({
|
|
6146
|
+
height: `${props.thickness}px`,
|
|
6147
|
+
width: "100%"
|
|
6148
|
+
})),
|
|
6149
|
+
get cn() {
|
|
6150
|
+
return cn;
|
|
6151
|
+
}
|
|
6152
|
+
};
|
|
6153
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
6154
|
+
enumerable: false,
|
|
6155
|
+
value: true
|
|
6156
|
+
});
|
|
6157
|
+
return __returned__;
|
|
6158
|
+
}
|
|
6159
|
+
});
|
|
6160
|
+
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
|
|
6161
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
6162
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel", $setup.animClass, $setup.props.class)),
|
|
6163
|
+
style: (0, vue.normalizeStyle)($setup.rootStyle),
|
|
6164
|
+
role: "separator",
|
|
6165
|
+
"aria-hidden": "true"
|
|
6166
|
+
}, null, 6);
|
|
6167
|
+
}
|
|
6168
|
+
var ASkeletonDivider_default = /* @__PURE__ */ export_helper_default(_sfc_main$1, [["render", _sfc_render$1], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonDivider.vue"]]);
|
|
6169
|
+
//#endregion
|
|
6170
|
+
//#region src/components/variants/ASkeletonChip.vue
|
|
6171
|
+
const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
6172
|
+
__name: "ASkeletonChip",
|
|
6173
|
+
props: {
|
|
6174
|
+
width: {
|
|
6175
|
+
type: [Number, String],
|
|
6176
|
+
required: false,
|
|
6177
|
+
default: 80
|
|
6178
|
+
},
|
|
6179
|
+
height: {
|
|
6180
|
+
type: [Number, String],
|
|
6181
|
+
required: false,
|
|
6182
|
+
default: 24
|
|
6183
|
+
},
|
|
6184
|
+
animation: {
|
|
6185
|
+
type: String,
|
|
6186
|
+
required: false,
|
|
6187
|
+
default: "pulse"
|
|
6188
|
+
},
|
|
6189
|
+
class: {
|
|
6190
|
+
type: [
|
|
6191
|
+
Boolean,
|
|
6192
|
+
null,
|
|
6193
|
+
String,
|
|
6194
|
+
Object,
|
|
6195
|
+
Array
|
|
6196
|
+
],
|
|
6197
|
+
required: false,
|
|
6198
|
+
skipCheck: true
|
|
6199
|
+
}
|
|
6200
|
+
},
|
|
6201
|
+
setup(__props, { expose: __expose }) {
|
|
6202
|
+
__expose();
|
|
6203
|
+
const props = __props;
|
|
6204
|
+
const __returned__ = {
|
|
6205
|
+
props,
|
|
6206
|
+
animClass: (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-anim-${props.animation}`),
|
|
6207
|
+
rootStyle: (0, vue.computed)(() => ({
|
|
6208
|
+
width: typeof props.width === "number" ? `${props.width}px` : String(props.width),
|
|
6209
|
+
height: typeof props.height === "number" ? `${props.height}px` : String(props.height),
|
|
6210
|
+
borderRadius: "9999px",
|
|
6211
|
+
display: "inline-block"
|
|
6212
|
+
})),
|
|
6213
|
+
get cn() {
|
|
6214
|
+
return cn;
|
|
6215
|
+
}
|
|
6216
|
+
};
|
|
6217
|
+
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
6218
|
+
enumerable: false,
|
|
6219
|
+
value: true
|
|
6220
|
+
});
|
|
6221
|
+
return __returned__;
|
|
6222
|
+
}
|
|
6223
|
+
});
|
|
6224
|
+
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
6225
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
6226
|
+
class: (0, vue.normalizeClass)($setup.cn("a-skel", $setup.animClass, $setup.props.class)),
|
|
6227
|
+
style: (0, vue.normalizeStyle)($setup.rootStyle),
|
|
6228
|
+
role: "status",
|
|
6229
|
+
"aria-busy": "true"
|
|
6230
|
+
}, null, 6);
|
|
6231
|
+
}
|
|
6232
|
+
var ASkeletonChip_default = /* @__PURE__ */ export_helper_default(_sfc_main, [["render", _sfc_render], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/variants/ASkeletonChip.vue"]]);
|
|
6233
|
+
//#endregion
|
|
6234
|
+
//#region src/utils/walkDom.ts
|
|
6235
|
+
const DEFAULT_MAX_NODES$1 = 500;
|
|
6236
|
+
const DEFAULT_MIN_SIZE$1 = 4;
|
|
6237
|
+
const LEAF_TAGS = new Set([
|
|
6238
|
+
"IMG",
|
|
6239
|
+
"SVG",
|
|
6240
|
+
"CANVAS",
|
|
6241
|
+
"VIDEO",
|
|
6242
|
+
"INPUT",
|
|
6243
|
+
"TEXTAREA",
|
|
6244
|
+
"SELECT",
|
|
6245
|
+
"BUTTON",
|
|
6246
|
+
"PROGRESS",
|
|
6247
|
+
"METER",
|
|
6248
|
+
"HR"
|
|
6249
|
+
]);
|
|
6250
|
+
/**
|
|
6251
|
+
* Walk `root`'s descendants and produce a list of shimmer blocks that mirror its
|
|
6252
|
+
* rendered layout. Coordinates are relative to `root`'s top-left so the result can
|
|
6253
|
+
* be replayed in any container of the same size.
|
|
6254
|
+
*
|
|
6255
|
+
* Performance:
|
|
6256
|
+
* - `maxNodes` caps the walk so a 5000-row table doesn't lock up the main thread.
|
|
6257
|
+
* - `minSize` filters out hairlines (1-2 px paddings, decorative dots) that
|
|
6258
|
+
* inflate node count without adding visual signal.
|
|
6259
|
+
* - All `getBoundingClientRect` / `getComputedStyle` reads happen in a single
|
|
6260
|
+
* top-down pass with no intervening writes, so the browser does one layout
|
|
6261
|
+
* up front and serves cached values from then on (no layout thrashing).
|
|
6262
|
+
* - Each emitted `ShapeNode` has a frozen pre-computed `style` (and `lineStyles`
|
|
6263
|
+
* for multi-line text) so the render path is allocation-free.
|
|
6264
|
+
*/
|
|
6265
|
+
function walkDom(root, options) {
|
|
6266
|
+
const maxNodes = options.maxNodes ?? DEFAULT_MAX_NODES$1;
|
|
6267
|
+
const minSize = options.minSize ?? DEFAULT_MIN_SIZE$1;
|
|
6268
|
+
const nodes = [];
|
|
6269
|
+
const rootRect = root.getBoundingClientRect();
|
|
6270
|
+
let truncated = false;
|
|
6271
|
+
function visit(el, depth) {
|
|
6272
|
+
if (nodes.length >= maxNodes) {
|
|
6273
|
+
truncated = true;
|
|
6274
|
+
return;
|
|
6275
|
+
}
|
|
6276
|
+
const html = el;
|
|
6277
|
+
if (html.dataset?.skeletonIgnore !== void 0) return;
|
|
6278
|
+
const cs = window.getComputedStyle(el);
|
|
6279
|
+
if (cs.display === "none" || cs.visibility === "hidden" || cs.opacity === "0") return;
|
|
6280
|
+
const rect = el.getBoundingClientRect();
|
|
6281
|
+
if (rect.width < minSize || rect.height < minSize) return;
|
|
6282
|
+
const tag = el.tagName.toUpperCase();
|
|
6283
|
+
const isLeafTag = LEAF_TAGS.has(tag);
|
|
6284
|
+
const hasStop = html.dataset?.skeletonStop !== void 0;
|
|
6285
|
+
const childElements = [];
|
|
6286
|
+
for (let i = 0; i < el.children.length; i++) {
|
|
6287
|
+
const c = el.children[i];
|
|
6288
|
+
if (c.dataset?.skeletonIgnore === void 0) childElements.push(c);
|
|
6289
|
+
}
|
|
6290
|
+
const hasOwnText = hasDirectTextContent(el);
|
|
6291
|
+
const reachedDepth = depth >= options.maxDepth;
|
|
6292
|
+
if (isLeafTag || hasStop || reachedDepth || childElements.length === 0) {
|
|
6293
|
+
const node = elementToShape(el, tag, cs, rect, rootRect, hasOwnText);
|
|
6294
|
+
if (node) nodes.push(node);
|
|
6295
|
+
return;
|
|
6296
|
+
}
|
|
6297
|
+
const containerNode = containerSurfaceToShape(cs, rect, rootRect);
|
|
6298
|
+
if (containerNode) nodes.push(containerNode);
|
|
6299
|
+
for (let i = 0; i < childElements.length; i++) {
|
|
6300
|
+
if (nodes.length >= maxNodes) {
|
|
6301
|
+
truncated = true;
|
|
6302
|
+
return;
|
|
6303
|
+
}
|
|
6304
|
+
visit(childElements[i], depth + 1);
|
|
6305
|
+
}
|
|
6306
|
+
}
|
|
6307
|
+
for (let i = 0; i < root.children.length; i++) {
|
|
6308
|
+
if (nodes.length >= maxNodes) {
|
|
6309
|
+
truncated = true;
|
|
6310
|
+
break;
|
|
6311
|
+
}
|
|
6312
|
+
visit(root.children[i], 1);
|
|
6313
|
+
}
|
|
6314
|
+
return Object.freeze({
|
|
6315
|
+
nodes: Object.freeze(nodes),
|
|
6316
|
+
width: Math.round(rootRect.width),
|
|
6317
|
+
height: Math.round(rootRect.height),
|
|
6318
|
+
truncated
|
|
6319
|
+
});
|
|
6320
|
+
}
|
|
6321
|
+
function hasDirectTextContent(el) {
|
|
6322
|
+
for (let i = 0; i < el.childNodes.length; i++) {
|
|
6323
|
+
const node = el.childNodes[i];
|
|
6324
|
+
if (node.nodeType === Node.TEXT_NODE && (node.textContent ?? "").trim().length > 0) return true;
|
|
6325
|
+
}
|
|
6326
|
+
return false;
|
|
6327
|
+
}
|
|
6328
|
+
/**
|
|
6329
|
+
* Emit a backing block for a container with its own visible surface — real
|
|
6330
|
+
* background, border, box-shadow, or non-full opacity. Geometry is the
|
|
6331
|
+
* container's exact bounding rect so the width / height / radius match the
|
|
6332
|
+
* real DOM 1:1. Returns `null` when the container has no visible surface
|
|
6333
|
+
* (a plain unstyled `<div>` is layout-only and shouldn't add a block).
|
|
6334
|
+
*/
|
|
6335
|
+
function containerSurfaceToShape(cs, rect, origin) {
|
|
6336
|
+
const bg = readBackgroundColor(cs);
|
|
6337
|
+
const border = readBorder(cs);
|
|
6338
|
+
const boxShadow = readBoxShadow(cs);
|
|
6339
|
+
const opacity = readOpacity(cs);
|
|
6340
|
+
if (!bg && !border && !boxShadow && opacity === void 0) return null;
|
|
6341
|
+
return freezeShape({
|
|
6342
|
+
type: "block",
|
|
6343
|
+
x: Math.round(rect.left - origin.left),
|
|
6344
|
+
y: Math.round(rect.top - origin.top),
|
|
6345
|
+
w: Math.round(rect.width),
|
|
6346
|
+
h: Math.round(rect.height),
|
|
6347
|
+
radius: parseFloat(cs.borderRadius) || 0,
|
|
6348
|
+
bg,
|
|
6349
|
+
border,
|
|
6350
|
+
boxShadow,
|
|
6351
|
+
opacity
|
|
6352
|
+
});
|
|
6353
|
+
}
|
|
6354
|
+
function elementToShape(el, tag, cs, rect, origin, hasText) {
|
|
6355
|
+
const x = Math.round(rect.left - origin.left);
|
|
6356
|
+
const y = Math.round(rect.top - origin.top);
|
|
6357
|
+
const w = Math.round(rect.width);
|
|
6358
|
+
const h = Math.round(rect.height);
|
|
6359
|
+
const radius = parseFloat(cs.borderRadius) || 0;
|
|
6360
|
+
const minDim = Math.min(w, h);
|
|
6361
|
+
const isCircle = radius >= minDim / 2 - 1 && Math.abs(w - h) <= 2 && minDim > 0;
|
|
6362
|
+
let type;
|
|
6363
|
+
let resolvedRadius = radius;
|
|
6364
|
+
let lines;
|
|
6365
|
+
let lineHeight;
|
|
6366
|
+
let textRects;
|
|
6367
|
+
let textAlign;
|
|
6368
|
+
if (tag === "IMG" || tag === "SVG" || tag === "VIDEO" || tag === "CANVAS") type = "image";
|
|
6369
|
+
else if (isCircle) {
|
|
6370
|
+
type = "circle";
|
|
6371
|
+
resolvedRadius = Math.floor(minDim / 2);
|
|
6372
|
+
} else if (hasText) {
|
|
6373
|
+
type = "text";
|
|
6374
|
+
lineHeight = Math.round(parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.4 || 16);
|
|
6375
|
+
lines = Math.max(1, Math.round(h / lineHeight));
|
|
6376
|
+
resolvedRadius = Math.min(radius, 4);
|
|
6377
|
+
textAlign = readTextAlign(cs);
|
|
6378
|
+
textRects = measureTextRects(el, origin);
|
|
6379
|
+
} else type = "block";
|
|
6380
|
+
const bg = readBackgroundColor(cs);
|
|
6381
|
+
const border = readBorder(cs);
|
|
6382
|
+
const boxShadow = readBoxShadow(cs);
|
|
6383
|
+
const opacity = readOpacity(cs);
|
|
6384
|
+
return freezeShape({
|
|
6385
|
+
type,
|
|
6386
|
+
x,
|
|
6387
|
+
y,
|
|
6388
|
+
w,
|
|
6389
|
+
h,
|
|
6390
|
+
radius: resolvedRadius,
|
|
6391
|
+
lines,
|
|
6392
|
+
lineHeight,
|
|
6393
|
+
textRects,
|
|
6394
|
+
bg,
|
|
6395
|
+
border,
|
|
6396
|
+
boxShadow,
|
|
6397
|
+
opacity,
|
|
6398
|
+
textAlign
|
|
6399
|
+
});
|
|
6400
|
+
}
|
|
6401
|
+
/**
|
|
6402
|
+
* Per-line text rects via `Range.getClientRects()`. Returns one rect per visual
|
|
6403
|
+
* line of rendered text — exact left/width for each line so wrapped paragraphs,
|
|
6404
|
+
* RTL last-line position, centered headings all replay 1:1 without heuristics.
|
|
6405
|
+
* Returns `undefined` if the element has no direct text content or the Range
|
|
6406
|
+
* API isn't usable in this environment.
|
|
6407
|
+
*/
|
|
6408
|
+
function measureTextRects(el, origin) {
|
|
6409
|
+
if (typeof document === "undefined" || typeof document.createRange !== "function") return void 0;
|
|
6410
|
+
let range;
|
|
6411
|
+
try {
|
|
6412
|
+
range = document.createRange();
|
|
6413
|
+
range.selectNodeContents(el);
|
|
6414
|
+
} catch {
|
|
6415
|
+
return;
|
|
6416
|
+
}
|
|
6417
|
+
const rects = range.getClientRects();
|
|
6418
|
+
if (!rects || rects.length === 0) return void 0;
|
|
6419
|
+
const merged = [];
|
|
6420
|
+
for (let i = 0; i < rects.length; i++) {
|
|
6421
|
+
const r = rects[i];
|
|
6422
|
+
if (r.width <= 0 || r.height <= 0) continue;
|
|
6423
|
+
const lr = {
|
|
6424
|
+
x: Math.round(r.left - origin.left),
|
|
6425
|
+
y: Math.round(r.top - origin.top),
|
|
6426
|
+
w: Math.round(r.width),
|
|
6427
|
+
h: Math.round(r.height)
|
|
6428
|
+
};
|
|
6429
|
+
const last = merged[merged.length - 1];
|
|
6430
|
+
if (last && Math.abs(last.y - lr.y) <= 1 && Math.abs(last.h - lr.h) <= 1 && Math.max(last.x, lr.x) - Math.min(last.x + last.w, lr.x + lr.w) <= 2) {
|
|
6431
|
+
const leftEdge = Math.min(last.x, lr.x);
|
|
6432
|
+
const rightEdge = Math.max(last.x + last.w, lr.x + lr.w);
|
|
6433
|
+
last.x = leftEdge;
|
|
6434
|
+
last.w = rightEdge - leftEdge;
|
|
6435
|
+
} else merged.push(lr);
|
|
6436
|
+
}
|
|
6437
|
+
return merged.length > 0 ? merged : void 0;
|
|
6438
|
+
}
|
|
6439
|
+
const TRANSPARENT_RE = /^(transparent|rgba?\([^)]*,\s*0(\.0+)?\s*\)|hsla?\([^)]*,\s*0%?\s*\))$/i;
|
|
6440
|
+
function readBackgroundColor(cs) {
|
|
6441
|
+
const bg = cs.backgroundColor;
|
|
6442
|
+
if (!bg || TRANSPARENT_RE.test(bg)) return void 0;
|
|
6443
|
+
return bg;
|
|
6444
|
+
}
|
|
6445
|
+
function readBorder(cs) {
|
|
6446
|
+
const width = parseFloat(cs.borderTopWidth) || 0;
|
|
6447
|
+
if (width < .5) return void 0;
|
|
6448
|
+
const style = cs.borderTopStyle;
|
|
6449
|
+
if (!style || style === "none" || style === "hidden") return void 0;
|
|
6450
|
+
const color = cs.borderTopColor;
|
|
6451
|
+
if (!color || TRANSPARENT_RE.test(color)) return void 0;
|
|
6452
|
+
return `${Math.round(width)}px ${style} ${color}`;
|
|
6453
|
+
}
|
|
6454
|
+
function readBoxShadow(cs) {
|
|
6455
|
+
const sh = cs.boxShadow;
|
|
6456
|
+
if (!sh || sh === "none") return void 0;
|
|
6457
|
+
return sh;
|
|
6458
|
+
}
|
|
6459
|
+
function readOpacity(cs) {
|
|
6460
|
+
const o = parseFloat(cs.opacity);
|
|
6461
|
+
if (!Number.isFinite(o) || o >= 1) return void 0;
|
|
6462
|
+
if (o <= 0) return void 0;
|
|
6463
|
+
return Math.round(o * 100) / 100;
|
|
6464
|
+
}
|
|
6465
|
+
function readTextAlign(cs) {
|
|
6466
|
+
const ta = cs.textAlign;
|
|
6467
|
+
if (!ta) return void 0;
|
|
6468
|
+
if (ta === "left" || ta === "right" || ta === "center" || ta === "justify" || ta === "start" || ta === "end") return ta;
|
|
6469
|
+
}
|
|
6470
|
+
/**
|
|
6471
|
+
* Pre-compute (and freeze) the inline styles used at render time. Doing it once
|
|
6472
|
+
* here means rendering 500 blocks doesn't allocate 500 style objects per frame.
|
|
6473
|
+
*
|
|
6474
|
+
* Captured visual signals (bg, border, shadow, opacity) are merged into the
|
|
6475
|
+
* frozen style so the replay carries the real DOM's surface — a white button
|
|
6476
|
+
* stays white, a ring-bordered button keeps its ring, a shadowed card keeps
|
|
6477
|
+
* its elevation.
|
|
6478
|
+
*/
|
|
6479
|
+
function freezeShape(node) {
|
|
6480
|
+
const baseStyle = {
|
|
6481
|
+
left: `${node.x}px`,
|
|
6482
|
+
top: `${node.y}px`,
|
|
6483
|
+
width: `${node.w}px`,
|
|
6484
|
+
height: `${node.h}px`,
|
|
6485
|
+
borderRadius: `${node.radius}px`
|
|
6486
|
+
};
|
|
6487
|
+
applyVisualSignals$1(baseStyle, node);
|
|
6488
|
+
const style = Object.freeze(baseStyle);
|
|
6489
|
+
let lineStyles;
|
|
6490
|
+
if (node.type === "text" && node.textRects && node.textRects.length > 0) {
|
|
6491
|
+
const radiusStr = `${node.radius}px`;
|
|
6492
|
+
const arr = [];
|
|
6493
|
+
for (const r of node.textRects) {
|
|
6494
|
+
const lineStyle = {
|
|
6495
|
+
left: `${r.x}px`,
|
|
6496
|
+
top: `${r.y}px`,
|
|
6497
|
+
width: `${r.w}px`,
|
|
6498
|
+
height: `${r.h}px`,
|
|
6499
|
+
borderRadius: radiusStr
|
|
6500
|
+
};
|
|
6501
|
+
applyVisualSignals$1(lineStyle, node);
|
|
6502
|
+
arr.push(Object.freeze(lineStyle));
|
|
6503
|
+
}
|
|
6504
|
+
lineStyles = Object.freeze(arr);
|
|
6505
|
+
} else if (node.type === "text" && node.lines && node.lines > 1) {
|
|
6506
|
+
const lh = node.lineHeight ?? Math.round(node.h / node.lines);
|
|
6507
|
+
const barHeight = Math.max(8, Math.round(lh * .7));
|
|
6508
|
+
const widthFull = node.w;
|
|
6509
|
+
const widthLast = Math.max(40, Math.round(node.w * .7));
|
|
6510
|
+
const heightStr = `${barHeight}px`;
|
|
6511
|
+
const radiusStr = `${node.radius}px`;
|
|
6512
|
+
const arr = [];
|
|
6513
|
+
for (let i = 1; i <= node.lines; i++) {
|
|
6514
|
+
const isLast = i === node.lines;
|
|
6515
|
+
const lineWidth = isLast ? widthLast : widthFull;
|
|
6516
|
+
let leftX = node.x;
|
|
6517
|
+
if (isLast && node.textAlign) {
|
|
6518
|
+
const slack = widthFull - lineWidth;
|
|
6519
|
+
if (node.textAlign === "center") leftX = node.x + Math.round(slack / 2);
|
|
6520
|
+
else if (node.textAlign === "right" || node.textAlign === "end") leftX = node.x + slack;
|
|
6521
|
+
}
|
|
6522
|
+
const lineStyle = {
|
|
6523
|
+
left: `${leftX}px`,
|
|
6524
|
+
top: `${node.y + (i - 1) * lh}px`,
|
|
6525
|
+
width: `${lineWidth}px`,
|
|
6526
|
+
height: heightStr,
|
|
6527
|
+
borderRadius: radiusStr
|
|
6528
|
+
};
|
|
6529
|
+
applyVisualSignals$1(lineStyle, node);
|
|
6530
|
+
arr.push(Object.freeze(lineStyle));
|
|
6531
|
+
}
|
|
6532
|
+
lineStyles = Object.freeze(arr);
|
|
6533
|
+
}
|
|
6534
|
+
return Object.freeze({
|
|
6535
|
+
type: node.type,
|
|
6536
|
+
x: node.x,
|
|
6537
|
+
y: node.y,
|
|
6538
|
+
w: node.w,
|
|
6539
|
+
h: node.h,
|
|
6540
|
+
radius: node.radius,
|
|
6541
|
+
lines: node.lines,
|
|
6542
|
+
lineHeight: node.lineHeight,
|
|
6543
|
+
textRects: node.textRects ? Object.freeze(node.textRects) : void 0,
|
|
6544
|
+
bg: node.bg,
|
|
6545
|
+
border: node.border,
|
|
6546
|
+
boxShadow: node.boxShadow,
|
|
6547
|
+
opacity: node.opacity,
|
|
6548
|
+
textAlign: node.textAlign,
|
|
6549
|
+
style,
|
|
6550
|
+
lineStyles
|
|
6551
|
+
});
|
|
6552
|
+
}
|
|
6553
|
+
/**
|
|
6554
|
+
* Merge captured surface signals into a style object in place. Each signal is
|
|
6555
|
+
* additive; `bg` uses `background` shorthand so it wipes the default linear
|
|
6556
|
+
* gradient on `.a-skel-block` cleanly.
|
|
6557
|
+
*/
|
|
6558
|
+
function applyVisualSignals$1(out, node) {
|
|
6559
|
+
if (node.bg) out.background = node.bg;
|
|
6560
|
+
if (node.border) out.border = node.border;
|
|
6561
|
+
if (node.boxShadow) out.boxShadow = node.boxShadow;
|
|
6562
|
+
if (node.opacity !== void 0) out.opacity = node.opacity;
|
|
6563
|
+
}
|
|
6564
|
+
//#endregion
|
|
6565
|
+
//#region src/composables/useShapeProbe.ts
|
|
6566
|
+
const DEFAULT_RESIZE_DEBOUNCE_MS = 150;
|
|
6567
|
+
/**
|
|
6568
|
+
* Observe `getTarget()` and capture its rendered shape whenever the element
|
|
6569
|
+
* appears or resizes.
|
|
6570
|
+
*
|
|
6571
|
+
* Performance:
|
|
6572
|
+
* - Initial capture runs via `requestAnimationFrame` so it sneaks into the
|
|
6573
|
+
* first idle window after mount — no synchronous layout from inside the
|
|
6574
|
+
* render queue.
|
|
6575
|
+
* - Subsequent `ResizeObserver` callbacks are debounced (default 150 ms) so a
|
|
6576
|
+
* drag-resize doesn't trigger a fresh DOM walk per frame.
|
|
6577
|
+
* - The capture strategy itself enforces `maxNodes` so even a worst-case
|
|
6578
|
+
* capture (10k descendants) returns in bounded time.
|
|
6579
|
+
*/
|
|
6580
|
+
function useShapeProbe(getTarget, options) {
|
|
6581
|
+
let observer;
|
|
6582
|
+
let frame;
|
|
6583
|
+
let timer;
|
|
6584
|
+
let hasCaptured = false;
|
|
6585
|
+
const debounceMs = options.resizeDebounceMs ?? DEFAULT_RESIZE_DEBOUNCE_MS;
|
|
6586
|
+
const captureFn = options.capture ?? walkDom;
|
|
6587
|
+
function cleanup() {
|
|
6588
|
+
if (observer) {
|
|
6589
|
+
observer.disconnect();
|
|
6590
|
+
observer = void 0;
|
|
6591
|
+
}
|
|
6592
|
+
if (frame !== void 0) {
|
|
6593
|
+
cancelAnimationFrame(frame);
|
|
6594
|
+
frame = void 0;
|
|
6595
|
+
}
|
|
6596
|
+
if (timer !== void 0) {
|
|
6597
|
+
clearTimeout(timer);
|
|
6598
|
+
timer = void 0;
|
|
6599
|
+
}
|
|
6600
|
+
}
|
|
6601
|
+
function capture(el) {
|
|
6602
|
+
const result = captureFn(el, {
|
|
6603
|
+
maxDepth: options.maxDepth,
|
|
6604
|
+
maxNodes: options.maxNodes,
|
|
6605
|
+
minSize: options.minSize
|
|
6606
|
+
});
|
|
6607
|
+
if (result.width > 0 && result.height > 0 && result.nodes.length > 0) {
|
|
6608
|
+
hasCaptured = true;
|
|
6609
|
+
options.onCapture(result);
|
|
6610
|
+
}
|
|
6611
|
+
}
|
|
6612
|
+
function scheduleImmediate(el) {
|
|
6613
|
+
if (frame !== void 0) cancelAnimationFrame(frame);
|
|
6614
|
+
frame = requestAnimationFrame(() => {
|
|
6615
|
+
frame = void 0;
|
|
6616
|
+
capture(el);
|
|
6617
|
+
});
|
|
6618
|
+
}
|
|
6619
|
+
function scheduleDebounced(el) {
|
|
6620
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
6621
|
+
timer = setTimeout(() => {
|
|
6622
|
+
timer = void 0;
|
|
6623
|
+
capture(el);
|
|
6624
|
+
}, debounceMs);
|
|
6625
|
+
}
|
|
6626
|
+
(0, vue.watch)(getTarget, (el) => {
|
|
6627
|
+
cleanup();
|
|
6628
|
+
hasCaptured = false;
|
|
6629
|
+
if (!el || typeof window === "undefined") return;
|
|
6630
|
+
scheduleImmediate(el);
|
|
6631
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
6632
|
+
observer = new ResizeObserver(() => {
|
|
6633
|
+
if (hasCaptured) scheduleDebounced(el);
|
|
6634
|
+
});
|
|
6635
|
+
observer.observe(el);
|
|
6636
|
+
}
|
|
6637
|
+
}, {
|
|
6638
|
+
immediate: true,
|
|
6639
|
+
flush: "post"
|
|
6640
|
+
});
|
|
6641
|
+
(0, vue.onBeforeUnmount)(cleanup);
|
|
6642
|
+
}
|
|
6643
|
+
//#endregion
|
|
6644
|
+
//#region src/composables/useSkeletonCache.ts
|
|
6645
|
+
const memory = /* @__PURE__ */ new Map();
|
|
6646
|
+
const STORAGE_PREFIX = "a-skeleton:";
|
|
6647
|
+
/**
|
|
6648
|
+
* Schema version for persisted flat `CachedShape` entries. Bump whenever the
|
|
6649
|
+
* `ShapeNode` / `CachedShape` field set changes so stale localStorage payloads
|
|
6650
|
+
* from older releases get dropped on read instead of rehydrating into a wrong
|
|
6651
|
+
* layout.
|
|
6652
|
+
*
|
|
6653
|
+
* v2 — added advanced surface signals (textRects, bg, border, boxShadow,
|
|
6654
|
+
* opacity, textAlign).
|
|
6655
|
+
*/
|
|
6656
|
+
const SCHEMA_VERSION = 2;
|
|
6657
|
+
/**
|
|
6658
|
+
* Separate in-memory + localStorage namespace for the Recipe 3 structural
|
|
6659
|
+
* shape (`StructuralShape`, `v: 3`). Kept apart from the flat-shape cache so
|
|
6660
|
+
* the two pipelines can't collide on the same `cacheKey` — `<ASkeleton>`'s
|
|
6661
|
+
* legacy cache-replay path and Recipe 3 may both run on the same page with
|
|
6662
|
+
* the same key.
|
|
6663
|
+
*/
|
|
6664
|
+
const structuralMemory = /* @__PURE__ */ new Map();
|
|
6665
|
+
const STRUCTURAL_PREFIX = "a-skeleton:s:";
|
|
6666
|
+
const STRUCTURAL_SCHEMA_VERSION = 3;
|
|
6667
|
+
/**
|
|
6668
|
+
* Lookup a captured shape by key. Reads in-memory first, then `localStorage` if
|
|
6669
|
+
* `persist` is enabled. SSR-safe — bypasses `window` access on the server.
|
|
6670
|
+
* Rehydrates pre-computed styles for shapes deserialized from older sessions
|
|
6671
|
+
* (Object.freeze + style/lineStyles don't survive `JSON.stringify` round-trip).
|
|
6672
|
+
* Drops the entry if it was written by a different schema version.
|
|
6673
|
+
*/
|
|
6674
|
+
function getCached(key, persist) {
|
|
6675
|
+
const hit = memory.get(key);
|
|
6676
|
+
if (hit) return hit;
|
|
6677
|
+
if (!persist || typeof window === "undefined") return void 0;
|
|
6678
|
+
try {
|
|
6679
|
+
const storageKey = STORAGE_PREFIX + key;
|
|
6680
|
+
const raw = window.localStorage.getItem(storageKey);
|
|
6681
|
+
if (!raw) return void 0;
|
|
6682
|
+
const parsed = JSON.parse(raw);
|
|
6683
|
+
if (parsed.v !== SCHEMA_VERSION) {
|
|
6684
|
+
window.localStorage.removeItem(storageKey);
|
|
6685
|
+
return;
|
|
6686
|
+
}
|
|
6687
|
+
const rehydrated = rehydrateShape(parsed);
|
|
6688
|
+
memory.set(key, rehydrated);
|
|
6689
|
+
return rehydrated;
|
|
6690
|
+
} catch {
|
|
6691
|
+
return;
|
|
6692
|
+
}
|
|
6693
|
+
}
|
|
6694
|
+
/** Store a captured shape. `persist=true` mirrors to `localStorage`. */
|
|
6695
|
+
function setCached(key, value, persist) {
|
|
6696
|
+
memory.set(key, value);
|
|
6697
|
+
if (!persist || typeof window === "undefined") return;
|
|
6698
|
+
try {
|
|
6699
|
+
const lean = {
|
|
6700
|
+
v: SCHEMA_VERSION,
|
|
6701
|
+
width: value.width,
|
|
6702
|
+
height: value.height,
|
|
6703
|
+
nodes: leanNodes(value.nodes),
|
|
6704
|
+
truncated: value.truncated
|
|
6705
|
+
};
|
|
6706
|
+
window.localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(lean));
|
|
6707
|
+
} catch {}
|
|
6708
|
+
}
|
|
6709
|
+
/**
|
|
6710
|
+
* Drop a single entry (or all entries when no key) — wipes both the flat-shape
|
|
6711
|
+
* and the structural-shape namespaces. Exposed for tests + manual invalidation.
|
|
6712
|
+
*/
|
|
6713
|
+
function clearCached(key) {
|
|
6714
|
+
if (!key) {
|
|
6715
|
+
memory.clear();
|
|
6716
|
+
structuralMemory.clear();
|
|
6717
|
+
if (typeof window === "undefined") return;
|
|
6718
|
+
try {
|
|
6719
|
+
const ls = window.localStorage;
|
|
6720
|
+
const stale = [];
|
|
6721
|
+
for (let i = 0; i < ls.length; i++) {
|
|
6722
|
+
const k = ls.key(i);
|
|
6723
|
+
if (!k) continue;
|
|
6724
|
+
if (k.startsWith(STORAGE_PREFIX) || k.startsWith(STRUCTURAL_PREFIX)) stale.push(k);
|
|
6725
|
+
}
|
|
6726
|
+
for (const k of stale) ls.removeItem(k);
|
|
6727
|
+
} catch {}
|
|
6728
|
+
return;
|
|
6729
|
+
}
|
|
6730
|
+
memory.delete(key);
|
|
6731
|
+
structuralMemory.delete(key);
|
|
6732
|
+
if (typeof window === "undefined") return;
|
|
6733
|
+
try {
|
|
6734
|
+
window.localStorage.removeItem(STORAGE_PREFIX + key);
|
|
6735
|
+
window.localStorage.removeItem(STRUCTURAL_PREFIX + key);
|
|
6736
|
+
} catch {}
|
|
6737
|
+
}
|
|
6738
|
+
/**
|
|
6739
|
+
* Drop a single structural-shape entry (or all when no key). Kept separate
|
|
6740
|
+
* from `clearCached` so callers that only manage structural shapes don't have
|
|
6741
|
+
* to reach across namespaces.
|
|
6742
|
+
*/
|
|
6743
|
+
function clearCachedStructural(key) {
|
|
6744
|
+
if (!key) {
|
|
6745
|
+
structuralMemory.clear();
|
|
6746
|
+
if (typeof window === "undefined") return;
|
|
6747
|
+
try {
|
|
6748
|
+
const ls = window.localStorage;
|
|
6749
|
+
const stale = [];
|
|
6750
|
+
for (let i = 0; i < ls.length; i++) {
|
|
6751
|
+
const k = ls.key(i);
|
|
6752
|
+
if (k && k.startsWith(STRUCTURAL_PREFIX)) stale.push(k);
|
|
6753
|
+
}
|
|
6754
|
+
for (const k of stale) ls.removeItem(k);
|
|
6755
|
+
} catch {}
|
|
6756
|
+
return;
|
|
6757
|
+
}
|
|
6758
|
+
structuralMemory.delete(key);
|
|
6759
|
+
if (typeof window === "undefined") return;
|
|
6760
|
+
try {
|
|
6761
|
+
window.localStorage.removeItem(STRUCTURAL_PREFIX + key);
|
|
6762
|
+
} catch {}
|
|
6763
|
+
}
|
|
6764
|
+
/**
|
|
6765
|
+
* Lookup a structural shape by key. Reads in-memory first, then `localStorage`
|
|
6766
|
+
* when `persist` is enabled. Stale schema versions get purged on read.
|
|
6767
|
+
*/
|
|
6768
|
+
function getCachedStructural(key, persist) {
|
|
6769
|
+
const hit = structuralMemory.get(key);
|
|
6770
|
+
if (hit) return hit;
|
|
6771
|
+
if (!persist || typeof window === "undefined") return void 0;
|
|
6772
|
+
try {
|
|
6773
|
+
const storageKey = STRUCTURAL_PREFIX + key;
|
|
6774
|
+
const raw = window.localStorage.getItem(storageKey);
|
|
6775
|
+
if (!raw) return void 0;
|
|
6776
|
+
const parsed = JSON.parse(raw);
|
|
6777
|
+
if (parsed.v !== STRUCTURAL_SCHEMA_VERSION) {
|
|
6778
|
+
window.localStorage.removeItem(storageKey);
|
|
6779
|
+
return;
|
|
6780
|
+
}
|
|
6781
|
+
const rehydrated = rehydrateStructuralShape(parsed);
|
|
6782
|
+
structuralMemory.set(key, rehydrated);
|
|
6783
|
+
return rehydrated;
|
|
6784
|
+
} catch {
|
|
6785
|
+
return;
|
|
6786
|
+
}
|
|
6787
|
+
}
|
|
6788
|
+
/** Store a structural shape. `persist=true` mirrors to `localStorage`. */
|
|
6789
|
+
function setCachedStructural(key, value, persist) {
|
|
6790
|
+
structuralMemory.set(key, value);
|
|
6791
|
+
if (!persist || typeof window === "undefined") return;
|
|
6792
|
+
try {
|
|
6793
|
+
const payload = {
|
|
6794
|
+
v: STRUCTURAL_SCHEMA_VERSION,
|
|
6795
|
+
width: value.width,
|
|
6796
|
+
height: value.height,
|
|
6797
|
+
nodes: value.nodes.map(leanStructuralNode),
|
|
6798
|
+
truncated: value.truncated,
|
|
6799
|
+
capturedAt: value.capturedAt
|
|
6800
|
+
};
|
|
6801
|
+
window.localStorage.setItem(STRUCTURAL_PREFIX + key, JSON.stringify(payload));
|
|
6802
|
+
} catch {}
|
|
6803
|
+
}
|
|
6804
|
+
function leanStructuralNode(node) {
|
|
6805
|
+
if (node.kind === "container") return {
|
|
6806
|
+
kind: "container",
|
|
6807
|
+
tag: node.tag,
|
|
6808
|
+
className: node.className,
|
|
6809
|
+
style: node.style,
|
|
6810
|
+
children: node.children.map(leanStructuralNode)
|
|
6811
|
+
};
|
|
6812
|
+
return {
|
|
6813
|
+
kind: "leaf",
|
|
6814
|
+
leafKind: node.leafKind,
|
|
6815
|
+
className: node.className,
|
|
6816
|
+
style: node.style,
|
|
6817
|
+
textLines: node.textLines
|
|
6818
|
+
};
|
|
6819
|
+
}
|
|
6820
|
+
function rehydrateStructuralShape(persisted) {
|
|
6821
|
+
return Object.freeze({
|
|
6822
|
+
v: STRUCTURAL_SCHEMA_VERSION,
|
|
6823
|
+
width: persisted.width,
|
|
6824
|
+
height: persisted.height,
|
|
6825
|
+
nodes: Object.freeze(persisted.nodes.map(rehydrateStructuralNode)),
|
|
6826
|
+
truncated: persisted.truncated,
|
|
6827
|
+
capturedAt: persisted.capturedAt
|
|
6828
|
+
});
|
|
6829
|
+
}
|
|
6830
|
+
function rehydrateStructuralNode(node) {
|
|
6831
|
+
if (node.kind === "container") return Object.freeze({
|
|
6832
|
+
kind: "container",
|
|
6833
|
+
tag: node.tag,
|
|
6834
|
+
className: node.className,
|
|
6835
|
+
style: Object.freeze({ ...node.style }),
|
|
6836
|
+
children: Object.freeze(node.children.map(rehydrateStructuralNode))
|
|
6837
|
+
});
|
|
6838
|
+
return Object.freeze({
|
|
6839
|
+
kind: "leaf",
|
|
6840
|
+
leafKind: node.leafKind,
|
|
6841
|
+
className: node.className ?? "",
|
|
6842
|
+
style: Object.freeze({ ...node.style }),
|
|
6843
|
+
textLines: node.textLines ? Object.freeze([...node.textLines]) : void 0
|
|
6844
|
+
});
|
|
6845
|
+
}
|
|
6846
|
+
/**
|
|
6847
|
+
* Rebuild `style` + `lineStyles` for nodes that lost them via serialization.
|
|
6848
|
+
* Walks the array in-place where possible and freezes the result so the render
|
|
6849
|
+
* path stays allocation-free.
|
|
6850
|
+
*/
|
|
6851
|
+
function rehydrateShape(shape) {
|
|
6852
|
+
const nodes = shape.nodes.map((n) => freezeNodeStyles(n));
|
|
6853
|
+
return Object.freeze({
|
|
6854
|
+
nodes: Object.freeze(nodes),
|
|
6855
|
+
width: shape.width,
|
|
6856
|
+
height: shape.height,
|
|
6857
|
+
truncated: shape.truncated
|
|
6858
|
+
});
|
|
6859
|
+
}
|
|
6860
|
+
function leanNodes(nodes) {
|
|
6861
|
+
return nodes.map((n) => ({
|
|
6862
|
+
type: n.type,
|
|
6863
|
+
x: n.x,
|
|
6864
|
+
y: n.y,
|
|
6865
|
+
w: n.w,
|
|
6866
|
+
h: n.h,
|
|
6867
|
+
radius: n.radius,
|
|
6868
|
+
lines: n.lines,
|
|
6869
|
+
lineHeight: n.lineHeight,
|
|
6870
|
+
textRects: n.textRects ? n.textRects.map((r) => ({
|
|
6871
|
+
x: r.x,
|
|
6872
|
+
y: r.y,
|
|
6873
|
+
w: r.w,
|
|
6874
|
+
h: r.h
|
|
6875
|
+
})) : void 0,
|
|
6876
|
+
bg: n.bg,
|
|
6877
|
+
border: n.border,
|
|
6878
|
+
boxShadow: n.boxShadow,
|
|
6879
|
+
opacity: n.opacity,
|
|
6880
|
+
textAlign: n.textAlign
|
|
6881
|
+
}));
|
|
6882
|
+
}
|
|
6883
|
+
function freezeNodeStyles(node) {
|
|
6884
|
+
const baseStyle = {
|
|
6885
|
+
left: `${node.x}px`,
|
|
6886
|
+
top: `${node.y}px`,
|
|
6887
|
+
width: `${node.w}px`,
|
|
6888
|
+
height: `${node.h}px`,
|
|
6889
|
+
borderRadius: `${node.radius}px`
|
|
6890
|
+
};
|
|
6891
|
+
applyVisualSignals(baseStyle, node);
|
|
6892
|
+
const style = Object.freeze(baseStyle);
|
|
6893
|
+
let lineStyles;
|
|
6894
|
+
if (node.type === "text" && node.textRects && node.textRects.length > 0) {
|
|
6895
|
+
const radiusStr = `${node.radius}px`;
|
|
6896
|
+
const arr = [];
|
|
6897
|
+
for (const r of node.textRects) {
|
|
6898
|
+
const lineStyle = {
|
|
6899
|
+
left: `${r.x}px`,
|
|
6900
|
+
top: `${r.y}px`,
|
|
6901
|
+
width: `${r.w}px`,
|
|
6902
|
+
height: `${r.h}px`,
|
|
6903
|
+
borderRadius: radiusStr
|
|
6904
|
+
};
|
|
6905
|
+
applyVisualSignals(lineStyle, node);
|
|
6906
|
+
arr.push(Object.freeze(lineStyle));
|
|
6907
|
+
}
|
|
6908
|
+
lineStyles = Object.freeze(arr);
|
|
6909
|
+
} else if (node.type === "text" && node.lines && node.lines > 1) {
|
|
6910
|
+
const lh = node.lineHeight ?? Math.round(node.h / node.lines);
|
|
6911
|
+
const barHeight = Math.max(8, Math.round(lh * .7));
|
|
6912
|
+
const widthFull = node.w;
|
|
6913
|
+
const widthLast = Math.max(40, Math.round(node.w * .7));
|
|
6914
|
+
const heightStr = `${barHeight}px`;
|
|
6915
|
+
const radiusStr = `${node.radius}px`;
|
|
6916
|
+
const arr = [];
|
|
6917
|
+
for (let i = 1; i <= node.lines; i++) {
|
|
6918
|
+
const isLast = i === node.lines;
|
|
6919
|
+
const lineWidth = isLast ? widthLast : widthFull;
|
|
6920
|
+
let leftX = node.x;
|
|
6921
|
+
if (isLast && node.textAlign) {
|
|
6922
|
+
const slack = widthFull - lineWidth;
|
|
6923
|
+
if (node.textAlign === "center") leftX = node.x + Math.round(slack / 2);
|
|
6924
|
+
else if (node.textAlign === "right" || node.textAlign === "end") leftX = node.x + slack;
|
|
6925
|
+
}
|
|
6926
|
+
const lineStyle = {
|
|
6927
|
+
left: `${leftX}px`,
|
|
6928
|
+
top: `${node.y + (i - 1) * lh}px`,
|
|
6929
|
+
width: `${lineWidth}px`,
|
|
6930
|
+
height: heightStr,
|
|
6931
|
+
borderRadius: radiusStr
|
|
6932
|
+
};
|
|
6933
|
+
applyVisualSignals(lineStyle, node);
|
|
6934
|
+
arr.push(Object.freeze(lineStyle));
|
|
6935
|
+
}
|
|
6936
|
+
lineStyles = Object.freeze(arr);
|
|
4300
6937
|
}
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
6938
|
+
return Object.freeze({
|
|
6939
|
+
type: node.type,
|
|
6940
|
+
x: node.x,
|
|
6941
|
+
y: node.y,
|
|
6942
|
+
w: node.w,
|
|
6943
|
+
h: node.h,
|
|
6944
|
+
radius: node.radius,
|
|
6945
|
+
lines: node.lines,
|
|
6946
|
+
lineHeight: node.lineHeight,
|
|
6947
|
+
textRects: node.textRects ? Object.freeze(node.textRects) : void 0,
|
|
6948
|
+
bg: node.bg,
|
|
6949
|
+
border: node.border,
|
|
6950
|
+
boxShadow: node.boxShadow,
|
|
6951
|
+
opacity: node.opacity,
|
|
6952
|
+
textAlign: node.textAlign,
|
|
6953
|
+
style,
|
|
6954
|
+
lineStyles
|
|
6955
|
+
});
|
|
6956
|
+
}
|
|
6957
|
+
function applyVisualSignals(out, node) {
|
|
6958
|
+
if (node.bg) out.background = node.bg;
|
|
6959
|
+
if (node.border) out.border = node.border;
|
|
6960
|
+
if (node.boxShadow) out.boxShadow = node.boxShadow;
|
|
6961
|
+
if (node.opacity !== void 0) out.opacity = node.opacity;
|
|
4323
6962
|
}
|
|
4324
|
-
var ASkeletonLayer_default = /* @__PURE__ */ export_helper_default(_sfc_main$1, [["render", _sfc_render$1], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/ASkeletonLayer.vue"]]);
|
|
4325
6963
|
//#endregion
|
|
4326
|
-
//#region src/
|
|
4327
|
-
const
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
6964
|
+
//#region src/utils/walkStructural.ts
|
|
6965
|
+
const DEFAULT_MAX_DEPTH$1 = 12;
|
|
6966
|
+
const DEFAULT_MAX_NODES = 500;
|
|
6967
|
+
const DEFAULT_MIN_SIZE = 4;
|
|
6968
|
+
/** Tags treated as atomic leaves — never recursed into, rendered as one block. */
|
|
6969
|
+
const ATOMIC_TAGS = new Set([
|
|
6970
|
+
"img",
|
|
6971
|
+
"picture",
|
|
6972
|
+
"svg",
|
|
6973
|
+
"canvas",
|
|
6974
|
+
"video",
|
|
6975
|
+
"audio",
|
|
6976
|
+
"input",
|
|
6977
|
+
"textarea",
|
|
6978
|
+
"select",
|
|
6979
|
+
"button",
|
|
6980
|
+
"progress",
|
|
6981
|
+
"meter",
|
|
6982
|
+
"hr",
|
|
6983
|
+
"iframe",
|
|
6984
|
+
"object",
|
|
6985
|
+
"embed",
|
|
6986
|
+
"br"
|
|
6987
|
+
]);
|
|
6988
|
+
/**
|
|
6989
|
+
* Tags whose content is conventionally text. When the author writes
|
|
6990
|
+
* `<h3>{{ data?.name }}</h3>` and `data` is null during loading, the
|
|
6991
|
+
* interpolation is empty and `hasDirectText` returns false — without this
|
|
6992
|
+
* fallback the element classifies as `leaf-block` (a generic shimmer rect)
|
|
6993
|
+
* instead of `leaf-text` (a shimmer text bar at the element's natural
|
|
6994
|
+
* rendered dimensions).
|
|
6995
|
+
*/
|
|
6996
|
+
const TEXT_OWNERS = new Set([
|
|
6997
|
+
"h1",
|
|
6998
|
+
"h2",
|
|
6999
|
+
"h3",
|
|
7000
|
+
"h4",
|
|
7001
|
+
"h5",
|
|
7002
|
+
"h6",
|
|
7003
|
+
"p",
|
|
7004
|
+
"span",
|
|
7005
|
+
"a",
|
|
7006
|
+
"em",
|
|
7007
|
+
"strong",
|
|
7008
|
+
"small",
|
|
7009
|
+
"code",
|
|
7010
|
+
"b",
|
|
7011
|
+
"i",
|
|
7012
|
+
"mark",
|
|
7013
|
+
"label",
|
|
7014
|
+
"caption",
|
|
7015
|
+
"time",
|
|
7016
|
+
"dt",
|
|
7017
|
+
"dd",
|
|
7018
|
+
"li",
|
|
7019
|
+
"th",
|
|
7020
|
+
"td",
|
|
7021
|
+
"figcaption",
|
|
7022
|
+
"blockquote",
|
|
7023
|
+
"cite",
|
|
7024
|
+
"q"
|
|
7025
|
+
]);
|
|
7026
|
+
/**
|
|
7027
|
+
* Layout-affecting CSS captured on **every** node. Includes display + box
|
|
7028
|
+
* model + flex + grid + positioning so any element type renders correctly
|
|
7029
|
+
* regardless of its role in the parent's layout (flex item, inline-block,
|
|
7030
|
+
* absolutely-positioned overlay, sticky bar, grid cell).
|
|
7031
|
+
*
|
|
7032
|
+
* Width / height are intentionally NOT here — containers get sized by their
|
|
7033
|
+
* children; leaves get explicit width / height from their `getBoundingClientRect`
|
|
7034
|
+
* in `emitLeaf` below.
|
|
7035
|
+
*/
|
|
7036
|
+
const LAYOUT_PROPS = [
|
|
7037
|
+
"display",
|
|
7038
|
+
"position",
|
|
7039
|
+
"top",
|
|
7040
|
+
"right",
|
|
7041
|
+
"bottom",
|
|
7042
|
+
"left",
|
|
7043
|
+
"z-index",
|
|
7044
|
+
"vertical-align",
|
|
7045
|
+
"float",
|
|
7046
|
+
"clear",
|
|
7047
|
+
"box-sizing",
|
|
7048
|
+
"margin-top",
|
|
7049
|
+
"margin-right",
|
|
7050
|
+
"margin-bottom",
|
|
7051
|
+
"margin-left",
|
|
7052
|
+
"padding-top",
|
|
7053
|
+
"padding-right",
|
|
7054
|
+
"padding-bottom",
|
|
7055
|
+
"padding-left",
|
|
7056
|
+
"flex",
|
|
7057
|
+
"flex-direction",
|
|
7058
|
+
"flex-wrap",
|
|
7059
|
+
"flex-grow",
|
|
7060
|
+
"flex-shrink",
|
|
7061
|
+
"flex-basis",
|
|
7062
|
+
"justify-content",
|
|
7063
|
+
"align-items",
|
|
7064
|
+
"align-content",
|
|
7065
|
+
"align-self",
|
|
7066
|
+
"justify-self",
|
|
7067
|
+
"gap",
|
|
7068
|
+
"row-gap",
|
|
7069
|
+
"column-gap",
|
|
7070
|
+
"order",
|
|
7071
|
+
"grid-template-columns",
|
|
7072
|
+
"grid-template-rows",
|
|
7073
|
+
"grid-template-areas",
|
|
7074
|
+
"grid-auto-flow",
|
|
7075
|
+
"grid-auto-columns",
|
|
7076
|
+
"grid-auto-rows",
|
|
7077
|
+
"grid-column",
|
|
7078
|
+
"grid-row",
|
|
7079
|
+
"grid-area"
|
|
7080
|
+
];
|
|
7081
|
+
/**
|
|
7082
|
+
* Visual signals captured on **every** node. These give each rendered
|
|
7083
|
+
* placeholder the same surface identity the real element had — fill,
|
|
7084
|
+
* outline, elevation, transparency, transforms.
|
|
7085
|
+
*/
|
|
7086
|
+
const VISUAL_PROPS = [
|
|
7087
|
+
"border-top-width",
|
|
7088
|
+
"border-right-width",
|
|
7089
|
+
"border-bottom-width",
|
|
7090
|
+
"border-left-width",
|
|
7091
|
+
"border-top-style",
|
|
7092
|
+
"border-right-style",
|
|
7093
|
+
"border-bottom-style",
|
|
7094
|
+
"border-left-style",
|
|
7095
|
+
"border-top-color",
|
|
7096
|
+
"border-right-color",
|
|
7097
|
+
"border-bottom-color",
|
|
7098
|
+
"border-left-color",
|
|
7099
|
+
"border-top-left-radius",
|
|
7100
|
+
"border-top-right-radius",
|
|
7101
|
+
"border-bottom-right-radius",
|
|
7102
|
+
"border-bottom-left-radius",
|
|
7103
|
+
"background-color",
|
|
7104
|
+
"background-image",
|
|
7105
|
+
"background-position",
|
|
7106
|
+
"background-size",
|
|
7107
|
+
"background-repeat",
|
|
7108
|
+
"background-origin",
|
|
7109
|
+
"background-clip",
|
|
7110
|
+
"box-shadow",
|
|
7111
|
+
"opacity",
|
|
7112
|
+
"filter",
|
|
7113
|
+
"backdrop-filter",
|
|
7114
|
+
"transform",
|
|
7115
|
+
"transform-origin",
|
|
7116
|
+
"mix-blend-mode"
|
|
7117
|
+
];
|
|
7118
|
+
const ALL_PROPS = [...LAYOUT_PROPS, ...VISUAL_PROPS];
|
|
7119
|
+
/**
|
|
7120
|
+
* Walk `root`'s descendants and produce a frozen tree-shaped capture.
|
|
7121
|
+
* Direct children of `root` become top-level `nodes`; recursive children
|
|
7122
|
+
* live on their respective `ContainerNode`s.
|
|
7123
|
+
*/
|
|
7124
|
+
function walkStructural(root, options = {}) {
|
|
7125
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH$1;
|
|
7126
|
+
const maxNodes = options.maxNodes ?? DEFAULT_MAX_NODES;
|
|
7127
|
+
const minSize = options.minSize ?? DEFAULT_MIN_SIZE;
|
|
7128
|
+
const rootRect = root.getBoundingClientRect();
|
|
7129
|
+
const state = {
|
|
7130
|
+
count: 0,
|
|
7131
|
+
truncated: false
|
|
7132
|
+
};
|
|
7133
|
+
const ctx = {
|
|
7134
|
+
maxDepth,
|
|
7135
|
+
maxNodes,
|
|
7136
|
+
minSize,
|
|
7137
|
+
state
|
|
7138
|
+
};
|
|
7139
|
+
const nodes = [];
|
|
7140
|
+
for (let i = 0; i < root.children.length; i++) {
|
|
7141
|
+
if (state.count >= maxNodes) {
|
|
7142
|
+
state.truncated = true;
|
|
7143
|
+
break;
|
|
4367
7144
|
}
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
7145
|
+
const node = capture(root.children[i], rootRect, 1, ctx);
|
|
7146
|
+
if (node) nodes.push(node);
|
|
7147
|
+
}
|
|
7148
|
+
return Object.freeze({
|
|
7149
|
+
width: Math.round(rootRect.width),
|
|
7150
|
+
height: Math.round(rootRect.height),
|
|
7151
|
+
nodes: Object.freeze(nodes),
|
|
7152
|
+
truncated: state.truncated,
|
|
7153
|
+
capturedAt: Date.now(),
|
|
7154
|
+
v: 3
|
|
7155
|
+
});
|
|
7156
|
+
}
|
|
7157
|
+
function capture(el, origin, depth, ctx) {
|
|
7158
|
+
if (ctx.state.count >= ctx.maxNodes) {
|
|
7159
|
+
ctx.state.truncated = true;
|
|
7160
|
+
return null;
|
|
7161
|
+
}
|
|
7162
|
+
if (el.dataset?.skeletonIgnore !== void 0) return null;
|
|
7163
|
+
const cs = window.getComputedStyle(el);
|
|
7164
|
+
if (cs.display === "none" || cs.visibility === "hidden") return null;
|
|
7165
|
+
const opacity = parseFloat(cs.opacity);
|
|
7166
|
+
if (Number.isFinite(opacity) && opacity === 0) return null;
|
|
7167
|
+
const rect = el.getBoundingClientRect();
|
|
7168
|
+
const tag = el.tagName.toLowerCase();
|
|
7169
|
+
const isTextOwner = TEXT_OWNERS.has(tag);
|
|
7170
|
+
let effectiveHeight = rect.height;
|
|
7171
|
+
if (isTextOwner && effectiveHeight < ctx.minSize) {
|
|
7172
|
+
const lh = parseFloat(cs.lineHeight);
|
|
7173
|
+
const fs = parseFloat(cs.fontSize);
|
|
7174
|
+
if (Number.isFinite(lh) && lh > 0) effectiveHeight = lh;
|
|
7175
|
+
else if (Number.isFinite(fs) && fs > 0) effectiveHeight = fs * 1.4;
|
|
7176
|
+
}
|
|
7177
|
+
if (rect.width < ctx.minSize || effectiveHeight < ctx.minSize) return null;
|
|
7178
|
+
const hasStop = el.dataset?.skeletonStop !== void 0;
|
|
7179
|
+
const isAtomic = ATOMIC_TAGS.has(tag);
|
|
7180
|
+
const reachedDepth = depth >= ctx.maxDepth;
|
|
7181
|
+
const childrenEls = collectVisibleChildren(el);
|
|
7182
|
+
if (hasStop || isAtomic || reachedDepth || childrenEls.length === 0) return emitLeaf(el, tag, cs, rect, effectiveHeight, isAtomic, ctx);
|
|
7183
|
+
const children = [];
|
|
7184
|
+
for (const child of childrenEls) {
|
|
7185
|
+
if (ctx.state.count >= ctx.maxNodes) {
|
|
7186
|
+
ctx.state.truncated = true;
|
|
7187
|
+
break;
|
|
4381
7188
|
}
|
|
4382
|
-
const
|
|
4383
|
-
|
|
4384
|
-
props,
|
|
4385
|
-
animationClass,
|
|
4386
|
-
blockClass,
|
|
4387
|
-
toLength,
|
|
4388
|
-
radiusValue,
|
|
4389
|
-
blockStyle: (0, vue.computed)(() => ({
|
|
4390
|
-
width: toLength(props.w),
|
|
4391
|
-
height: toLength(props.h),
|
|
4392
|
-
borderRadius: radiusValue.value
|
|
4393
|
-
})),
|
|
4394
|
-
isMultiLineText: (0, vue.computed)(() => props.type === "text" && props.lines > 1),
|
|
4395
|
-
get cn() {
|
|
4396
|
-
return cn;
|
|
4397
|
-
}
|
|
4398
|
-
};
|
|
4399
|
-
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
4400
|
-
enumerable: false,
|
|
4401
|
-
value: true
|
|
4402
|
-
});
|
|
4403
|
-
return __returned__;
|
|
7189
|
+
const node = capture(child, origin, depth + 1, ctx);
|
|
7190
|
+
if (node) children.push(node);
|
|
4404
7191
|
}
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
7192
|
+
if (children.length === 0) return emitLeaf(el, tag, cs, rect, effectiveHeight, false, ctx);
|
|
7193
|
+
ctx.state.count++;
|
|
7194
|
+
const node = {
|
|
7195
|
+
kind: "container",
|
|
7196
|
+
tag,
|
|
7197
|
+
className: el.getAttribute("class") ?? "",
|
|
7198
|
+
style: readComputedStyles(cs, ALL_PROPS),
|
|
7199
|
+
children: Object.freeze(children)
|
|
7200
|
+
};
|
|
7201
|
+
return Object.freeze(node);
|
|
7202
|
+
}
|
|
7203
|
+
function emitLeaf(el, tag, cs, rect, effectiveHeight, isAtomic, ctx) {
|
|
7204
|
+
const w = Math.round(rect.width);
|
|
7205
|
+
const h = Math.round(effectiveHeight);
|
|
7206
|
+
const leafKind = classifyLeaf(tag, el, isAtomic);
|
|
7207
|
+
const captured = { ...readComputedStyles(cs, ALL_PROPS) };
|
|
7208
|
+
if (captured.display === "inline") captured.display = "inline-block";
|
|
7209
|
+
const style = {
|
|
7210
|
+
...captured,
|
|
7211
|
+
width: `${w}px`,
|
|
7212
|
+
height: `${h}px`
|
|
7213
|
+
};
|
|
7214
|
+
let textLines;
|
|
7215
|
+
if (leafKind === "text") {
|
|
7216
|
+
const rawLines = captureTextLines(el, rect);
|
|
7217
|
+
if (rawLines && rawLines.length > 0) textLines = Object.freeze(rawLines.map((r) => ({
|
|
7218
|
+
x: Math.max(0, r.x),
|
|
7219
|
+
y: Math.max(0, r.y),
|
|
7220
|
+
w: r.w,
|
|
7221
|
+
h: r.h
|
|
7222
|
+
})));
|
|
7223
|
+
}
|
|
7224
|
+
ctx.state.count++;
|
|
7225
|
+
const node = {
|
|
7226
|
+
kind: "leaf",
|
|
7227
|
+
leafKind,
|
|
7228
|
+
className: el.getAttribute("class") ?? "",
|
|
7229
|
+
style: Object.freeze(style),
|
|
7230
|
+
textLines
|
|
7231
|
+
};
|
|
7232
|
+
return Object.freeze(node);
|
|
7233
|
+
}
|
|
7234
|
+
function classifyLeaf(tag, el, isAtomic) {
|
|
7235
|
+
if (tag === "img" || tag === "picture") return "image";
|
|
7236
|
+
if (tag === "video") return "image";
|
|
7237
|
+
if (isAtomic) return "media";
|
|
7238
|
+
if (hasDirectText(el)) return "text";
|
|
7239
|
+
if (TEXT_OWNERS.has(tag)) return "text";
|
|
7240
|
+
return "block";
|
|
4429
7241
|
}
|
|
4430
|
-
var ASkeletonBlock_default = /* @__PURE__ */ export_helper_default(_sfc_main, [
|
|
4431
|
-
["render", _sfc_render],
|
|
4432
|
-
["__scopeId", "data-v-bdfba69a"],
|
|
4433
|
-
["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/ASkeletonBlock.vue"]
|
|
4434
|
-
]);
|
|
4435
7242
|
//#endregion
|
|
4436
7243
|
//#region src/composables/useSkeleton.ts
|
|
7244
|
+
const DEFAULT_MAX_DEPTH = 12;
|
|
4437
7245
|
/**
|
|
4438
|
-
* High-level building block for
|
|
4439
|
-
* + reactivity
|
|
7246
|
+
* High-level building block for Recipe 3 — wires the structural capture +
|
|
7247
|
+
* cache + reactivity around a target element. The reactive `shape` is fed to
|
|
7248
|
+
* `<ASkeletonLayer>` which renders it in normal flow inside the consumer's
|
|
7249
|
+
* container.
|
|
4440
7250
|
*
|
|
4441
7251
|
* ```ts
|
|
4442
7252
|
* const containerRef = ref<HTMLElement | null>(null);
|
|
@@ -4453,13 +7263,14 @@ var ASkeletonBlock_default = /* @__PURE__ */ export_helper_default(_sfc_main, [
|
|
|
4453
7263
|
* </div>
|
|
4454
7264
|
* ```
|
|
4455
7265
|
*
|
|
4456
|
-
* For more control, drop down to `useShapeProbe`
|
|
4457
|
-
*
|
|
7266
|
+
* For more control, drop down to `useShapeProbe` (pass `walkStructural` as the
|
|
7267
|
+
* capture strategy) + `getCachedStructural` / `setCachedStructural` and compose
|
|
7268
|
+
* your own orchestration.
|
|
4458
7269
|
*/
|
|
4459
7270
|
function useSkeleton(options) {
|
|
4460
7271
|
const persist = options.persist ?? false;
|
|
4461
|
-
const maxDepth = options.maxDepth ??
|
|
4462
|
-
const shape = (0, vue.shallowRef)(
|
|
7272
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
7273
|
+
const shape = (0, vue.shallowRef)(getCachedStructural(options.cacheKey, persist));
|
|
4463
7274
|
if (options.target) {
|
|
4464
7275
|
const getTarget = options.target;
|
|
4465
7276
|
useShapeProbe(getTarget, {
|
|
@@ -4467,8 +7278,9 @@ function useSkeleton(options) {
|
|
|
4467
7278
|
maxNodes: options.maxNodes,
|
|
4468
7279
|
minSize: options.minSize,
|
|
4469
7280
|
resizeDebounceMs: options.resizeDebounceMs,
|
|
7281
|
+
capture: walkStructural,
|
|
4470
7282
|
onCapture: (captured) => {
|
|
4471
|
-
|
|
7283
|
+
setCachedStructural(options.cacheKey, captured, persist);
|
|
4472
7284
|
shape.value = captured;
|
|
4473
7285
|
}
|
|
4474
7286
|
});
|
|
@@ -4476,18 +7288,18 @@ function useSkeleton(options) {
|
|
|
4476
7288
|
function captureNow() {
|
|
4477
7289
|
const el = options.target?.();
|
|
4478
7290
|
if (!el || typeof window === "undefined") return void 0;
|
|
4479
|
-
const captured =
|
|
7291
|
+
const captured = walkStructural(el, {
|
|
4480
7292
|
maxDepth,
|
|
4481
7293
|
maxNodes: options.maxNodes,
|
|
4482
7294
|
minSize: options.minSize
|
|
4483
7295
|
});
|
|
4484
7296
|
if (captured.width <= 0 || captured.height <= 0 || captured.nodes.length === 0) return void 0;
|
|
4485
|
-
|
|
7297
|
+
setCachedStructural(options.cacheKey, captured, persist);
|
|
4486
7298
|
shape.value = captured;
|
|
4487
7299
|
return captured;
|
|
4488
7300
|
}
|
|
4489
7301
|
function clear() {
|
|
4490
|
-
|
|
7302
|
+
clearCachedStructural(options.cacheKey);
|
|
4491
7303
|
shape.value = void 0;
|
|
4492
7304
|
}
|
|
4493
7305
|
return {
|
|
@@ -4497,17 +7309,73 @@ function useSkeleton(options) {
|
|
|
4497
7309
|
};
|
|
4498
7310
|
}
|
|
4499
7311
|
//#endregion
|
|
7312
|
+
//#region src/utils/fingerprint.ts
|
|
7313
|
+
/**
|
|
7314
|
+
* Derive a default cache key from a slot's vnode tree. Returns the first
|
|
7315
|
+
* non-comment child's component name (or HTML tag). When the slot only contains
|
|
7316
|
+
* text / comments / unknown content, returns `'anonymous'` so the caller can
|
|
7317
|
+
* still cache, but with no useful identity — encourage an explicit `cacheKey`.
|
|
7318
|
+
*/
|
|
7319
|
+
function fingerprintSlot(vnodes) {
|
|
7320
|
+
if (!vnodes) return "anonymous";
|
|
7321
|
+
for (const vnode of vnodes) {
|
|
7322
|
+
const tag = describeVNode(vnode);
|
|
7323
|
+
if (tag) return tag;
|
|
7324
|
+
}
|
|
7325
|
+
return "anonymous";
|
|
7326
|
+
}
|
|
7327
|
+
function describeVNode(vnode) {
|
|
7328
|
+
const t = vnode.type;
|
|
7329
|
+
if (t === vue.Comment || t === vue.Text) return void 0;
|
|
7330
|
+
if (t === vue.Fragment) {
|
|
7331
|
+
const children = vnode.children;
|
|
7332
|
+
if (Array.isArray(children)) {
|
|
7333
|
+
for (const child of children) if (child && typeof child === "object" && "type" in child) {
|
|
7334
|
+
const found = describeVNode(child);
|
|
7335
|
+
if (found) return found;
|
|
7336
|
+
}
|
|
7337
|
+
}
|
|
7338
|
+
return;
|
|
7339
|
+
}
|
|
7340
|
+
if (typeof t === "string") return t;
|
|
7341
|
+
if (typeof t === "object" && t !== null) {
|
|
7342
|
+
const named = t.name ?? t.__name ?? t.displayName;
|
|
7343
|
+
if (named) return named;
|
|
7344
|
+
}
|
|
7345
|
+
}
|
|
7346
|
+
//#endregion
|
|
4500
7347
|
exports.ASkeleton = ASkeleton_default;
|
|
7348
|
+
exports.ASkeletonArticle = ASkeletonArticle_default;
|
|
7349
|
+
exports.ASkeletonAvatar = ASkeletonAvatar_default;
|
|
4501
7350
|
exports.ASkeletonBlock = ASkeletonBlock_default;
|
|
7351
|
+
exports.ASkeletonButton = ASkeletonButton_default;
|
|
7352
|
+
exports.ASkeletonCard = ASkeletonCard_default;
|
|
7353
|
+
exports.ASkeletonChart = ASkeletonChart_default;
|
|
7354
|
+
exports.ASkeletonChip = ASkeletonChip_default;
|
|
7355
|
+
exports.ASkeletonClone = ASkeletonClone_default;
|
|
7356
|
+
exports.ASkeletonDivider = ASkeletonDivider_default;
|
|
7357
|
+
exports.ASkeletonForm = ASkeletonForm_default;
|
|
7358
|
+
exports.ASkeletonHeading = ASkeletonHeading_default;
|
|
7359
|
+
exports.ASkeletonImage = ASkeletonImage_default;
|
|
7360
|
+
exports.ASkeletonInput = ASkeletonInput_default;
|
|
4502
7361
|
exports.ASkeletonLayer = ASkeletonLayer_default;
|
|
7362
|
+
exports.ASkeletonListItem = ASkeletonListItem_default;
|
|
7363
|
+
exports.ASkeletonTable = ASkeletonTable_default;
|
|
7364
|
+
exports.ASkeletonText = ASkeletonText_default;
|
|
7365
|
+
exports.ASkeletonVideo = ASkeletonVideo_default;
|
|
4503
7366
|
exports.StructuralSkeleton = StructuralSkeleton;
|
|
4504
7367
|
exports.buildStructuralSkeleton = buildStructuralSkeleton;
|
|
7368
|
+
exports.captureSnapshot = captureSnapshot;
|
|
4505
7369
|
exports.clearCached = clearCached;
|
|
7370
|
+
exports.clearCachedStructural = clearCachedStructural;
|
|
4506
7371
|
exports.fingerprintSlot = fingerprintSlot;
|
|
4507
7372
|
exports.getCached = getCached;
|
|
7373
|
+
exports.getCachedStructural = getCachedStructural;
|
|
4508
7374
|
exports.setCached = setCached;
|
|
7375
|
+
exports.setCachedStructural = setCachedStructural;
|
|
4509
7376
|
exports.useShapeProbe = useShapeProbe;
|
|
4510
7377
|
exports.useSkeleton = useSkeleton;
|
|
4511
7378
|
exports.walkDom = walkDom;
|
|
7379
|
+
exports.walkStructural = walkStructural;
|
|
4512
7380
|
|
|
4513
7381
|
//# sourceMappingURL=index.cjs.map
|