@dcodegroup-au/page-builder 0.1.1 → 0.1.2

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.
@@ -4,12 +4,10 @@ const page = {
4
4
  title: "Page Builder",
5
5
  sections: [
6
6
  {
7
- id: 1,
8
7
  title: "Hero header",
9
8
  type: "header",
10
9
  components: [
11
10
  {
12
- id: 1,
13
11
  name: "Sliders",
14
12
  type: "sliders",
15
13
  supportive_text: "Manage the slides in the slider.",
@@ -99,7 +97,6 @@ const page = {
99
97
  ]
100
98
  },
101
99
  {
102
- id: 2,
103
100
  name: "Links",
104
101
  type: "links",
105
102
  supportive_text: "Manage the slides in the slider.",
@@ -122,12 +119,10 @@ const page = {
122
119
  ]
123
120
  },
124
121
  {
125
- id: 2,
126
122
  title: "Logo cloud",
127
123
  type: "logo",
128
124
  components: [
129
125
  {
130
- id: 1,
131
126
  name: "Section header",
132
127
  type: "header",
133
128
  is_public: true,
@@ -140,74 +135,126 @@ const page = {
140
135
  ]
141
136
  },
142
137
  {
143
- id: 3,
144
138
  title: "Vertical tabs module",
145
- type: "tab_module",
139
+ type: "tabs",
146
140
  components: [
147
141
  {
148
- id: 1,
149
142
  name: "Section header",
150
143
  type: "header",
151
- is_public: true,
144
+ title: 'Rich with resources',
145
+ center: true,
146
+ dark: true,
147
+ supporting_text: 'Comprehensive free and paid resources covering every aspect of early childhood education.',
152
148
  },
153
149
  {
154
- id: 2,
155
150
  name: "Vertical tabs",
156
151
  type: "vertical_tabs",
152
+ supportive_text: "Manage the tabs",
153
+ max_items: 5,
154
+ public: true,
155
+ data: [
156
+ {
157
+ title: "Free Downloadable Resources",
158
+ description: "Explore a variety of no-cost tools and information available to everyone.",
159
+ public: true,
160
+ featured_image: 'https://beta-frontend.elaa.org.au/img/homepage/asset_free_resources.png',
161
+ primary_button: {
162
+ show: true,
163
+ name: 'Linked to',
164
+ label: 'View all',
165
+ url: 'google.com', // external could be an url
166
+ type: 'external-page',
167
+ is_new_tab: true,
168
+ },
169
+ },
170
+ {
171
+ title: "Paid Resources & Subscriptions",
172
+ description: "Access premium content and exclusive materials with our subscription services.",
173
+ public: true,
174
+ featured_image: 'https://beta-frontend.elaa.org.au/img/homepage/asset_paid_resources.png',
175
+ primary_button: {
176
+ show: true,
177
+ name: 'Linked to',
178
+ label: 'View all',
179
+ url: 'google.com', // external could be an url
180
+ type: 'external-page',
181
+ is_new_tab: true,
182
+ },
183
+ },
184
+ {
185
+ title: "Video Library",
186
+ description: "Watch quick, informative videos (10-15 mins each) available for free.",
187
+ public: true,
188
+ featured_image: 'https://beta-frontend.elaa.org.au/img/homepage/asset_video.png',
189
+ primary_button: {
190
+ show: true,
191
+ name: 'Linked to',
192
+ label: 'View all',
193
+ url: 'google.com', // external could be an url
194
+ type: 'external-page',
195
+ is_new_tab: true,
196
+ },
197
+ },
198
+ {
199
+ title: "Self-paced Modules",
200
+ description: "Engage with our flexible, on-demand training modules at your own pace.",
201
+ public: true,
202
+ featured_image: 'https://beta-frontend.elaa.org.au/img/homepage/asset_self_paced.png',
203
+ primary_button: {
204
+ show: true,
205
+ name: 'Linked to',
206
+ label: 'View all',
207
+ url: 'google.com', // external could be an url
208
+ type: 'external-page',
209
+ is_new_tab: true,
210
+ },
211
+ },
212
+ ]
157
213
  }
158
214
  ]
159
215
  },
160
216
  {
161
- id: 4,
162
217
  title: "Collection grid",
163
218
  type: "collection_grid",
164
219
  components: [
165
220
  {
166
- id: 1,
167
221
  name: "Section header",
168
222
  type: "header",
169
- is_public: true,
223
+ title: 'Services',
224
+ supporting_text: 'Our knowledge and expertise of the early childhood sector enables ELAA to provide expert professional advice and support.',
170
225
  },
171
226
  {
172
- id: 2,
173
227
  name: "Grid",
174
228
  type: "grid",
175
229
  }
176
230
  ]
177
231
  },
178
232
  {
179
- id: 4,
180
233
  title: "Collection carousel",
181
234
  type: "collection_carousel",
182
235
  components: [
183
236
  {
184
- id: 1,
185
237
  name: "Section header",
186
238
  type: "header",
187
- is_public: true,
239
+ title: 'Services',
240
+ supporting_text: 'Our knowledge and expertise of the early childhood sector enables ELAA to provide expert professional advice and support.',
188
241
  },
189
242
  {
190
- id: 2,
191
243
  name: "Carousel",
192
244
  type: "carousel",
193
245
  }
194
246
  ]
195
247
  },
196
248
  {
197
- id: 4,
198
249
  title: "Quick links",
199
250
  type: "quick_links",
200
251
  components: [
201
252
  {
202
- id: 1,
203
- name: "Section header",
204
253
  type: "header",
205
- is_public: true,
206
254
  title: 'Services',
207
255
  supporting_text: 'Our knowledge and expertise of the early childhood sector enables ELAA to provide expert professional advice and support.',
208
256
  },
209
257
  {
210
- id: 2,
211
258
  name: "Link grid",
212
259
  type: "link_grid",
213
260
  supportive_text: "This section can contain up to 10 links.",
@@ -271,14 +318,29 @@ const slide = {
271
318
  },
272
319
  }
273
320
 
321
+ const tab = {
322
+ title: "Free Downloadable Resources",
323
+ description: "Explore a variety of no-cost tools and information available to everyone.",
324
+ public: true,
325
+ featured_image: 'https://beta-frontend.elaa.org.au/img/homepage/asset_free_resources.png',
326
+ primary_button: {
327
+ show: true,
328
+ name: 'Linked to',
329
+ label: 'View all',
330
+ url: 'google.com', // external could be an url
331
+ type: 'external-page',
332
+ is_new_tab: true,
333
+ },
334
+ }
335
+
274
336
  const sites = [
275
337
  {
276
338
  name: "Site 1",
277
- url: "/site-1",
339
+ href: "/site-1",
278
340
  },
279
341
  {
280
342
  name: "Site 2",
281
- url: "/site-2",
343
+ href: "/site-2",
282
344
  }
283
345
  ]
284
346
  </script>
@@ -291,7 +353,11 @@ const sites = [
291
353
  </div>
292
354
  <div style="margin: 40px 0">
293
355
  <h1 style="margin-bottom: 20px; font-size: 50px;">Slider Edit</h1>
294
- <SlideEdit :slide="slide" :sites="sites"/>
356
+ <ItemEdit :item="slide" :sites="sites"/>
357
+ </div>
358
+ <div style="margin: 40px 0">
359
+ <h1 style="margin-bottom: 20px; font-size: 50px;">Vertical Tab Edit</h1>
360
+ <ItemEdit :item="tab" :sites="sites"/>
295
361
  </div>
296
362
  <div style="margin: 40px 0">
297
363
  <h1 style="margin-bottom: 20px; font-size: 50px;">Page Builder</h1>
@@ -1,11 +1,11 @@
1
1
  import { createApp } from 'vue'
2
2
 
3
3
  import App from './App.vue'
4
- import { PageBuilder, SlideEdit, PageRender } from '../../dist/page-builder.es.js'
4
+ import { PageBuilder, ItemEdit, PageRender } from '../../dist/page-builder.es.js'
5
5
  import '../../dist/page-builder.css'
6
6
 
7
7
  const app = createApp(App)
8
- app.component('SlideEdit', SlideEdit)
8
+ app.component('ItemEdit', ItemEdit)
9
9
  app.component('PageBuilder', PageBuilder)
10
10
  app.component('PageRender', PageRender)
11
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcodegroup-au/page-builder",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./dist/page-builder.es.js"
@@ -2,7 +2,7 @@
2
2
  @tailwind components;
3
3
  @tailwind utilities;
4
4
 
5
- .page-builder, .page-render, .slide-edit {
5
+ .page-builder, .page-render, .item-edit {
6
6
  [type="text"],
7
7
  [type="email"],
8
8
  [type="url"],
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="slide-edit">
2
+ <div class="item-edit">
3
3
  <div class="flex items-start gap-4 px-6 pt-4 h-full">
4
4
  <div class="flex flex-1 flex-col gap-4">
5
5
  <card title="Description">
@@ -33,7 +33,7 @@
33
33
  </input-wrapper>
34
34
  </div>
35
35
  </card>
36
- <card title="Primary button">
36
+ <card v-if="form.hasOwnProperty('primary_button')" :title="item?.primary_button?.name ?? 'Primary button'">
37
37
  <div class="flex flex-col gap-8">
38
38
  <VToggle name="show" v-model="form.primary_button.show" title="Show Button" />
39
39
  <input-wrapper
@@ -62,7 +62,7 @@
62
62
  />
63
63
  </div>
64
64
  </card>
65
- <card title="Secondary button">
65
+ <card v-if="form.hasOwnProperty('secondary_button')" :title="item?.secondary_button?.name ?? 'Secondary button'">
66
66
  <div class="flex flex-col gap-4">
67
67
  <VToggle name="show" v-model="form.secondary_button.show" title="Show Button" />
68
68
  <template v-if="form.secondary_button?.show">
@@ -132,7 +132,7 @@ import axios from "axios";
132
132
  const emit = defineEmits(["update"]);
133
133
 
134
134
  const props = defineProps({
135
- slide: {
135
+ item: {
136
136
  type: Object,
137
137
  required: true,
138
138
  },
@@ -151,19 +151,25 @@ const props = defineProps({
151
151
  });
152
152
 
153
153
  const form = ref({
154
- public: props.slide.public,
155
- description: props.slide.description,
156
- featured_image: props.slide.featured_image,
157
- primary_button: props.slide.primary_button,
158
- secondary_button: props.slide.secondary_button,
159
- title: props.slide.title,
154
+ public: props.item.public,
155
+ title: props.item.title,
156
+ description: props.item.description,
157
+ featured_image: props.item.featured_image,
160
158
  });
161
159
 
160
+ if (props.item.hasOwnProperty('primary_button')) {
161
+ form.value.primary_button = props.item.primary_button;
162
+ }
163
+
164
+ if (props.item.hasOwnProperty('secondary_button')) {
165
+ form.value.secondary_button = props.item.secondary_button;
166
+ }
167
+
162
168
  const save = () => {
163
169
  emit("update", form.value);
164
170
 
165
171
  if (props.saveEndpoint) {
166
- axios.post(props.saveEndpoint, form.value).then(() => {
172
+ axios.post(props.saveEndpoint, form.value).then (() => {
167
173
  if (props.cancelEndpoint) {
168
174
  window.location.href = props.cancelEndpoint;
169
175
  }
@@ -67,7 +67,7 @@ import { ref, markRaw, computed } from "vue";
67
67
  import ChevronRight from "@/assets/img/icons/chevron-right.svg";
68
68
  import ChevronDown from "@/assets/img/icons/chevron-down.svg";
69
69
  import Instructions from "@/components/builders/Instructions.vue";
70
- import VSliders from "@/components/builders/VSliders.vue";
70
+ import VItems from "@/components/builders/VItems.vue";
71
71
  import VLinks from "@/components/builders/VLinks.vue";
72
72
  import VHeader from "@/components/builders/VHeader.vue";
73
73
 
@@ -89,7 +89,8 @@ const openStates = ref(JSON.parse(window.localStorage.getItem("pageBuilderOpenSt
89
89
  const sections = ref(props.modelValue?.sections ?? []);
90
90
  let selected = ref(null);
91
91
  const componentMaps = ref({
92
- sliders: markRaw(VSliders),
92
+ sliders: markRaw(VItems),
93
+ vertical_tabs: markRaw(VItems),
93
94
  links: markRaw(VLinks),
94
95
  header: markRaw(VHeader),
95
96
  link_grid: markRaw(VLinks),
@@ -13,6 +13,7 @@
13
13
  import {ref, markRaw} from "vue";
14
14
  import HeroHeader from "@/components/presenters/modules/HeroHeader.vue";
15
15
  import QuickLinks from "@/components/presenters/modules/QuickLinks.vue";
16
+ import VTabs from "@/components/presenters/modules/VTabs.vue";
16
17
 
17
18
  const props = defineProps({
18
19
  page: {
@@ -24,6 +25,7 @@ const props = defineProps({
24
25
  const componentMaps = ref({
25
26
  header: markRaw(HeroHeader),
26
27
  quick_links: markRaw(QuickLinks),
28
+ tabs: markRaw(VTabs),
27
29
  });
28
30
 
29
31
  const currentComponent = (section) => {
@@ -11,8 +11,8 @@
11
11
  <div class="flex flex-col gap-3">
12
12
  <div class="flex justify-between">
13
13
  <div class="flex flex-col gap-1">
14
- <div class="font-semibold text-gray-900">Sliders</div>
15
- <div class="text-sm text-gray-600">This slider can contain up to {{ dataRef.max_items }} slides</div>
14
+ <div class="font-semibold text-gray-900">{{ parseName(type) }}</div>
15
+ <div class="text-sm text-gray-600">This {{ singularize(type) }} can contain up to {{ dataRef.max_items }} {{ parseName(type, false) }}</div>
16
16
  </div>
17
17
  <div>
18
18
  <button
@@ -34,7 +34,7 @@
34
34
  <div class="flex flex-1 items-center justify-between relative" @click="toggleSlide(index);">
35
35
  <div class="flex flex-1 flex-col cursor-pointer" @click="edit(item, index)">
36
36
  <div class="text-xs text-gray-600">
37
- Slider #{{ index + 1 }}
37
+ {{ singularize(parseName(type)) }} #{{ index + 1 }}
38
38
  </div>
39
39
  <div class="text-sm font-medium text-gray-900">
40
40
  {{ item.title }}
@@ -46,13 +46,14 @@
46
46
  </div>
47
47
  </div>
48
48
  </div>
49
- <VModal ref="modalRef" entity="slide" :callback="deleteCallback"></VModal>
49
+ <VModal ref="modalRef" :entity="singularize(type)" :callback="deleteCallback"></VModal>
50
50
  </template>
51
51
  <script setup>
52
52
  import {ref} from "vue";
53
53
  import PlusIcon from "@/assets/img/icons/plus.svg";
54
- import {defaultProps} from "@/components/helpers/defaultProps.js";
55
- import { createSlide } from "@/components/helpers/pageBuilderFactory.js";
54
+ import { defaultProps } from "@/components/helpers/defaultProps";
55
+ import { singularize, parseName } from "@/components/helpers/common";
56
+ import { createSlide } from "@/components/helpers/pageBuilderFactory";
56
57
  import ActionMenu from "@/components/common/ActionMenu.vue";
57
58
  import VModal from "@/components/common/VModal.vue";
58
59
 
@@ -62,6 +63,7 @@ const props = defineProps({
62
63
  });
63
64
 
64
65
  const dataRef = ref(props.data.component);
66
+ const type = dataRef.value.type;
65
67
  const modalRef = ref(null);
66
68
 
67
69
  const openSlideStates = ref(JSON.parse(window.localStorage.getItem("openSlideStates")));
@@ -13,7 +13,7 @@
13
13
  <a
14
14
  @click="addItem"
15
15
  class="text-sm cursor-pointer flex items-center justify-center gap-1 rounded-[99px] border border-brand-600 bg-brand-500 px-3.5 py-2 font-semibold text-white hover:bg-brand-600"
16
- :class="{ 'border-gray-100 bg-gray-100 !text-gray-400 hover:bg-gray-100': componentData.data.length >= componentData.max_items }"
16
+ :class="{ 'border-gray-100 bg-gray-100 !text-gray-400 hover:bg-gray-100': componentData.data?.length >= componentData.max_items }"
17
17
  >
18
18
  <PlusIcon class="h-5 w-5"></PlusIcon>
19
19
  <span>Add</span>
@@ -72,7 +72,7 @@
72
72
  <script setup>
73
73
  import {ref, nextTick} from "vue";
74
74
  import PlusIcon from "@/assets/img/icons/plus.svg";
75
- import {defaultProps} from "@/components/helpers/defaultProps.js";
75
+ import {defaultProps} from "@/components/helpers/defaultProps";
76
76
  import {createLink} from "@/components/helpers/pageBuilderFactory";
77
77
  import ActionMenu from "@/components/common/ActionMenu.vue";
78
78
  import InputWrapper from "@/components/common/InputWrapper.vue";
@@ -91,10 +91,14 @@ const lastItemRef = ref(null);
91
91
  const deleteItemIndex = ref(null);
92
92
 
93
93
  function addItem() {
94
- if (componentData.value.data.length >= componentData.value.max_items) {
94
+ if (!componentData.value.hasOwnProperty('data')) {
95
+ componentData.value.data = [];
96
+ }
97
+
98
+ if (componentData.value.data?.length >= componentData.value.max_items) {
95
99
  return;
96
100
  }
97
- componentData.value.data.push(createLink());
101
+ componentData.value.data?.push(createLink());
98
102
 
99
103
  nextTick(() => {
100
104
  if (lastItemRef.value) {
@@ -111,7 +115,7 @@ const handleDeleteItem = (index) => {
111
115
  }
112
116
 
113
117
  const deleteCallback = (index) => {
114
- componentData.value.data.splice(index, 1);
118
+ componentData.value.data?.splice(index, 1);
115
119
  emit("update", false);
116
120
  };
117
121
  </script>
@@ -0,0 +1,61 @@
1
+ export function pluralize(word, capitalizeFirst = false) {
2
+ const irregularPlurals = {
3
+ child: "children",
4
+ person: "people",
5
+ mouse: "mice",
6
+ foot: "feet",
7
+ tooth: "teeth",
8
+ goose: "geese",
9
+ };
10
+
11
+ let pluralWord = irregularPlurals[word] || `${word}s`;
12
+
13
+ if (capitalizeFirst) {
14
+ pluralWord = pluralWord.charAt(0).toUpperCase() + pluralWord.slice(1);
15
+ }
16
+
17
+ return pluralWord;
18
+ }
19
+
20
+ export function capitalize(str) {
21
+ return str.charAt(0).toUpperCase() + str.slice(1);
22
+ }
23
+
24
+ export function singularize(word, capitalizeFirst = false) {
25
+ const irregularSingulars = {
26
+ children: "child",
27
+ people: "person",
28
+ mice: "mouse",
29
+ feet: "foot",
30
+ teeth: "tooth",
31
+ geese: "goose",
32
+ };
33
+
34
+ word = word.replace(/_/g, ' ');
35
+
36
+ // Check if the word is in the irregular singulars dictionary
37
+ let singularWord = Object.keys(irregularSingulars).find(
38
+ (plural) => plural === word
39
+ )
40
+ ? irregularSingulars[word]
41
+ : word.endsWith("s")
42
+ ? word.slice(0, -1) // Remove the trailing "s" for regular pluralization
43
+ : word;
44
+
45
+ // Capitalize the first letter if the option is enabled
46
+ if (capitalizeFirst) {
47
+ singularWord = singularWord.charAt(0).toUpperCase() + singularWord.slice(1);
48
+ }
49
+
50
+ return singularWord;
51
+ }
52
+
53
+ export function parseName(str, capitalizeFirst = true) {
54
+ if (!str) return '';
55
+ const lastWord = str.split('_').pop();
56
+ if (capitalizeFirst) {
57
+ return capitalize(lastWord);
58
+ }
59
+
60
+ return lastWord;
61
+ }
@@ -1,6 +1,6 @@
1
1
  import '@/assets/css/style.css';
2
2
  import PageBuilder from './PageBuilder.vue';
3
3
  import PageRender from './PageRender.vue';
4
- import SlideEdit from './SlideEdit.vue';
4
+ import ItemEdit from './ItemEdit.vue';
5
5
 
6
- export { PageBuilder, PageRender, SlideEdit };
6
+ export { PageBuilder, PageRender, ItemEdit };
@@ -1,12 +1,21 @@
1
1
  <template>
2
- <div class="flex flex-col">
3
- <p v-if="component?.title" class="pb-4 text-4xl font-semibold text-white">{{ component?.title }}</p>
4
- <p v-if="component?.supporting_text" class="text-navy-25 text-xl font-normal leading-[30px]">{{ component?.supporting_text }}</p>
2
+ <div class="flex flex-col" :class="{'items-center': component?.center}">
3
+ <p
4
+ v-if="component?.title"
5
+ class="pb-4 text-4xl font-semibold text-white"
6
+ :class="{'!text-gray-900': component?.dark}"
7
+ >{{ component?.title }}</p>
8
+ <p
9
+ v-if="component?.supporting_text"
10
+ class="text-navy-25 text-xl font-normal leading-[30px]"
11
+ :class="{'!text-navy-900': component?.dark}"
12
+ >
13
+ {{ component?.supporting_text }}</p>
5
14
  </div>
6
15
  </template>
7
16
 
8
17
  <script setup>
9
- import { ref } from "vue";
18
+ import {ref} from "vue";
10
19
 
11
20
  const props = defineProps({
12
21
  component: {
@@ -3,7 +3,7 @@
3
3
  <div class="flex justify-center" :class="{'!block': isLinkGrid}" v-for="link in component.data">
4
4
  <a
5
5
  :target="link.open_in_new_tab ? '_blank' : ''"
6
- :href="link.url"
6
+ :href="`/${link.url}`"
7
7
  class="text-white flex gap-1.5 items-center cursor-pointer mt-12 text-sm font-semibold hover:text-gray-300"
8
8
  :class="{'bg-white border border-gray-300 rounded-full !text-gray-700 px-4 h-[44px] w-max !mt-0 !text-base hover:bg-gray-100': isLinkGrid}"
9
9
  >
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <div class="flex flex-col md:flex-row justify-center gap-4 items-stretch mt-6">
3
+ <!-- Left Column: Links -->
4
+ <div class="flex-1 w-full md:w-1/2 flex flex-col">
5
+ <div v-for="item in publicTabs" :key="item.title">
6
+ <div
7
+ class="flex-col flex gap-1.5 cursor-pointer hover:bg-navy-50 mb-4 group"
8
+ :class="{'border-l-4 border-brand-600': selectedItem === item}"
9
+ @click.prevent="selectItem(item)"
10
+ >
11
+ <div class="py-4 pl-4 md:pl-6">
12
+ <p
13
+ :class="{'text-gray-900': selectedItem === item, 'text-gray-600': selectedItem !== item}"
14
+ class="text-lg md:text-xl font-semibold mb-2 group-hover:text-gray-900"
15
+ >
16
+ {{ item.title }}
17
+ </p>
18
+ <p
19
+ :class="{'text-gray-700': selectedItem === item, 'text-gray-400': selectedItem !== item}"
20
+ class="text-sm md:text-md font-normal group-hover:text-gray-700"
21
+ >
22
+ {{ item.description }}
23
+ </p>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </div>
28
+
29
+ <!-- Right Column: Image and Button -->
30
+ <div class="flex-1 w-full md:w-1/2 bg-transparent flex flex-col items-center">
31
+ <transition name="fade" mode="out-in">
32
+ <div class="flex flex-col items-center" :key="selectedItem?.title">
33
+ <img
34
+ v-if="selectedItem?.featured_image"
35
+ :src="selectedItem.featured_image"
36
+ alt="Selected Item Image"
37
+ class="rounded-[20px] md:rounded-[40px] object-contain max-h-[200px] md:max-h-[387px] w-full"/>
38
+ <img
39
+ v-else
40
+ class="rounded-[20px] md:rounded-[40px] object-contain max-h-[200px] md:max-h-[387px] w-full"
41
+ src="@/assets/img/no_image_available.jpeg"
42
+ alt="No Image Available">
43
+ <a
44
+ v-if="selectedItem?.primary_button?.show"
45
+ class="text-sm md:text-md font-semibold text-brand-600 flex items-center gap-2 mt-4"
46
+ :target="selectedItem?.primary_button?.is_new_tab ? '_blank' : ''"
47
+ :href="selectedItem?.primary_button?.url">
48
+ {{ selectedItem?.primary_button?.label ?? 'N/A' }}
49
+ <ArrowUpRight class="w-4 h-4 md:w-5 md:h-5" />
50
+ </a>
51
+ </div>
52
+ </transition>
53
+ </div>
54
+ </div>
55
+ </template>
56
+
57
+ <script setup>
58
+ import { ref, onMounted } from "vue";
59
+ import ArrowUpRight from '@/assets/img/icons/arrow-up-right.svg';
60
+
61
+ const props = defineProps({
62
+ component: {
63
+ required: true,
64
+ type: Object,
65
+ },
66
+ });
67
+
68
+ const component = ref(props.component);
69
+ const selectedItem = ref(null);
70
+
71
+ // Filter to get only public tabs
72
+ const publicTabs = ref(component.value.data.filter((item) => item.public));
73
+
74
+ // Method to select an item
75
+ const selectItem = (item) => {
76
+ selectedItem.value = item;
77
+ };
78
+
79
+ // Preselect the first public item on component mount
80
+ onMounted(() => {
81
+ if (publicTabs.value.length > 0) {
82
+ selectItem(publicTabs.value[0]);
83
+ }
84
+ });
85
+ </script>