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