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