UNPKG

@vuecs/navigation

Version:

A package for multi level navigations.

1,107 lines (1,106 loc) 33.3 kB
import { hasNormalizedSlot, inject, installDefaultsManager, installThemeManager, isPromise, normalizeSlot, provide, useArrowNavigation, useComponentTheme } from "@vuecs/core"; import { computed, defineComponent, getCurrentInstance, h, inject as inject$1, mergeProps, onMounted, onUnmounted, provide as provide$1, ref, resolveComponent, shallowReactive, toRef, watch, watchEffect } from "vue"; import { VCLink } from "@vuecs/link"; import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuRoot, NavigationMenuTrigger, StepperDescription, StepperIndicator, StepperItem, StepperRoot, StepperSeparator, StepperTitle, StepperTrigger } from "reka-ui"; //#region src/registry/module.ts function createEmptyEntry() { return { items: ref([]), active: computed(() => []), activeTrail: computed(() => []) }; } /** * Reactive, app-wide navigation registry. `<VCNavItems registry>` * publishes its resolved output here under a `registry-id`; other navs * read it reactively + empty-safe via the resolver context's * `registry(id)`. * * The backing map is `shallowReactive`, so membership changes * (register + the returned unregister closure) are tracked dependencies * — a consumer reading `get(id)` inside a `computed` / `watchEffect` * re-runs when the id's occupancy flips. */ var NavigationRegistry = class { map = shallowReactive(/* @__PURE__ */ new Map()); /** * Stable empty entries handed out for absent ids, memoized per id so * the SAME reactive handle is returned every call — a consumer * subscribed to an absent id keeps its dependency and lights up the * moment an occupant registers. */ empties = /* @__PURE__ */ new Map(); /** * Claim `id`. Last-wins: a newer occupant replaces the current one * (dev warning on collision). Returns a token-guarded unregister * closure: it releases `id` ONLY if this registration is still the * occupant. During a route handoff (Vue mounts the new page before * unmounting the old) the departing nav's closure holds a stale token * and cannot evict the incoming occupant. */ register(id, entry) { if (this.map.has(id)) console.warn(`[vuecs] navigation registry id "${id}" reassigned to a new occupant.`); const token = Symbol("vc-nav-registry-token"); this.map.set(id, { token, entry }); return () => { const occupant = this.map.get(id); if (occupant && occupant.token === token) this.map.delete(id); }; } /** Reactive, empty-safe read. Never returns `undefined`. */ get(id) { const occupant = this.map.get(id); if (occupant) return occupant.entry; let empty = this.empties.get(id); if (!empty) { empty = createEmptyEntry(); this.empties.set(id, empty); } return empty; } /** True when an occupant currently holds `id`. */ has(id) { return this.map.has(id); } }; //#endregion //#region src/registry/singleton.ts const sym = Symbol.for("VCNavigationRegistry"); function tryInjectNavigationRegistry(app) { return inject(sym, app); } function injectNavigationRegistry(app) { const instance = tryInjectNavigationRegistry(app); if (!instance) throw new Error("A navigation registry has not been provided."); return instance; } function provideNavigationRegistry(registry = new NavigationRegistry(), app) { provide(sym, registry, app); return registry; } //#endregion //#region src/helpers/match.ts function calculateItemScoreForPath(item, currentPath) { if (item.url === "/") return 1; if (item.activeMatch) { if (item.activeMatch === currentPath) return 6; if (currentPath.startsWith(item.activeMatch)) return 4; } if (item.url) { if (item.url === currentPath) return 3; if (currentPath.startsWith(item.url)) return 2; } return 0; } function findItemMatchesIF(items, options, parent) { const output = []; for (const item of items) { let { score } = parent; if (options.path) score += calculateItemScoreForPath(item, options.path); if (item.default) score += 1; if (item.children) { const childMatches = findItemMatchesIF(item.children, options, { score }); output.push(...childMatches); } output.push({ data: item, score }); } return output.sort((a, b) => b.score - a.score); } function findBestItemMatches(items, options = {}) { const result = findItemMatchesIF(items, options, { score: 0 }); const [first] = result; if (!first) return []; return result.filter((match) => match.score === first.score).map((match) => match.data); } //#endregion //#region src/helpers/normalize.ts function normalizeItemIF(item, trace) { const output = { ...item, children: [], trace: [...trace, item.name], meta: item.meta || {} }; if (!item.children) return output; for (let i = 0; i < item.children.length; i++) output.children.push(normalizeItemIF(item.children[i], output.trace)); return output; } function normalizeItem(item) { return normalizeItemIF(item, []); } function normalizeItems(items) { return items.map((item) => normalizeItem(item)); } //#endregion //#region src/helpers/trace.ts function isTraceEqual(a, b) { if (a.length !== b.length) return false; for (const [i, element] of a.entries()) if (element !== b[i]) return false; return true; } function isTracePartOf(item, parent) { for (const [i, element] of item.entries()) if (parent[i] !== element) return false; return true; } //#endregion //#region src/helpers/reset.ts function resetItemsByTraceIF(items, trace) { for (const item of items) { const isEqual = isTraceEqual(item.trace, trace); item.active = isEqual; item.display = true; if (isEqual) { item.activeWithin = false; item.displayChildren = true; } else { const isAncestor = isTracePartOf(item.trace, trace); item.activeWithin = isAncestor; item.displayChildren = isAncestor; } item.children = resetItemsByTraceIF(item.children, trace); } return items; } function resetItemsByTrace(items, trace) { return resetItemsByTraceIF(items, trace); } //#endregion //#region src/helpers/submenu.ts /** * Resolve the effective submenu presentation. An explicit `collapse` / * `dropdown` wins; `auto` derives from orientation — only an explicit * `horizontal` opts into the dropdown (NavigationMenu) path, everything * else collapses (Collapsible). */ function resolveSubmenuMode(submenu, orientation) { if (submenu === "collapse" || submenu === "dropdown") return submenu; return orientation === "horizontal" ? "dropdown" : "collapse"; } //#endregion //#region src/helpers/trail.ts /** * Walk a normalized tree along `trace` (an ordered list of item names, * root → leaf) and collect the item at each depth. Returns the ordered * active trail: `[0]` is the top-level section, `.at(-1)` is the leaf. */ function collectTrail(items, trace) { const output = []; let level = items; for (const name of trace) { const found = level.find((item) => item.name === name); if (!found) break; output.push(found); level = found.children; } return output; } /** * Depth-first collect of every item in the tree matching `predicate`. */ function flattenWhere(items, predicate) { const output = []; for (const item of items) { if (predicate(item)) output.push(item); if (item.children.length > 0) output.push(...flattenWhere(item.children, predicate)); } return output; } //#endregion //#region src/helpers/url.ts function isAbsoluteURL(str) { return str.substring(0, 7) === "http://" || str.substring(0, 8) === "https://"; } //#endregion //#region src/components/items/theme.ts /** * Default classes for the `navigation` theme entry. Shared between * `<VCNavItems>` (the container) and `<VCNavItem>` (the per-row * component) — both call `useComponentTheme('navigation', …)` with * the same slot defaults, so the source of truth lives here. */ const navigationThemeDefaults = { classes: { group: "vc-nav-items", item: "vc-nav-item", itemNested: "vc-nav-item-nested", separator: "vc-nav-separator", link: "vc-nav-link", linkRoot: "vc-nav-link-root", linkIcon: "vc-nav-link-icon", linkText: "vc-nav-link-text", trigger: "vc-nav-trigger", content: "vc-nav-content", viewport: "vc-nav-viewport" }, variants: { variant: { list: {}, pills: { group: "vc-nav-items--pills", item: "vc-nav-item--pills", link: "vc-nav-link--pills" } }, orientation: { horizontal: {}, vertical: { group: "vc-nav-items--vertical" } } }, defaultVariants: { variant: "list", orientation: "horizontal" } }; //#endregion //#region src/components/select-context.ts /** * Channels a `<VCNavItem>`'s already-normalized + scored `children` down * to the nested `<VCNavItems>` that renders its submenu. * * The top-level nav scores the WHOLE tree once; nested lists must render * those results as-is rather than re-resolving / re-scoring a subtree * (which would clobber traces and lose whole-tree active context). The * nested `<VCNavItems>` reads this when it has no own `data` prop — * presence of the injected nodes is what marks it as a nested renderer * rather than a resolving root. Each `<VCNavItem>` re-provides its own * children, so the value is correctly scoped per nesting level. */ const NAVIGATION_NODES_KEY = Symbol("vc-navigation-nodes"); const NAVIGATION_SELECT_KEY = Symbol("vc-navigation-select"); const VCNavItem = defineComponent({ name: "VCNavItem", props: { data: { type: Object, required: true }, variant: { type: String, default: void 0 }, orientation: { type: String, default: void 0 }, /** * Resolved submenu presentation handed down by the parent * `<VCNavItems>`. `collapse` renders groups as an inline * Reka `Collapsible`; `dropdown` renders them as Reka * `NavigationMenu` flyouts. */ submenu: { type: String, default: "collapse" }, /** * The tag (or component) this item renders as — its own wrapper * (`<li>` by default). Receives `<VCNavItems>`' `itemAs`. Honored in * collapse mode only. */ as: { type: [String, Object], default: "li" }, /** * The list-container tag for this item's nested submenu * `<VCNavItems>` (`<ul>` by default). Receives `<VCNavItems>`' `as`. * Honored in collapse mode only. */ itemsAs: { type: [String, Object], default: "ul" }, themeClass: { type: Object, default: void 0 }, themeVariant: { type: Object, default: void 0 } }, slots: Object, setup(props, { slots }) { const itemsNode = resolveComponent("VCNavItems"); const theme = useComponentTheme("navigation", { get themeClass() { return props.themeClass; }, get themeVariant() { return { ...props.themeVariant ?? {}, ...props.variant !== void 0 ? { variant: props.variant } : {} }; } }, navigationThemeDefaults); const data = toRef(props, "data"); const hasChildren = computed(() => data.value.children && data.value.children.length > 0); provide$1(NAVIGATION_NODES_KEY, computed(() => data.value.children)); const open = ref(!!data.value.displayChildren); watch(() => data.value.displayChildren, (value) => { open.value = !!value; }); const selectContext = inject$1(NAVIGATION_SELECT_KEY, null); const select = () => { selectContext?.select(data.value); }; const toggle = () => { open.value = !open.value; }; const renderIcon = (icon) => { if (icon.includes(":")) return h(resolveComponent("VCIcon"), { name: icon }); return h("i", { class: icon }); }; const renderTitleInner = (resolved) => [...data.value.icon ? [h("div", { class: resolved.linkIcon || void 0 }, [renderIcon(data.value.icon)])] : [], h("div", { class: resolved.linkText || void 0 }, [data.value.name])]; const renderLeaf = (resolved) => { if (hasNormalizedSlot("link", slots)) return normalizeSlot("link", { data: data.value, select, isActive: data.value.active }, slots); const linkProps = { active: data.value.active, disabled: false, prefetch: true }; if (data.value.url) if (isAbsoluteURL(data.value.url) || data.value.url.startsWith("#")) { linkProps.href = data.value.url; if (data.value.urlTarget) linkProps.target = data.value.urlTarget; } else linkProps.to = data.value.url; return h(VCLink, { class: [resolved.link], "data-vc-collection-item": "", ...linkProps, onClicked: select }, { default: () => renderTitleInner(resolved) }); }; const renderChildren = () => { if (hasNormalizedSlot("sub-items", slots)) return normalizeSlot("sub-items", { data: data.value, select, toggle }); return h(itemsNode, { variant: props.variant, orientation: props.orientation, submenu: props.submenu === "dropdown" ? "collapse" : props.submenu, as: props.itemsAs, itemAs: props.as, themeClass: props.themeClass, themeVariant: props.themeVariant }); }; return () => { const resolved = theme.value; const isDropdown = props.submenu === "dropdown"; const isActive = data.value.active || data.value.activeWithin; if (data.value.type === "separator") { const body = hasNormalizedSlot("separator", slots) ? normalizeSlot("separator", { data: data.value }, slots) : h("div", { class: resolved.separator || void 0 }, data.value.name); if (isDropdown) return h(NavigationMenuItem, { class: [resolved.item] }, { default: () => body }); return h(props.as, { class: [resolved.item] }, [body]); } if (!hasChildren.value) { const leaf = renderLeaf(resolved); if (isDropdown) return h(NavigationMenuItem, { class: [resolved.item], "data-active": data.value.active ? "" : void 0 }, { default: () => h(NavigationMenuLink, { active: data.value.active, asChild: true }, { default: () => leaf }) }); return h(props.as, { class: [resolved.item, { active: data.value.active }], "data-active": data.value.active ? "" : void 0 }, [leaf]); } if (hasNormalizedSlot("sub", slots)) { const body = normalizeSlot("sub", { data: data.value, select, toggle }, slots); if (isDropdown) return h(NavigationMenuItem, { class: [resolved.item, resolved.itemNested], "data-active": isActive ? "" : void 0 }, { default: () => body }); return h(props.as, { class: [ resolved.item, resolved.itemNested, { active: isActive } ], "data-active": isActive ? "" : void 0 }, [body]); } const title = hasNormalizedSlot("sub-title", slots) ? normalizeSlot("sub-title", { data: data.value, select, toggle }) : renderTitleInner(resolved); if (isDropdown) return h(NavigationMenuItem, { class: [resolved.item, resolved.itemNested], "data-active": isActive ? "" : void 0 }, { default: () => [h(NavigationMenuTrigger, { class: resolved.trigger || void 0, "data-vc-collection-item": "", "data-active": isActive ? "" : void 0 }, { default: () => title }), h(NavigationMenuContent, { class: resolved.content || void 0 }, { default: () => renderChildren() })] }); return h(CollapsibleRoot, { as: props.as, class: [ resolved.item, resolved.itemNested, { active: data.value.active || open.value } ], "data-active": isActive ? "" : void 0, open: open.value, "onUpdate:open": (value) => { open.value = value; } }, { default: () => [h(CollapsibleTrigger, { class: resolved.trigger || void 0, "data-vc-collection-item": "", "data-active": isActive ? "" : void 0 }, { default: () => title }), h(CollapsibleContent, { class: resolved.content || void 0 }, { default: () => renderChildren() })] }); }; } }); const VCNavItems = defineComponent({ name: "VCNavItems", props: { /** * The source of this nav's items. Plain array, sync fn, or async fn. * A fn receives a NavigationResolverContext and may read reactive * state freely — the nav re-runs it automatically when that state * changes. * * When omitted, the nav checks whether it is a nested submenu of a * parent `<VCNavItem>` (via the {@link NAVIGATION_NODES_KEY} inject) * and, if so, renders that parent's already-scored children as-is. */ data: { type: [Array, Function], default: void 0 }, /** Opt in to publishing this nav's resolved output into the registry. */ registry: { type: Boolean, default: false }, /** The key under which to publish. Required when `registry` is true. */ registryId: { type: String, default: void 0 }, /** * Current path for active-state matching. When omitted, the nav softly * reads vue-router's current route (via the `$route` global property) * if a router is installed; router-free apps simply get `undefined`. */ path: { type: String, default: void 0 }, /** * Extra reactive deps that should retrigger the resolver — for state * read only AFTER the first `await` in an async resolver (auto-track * can't see past an await). */ watch: { type: Array, default: void 0 }, variant: { type: String, default: void 0 }, orientation: { type: String, default: void 0 }, /** * How items with children render their submenu. `auto` derives from * orientation (horizontal → dropdown, otherwise collapse). */ submenu: { type: String, default: "auto" }, /** * The tag (or component) for this nav's list container. Defaults to * `'ul'`. Forwarded unchanged to every nesting level so the whole tree * renders the same container tag. Honored in collapse mode only — * dropdown mode keeps Reka's NavigationMenu primitives. */ as: { type: [String, Object], default: "ul" }, /** * The tag (or component) for each item wrapper. Defaults to `'li'`. * Forwarded unchanged to every nesting level. Honored in collapse mode * only — dropdown mode keeps Reka's NavigationMenu primitives. */ itemAs: { type: [String, Object], default: "li" }, themeClass: { type: Object, default: void 0 }, themeVariant: { type: Object, default: void 0 } }, slots: Object, setup(props, { slots, expose }) { const theme = useComponentTheme("navigation", { get themeClass() { return props.themeClass; }, get themeVariant() { return { ...props.themeVariant ?? {}, ...props.variant !== void 0 ? { variant: props.variant } : {}, ...props.orientation !== void 0 ? { orientation: props.orientation } : {} }; } }, navigationThemeDefaults); const rootRef = ref(null); const onKeyDown = (event) => { useArrowNavigation(event, event.target, rootRef.value, { arrowKeyOptions: "vertical", focus: true, loop: true }); }; const registry = tryInjectNavigationRegistry() ?? new NavigationRegistry(); const globals = getCurrentInstance()?.appContext.config.globalProperties; const currentPath = computed(() => { if (typeof props.path !== "undefined") return props.path; return (globals?.$route)?.path; }); const injectedNodes = inject$1(NAVIGATION_NODES_KEY, null); const isNested = computed(() => typeof props.data === "undefined" && injectedNodes !== null); const selectedTrace = ref(null); if (!isNested.value) { provide$1(NAVIGATION_SELECT_KEY, { select: (item) => { selectedTrace.value = item.trace; } }); watch(currentPath, () => { selectedTrace.value = null; }); } const raw = ref([]); let runToken = 0; async function run() { const token = ++runToken; const value = typeof props.data === "function" ? props.data({ path: currentPath.value, registry: (id) => registry.get(id) }) : props.data ?? []; if (!isPromise(value)) { raw.value = value ?? []; return; } try { const awaited = await value ?? []; if (token === runToken) raw.value = awaited; } catch (error) { if (token === runToken) console.error("[vuecs] <VCNavItems> resolver rejected:", error); } } if (!isNested.value) { watchEffect(run); if (props.watch) watch(props.watch, run); } expose({ refresh: run }); const resolved = computed(() => { if (isNested.value && injectedNodes) return { items: injectedNodes.value, trace: [] }; const normalized = normalizeItems(raw.value); const [match] = findBestItemMatches(normalized, { path: currentPath.value }); const trace = selectedTrace.value ?? (match ? match.trace : []); resetItemsByTrace(normalized, trace); return { items: normalized, trace }; }); const tree = computed(() => resolved.value.items); const active = computed(() => flattenWhere(tree.value, (item) => !!item.active)); const activeTrail = computed(() => collectTrail(tree.value, resolved.value.trace)); if (props.registry) { let unsubscribeFn; const entry = { items: tree, active, activeTrail }; onMounted(() => { if (!props.registryId) { console.warn("[vuecs] <VCNavItems registry> requires a `registry-id`."); return; } unsubscribeFn = registry.register(props.registryId, entry); }); onUnmounted(() => { if (!props.registryId || !unsubscribeFn) return; unsubscribeFn(); }); } const submenuMode = computed(() => resolveSubmenuMode(props.submenu, props.orientation)); return () => { const resolvedTheme = theme.value; const vNodes = []; for (let i = 0; i < tree.value.length; i++) { const item = tree.value[i]; if (!item.display && !item.displayChildren) continue; let vNode; if (hasNormalizedSlot("item", slots)) vNode = normalizeSlot("item", { data: item }, slots); else vNode = h(VCNavItem, { key: item.trace.join("/") || i, data: item, variant: props.variant, orientation: props.orientation, submenu: submenuMode.value, as: props.itemAs, itemsAs: props.as, themeClass: props.themeClass, themeVariant: props.themeVariant }); vNodes.push(vNode); } if (submenuMode.value === "dropdown") return h(NavigationMenuRoot, { orientation: "horizontal" }, { default: () => h(NavigationMenuList, { class: resolvedTheme.group || void 0 }, { default: () => vNodes }) }); const isRoot = !isNested.value; return h(props.as, { class: resolvedTheme.group || void 0, ...isRoot ? { ref: rootRef, onKeydown: onKeyDown } : {} }, vNodes); }; } }); //#endregion //#region src/components/stepper/context.ts const STEPPER_CONTEXT_KEY = Symbol("vcStepperContext"); function provideStepperContext(ctx) { provide$1(STEPPER_CONTEXT_KEY, ctx); } function useStepperContext() { return inject$1(STEPPER_CONTEXT_KEY, null); } //#endregion //#region src/components/stepper/theme.ts const stepperThemeDefaults = { classes: { root: "vc-stepper", item: "vc-stepper-item", trigger: "vc-stepper-trigger", indicator: "vc-stepper-indicator", title: "vc-stepper-title", description: "vc-stepper-description", separator: "vc-stepper-separator" } }; //#endregion //#region src/components/stepper/Stepper.vue var Stepper_default = defineComponent({ name: "VCStepper", inheritAttrs: false, props: { /** Active step (1-based). v-modeled. */ modelValue: { type: Number, default: void 0 }, /** Initial active step for uncontrolled usage. */ defaultValue: { type: Number, default: 1 }, /** Layout direction. */ orientation: { type: String, default: "horizontal" }, /** Reading direction. Falls back to the ConfigManager's `dir` value when omitted. */ dir: { type: String, default: void 0 }, /** When `true`, steps must be completed in order — Reka blocks navigation past the next incomplete step. */ linear: { type: Boolean, default: true }, /** Theme-class overrides for this component instance. */ themeClass: { type: Object, default: void 0 }, /** Theme-variant values for this component instance. */ themeVariant: { type: Object, default: void 0 } }, emits: ["update:modelValue"], setup(props, { attrs, slots, emit }) { provideStepperContext({ themeClass: () => props.themeClass, themeVariant: () => props.themeVariant }); const theme = useComponentTheme("stepper", props, stepperThemeDefaults); return () => h(StepperRoot, mergeProps(attrs, { modelValue: props.modelValue, defaultValue: props.defaultValue, orientation: props.orientation, dir: props.dir, linear: props.linear, "onUpdate:modelValue": (value) => emit("update:modelValue", value), class: theme.value.root || void 0 }), { default: () => slots.default?.() }); } }); //#endregion //#region src/components/stepper/StepperItem.vue var StepperItem_default = defineComponent({ name: "VCStepperItem", inheritAttrs: false, props: { /** 1-based step index. Required by Reka — used to determine completion / active state. */ step: { type: Number, required: true }, /** Block interaction with this step. */ disabled: { type: Boolean, default: false }, /** Force completion state. Reka derives this automatically when `false`. */ completed: { type: Boolean, default: false }, /** Theme-class overrides for this component instance. */ themeClass: { type: Object, default: void 0 }, /** Theme-variant values for this component instance. */ themeVariant: { type: Object, default: void 0 } }, slots: Object, setup(props, { attrs, slots }) { const ctx = useStepperContext(); const theme = useComponentTheme("stepper", { get themeClass() { return { ...ctx?.themeClass() ?? {}, ...props.themeClass ?? {} }; }, get themeVariant() { return { ...ctx?.themeVariant() ?? {}, ...props.themeVariant ?? {} }; } }, stepperThemeDefaults); return () => h(StepperItem, { step: props.step, disabled: props.disabled, completed: props.completed, ...mergeProps(attrs, { class: ["group", theme.value.item || void 0] }) }, { default: ({ state }) => slots.default?.({ state }) }); } }); //#endregion //#region src/components/stepper/StepperTrigger.vue var StepperTrigger_default = defineComponent({ name: "VCStepperTrigger", inheritAttrs: false, props: { /** Render the consumer's slot child as the trigger root (Reka `asChild` pattern). */ asChild: { type: Boolean, default: false }, /** HTML tag to render. */ as: { type: [String, Object], default: "button" }, /** Theme-class overrides for this component instance. */ themeClass: { type: Object, default: void 0 }, /** Theme-variant values for this component instance. */ themeVariant: { type: Object, default: void 0 } }, setup(props, { attrs, slots }) { const ctx = useStepperContext(); const theme = useComponentTheme("stepper", { get themeClass() { return { ...ctx?.themeClass() ?? {}, ...props.themeClass ?? {} }; }, get themeVariant() { return { ...ctx?.themeVariant() ?? {}, ...props.themeVariant ?? {} }; } }, stepperThemeDefaults); return () => h(StepperTrigger, mergeProps(props.as === "button" ? { type: "button" } : {}, attrs, { as: props.as, asChild: props.asChild, class: theme.value.trigger || void 0 }), { default: () => slots.default?.() }); } }); //#endregion //#region src/components/stepper/StepperIndicator.vue var StepperIndicator_default = defineComponent({ name: "VCStepperIndicator", inheritAttrs: false, props: { /** Render the consumer's slot child as the indicator root (Reka `asChild` pattern). */ asChild: { type: Boolean, default: false }, /** HTML tag to render. */ as: { type: [String, Object], default: "div" }, /** Theme-class overrides for this component instance. */ themeClass: { type: Object, default: void 0 }, /** Theme-variant values for this component instance. */ themeVariant: { type: Object, default: void 0 } }, setup(props, { attrs, slots }) { const ctx = useStepperContext(); const theme = useComponentTheme("stepper", { get themeClass() { return { ...ctx?.themeClass() ?? {}, ...props.themeClass ?? {} }; }, get themeVariant() { return { ...ctx?.themeVariant() ?? {}, ...props.themeVariant ?? {} }; } }, stepperThemeDefaults); return () => h(StepperIndicator, mergeProps(attrs, { as: props.as, asChild: props.asChild, class: theme.value.indicator || void 0 }), { default: () => slots.default?.() }); } }); //#endregion //#region src/components/stepper/StepperTitle.vue var StepperTitle_default = defineComponent({ name: "VCStepperTitle", inheritAttrs: false, props: { /** Render the consumer's slot child as the title root (Reka `asChild` pattern). */ asChild: { type: Boolean, default: false }, /** HTML tag to render. */ as: { type: [String, Object], default: "h4" }, /** Theme-class overrides for this component instance. */ themeClass: { type: Object, default: void 0 }, /** Theme-variant values for this component instance. */ themeVariant: { type: Object, default: void 0 } }, setup(props, { attrs, slots }) { const ctx = useStepperContext(); const theme = useComponentTheme("stepper", { get themeClass() { return { ...ctx?.themeClass() ?? {}, ...props.themeClass ?? {} }; }, get themeVariant() { return { ...ctx?.themeVariant() ?? {}, ...props.themeVariant ?? {} }; } }, stepperThemeDefaults); return () => h(StepperTitle, mergeProps(attrs, { as: props.as, asChild: props.asChild, class: theme.value.title || void 0 }), { default: () => slots.default?.() }); } }); //#endregion //#region src/components/stepper/StepperDescription.vue var StepperDescription_default = defineComponent({ name: "VCStepperDescription", inheritAttrs: false, props: { /** Render the consumer's slot child as the description root (Reka `asChild` pattern). */ asChild: { type: Boolean, default: false }, /** HTML tag to render. */ as: { type: [String, Object], default: "p" }, /** Theme-class overrides for this component instance. */ themeClass: { type: Object, default: void 0 }, /** Theme-variant values for this component instance. */ themeVariant: { type: Object, default: void 0 } }, setup(props, { attrs, slots }) { const ctx = useStepperContext(); const theme = useComponentTheme("stepper", { get themeClass() { return { ...ctx?.themeClass() ?? {}, ...props.themeClass ?? {} }; }, get themeVariant() { return { ...ctx?.themeVariant() ?? {}, ...props.themeVariant ?? {} }; } }, stepperThemeDefaults); return () => h(StepperDescription, mergeProps(attrs, { as: props.as, asChild: props.asChild, class: theme.value.description || void 0 }), { default: () => slots.default?.() }); } }); //#endregion //#region src/components/stepper/StepperSeparator.vue var StepperSeparator_default = defineComponent({ name: "VCStepperSeparator", inheritAttrs: false, props: { /** Render the consumer's slot child as the separator root (Reka `asChild` pattern). */ asChild: { type: Boolean, default: false }, /** HTML tag to render. */ as: { type: [String, Object], default: "div" }, /** Theme-class overrides for this component instance. */ themeClass: { type: Object, default: void 0 }, /** Theme-variant values for this component instance. */ themeVariant: { type: Object, default: void 0 } }, setup(props, { attrs, slots }) { const ctx = useStepperContext(); const theme = useComponentTheme("stepper", { get themeClass() { return { ...ctx?.themeClass() ?? {}, ...props.themeClass ?? {} }; }, get themeVariant() { return { ...ctx?.themeVariant() ?? {}, ...props.themeVariant ?? {} }; } }, stepperThemeDefaults); return () => h(StepperSeparator, mergeProps(attrs, { as: props.as, asChild: props.asChild, class: theme.value.separator || void 0 }), { default: () => slots.default?.() }); } }); //#endregion //#region src/index.ts function install(instance, options = {}) { provideNavigationRegistry(new NavigationRegistry(), instance); installThemeManager(instance, options); installDefaultsManager(instance, options); Object.entries({ VCNavItem, VCNavItems, VCStepper: Stepper_default, VCStepperItem: StepperItem_default, VCStepperTrigger: StepperTrigger_default, VCStepperIndicator: StepperIndicator_default, VCStepperTitle: StepperTitle_default, VCStepperDescription: StepperDescription_default, VCStepperSeparator: StepperSeparator_default }).forEach(([componentName, component]) => { instance.component(componentName, component); }); } var src_default = { install }; //#endregion export { NAVIGATION_NODES_KEY, NAVIGATION_SELECT_KEY, NavigationRegistry, VCNavItem, VCNavItems, Stepper_default as VCStepper, StepperDescription_default as VCStepperDescription, StepperIndicator_default as VCStepperIndicator, StepperItem_default as VCStepperItem, StepperSeparator_default as VCStepperSeparator, StepperTitle_default as VCStepperTitle, StepperTrigger_default as VCStepperTrigger, src_default as default, injectNavigationRegistry, install, navigationThemeDefaults, provideNavigationRegistry, provideStepperContext, stepperThemeDefaults, tryInjectNavigationRegistry, useStepperContext }; //# sourceMappingURL=index.mjs.map