@vuecs/navigation
Version:
A package for multi level navigations.
1,107 lines (1,106 loc) • 33.3 kB
JavaScript
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