@scalar/api-reference 1.48.8 → 1.49.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/browser/standalone.js +9866 -9838
  3. package/dist/browser/webpack-stats.json +1 -1
  4. package/dist/components/ApiReference.vue.d.ts.map +1 -1
  5. package/dist/components/ApiReference.vue.js +1 -1
  6. package/dist/components/ApiReference.vue2.js +2 -4
  7. package/dist/components/Content/Content.vue.d.ts +2 -4
  8. package/dist/components/Content/Content.vue.d.ts.map +1 -1
  9. package/dist/components/Content/Content.vue.js +77 -83
  10. package/dist/components/Content/Models/ModelTag.vue.d.ts +3 -3
  11. package/dist/components/Content/Models/ModelTag.vue.d.ts.map +1 -1
  12. package/dist/components/Content/Models/ModelTag.vue.js +42 -48
  13. package/dist/components/Content/Operations/TraversedEntry.vue.d.ts.map +1 -1
  14. package/dist/components/Content/Operations/TraversedEntry.vue.js +3 -2
  15. package/dist/components/Content/Schema/SchemaProperty.vue.d.ts.map +1 -1
  16. package/dist/components/Content/Schema/SchemaProperty.vue.js +1 -1
  17. package/dist/components/Content/Schema/SchemaProperty.vue2.js +9 -1
  18. package/dist/components/Content/Schema/helpers/should-display-description.js +1 -1
  19. package/dist/components/Content/Tags/components/ModernLayout.vue.d.ts +2 -2
  20. package/dist/components/Content/Tags/components/ModernLayout.vue.d.ts.map +1 -1
  21. package/dist/components/Content/Tags/components/ModernLayout.vue.js +1 -1
  22. package/dist/components/Content/Tags/components/ModernLayout.vue2.js +18 -26
  23. package/dist/components/Lazy/Lazy.vue.d.ts +3 -0
  24. package/dist/components/Lazy/Lazy.vue.d.ts.map +1 -1
  25. package/dist/components/Lazy/Lazy.vue.js +93 -4
  26. package/dist/features/ask-agent-button/AskAgentButton.vue.d.ts.map +1 -1
  27. package/dist/features/ask-agent-button/AskAgentButton.vue.js +1 -1
  28. package/dist/features/ask-agent-button/AskAgentButton.vue2.js +18 -14
  29. package/dist/helpers/id-routing.d.ts +4 -0
  30. package/dist/helpers/id-routing.d.ts.map +1 -1
  31. package/dist/helpers/id-routing.js +51 -5
  32. package/dist/helpers/lazy-bus.d.ts +19 -19
  33. package/dist/helpers/lazy-bus.d.ts.map +1 -1
  34. package/dist/helpers/lazy-bus.js +63 -28
  35. package/dist/style.css +126 -124
  36. package/package.json +11 -11
@@ -1,14 +1,103 @@
1
- import { defineComponent, renderSlot, createCommentVNode, unref } from "vue";
2
- import { useLazyBus } from "../../helpers/lazy-bus.js";
1
+ import { defineComponent, ref, computed, onMounted, watch, nextTick, onBeforeUnmount, createElementBlock, openBlock, normalizeStyle, renderSlot, createCommentVNode } from "vue";
2
+ import { useIntersectionObserver } from "@vueuse/core";
3
+ import { useLazyBus, getLazyPlaceholderHeight, requestLazyRender, setLazyPlaceholderHeight } from "../../helpers/lazy-bus.js";
4
+ const _hoisted_1 = ["id", "data-placeholder"];
5
+ const PLACEHOLDER_HEIGHT_PX = 760;
6
+ const VIEWPORT_OVERSCAN_PX = 1200;
3
7
  const _sfc_main = /* @__PURE__ */ defineComponent({
4
8
  __name: "Lazy",
5
9
  props: {
6
- id: {}
10
+ id: {},
11
+ expanded: { type: Boolean, default: false }
7
12
  },
8
13
  setup(__props) {
14
+ const VIEWPORT_ROOT_MARGIN = `${VIEWPORT_OVERSCAN_PX}px 0px`;
9
15
  const { isReady } = useLazyBus(__props.id);
16
+ const lazyContainerRef = ref(null);
17
+ const placeholderHeight = ref(
18
+ getLazyPlaceholderHeight(__props.id) ?? PLACEHOLDER_HEIGHT_PX
19
+ );
20
+ let contentResizeObserver = null;
21
+ const shouldRender = computed(() => isReady.value || __props.expanded);
22
+ onMounted(() => {
23
+ if (typeof window === "undefined") {
24
+ return;
25
+ }
26
+ if (!("IntersectionObserver" in window)) {
27
+ requestLazyRender(__props.id, true);
28
+ return;
29
+ }
30
+ useIntersectionObserver(
31
+ lazyContainerRef,
32
+ ([entry]) => {
33
+ if (entry?.isIntersecting && !isReady.value) {
34
+ requestLazyRender(__props.id, true);
35
+ }
36
+ },
37
+ { rootMargin: VIEWPORT_ROOT_MARGIN }
38
+ );
39
+ });
40
+ watch(
41
+ () => shouldRender.value,
42
+ (rendered, wasRendered) => {
43
+ if (wasRendered && !rendered && lazyContainerRef.value) {
44
+ const h = lazyContainerRef.value.offsetHeight;
45
+ if (Number.isFinite(h) && h > 0) {
46
+ placeholderHeight.value = h;
47
+ setLazyPlaceholderHeight(__props.id, h);
48
+ }
49
+ }
50
+ },
51
+ { flush: "pre" }
52
+ );
53
+ watch(
54
+ () => shouldRender.value,
55
+ (rendered) => {
56
+ if (!rendered) {
57
+ contentResizeObserver?.disconnect();
58
+ contentResizeObserver = null;
59
+ return;
60
+ }
61
+ void nextTick(() => {
62
+ if (!lazyContainerRef.value || typeof ResizeObserver === "undefined") {
63
+ return;
64
+ }
65
+ if (!contentResizeObserver) {
66
+ contentResizeObserver = new ResizeObserver(() => {
67
+ if (!lazyContainerRef.value) {
68
+ return;
69
+ }
70
+ const h2 = lazyContainerRef.value.offsetHeight;
71
+ if (Number.isFinite(h2) && h2 > 0) {
72
+ placeholderHeight.value = h2;
73
+ setLazyPlaceholderHeight(__props.id, h2);
74
+ }
75
+ });
76
+ }
77
+ contentResizeObserver.observe(lazyContainerRef.value);
78
+ const h = lazyContainerRef.value.offsetHeight;
79
+ if (Number.isFinite(h) && h > 0) {
80
+ placeholderHeight.value = h;
81
+ setLazyPlaceholderHeight(__props.id, h);
82
+ }
83
+ });
84
+ },
85
+ { immediate: true }
86
+ );
87
+ onBeforeUnmount(() => {
88
+ contentResizeObserver?.disconnect();
89
+ });
10
90
  return (_ctx, _cache) => {
11
- return unref(isReady) ? renderSlot(_ctx.$slots, "default", { key: 0 }) : createCommentVNode("", true);
91
+ return openBlock(), createElementBlock("div", {
92
+ id: !shouldRender.value ? __props.id : void 0,
93
+ ref_key: "lazyContainerRef",
94
+ ref: lazyContainerRef,
95
+ "data-placeholder": !shouldRender.value,
96
+ "data-testid": "lazy-container",
97
+ style: normalizeStyle({ height: shouldRender.value ? void 0 : `${placeholderHeight.value}px` })
98
+ }, [
99
+ shouldRender.value ? renderSlot(_ctx.$slots, "default", { key: 0 }) : createCommentVNode("", true)
100
+ ], 12, _hoisted_1);
12
101
  };
13
102
  }
14
103
  });
@@ -1 +1 @@
1
- {"version":3,"file":"AskAgentButton.vue.d.ts","sourceRoot":"","sources":["../../../src/features/ask-agent-button/AskAgentButton.vue"],"names":[],"mappings":"AAgPA,QAAA,MAAM,YAAY,+QAChB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"AskAgentButton.vue.d.ts","sourceRoot":"","sources":["../../../src/features/ask-agent-button/AskAgentButton.vue"],"names":[],"mappings":"AAoQA,QAAA,MAAM,YAAY,+QAChB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1,7 +1,7 @@
1
1
  import _sfc_main from "./AskAgentButton.vue2.js";
2
2
  /* empty css */
3
3
  import _export_sfc from "../../_virtual/_plugin-vue_export-helper.js";
4
- const AskAgentButton = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-f43e32ab"]]);
4
+ const AskAgentButton = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-78f5377c"]]);
5
5
  export {
6
6
  AskAgentButton as default
7
7
  };
@@ -1,44 +1,48 @@
1
- import { defineComponent, ref, createElementBlock, createCommentVNode, unref, openBlock, createVNode, withDirectives, createElementVNode, withKeys, withModifiers, vModelText } from "vue";
1
+ import { defineComponent, ref, createElementBlock, createCommentVNode, unref, openBlock, withModifiers, createVNode, createElementVNode, withDirectives, normalizeClass, vModelText } from "vue";
2
2
  import { ScalarIconSparkle, ScalarIconArrowUp } from "@scalar/icons";
3
3
  import { useAgentContext } from "../../hooks/use-agent.js";
4
4
  const _hoisted_1 = {
5
- key: 0,
6
- class: "agent-button-container flex"
5
+ class: "ask-agent-scalar-send",
6
+ type: "submit"
7
7
  };
8
8
  const _sfc_main = /* @__PURE__ */ defineComponent({
9
9
  __name: "AskAgentButton",
10
10
  setup(__props) {
11
11
  const agentContext = useAgentContext();
12
12
  const message = ref("");
13
+ const inputRef = ref();
13
14
  function handleSubmit() {
14
15
  agentContext.value?.openAgent(message.value);
15
16
  message.value = "";
16
17
  }
17
18
  return (_ctx, _cache) => {
18
- return unref(agentContext)?.agentEnabled.value ? (openBlock(), createElementBlock("div", _hoisted_1, [
19
+ return unref(agentContext)?.agentEnabled.value ? (openBlock(), createElementBlock("form", {
20
+ key: 0,
21
+ class: "agent-button-container",
22
+ onClick: _cache[1] || (_cache[1] = ($event) => inputRef.value?.focus()),
23
+ onSubmit: _cache[2] || (_cache[2] = withModifiers(($event) => handleSubmit(), ["prevent"]))
24
+ }, [
19
25
  createVNode(unref(ScalarIconSparkle), {
20
26
  class: "size-3 shrink-0",
21
27
  weight: "fill"
22
28
  }),
29
+ _cache[3] || (_cache[3] = createElementVNode("div", { class: "ask-agent-scalar-input-label" }, "Ask AI Agent", -1)),
23
30
  withDirectives(createElementVNode("input", {
31
+ ref_key: "inputRef",
32
+ ref: inputRef,
24
33
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => message.value = $event),
25
- class: "ask-agent-scalar-input",
26
- placeholder: "Ask AI Agent",
27
- onKeydown: _cache[1] || (_cache[1] = withKeys(withModifiers(($event) => handleSubmit(), ["stop"]), ["enter"]))
28
- }, null, 544), [
34
+ class: normalizeClass(["ask-agent-scalar-input", { "ask-agent-scalar-input-not-empty": message.value.length > 0 }]),
35
+ placeholder: "Ask AI Agent"
36
+ }, null, 2), [
29
37
  [vModelText, message.value]
30
38
  ]),
31
- createElementVNode("button", {
32
- class: "ask-agent-scalar-send",
33
- type: "button",
34
- onClick: _cache[2] || (_cache[2] = ($event) => handleSubmit())
35
- }, [
39
+ createElementVNode("button", _hoisted_1, [
36
40
  createVNode(unref(ScalarIconArrowUp), {
37
41
  class: "size-3",
38
42
  weight: "bold"
39
43
  })
40
44
  ])
41
- ])) : createCommentVNode("", true);
45
+ ], 32)) : createCommentVNode("", true);
42
46
  };
43
47
  }
44
48
  });
@@ -3,6 +3,10 @@ export declare const sanitizeBasePath: (basePath: string) => string;
3
3
  export declare const getIdFromHash: (location: string | URL, slugPrefix: string | undefined) => string;
4
4
  /** Extracts an element id from the path when using path routing */
5
5
  export declare const getIdFromPath: (location: string | URL, basePath: string, slugPrefix: string | undefined) => string;
6
+ /** Extracts an element id from a hash-prefixed basePath */
7
+ export declare const getIdFromHashBasePath: (location: string | URL, basePath: string, slugPrefix: string | undefined) => string;
8
+ /** Determines whether a URL matches the provided basePath. */
9
+ export declare const matchesBasePath: (location: string | URL, basePath: string) => boolean;
6
10
  /**
7
11
  * Extracts a navigation id from a URL based on the routing type
8
12
  *
@@ -1 +1 @@
1
- {"version":3,"file":"id-routing.d.ts","sourceRoot":"","sources":["../../src/helpers/id-routing.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,WAEhD,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,YAAY,MAAM,GAAG,SAAS,WAMnF,CAAA;AACD,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,EAAE,YAAY,MAAM,GAAG,SAAS,WAqBrG,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,GAAG,SAAS,EAAE,YAAY,MAAM,GAAG,SAAS,WAE3G,CAAA;AAmBD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,EAAE,UAAU,MAAM,GAAG,SAAS,EAAE,iBAAiB,OAAO,oBAiBhG,CAAA;AAED,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,GAAI,IAAI,MAAM,KAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAajF,CAAA"}
1
+ {"version":3,"file":"id-routing.d.ts","sourceRoot":"","sources":["../../src/helpers/id-routing.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,WAEhD,CAAA;AAwBD,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,YAAY,MAAM,GAAG,SAAS,WAMnF,CAAA;AACD,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,EAAE,YAAY,MAAM,GAAG,SAAS,WAqBrG,CAAA;AAED,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,EAAE,YAAY,MAAM,GAAG,SAAS,WAW7G,CAAA;AAED,8DAA8D;AAC9D,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,YAiBvE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,GAAG,GAAG,EAAE,UAAU,MAAM,GAAG,SAAS,EAAE,YAAY,MAAM,GAAG,SAAS,WAQ3G,CAAA;AAmBD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,EAAE,UAAU,MAAM,GAAG,SAAS,EAAE,iBAAiB,OAAO,oBAsBhG,CAAA;AAED,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,GAAI,IAAI,MAAM,KAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAajF,CAAA"}
@@ -1,10 +1,26 @@
1
1
  const sanitizeBasePath = (basePath) => {
2
2
  return basePath.replace(/^\/+|\/+$/g, "");
3
3
  };
4
+ const isHashBasePath = (basePath) => basePath.startsWith("#");
5
+ const sanitizeHashBasePath = (basePath) => {
6
+ return basePath.replace(/^#+/, "").replace(/\/+$/g, "");
7
+ };
8
+ const applySlugPrefix = (base, slugPrefix) => {
9
+ return slugPrefix ? `${slugPrefix}${base ? "/" : ""}${base}` : base;
10
+ };
11
+ const stripBasePathPrefix = (value, basePath) => {
12
+ if (value === basePath) {
13
+ return "";
14
+ }
15
+ if (value.startsWith(`${basePath}/`)) {
16
+ return value.slice(basePath.length + 1);
17
+ }
18
+ return null;
19
+ };
4
20
  const getIdFromHash = (location, slugPrefix) => {
5
21
  const url = typeof location === "string" ? new URL(location) : location;
6
22
  const base = decodeURIComponent(url.hash.slice(1));
7
- return slugPrefix ? `${slugPrefix}${base ? "/" : ""}${base}` : base;
23
+ return applySlugPrefix(base, slugPrefix);
8
24
  };
9
25
  const getIdFromPath = (location, basePath, slugPrefix) => {
10
26
  const url = typeof location === "string" ? new URL(location) : location;
@@ -13,12 +29,35 @@ const getIdFromPath = (location, basePath, slugPrefix) => {
13
29
  if (url.pathname.startsWith(basePathWithSlash)) {
14
30
  const remainder = url.pathname.slice(basePathWithSlash.length);
15
31
  const base = decodeURIComponent(remainder.startsWith("/") ? remainder.slice(1) : remainder);
16
- return slugPrefix ? `${slugPrefix}${base ? "/" : ""}${base}` : base;
32
+ return applySlugPrefix(base, slugPrefix);
17
33
  }
18
34
  return slugPrefix ?? "";
19
35
  };
36
+ const getIdFromHashBasePath = (location, basePath, slugPrefix) => {
37
+ const url = typeof location === "string" ? new URL(location) : location;
38
+ const hash = decodeURIComponent(url.hash.slice(1));
39
+ const sanitized = sanitizeHashBasePath(basePath);
40
+ const remainder = stripBasePathPrefix(hash, sanitized);
41
+ if (remainder !== null) {
42
+ return applySlugPrefix(remainder, slugPrefix);
43
+ }
44
+ return slugPrefix ?? "";
45
+ };
46
+ const matchesBasePath = (location, basePath) => {
47
+ const url = typeof location === "string" ? new URL(location) : location;
48
+ if (isHashBasePath(basePath)) {
49
+ const hash = decodeURIComponent(url.hash);
50
+ return hash === basePath || hash.startsWith(`${basePath}/`);
51
+ }
52
+ const sanitized = sanitizeBasePath(basePath);
53
+ const basePathWithSlash = sanitized ? `/${sanitized.split("/").map((segment) => encodeURIComponent(segment)).join("/")}` : "";
54
+ return url.pathname === basePathWithSlash || url.pathname.startsWith(`${basePathWithSlash}/`);
55
+ };
20
56
  const getIdFromUrl = (url, basePath, slugPrefix) => {
21
- return typeof basePath === "string" ? getIdFromPath(url, basePath, slugPrefix) : getIdFromHash(url, slugPrefix);
57
+ if (typeof basePath !== "string") {
58
+ return getIdFromHash(url, slugPrefix);
59
+ }
60
+ return isHashBasePath(basePath) ? getIdFromHashBasePath(url, basePath, slugPrefix) : getIdFromPath(url, basePath, slugPrefix);
22
61
  };
23
62
  const stripFirstSegment = (id) => {
24
63
  const hasTrailingSlash = id.endsWith("/");
@@ -33,8 +72,13 @@ const makeUrlFromId = (_id, basePath, isMultiDocument) => {
33
72
  const id = isMultiDocument ? _id : stripFirstSegment(_id);
34
73
  const url = new URL(window.location.href);
35
74
  if (typeof basePath === "string") {
36
- const base = sanitizeBasePath(basePath);
37
- url.pathname = `${base}/${id}`;
75
+ if (isHashBasePath(basePath)) {
76
+ const base = sanitizeHashBasePath(basePath);
77
+ url.hash = [base, id].filter(Boolean).join("/");
78
+ } else {
79
+ const base = sanitizeBasePath(basePath);
80
+ url.pathname = `${base}/${id}`;
81
+ }
38
82
  } else {
39
83
  url.hash = id;
40
84
  }
@@ -55,9 +99,11 @@ const getSchemaParamsFromId = (id) => {
55
99
  };
56
100
  export {
57
101
  getIdFromHash,
102
+ getIdFromHashBasePath,
58
103
  getIdFromPath,
59
104
  getIdFromUrl,
60
105
  getSchemaParamsFromId,
61
106
  makeUrlFromId,
107
+ matchesBasePath,
62
108
  sanitizeBasePath
63
109
  };
@@ -1,12 +1,11 @@
1
- /** Tracks when the initial load is complete.
2
- * We will have placeholder content to allow the active item to be scrolled to the top while
3
- * the rest of the content is loaded.
4
- */
1
+ /** Tracks when the initial load is complete. */
5
2
  export declare const firstLazyLoadComplete: import("vue").Ref<boolean, boolean>;
3
+ export declare const getLazyPlaceholderHeight: (id: string) => number | undefined;
4
+ export declare const setLazyPlaceholderHeight: (id: string, height: number) => void;
6
5
  type UnblockFn = () => void;
7
6
  /**
8
- * Adds a unique identifier to the intersection blockers set
9
- * Intersection will not be enabled until the unblock callback is run
7
+ * Blocks intersection until the returned unblock callback is run.
8
+ * Prevents scroll jump while we render new lazy content.
10
9
  */
11
10
  export declare const blockIntersection: () => UnblockFn;
12
11
  /** If there are any pending blocking operations we disable intersection */
@@ -17,26 +16,27 @@ export declare const intersectionEnabled: import("vue").ComputedRef<boolean>;
17
16
  * but processQueue will skip actual re-rendering for items already ready.
18
17
  */
19
18
  export declare const addToPriorityQueue: (id: string | undefined) => void;
19
+ /**
20
+ * Request an item to be rendered (e.g. when it re-enters the overscan zone).
21
+ */
22
+ export declare const requestLazyRender: (id: string | undefined, priority?: boolean) => void;
23
+ /**
24
+ * Schedules a single run of the lazy bus so that documents with no Lazy components
25
+ * (e.g. no operations, tags, or models) still get firstLazyLoadComplete set and the
26
+ * full-viewport placeholder can be hidden. Call from content root on mount.
27
+ */
28
+ export declare const scheduleInitialLoadComplete: () => void;
20
29
  /**
21
30
  * Tracks the lazy loading state of an element.
22
- * The element should be conditionally rendered using the isReady property.
23
- *
24
- * @param id - The id of the element to track
25
- * @returns An object with the isReady property
31
+ * Use isReady (or expanded) to decide whether to render the slot or show a placeholder.
32
+ * The element is only added to the queue when it enters the viewport overscan (see Lazy.vue).
26
33
  */
27
34
  export declare function useLazyBus(id: string): {
28
35
  isReady: import("vue").ComputedRef<boolean>;
29
36
  };
30
37
  /**
31
- * Scroll to possible lazy element
32
- *
33
- * Will ensure that all parents are expanded and set to priority load before scrolling
34
- *
35
- * Similar to scrollToId BUT in the case of a section not being open,
36
- * it uses the lazyBus to ensure the section is open before scrolling to it
37
- *
38
- * Requires handlers to expand and lookup navigation items so that we can
39
- * traverse the parent structure and load all required items
38
+ * Scroll to a possibly lazy-loaded element. Expands parents and adds target (and
39
+ * parents) to the priority queue, then scrolls after Vue has flushed.
40
40
  */
41
41
  export declare const scrollToLazy: (id: string, setExpanded: (id: string, value: boolean) => void, getEntryById: (id: string) => {
42
42
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-bus.d.ts","sourceRoot":"","sources":["../../src/helpers/lazy-bus.ts"],"names":[],"mappings":"AAwBA;;;GAGG;AACH,eAAO,MAAM,qBAAqB,qCAAa,CAAA;AAc/C,KAAK,SAAS,GAAG,MAAM,IAAI,CAAA;AAE3B;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAO,SAMpC,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,oCAAkD,CAAA;AA8ElF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,IAAI,MAAM,GAAG,SAAS,SAIxD,CAAA;AAWD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM;;EAUpC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,GACvB,IAAI,MAAM,EACV,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,EACjD,cAAc,CAAC,EAAE,EAAE,MAAM,KAAK;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,GAAG,SAAS,SA2D/G,CAAA"}
1
+ {"version":3,"file":"lazy-bus.d.ts","sourceRoot":"","sources":["../../src/helpers/lazy-bus.ts"],"names":[],"mappings":"AAuBA,gDAAgD;AAChD,eAAO,MAAM,qBAAqB,qCAAa,CAAA;AAU/C,eAAO,MAAM,wBAAwB,GAAI,IAAI,MAAM,KAAG,MAAM,GAAG,SAA2C,CAAA;AAE1G,eAAO,MAAM,wBAAwB,GAAI,IAAI,MAAM,EAAE,QAAQ,MAAM,KAAG,IAKrE,CAAA;AASD,KAAK,SAAS,GAAG,MAAM,IAAI,CAAA;AAE3B;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAO,SAMpC,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,oCAAkD,CAAA;AAsFlF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,IAAI,MAAM,GAAG,SAAS,SAIxD,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,IAAI,MAAM,GAAG,SAAS,EAAE,kBAAgB,KAAG,IAY5E,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,QAAO,IAM9C,CAAA;AAYD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM;;EAQpC;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,GACvB,IAAI,MAAM,EACV,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,EACjD,cAAc,CAAC,EAAE,EAAE,MAAM,KAAK;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,GAAG,SAAS,SAwD/G,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { watchDebounced } from "@vueuse/core";
2
2
  import { nanoid } from "nanoid";
3
- import { computed, ref, reactive, onBeforeUnmount, nextTick } from "vue";
3
+ import { computed, reactive, ref, onBeforeUnmount, nextTick } from "vue";
4
4
  import { getSchemaParamsFromId } from "./id-routing.js";
5
5
  const priorityQueue = reactive(/* @__PURE__ */ new Set());
6
6
  const pendingQueue = reactive(/* @__PURE__ */ new Set());
@@ -10,6 +10,14 @@ const SCROLL_RETRY_MS = 3e3;
10
10
  const firstLazyLoadComplete = ref(false);
11
11
  const intersectionBlockers = reactive(/* @__PURE__ */ new Set());
12
12
  const onRenderComplete = /* @__PURE__ */ new Set();
13
+ const lazyPlaceholderHeights = reactive(/* @__PURE__ */ new Map());
14
+ const getLazyPlaceholderHeight = (id) => lazyPlaceholderHeights.get(id);
15
+ const setLazyPlaceholderHeight = (id, height) => {
16
+ if (!Number.isFinite(height) || height <= 0) {
17
+ return;
18
+ }
19
+ lazyPlaceholderHeights.set(id, Math.round(height));
20
+ };
13
21
  const addLazyCompleteCallback = (callback) => {
14
22
  if (callback) {
15
23
  onRenderComplete.add(callback);
@@ -25,22 +33,31 @@ const runLazyBus = () => {
25
33
  if (typeof window === "undefined") {
26
34
  return;
27
35
  }
28
- const unblock = blockIntersection();
36
+ if (isRunning.value) {
37
+ return;
38
+ }
39
+ isRunning.value = true;
29
40
  const processQueue = async () => {
30
- if (pendingQueue.size > 0 || priorityQueue.size > 0) {
31
- isRunning.value = true;
32
- for (const id of [...pendingQueue, ...priorityQueue]) {
33
- if (!readyQueue.has(id)) {
34
- readyQueue.add(id);
35
- }
36
- pendingQueue.delete(id);
37
- priorityQueue.delete(id);
38
- }
41
+ const priorityIds = [...priorityQueue];
42
+ const pendingIds = [...pendingQueue];
43
+ if (priorityIds.length === 0 && pendingIds.length === 0) {
44
+ onRenderComplete.forEach((fn) => fn());
45
+ onRenderComplete.clear();
46
+ isRunning.value = false;
47
+ firstLazyLoadComplete.value = true;
48
+ return;
49
+ }
50
+ for (const id of priorityIds) {
51
+ readyQueue.add(id);
52
+ priorityQueue.delete(id);
53
+ }
54
+ for (const id of pendingIds) {
55
+ readyQueue.add(id);
56
+ pendingQueue.delete(id);
39
57
  }
40
58
  await nextTick();
41
59
  onRenderComplete.forEach((fn) => fn());
42
60
  onRenderComplete.clear();
43
- unblock();
44
61
  isRunning.value = false;
45
62
  firstLazyLoadComplete.value = true;
46
63
  };
@@ -60,7 +77,7 @@ watchDebounced(
60
77
  { debounce: 300, maxWait: 1500 }
61
78
  );
62
79
  const addToPendingQueue = (id) => {
63
- if (!!id && !readyQueue.has(id) && !priorityQueue.has(id)) {
80
+ if (id && !readyQueue.has(id) && !priorityQueue.has(id)) {
64
81
  pendingQueue.add(id);
65
82
  }
66
83
  };
@@ -69,13 +86,33 @@ const addToPriorityQueue = (id) => {
69
86
  priorityQueue.add(id);
70
87
  }
71
88
  };
89
+ const requestLazyRender = (id, priority = false) => {
90
+ if (!id || readyQueue.has(id)) {
91
+ return;
92
+ }
93
+ if (priority) {
94
+ addToPriorityQueue(id);
95
+ } else {
96
+ addToPendingQueue(id);
97
+ }
98
+ if (!isRunning.value) {
99
+ runLazyBus();
100
+ }
101
+ };
102
+ const scheduleInitialLoadComplete = () => {
103
+ if (typeof window === "undefined") {
104
+ return;
105
+ }
106
+ const delay = 400;
107
+ window.setTimeout(() => runLazyBus(), delay);
108
+ };
72
109
  const resetLazyElement = (id) => {
73
110
  priorityQueue.delete(id);
74
111
  pendingQueue.delete(id);
75
112
  readyQueue.delete(id);
113
+ lazyPlaceholderHeights.delete(id);
76
114
  };
77
115
  function useLazyBus(id) {
78
- addToPendingQueue(id);
79
116
  onBeforeUnmount(() => {
80
117
  resetLazyElement(id);
81
118
  });
@@ -93,20 +130,15 @@ const scrollToLazy = (id, setExpanded, getEntryById) => {
93
130
  addToPriorityQueue(id);
94
131
  addToPriorityQueue(rawId);
95
132
  if (item?.children) {
96
- item.children.slice(0, 2).forEach((child) => {
97
- addToPriorityQueue(child.id);
98
- });
133
+ item.children.slice(0, 2).forEach((child) => addToPriorityQueue(child.id));
99
134
  }
100
135
  if (item?.parent) {
101
136
  const parent = getEntryById(item.parent.id);
102
137
  const elementIdx = parent?.children?.findIndex((child) => child.id === id);
103
138
  if (elementIdx !== void 0 && elementIdx >= 0) {
104
- parent?.children?.slice(elementIdx, elementIdx + 2).forEach((child) => {
105
- addToPriorityQueue(child.id);
106
- });
139
+ parent?.children?.slice(elementIdx, elementIdx + 2).forEach((child) => addToPriorityQueue(child.id));
107
140
  }
108
141
  }
109
- tryScroll(id, Date.now() + SCROLL_RETRY_MS, unblock, unfreeze);
110
142
  setExpanded(rawId, true);
111
143
  const addParents = (currentId) => {
112
144
  const parent = getEntryById(currentId)?.parent;
@@ -117,16 +149,17 @@ const scrollToLazy = (id, setExpanded, getEntryById) => {
117
149
  }
118
150
  };
119
151
  addParents(rawId);
152
+ void nextTick(() => {
153
+ tryScroll(id, Date.now() + SCROLL_RETRY_MS, unblock, unfreeze);
154
+ });
120
155
  };
121
156
  const tryScroll = (id, stopTime, onComplete, onFailure) => {
122
157
  const element = document.getElementById(id);
123
158
  if (element) {
124
- element.scrollIntoView({
125
- block: "start"
126
- });
159
+ element.scrollIntoView({ block: "start" });
127
160
  onComplete();
128
161
  } else if (Date.now() < stopTime) {
129
- requestAnimationFrame(() => tryScroll(id, stopTime, onComplete));
162
+ requestAnimationFrame(() => tryScroll(id, stopTime, onComplete, onFailure));
130
163
  } else {
131
164
  onComplete();
132
165
  onFailure?.();
@@ -137,9 +170,7 @@ const freeze = (id) => {
137
170
  const runFrame = (stopAfterFrame) => {
138
171
  const element = document.getElementById(id);
139
172
  if (element) {
140
- element.scrollIntoView({
141
- block: "start"
142
- });
173
+ element.scrollIntoView({ block: "start" });
143
174
  }
144
175
  if (!stopAfterFrame) {
145
176
  requestAnimationFrame(() => runFrame(stop));
@@ -154,7 +185,11 @@ export {
154
185
  addToPriorityQueue,
155
186
  blockIntersection,
156
187
  firstLazyLoadComplete,
188
+ getLazyPlaceholderHeight,
157
189
  intersectionEnabled,
190
+ requestLazyRender,
191
+ scheduleInitialLoadComplete,
158
192
  scrollToLazy,
193
+ setLazyPlaceholderHeight,
159
194
  useLazyBus
160
195
  };