@alikhalilll/a-skeleton 1.1.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 +458 -172
- package/dist/index.cjs +3685 -840
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +527 -40
- package/dist/index.d.ts +527 -40
- package/dist/index.js +3666 -842
- 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 +212 -113
- 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 +251 -22
- package/src/index.ts +48 -2
- package/src/nuxt/index.ts +16 -0
- package/src/resolver/index.ts +16 -0
- package/src/types.ts +118 -2
- 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 +9 -3
package/dist/index.cjs
CHANGED
|
@@ -3399,704 +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
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
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
|
+
})]);
|
|
3572
3897
|
}
|
|
3573
3898
|
//#endregion
|
|
3574
|
-
//#region src/
|
|
3575
|
-
const DEFAULT_RESIZE_DEBOUNCE_MS = 150;
|
|
3899
|
+
//#region src/components/CloneNode.ts
|
|
3576
3900
|
/**
|
|
3577
|
-
*
|
|
3578
|
-
* appears or resizes.
|
|
3901
|
+
* `CloneNode` — recursive renderer for one node of a `CaptureSnapshot`.
|
|
3579
3902
|
*
|
|
3580
|
-
*
|
|
3581
|
-
* -
|
|
3582
|
-
*
|
|
3583
|
-
*
|
|
3584
|
-
*
|
|
3585
|
-
*
|
|
3586
|
-
*
|
|
3587
|
-
*
|
|
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.
|
|
3588
3912
|
*/
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
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 ?? [{
|
|
3963
|
+
x: node.x,
|
|
3964
|
+
y: node.y,
|
|
3965
|
+
w: node.w,
|
|
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"
|
|
3988
|
+
});
|
|
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
|
|
3599
4016
|
}
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
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
|
+
});
|
|
4030
|
+
//#endregion
|
|
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
|
|
3603
4058
|
}
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
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
|
+
};
|
|
3607
4078
|
}
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
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
|
+
};
|
|
3618
4090
|
}
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
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
|
|
3625
4107
|
});
|
|
4108
|
+
return __returned__;
|
|
3626
4109
|
}
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
}
|
|
3645
|
-
}, {
|
|
3646
|
-
immediate: true,
|
|
3647
|
-
flush: "post"
|
|
3648
|
-
});
|
|
3649
|
-
(0, vue.onBeforeUnmount)(cleanup);
|
|
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);
|
|
3650
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
|
+
]);
|
|
3651
4133
|
//#endregion
|
|
3652
|
-
//#region src/
|
|
3653
|
-
const memory = /* @__PURE__ */ new Map();
|
|
3654
|
-
const STORAGE_PREFIX = "a-skeleton:";
|
|
4134
|
+
//#region src/utils/domRead.ts
|
|
3655
4135
|
/**
|
|
3656
|
-
*
|
|
3657
|
-
* `
|
|
3658
|
-
*
|
|
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.
|
|
3659
4139
|
*/
|
|
3660
|
-
const
|
|
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
|
+
]);
|
|
3661
4161
|
/**
|
|
3662
|
-
*
|
|
3663
|
-
*
|
|
3664
|
-
*
|
|
3665
|
-
* (Object.freeze + style/lineStyles don't survive `JSON.stringify` round-trip).
|
|
3666
|
-
* Drops the entry if it was written by a different schema version.
|
|
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).
|
|
3667
4165
|
*/
|
|
3668
|
-
function
|
|
3669
|
-
const
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
const parsed = JSON.parse(raw);
|
|
3677
|
-
if (parsed.v !== SCHEMA_VERSION) {
|
|
3678
|
-
window.localStorage.removeItem(storageKey);
|
|
3679
|
-
return;
|
|
3680
|
-
}
|
|
3681
|
-
const rehydrated = rehydrateShape(parsed);
|
|
3682
|
-
memory.set(key, rehydrated);
|
|
3683
|
-
return rehydrated;
|
|
3684
|
-
} catch {
|
|
3685
|
-
return;
|
|
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;
|
|
3686
4174
|
}
|
|
4175
|
+
return Object.freeze(out);
|
|
3687
4176
|
}
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
memory.set(key, value);
|
|
3691
|
-
if (!persist || typeof window === "undefined") return;
|
|
3692
|
-
try {
|
|
3693
|
-
const lean = {
|
|
3694
|
-
v: SCHEMA_VERSION,
|
|
3695
|
-
width: value.width,
|
|
3696
|
-
height: value.height,
|
|
3697
|
-
nodes: leanNodes(value.nodes),
|
|
3698
|
-
truncated: value.truncated
|
|
3699
|
-
};
|
|
3700
|
-
window.localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(lean));
|
|
3701
|
-
} catch {}
|
|
4177
|
+
function camelCase(prop) {
|
|
4178
|
+
return prop.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
3702
4179
|
}
|
|
3703
|
-
/**
|
|
3704
|
-
function
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
if (
|
|
3708
|
-
try {
|
|
3709
|
-
for (const k of Object.keys(window.localStorage)) if (k.startsWith(STORAGE_PREFIX)) window.localStorage.removeItem(k);
|
|
3710
|
-
} catch {}
|
|
3711
|
-
return;
|
|
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;
|
|
3712
4185
|
}
|
|
3713
|
-
|
|
3714
|
-
if (typeof window === "undefined") return;
|
|
3715
|
-
try {
|
|
3716
|
-
window.localStorage.removeItem(STORAGE_PREFIX + key);
|
|
3717
|
-
} catch {}
|
|
3718
|
-
}
|
|
3719
|
-
/**
|
|
3720
|
-
* Rebuild `style` + `lineStyles` for nodes that lost them via serialization.
|
|
3721
|
-
* Walks the array in-place where possible and freezes the result so the render
|
|
3722
|
-
* path stays allocation-free.
|
|
3723
|
-
*/
|
|
3724
|
-
function rehydrateShape(shape) {
|
|
3725
|
-
const nodes = shape.nodes.map((n) => freezeNodeStyles(n));
|
|
3726
|
-
return Object.freeze({
|
|
3727
|
-
nodes: Object.freeze(nodes),
|
|
3728
|
-
width: shape.width,
|
|
3729
|
-
height: shape.height,
|
|
3730
|
-
truncated: shape.truncated
|
|
3731
|
-
});
|
|
3732
|
-
}
|
|
3733
|
-
function leanNodes(nodes) {
|
|
3734
|
-
return nodes.map((n) => ({
|
|
3735
|
-
type: n.type,
|
|
3736
|
-
x: n.x,
|
|
3737
|
-
y: n.y,
|
|
3738
|
-
w: n.w,
|
|
3739
|
-
h: n.h,
|
|
3740
|
-
radius: n.radius,
|
|
3741
|
-
lines: n.lines,
|
|
3742
|
-
lineHeight: n.lineHeight
|
|
3743
|
-
}));
|
|
4186
|
+
return false;
|
|
3744
4187
|
}
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
});
|
|
3753
|
-
let lineStyles;
|
|
3754
|
-
if (node.type === "text" && node.lines && node.lines > 1) {
|
|
3755
|
-
const lh = node.lineHeight ?? Math.round(node.h / node.lines);
|
|
3756
|
-
const barHeight = Math.max(8, Math.round(lh * .7));
|
|
3757
|
-
const widthFull = `${node.w}px`;
|
|
3758
|
-
const widthLast = `${Math.max(40, Math.round(node.w * .7))}px`;
|
|
3759
|
-
const heightStr = `${barHeight}px`;
|
|
3760
|
-
const radiusStr = `${node.radius}px`;
|
|
3761
|
-
const arr = [];
|
|
3762
|
-
for (let i = 1; i <= node.lines; i++) {
|
|
3763
|
-
const isLast = i === node.lines;
|
|
3764
|
-
arr.push(Object.freeze({
|
|
3765
|
-
left: `${node.x}px`,
|
|
3766
|
-
top: `${node.y + (i - 1) * lh}px`,
|
|
3767
|
-
width: isLast ? widthLast : widthFull,
|
|
3768
|
-
height: heightStr,
|
|
3769
|
-
borderRadius: radiusStr
|
|
3770
|
-
}));
|
|
3771
|
-
}
|
|
3772
|
-
lineStyles = Object.freeze(arr);
|
|
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);
|
|
3773
4195
|
}
|
|
3774
|
-
return
|
|
3775
|
-
type: node.type,
|
|
3776
|
-
x: node.x,
|
|
3777
|
-
y: node.y,
|
|
3778
|
-
w: node.w,
|
|
3779
|
-
h: node.h,
|
|
3780
|
-
radius: node.radius,
|
|
3781
|
-
lines: node.lines,
|
|
3782
|
-
lineHeight: node.lineHeight,
|
|
3783
|
-
style,
|
|
3784
|
-
lineStyles
|
|
3785
|
-
});
|
|
4196
|
+
return out;
|
|
3786
4197
|
}
|
|
3787
|
-
//#endregion
|
|
3788
|
-
//#region src/utils/fingerprint.ts
|
|
3789
4198
|
/**
|
|
3790
|
-
*
|
|
3791
|
-
*
|
|
3792
|
-
*
|
|
3793
|
-
*
|
|
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.
|
|
3794
4208
|
*/
|
|
3795
|
-
function
|
|
3796
|
-
if (
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
}
|
|
3803
|
-
function describeVNode(vnode) {
|
|
3804
|
-
const t = vnode.type;
|
|
3805
|
-
if (t === vue.Comment || t === vue.Text) return void 0;
|
|
3806
|
-
if (t === vue.Fragment) {
|
|
3807
|
-
const children = vnode.children;
|
|
3808
|
-
if (Array.isArray(children)) {
|
|
3809
|
-
for (const child of children) if (child && typeof child === "object" && "type" in child) {
|
|
3810
|
-
const found = describeVNode(child);
|
|
3811
|
-
if (found) return found;
|
|
3812
|
-
}
|
|
3813
|
-
}
|
|
4209
|
+
function captureTextLines(el, origin) {
|
|
4210
|
+
if (typeof document === "undefined" || typeof document.createRange !== "function") return void 0;
|
|
4211
|
+
let range;
|
|
4212
|
+
try {
|
|
4213
|
+
range = document.createRange();
|
|
4214
|
+
range.selectNodeContents(el);
|
|
4215
|
+
} catch {
|
|
3814
4216
|
return;
|
|
3815
4217
|
}
|
|
3816
|
-
|
|
3817
|
-
if (
|
|
3818
|
-
|
|
3819
|
-
|
|
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);
|
|
3820
4237
|
}
|
|
4238
|
+
return merged.length > 0 ? merged : void 0;
|
|
3821
4239
|
}
|
|
3822
4240
|
//#endregion
|
|
3823
|
-
//#region src/utils/
|
|
4241
|
+
//#region src/utils/captureStyles.ts
|
|
3824
4242
|
/**
|
|
3825
|
-
*
|
|
3826
|
-
*
|
|
3827
|
-
*
|
|
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.
|
|
3828
4269
|
*/
|
|
3829
|
-
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([
|
|
3830
4275
|
"img",
|
|
3831
4276
|
"svg",
|
|
3832
4277
|
"canvas",
|
|
3833
4278
|
"video",
|
|
4279
|
+
"audio",
|
|
3834
4280
|
"input",
|
|
3835
4281
|
"textarea",
|
|
3836
4282
|
"select",
|
|
3837
|
-
"button",
|
|
3838
4283
|
"progress",
|
|
3839
4284
|
"meter",
|
|
3840
|
-
"hr"
|
|
4285
|
+
"hr",
|
|
4286
|
+
"iframe",
|
|
4287
|
+
"object",
|
|
4288
|
+
"embed",
|
|
4289
|
+
"picture",
|
|
4290
|
+
"br"
|
|
3841
4291
|
]);
|
|
3842
|
-
/**
|
|
3843
|
-
const
|
|
4292
|
+
/** Tags whose content is text — captured as text bars (per-line via Range). */
|
|
4293
|
+
const TEXT_OWNERS$1 = new Set([
|
|
4294
|
+
"p",
|
|
3844
4295
|
"h1",
|
|
3845
4296
|
"h2",
|
|
3846
4297
|
"h3",
|
|
3847
4298
|
"h4",
|
|
3848
4299
|
"h5",
|
|
3849
|
-
"h6"
|
|
3850
|
-
]);
|
|
3851
|
-
/** Multi-line text containers — produce N bars with a shortened last line. */
|
|
3852
|
-
const PARAGRAPH_TAGS = new Set(["p", "blockquote"]);
|
|
3853
|
-
/** Inline text — single bar, but inherits parent font sizing. */
|
|
3854
|
-
const INLINE_TEXT_TAGS = new Set([
|
|
4300
|
+
"h6",
|
|
3855
4301
|
"span",
|
|
3856
4302
|
"a",
|
|
3857
|
-
"small",
|
|
3858
|
-
"strong",
|
|
3859
4303
|
"em",
|
|
4304
|
+
"strong",
|
|
4305
|
+
"small",
|
|
3860
4306
|
"code",
|
|
3861
|
-
"time",
|
|
3862
|
-
"label",
|
|
3863
4307
|
"b",
|
|
3864
4308
|
"i",
|
|
3865
|
-
"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"
|
|
3866
4322
|
]);
|
|
3867
|
-
const DEFAULT_MAX_DEPTH = 8;
|
|
3868
|
-
const DEFAULT_MAX_NODES = 300;
|
|
3869
4323
|
/**
|
|
3870
|
-
*
|
|
3871
|
-
*
|
|
3872
|
-
*
|
|
3873
|
-
* blocks. The result renders correctly on the FIRST paint without any DOM
|
|
3874
|
-
* measurement, as long as the slot's template renders structure even when its
|
|
3875
|
-
* 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.
|
|
3876
4327
|
*
|
|
3877
|
-
*
|
|
3878
|
-
*
|
|
3879
|
-
* row list renders ~300 skeleton rows on first paint and then the measured cache
|
|
3880
|
-
* 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.
|
|
3881
4330
|
*/
|
|
3882
|
-
|
|
3883
|
-
|
|
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();
|
|
3884
4388
|
const state = {
|
|
3885
|
-
|
|
3886
|
-
|
|
4389
|
+
count: 0,
|
|
4390
|
+
truncated: false
|
|
3887
4391
|
};
|
|
3888
|
-
const
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
if (state.emitted >= state.cap) return;
|
|
3894
|
-
if (input == null || typeof input === "boolean") return;
|
|
3895
|
-
if (Array.isArray(input)) {
|
|
3896
|
-
for (let i = 0; i < input.length; i++) {
|
|
3897
|
-
if (state.emitted >= state.cap) return;
|
|
3898
|
-
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;
|
|
3899
4397
|
}
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
if (type === vue.Comment) return;
|
|
3909
|
-
if (type === vue.Text) {
|
|
3910
|
-
if (typeof v.children === "string" ? v.children.trim() : "") push(out, textBar(opts.animationClass), state);
|
|
3911
|
-
return;
|
|
3912
|
-
}
|
|
3913
|
-
if (type === vue.Fragment) {
|
|
3914
|
-
walk(v.children, opts, depth, max, state, out);
|
|
3915
|
-
return;
|
|
3916
|
-
}
|
|
3917
|
-
if (typeof type === "string") {
|
|
3918
|
-
push(out, transformElement(v, type.toLowerCase(), opts, depth, max, state), state);
|
|
3919
|
-
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);
|
|
3920
4406
|
}
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
style: v.props?.style
|
|
3928
|
-
}), state);
|
|
3929
|
-
}
|
|
3930
|
-
function push(out, vn, state) {
|
|
3931
|
-
if (state.emitted >= state.cap) return;
|
|
3932
|
-
out.push(vn);
|
|
3933
|
-
state.emitted++;
|
|
3934
|
-
}
|
|
3935
|
-
function transformElement(v, tag, opts, depth, max, state) {
|
|
3936
|
-
const cls = v.props?.class;
|
|
3937
|
-
const styl = v.props?.style;
|
|
3938
|
-
if (ATOMIC_TAGS.has(tag)) return (0, vue.h)("div", {
|
|
3939
|
-
class: [
|
|
3940
|
-
"a-skel-block",
|
|
3941
|
-
cls,
|
|
3942
|
-
opts.animationClass
|
|
3943
|
-
],
|
|
3944
|
-
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()
|
|
3945
4413
|
});
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
const children = v.children;
|
|
3952
|
-
const recursedChildren = [];
|
|
3953
|
-
walk(children, opts, depth + 1, max, state, recursedChildren);
|
|
3954
|
-
if (recursedChildren.length > 0) return (0, vue.h)(tag, {
|
|
3955
|
-
class: cls,
|
|
3956
|
-
style: styl
|
|
3957
|
-
}, recursedChildren);
|
|
3958
|
-
const lines = estimateLines(children, 3);
|
|
3959
|
-
return (0, vue.h)(tag, {
|
|
3960
|
-
class: cls,
|
|
3961
|
-
style: styl
|
|
3962
|
-
}, 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;
|
|
3963
4419
|
}
|
|
3964
|
-
if (
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
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;
|
|
3976
4434
|
}
|
|
3977
|
-
if (
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
const
|
|
3986
|
-
|
|
3987
|
-
if (
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
const key = Math.round(widthFraction * 10) / 10;
|
|
4023
|
-
const hit = PARTIAL_BAR_CACHE.get(key);
|
|
4024
|
-
if (hit) return hit;
|
|
4025
|
-
const made = Object.freeze({
|
|
4026
|
-
display: "inline-block",
|
|
4027
|
-
width: `${Math.round(key * 100)}%`,
|
|
4028
|
-
height: "0.75em",
|
|
4029
|
-
verticalAlign: "middle",
|
|
4030
|
-
borderRadius: "4px"
|
|
4031
|
-
});
|
|
4032
|
-
PARTIAL_BAR_CACHE.set(key, made);
|
|
4033
|
-
return made;
|
|
4034
|
-
}
|
|
4035
|
-
function textBar(animationClass, widthFraction = 1) {
|
|
4036
|
-
return (0, vue.h)("span", {
|
|
4037
|
-
class: [
|
|
4038
|
-
"a-skel-block",
|
|
4039
|
-
"a-skel-block--text",
|
|
4040
|
-
animationClass
|
|
4041
|
-
],
|
|
4042
|
-
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
|
|
4043
4480
|
});
|
|
4044
4481
|
}
|
|
4045
4482
|
//#endregion
|
|
4046
|
-
//#region src/components/
|
|
4483
|
+
//#region src/components/ASkeleton.vue
|
|
4047
4484
|
/**
|
|
4048
|
-
*
|
|
4049
|
-
* function — no template, no scoped styles — so the parent's class strings
|
|
4050
|
-
* pass through unchanged and Tailwind utilities continue to drive layout.
|
|
4485
|
+
* `<ASkeleton>` — the package's headline wrapper.
|
|
4051
4486
|
*
|
|
4052
|
-
*
|
|
4053
|
-
* `
|
|
4054
|
-
*
|
|
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.
|
|
4055
4500
|
*/
|
|
4056
|
-
const
|
|
4057
|
-
|
|
4058
|
-
props: {
|
|
4059
|
-
vnodes: {
|
|
4060
|
-
type: Array,
|
|
4061
|
-
required: true
|
|
4062
|
-
},
|
|
4063
|
-
animation: {
|
|
4064
|
-
type: String,
|
|
4065
|
-
default: null
|
|
4066
|
-
},
|
|
4067
|
-
maxDepth: {
|
|
4068
|
-
type: Number,
|
|
4069
|
-
default: 8
|
|
4070
|
-
},
|
|
4071
|
-
maxNodes: {
|
|
4072
|
-
type: Number,
|
|
4073
|
-
default: 300
|
|
4074
|
-
}
|
|
4075
|
-
},
|
|
4076
|
-
setup(props) {
|
|
4077
|
-
return () => buildStructuralSkeleton(props.vnodes, {
|
|
4078
|
-
animationClass: props.animation,
|
|
4079
|
-
maxDepth: props.maxDepth,
|
|
4080
|
-
maxNodes: props.maxNodes
|
|
4081
|
-
});
|
|
4082
|
-
}
|
|
4083
|
-
});
|
|
4084
|
-
//#endregion
|
|
4085
|
-
//#region \0/plugin-vue/export-helper
|
|
4086
|
-
var export_helper_default = (sfc, props) => {
|
|
4087
|
-
const target = sfc.__vccOpts || sfc;
|
|
4088
|
-
for (const [key, val] of props) target[key] = val;
|
|
4089
|
-
return target;
|
|
4090
|
-
};
|
|
4091
|
-
//#endregion
|
|
4092
|
-
//#region src/components/ASkeleton.vue
|
|
4093
|
-
const _sfc_main$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
4501
|
+
const MAX_RETRY_ATTEMPTS = 5;
|
|
4502
|
+
const _sfc_main$17 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
4094
4503
|
__name: "ASkeleton",
|
|
4095
4504
|
props: {
|
|
4096
4505
|
loading: {
|
|
4097
4506
|
type: Boolean,
|
|
4098
4507
|
required: true
|
|
4099
4508
|
},
|
|
4509
|
+
mode: {
|
|
4510
|
+
type: String,
|
|
4511
|
+
required: false,
|
|
4512
|
+
default: "clone"
|
|
4513
|
+
},
|
|
4100
4514
|
cacheKey: {
|
|
4101
4515
|
type: String,
|
|
4102
4516
|
required: false
|
|
@@ -4104,12 +4518,12 @@ const _sfc_main$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4104
4518
|
maxDepth: {
|
|
4105
4519
|
type: Number,
|
|
4106
4520
|
required: false,
|
|
4107
|
-
default:
|
|
4521
|
+
default: 16
|
|
4108
4522
|
},
|
|
4109
4523
|
maxNodes: {
|
|
4110
4524
|
type: Number,
|
|
4111
4525
|
required: false,
|
|
4112
|
-
default:
|
|
4526
|
+
default: 600
|
|
4113
4527
|
},
|
|
4114
4528
|
minNodeSize: {
|
|
4115
4529
|
type: Number,
|
|
@@ -4148,61 +4562,90 @@ const _sfc_main$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4148
4562
|
const props = __props;
|
|
4149
4563
|
const slots = (0, vue.useSlots)();
|
|
4150
4564
|
const instanceId = (0, vue.useId)();
|
|
4151
|
-
const resolvedKey = (0, vue.computed)(() => props.cacheKey ?? `${fingerprintSlot(slots.default?.())}:${instanceId}`);
|
|
4152
|
-
const warnedKeys = /* @__PURE__ */ new Set();
|
|
4153
|
-
const cached = (0, vue.shallowRef)(getCached(resolvedKey.value, props.persist));
|
|
4154
|
-
(0, vue.watch)(resolvedKey, (key) => {
|
|
4155
|
-
cached.value = getCached(key, props.persist);
|
|
4156
|
-
});
|
|
4157
|
-
const wrapperRef = (0, vue.ref)(null);
|
|
4158
|
-
useShapeProbe(() => props.loading ? null : wrapperRef.value, {
|
|
4159
|
-
maxDepth: props.maxDepth,
|
|
4160
|
-
maxNodes: props.maxNodes,
|
|
4161
|
-
minSize: props.minNodeSize,
|
|
4162
|
-
onCapture: (shape) => {
|
|
4163
|
-
setCached(resolvedKey.value, shape, props.persist);
|
|
4164
|
-
cached.value = shape;
|
|
4165
|
-
if (shape.truncated && !warnedKeys.has(resolvedKey.value)) {
|
|
4166
|
-
warnedKeys.add(resolvedKey.value);
|
|
4167
|
-
console.warn(`[ASkeleton] Capture truncated at maxNodes=${props.maxNodes} for cacheKey="${resolvedKey.value}". The replayed skeleton will be missing nodes. Raise \`max-nodes\` or mark dense subtrees with \`data-skeleton-stop\` to collapse them into a single block.`);
|
|
4168
|
-
}
|
|
4169
|
-
}
|
|
4170
|
-
});
|
|
4171
4565
|
const animationClass = (0, vue.computed)(() => props.animation === "none" ? null : `a-skel-block--anim-${props.animation}`);
|
|
4172
|
-
const
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
const
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
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
|
+
});
|
|
4184
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);
|
|
4185
4614
|
});
|
|
4186
|
-
const structuralVNodes = (0, vue.computed)(() => props.loading ? slots.default?.() ?? [] : []);
|
|
4187
4615
|
const __returned__ = {
|
|
4188
4616
|
props,
|
|
4189
4617
|
slots,
|
|
4190
4618
|
instanceId,
|
|
4191
|
-
resolvedKey,
|
|
4192
|
-
warnedKeys,
|
|
4193
|
-
cached,
|
|
4194
|
-
wrapperRef,
|
|
4195
4619
|
animationClass,
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
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,
|
|
4200
4642
|
get cn() {
|
|
4201
4643
|
return cn;
|
|
4202
4644
|
},
|
|
4203
4645
|
get StructuralSkeleton() {
|
|
4204
4646
|
return StructuralSkeleton;
|
|
4205
|
-
}
|
|
4647
|
+
},
|
|
4648
|
+
ASkeletonClone: ASkeletonClone_default
|
|
4206
4649
|
};
|
|
4207
4650
|
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
4208
4651
|
enumerable: false,
|
|
@@ -4211,44 +4654,61 @@ const _sfc_main$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4211
4654
|
return __returned__;
|
|
4212
4655
|
}
|
|
4213
4656
|
});
|
|
4214
|
-
const _hoisted_1 = [
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
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"
|
|
4220
4666
|
};
|
|
4221
|
-
const
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
"aria-busy": "true"
|
|
4667
|
+
const _hoisted_4 = {
|
|
4668
|
+
key: 1,
|
|
4669
|
+
class: "a-skeleton__replay"
|
|
4225
4670
|
};
|
|
4226
|
-
|
|
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) {
|
|
4227
4680
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
"data-loading": $setup.props.loading ? "" : void 0
|
|
4231
|
-
}, [$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", {
|
|
4232
|
-
key: 0,
|
|
4233
|
-
class: "a-skeleton__layer",
|
|
4234
|
-
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,
|
|
4235
4683
|
role: "status",
|
|
4236
|
-
"aria-
|
|
4237
|
-
"aria-
|
|
4238
|
-
}, [((0, vue.openBlock)(
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
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,
|
|
4252
4712
|
animation: $setup.animationClass,
|
|
4253
4713
|
"max-depth": $props.maxDepth,
|
|
4254
4714
|
"max-nodes": $props.maxNodes
|
|
@@ -4257,16 +4717,153 @@ function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
4257
4717
|
"animation",
|
|
4258
4718
|
"max-depth",
|
|
4259
4719
|
"max-nodes"
|
|
4260
|
-
])])
|
|
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);
|
|
4261
4721
|
}
|
|
4262
|
-
var ASkeleton_default = /* @__PURE__ */ export_helper_default(_sfc_main$
|
|
4263
|
-
["render", _sfc_render$
|
|
4722
|
+
var ASkeleton_default = /* @__PURE__ */ export_helper_default(_sfc_main$17, [
|
|
4723
|
+
["render", _sfc_render$17],
|
|
4264
4724
|
["__scopeId", "data-v-16717541"],
|
|
4265
4725
|
["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/ASkeleton.vue"]
|
|
4266
4726
|
]);
|
|
4267
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
|
|
4268
4857
|
//#region src/components/ASkeletonLayer.vue
|
|
4269
|
-
|
|
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)({
|
|
4270
4867
|
__name: "ASkeletonLayer",
|
|
4271
4868
|
props: {
|
|
4272
4869
|
shape: {
|
|
@@ -4290,176 +4887,2366 @@ const _sfc_main$1 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4290
4887
|
skipCheck: true
|
|
4291
4888
|
}
|
|
4292
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
|
+
},
|
|
4293
4970
|
setup(__props, { expose: __expose }) {
|
|
4294
4971
|
__expose();
|
|
4295
4972
|
const props = __props;
|
|
4296
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));
|
|
4297
4984
|
const __returned__ = {
|
|
4298
4985
|
props,
|
|
4299
4986
|
animationClass,
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
text: `a-skel-block a-skel-block--text${suffix}`,
|
|
4310
|
-
image: `a-skel-block a-skel-block--image${suffix}`,
|
|
4311
|
-
circle: `a-skel-block a-skel-block--circle${suffix}`
|
|
4312
|
-
});
|
|
4313
|
-
}),
|
|
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),
|
|
4314
4996
|
get cn() {
|
|
4315
4997
|
return cn;
|
|
4316
4998
|
}
|
|
4317
|
-
};
|
|
4318
|
-
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
4319
|
-
enumerable: false,
|
|
4320
|
-
value: true
|
|
4321
|
-
});
|
|
4322
|
-
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);
|
|
4323
6937
|
}
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
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;
|
|
4346
6962
|
}
|
|
4347
|
-
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"]]);
|
|
4348
6963
|
//#endregion
|
|
4349
|
-
//#region src/
|
|
4350
|
-
const
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
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;
|
|
4390
7144
|
}
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
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;
|
|
4404
7188
|
}
|
|
4405
|
-
const
|
|
4406
|
-
|
|
4407
|
-
props,
|
|
4408
|
-
animationClass,
|
|
4409
|
-
blockClass,
|
|
4410
|
-
toLength,
|
|
4411
|
-
radiusValue,
|
|
4412
|
-
blockStyle: (0, vue.computed)(() => ({
|
|
4413
|
-
width: toLength(props.w),
|
|
4414
|
-
height: toLength(props.h),
|
|
4415
|
-
borderRadius: radiusValue.value
|
|
4416
|
-
})),
|
|
4417
|
-
isMultiLineText: (0, vue.computed)(() => props.type === "text" && props.lines > 1),
|
|
4418
|
-
get cn() {
|
|
4419
|
-
return cn;
|
|
4420
|
-
}
|
|
4421
|
-
};
|
|
4422
|
-
Object.defineProperty(__returned__, "__isScriptSetup", {
|
|
4423
|
-
enumerable: false,
|
|
4424
|
-
value: true
|
|
4425
|
-
});
|
|
4426
|
-
return __returned__;
|
|
7189
|
+
const node = capture(child, origin, depth + 1, ctx);
|
|
7190
|
+
if (node) children.push(node);
|
|
4427
7191
|
}
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
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";
|
|
4452
7241
|
}
|
|
4453
|
-
var ASkeletonBlock_default = /* @__PURE__ */ export_helper_default(_sfc_main, [
|
|
4454
|
-
["render", _sfc_render],
|
|
4455
|
-
["__scopeId", "data-v-bdfba69a"],
|
|
4456
|
-
["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ASkeleton/src/components/ASkeletonBlock.vue"]
|
|
4457
|
-
]);
|
|
4458
7242
|
//#endregion
|
|
4459
7243
|
//#region src/composables/useSkeleton.ts
|
|
7244
|
+
const DEFAULT_MAX_DEPTH = 12;
|
|
4460
7245
|
/**
|
|
4461
|
-
* High-level building block for
|
|
4462
|
-
* + 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.
|
|
4463
7250
|
*
|
|
4464
7251
|
* ```ts
|
|
4465
7252
|
* const containerRef = ref<HTMLElement | null>(null);
|
|
@@ -4476,13 +7263,14 @@ var ASkeletonBlock_default = /* @__PURE__ */ export_helper_default(_sfc_main, [
|
|
|
4476
7263
|
* </div>
|
|
4477
7264
|
* ```
|
|
4478
7265
|
*
|
|
4479
|
-
* For more control, drop down to `useShapeProbe`
|
|
4480
|
-
*
|
|
7266
|
+
* For more control, drop down to `useShapeProbe` (pass `walkStructural` as the
|
|
7267
|
+
* capture strategy) + `getCachedStructural` / `setCachedStructural` and compose
|
|
7268
|
+
* your own orchestration.
|
|
4481
7269
|
*/
|
|
4482
7270
|
function useSkeleton(options) {
|
|
4483
7271
|
const persist = options.persist ?? false;
|
|
4484
|
-
const maxDepth = options.maxDepth ??
|
|
4485
|
-
const shape = (0, vue.shallowRef)(
|
|
7272
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
7273
|
+
const shape = (0, vue.shallowRef)(getCachedStructural(options.cacheKey, persist));
|
|
4486
7274
|
if (options.target) {
|
|
4487
7275
|
const getTarget = options.target;
|
|
4488
7276
|
useShapeProbe(getTarget, {
|
|
@@ -4490,8 +7278,9 @@ function useSkeleton(options) {
|
|
|
4490
7278
|
maxNodes: options.maxNodes,
|
|
4491
7279
|
minSize: options.minSize,
|
|
4492
7280
|
resizeDebounceMs: options.resizeDebounceMs,
|
|
7281
|
+
capture: walkStructural,
|
|
4493
7282
|
onCapture: (captured) => {
|
|
4494
|
-
|
|
7283
|
+
setCachedStructural(options.cacheKey, captured, persist);
|
|
4495
7284
|
shape.value = captured;
|
|
4496
7285
|
}
|
|
4497
7286
|
});
|
|
@@ -4499,18 +7288,18 @@ function useSkeleton(options) {
|
|
|
4499
7288
|
function captureNow() {
|
|
4500
7289
|
const el = options.target?.();
|
|
4501
7290
|
if (!el || typeof window === "undefined") return void 0;
|
|
4502
|
-
const captured =
|
|
7291
|
+
const captured = walkStructural(el, {
|
|
4503
7292
|
maxDepth,
|
|
4504
7293
|
maxNodes: options.maxNodes,
|
|
4505
7294
|
minSize: options.minSize
|
|
4506
7295
|
});
|
|
4507
7296
|
if (captured.width <= 0 || captured.height <= 0 || captured.nodes.length === 0) return void 0;
|
|
4508
|
-
|
|
7297
|
+
setCachedStructural(options.cacheKey, captured, persist);
|
|
4509
7298
|
shape.value = captured;
|
|
4510
7299
|
return captured;
|
|
4511
7300
|
}
|
|
4512
7301
|
function clear() {
|
|
4513
|
-
|
|
7302
|
+
clearCachedStructural(options.cacheKey);
|
|
4514
7303
|
shape.value = void 0;
|
|
4515
7304
|
}
|
|
4516
7305
|
return {
|
|
@@ -4520,17 +7309,73 @@ function useSkeleton(options) {
|
|
|
4520
7309
|
};
|
|
4521
7310
|
}
|
|
4522
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
|
|
4523
7347
|
exports.ASkeleton = ASkeleton_default;
|
|
7348
|
+
exports.ASkeletonArticle = ASkeletonArticle_default;
|
|
7349
|
+
exports.ASkeletonAvatar = ASkeletonAvatar_default;
|
|
4524
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;
|
|
4525
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;
|
|
4526
7366
|
exports.StructuralSkeleton = StructuralSkeleton;
|
|
4527
7367
|
exports.buildStructuralSkeleton = buildStructuralSkeleton;
|
|
7368
|
+
exports.captureSnapshot = captureSnapshot;
|
|
4528
7369
|
exports.clearCached = clearCached;
|
|
7370
|
+
exports.clearCachedStructural = clearCachedStructural;
|
|
4529
7371
|
exports.fingerprintSlot = fingerprintSlot;
|
|
4530
7372
|
exports.getCached = getCached;
|
|
7373
|
+
exports.getCachedStructural = getCachedStructural;
|
|
4531
7374
|
exports.setCached = setCached;
|
|
7375
|
+
exports.setCachedStructural = setCachedStructural;
|
|
4532
7376
|
exports.useShapeProbe = useShapeProbe;
|
|
4533
7377
|
exports.useSkeleton = useSkeleton;
|
|
4534
7378
|
exports.walkDom = walkDom;
|
|
7379
|
+
exports.walkStructural = walkStructural;
|
|
4535
7380
|
|
|
4536
7381
|
//# sourceMappingURL=index.cjs.map
|