@xenterprises/nuxt-x-marketing 1.4.8 → 1.4.10

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/CHANGELOG.md CHANGED
@@ -30,6 +30,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
30
30
  section and its sample product data were dropped with them. Every other section of the
31
31
  demo page is unchanged.
32
32
 
33
+ ## [1.4.10] - 2026-09-06
34
+
35
+ ### Added
36
+
37
+ - `XMarkBreadcrumb` for Docs/Academy articles: default trail is Home > Docs|Academy > current page (last crumb unlinked). Pass `items` to override. Built on `UBreadcrumb`.
38
+
39
+ ## [1.4.9] - 2026-09-06
40
+
41
+ ### Fixed
42
+
43
+ - `XMarkPricingPlans` spells billing periods as **per month** / **per year** by default (not `/month` `/year`), and normalizes common slash forms when mapping plans.
44
+
33
45
  ## [1.4.8] - 2026-09-06
34
46
 
35
47
  ### Fixed
@@ -0,0 +1,79 @@
1
+ <template>
2
+ <nav class="x-mark-breadcrumb" aria-label="Breadcrumb">
3
+ <UBreadcrumb :items="resolvedItems" />
4
+ </nav>
5
+ </template>
6
+
7
+ <script setup>
8
+ /**
9
+ * XMarkBreadcrumb — Docs/Academy article breadcrumbs (marketing page standard).
10
+ * Prefer section + current for the default Home > Docs|Academy > page trail.
11
+ * Pass items to fully override. Last item is the current page (no link).
12
+ */
13
+ const props = defineProps({
14
+ /** Full trail [{ label, to? }]. When set, overrides section/current builders. */
15
+ items: {
16
+ type: Array,
17
+ default: null,
18
+ },
19
+ /** Section root: docs or academy (builds Home > Section > current). */
20
+ section: {
21
+ type: String,
22
+ default: "docs",
23
+ validator: (v) => ["docs", "academy"].includes(v),
24
+ },
25
+ /** Current page label (required for the default trail when items is null). */
26
+ current: {
27
+ type: String,
28
+ default: "",
29
+ },
30
+ /** Home crumb label */
31
+ homeLabel: {
32
+ type: String,
33
+ default: "Home",
34
+ },
35
+ /** Home crumb path */
36
+ homeTo: {
37
+ type: String,
38
+ default: "/",
39
+ },
40
+ /** Override section crumb label */
41
+ sectionLabel: {
42
+ type: String,
43
+ default: "",
44
+ },
45
+ /** Override section crumb path */
46
+ sectionTo: {
47
+ type: String,
48
+ default: "",
49
+ },
50
+ });
51
+
52
+ const sectionDefaults = {
53
+ docs: { label: "Docs", to: "/docs" },
54
+ academy: { label: "Academy", to: "/academy" },
55
+ };
56
+
57
+ const resolvedItems = computed(() => {
58
+ if (Array.isArray(props.items) && props.items.length) {
59
+ return props.items.map((item, index, arr) => {
60
+ const isLast = index === arr.length - 1;
61
+ return {
62
+ label: item.label,
63
+ ...(isLast || !item.to ? {} : { to: item.to }),
64
+ };
65
+ });
66
+ }
67
+
68
+ const section = sectionDefaults[props.section] || sectionDefaults.docs;
69
+ const sectionLabel = props.sectionLabel || section.label;
70
+ const sectionTo = props.sectionTo || section.to;
71
+ const current = props.current || "Article";
72
+
73
+ return [
74
+ { label: props.homeLabel, to: props.homeTo },
75
+ { label: sectionLabel, to: sectionTo },
76
+ { label: current },
77
+ ];
78
+ });
79
+ </script>
@@ -82,7 +82,7 @@ const props = defineProps({
82
82
  {
83
83
  name: "Starter",
84
84
  price: "$9",
85
- period: "/month",
85
+ period: "per month",
86
86
  description: "Perfect for individuals and small projects.",
87
87
  features: ["5 projects", "10GB storage", "Email support"],
88
88
  button: { label: "Get Started" },
@@ -90,7 +90,7 @@ const props = defineProps({
90
90
  {
91
91
  name: "Pro",
92
92
  price: "$29",
93
- period: "/month",
93
+ period: "per month",
94
94
  description: "For growing teams that need more power.",
95
95
  features: [
96
96
  "Unlimited projects",
@@ -104,7 +104,7 @@ const props = defineProps({
104
104
  {
105
105
  name: "Enterprise",
106
106
  price: "$99",
107
- period: "/month",
107
+ period: "per month",
108
108
  description: "For large organizations with custom needs.",
109
109
  features: [
110
110
  "Unlimited everything",
@@ -171,6 +171,14 @@ const emit = defineEmits(["select"]);
171
171
 
172
172
  const billingPeriod = ref("monthly");
173
173
 
174
+ function normalizePeriod(period) {
175
+ if (!period) return period;
176
+ const trimmed = String(period).trim().toLowerCase();
177
+ if (["/month", "/mo", "mo", "month"].includes(trimmed)) return "per month";
178
+ if (["/year", "/yr", "yr", "year"].includes(trimmed)) return "per year";
179
+ return period;
180
+ }
181
+
174
182
  // Map XMark plan format to Nuxt UI format
175
183
  const mappedPlans = computed(() => {
176
184
  return props.plans.map((plan) => {
@@ -183,9 +191,13 @@ const mappedPlans = computed(() => {
183
191
  billingPeriod.value === "yearly"
184
192
  ? plan.price.yearly
185
193
  : plan.price.monthly;
186
- billingCycle = billingPeriod.value === "yearly" ? "/year" : "/month";
194
+ billingCycle =
195
+ billingPeriod.value === "yearly" ? "per year" : "per month";
187
196
  } else if (props.hasBillingToggle && !plan.period) {
188
- billingCycle = billingPeriod.value === "yearly" ? "/year" : "/month";
197
+ billingCycle =
198
+ billingPeriod.value === "yearly" ? "per year" : "per month";
199
+ } else {
200
+ billingCycle = normalizePeriod(billingCycle);
189
201
  }
190
202
 
191
203
  // Determine if this plan should be highlighted
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@xenterprises/nuxt-x-marketing",
3
3
  "license": "SEE LICENSE IN LICENSE",
4
4
  "type": "module",
5
- "version": "1.4.8",
5
+ "version": "1.4.10",
6
6
  "main": "./nuxt.config.ts",
7
7
  "scripts": {
8
8
  "dev": "nuxi dev .playground",