PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / trunk
Booking for Appointments and Events Calendar – Amelia vtrunk
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / v3 / src / assets / js / admin / useReactiveCustomize.js
ameliabooking / v3 / src / assets / js / admin Last commit date
admin.js 1 day ago appointment.js 1 day ago booking.js 1 day ago customers.js 1 day ago event.js 1 day ago package.js 1 day ago payment.js 1 day ago socialAuthOptions.js 1 day ago status.js 1 day ago useReactiveCustomize.js 1 day ago
useReactiveCustomize.js
130 lines
1 import { ref, computed, shallowRef, watch } from 'vue'
2
3 // Global tracker for window.v3 changes - shared across all components
4 let globalWindowV3Tracker = null
5 let isGlobalTriggerSetup = false
6
7 /**
8 * Composable for reactive amCustomize that works with both window.v3 and inject
9 * This handles the reactivity bridge between host app (redesign) and V3 components
10 *
11 * This ensures that amCustomize will react to changes made by the host app (redesign)
12 * when window.triggerV3Reactivity() is called.
13 *
14 * @returns {Object} - Object containing reactive amCustomize computed ref
15 */
16 export function useReactiveCustomize() {
17 // Initialize global tracker if not already done
18 if (!globalWindowV3Tracker) {
19 globalWindowV3Tracker = ref(0)
20 }
21
22 // Set up global trigger function once
23 if (!isGlobalTriggerSetup && typeof window !== 'undefined') {
24 window.triggerV3Reactivity = () => {
25 if (globalWindowV3Tracker) {
26 globalWindowV3Tracker.value++
27 }
28 }
29 isGlobalTriggerSetup = true
30 }
31
32 // Create reactive amCustomize that responds to both Vue reactivity and manual triggers
33 //
34 // WHY shallowRef + two-level spread instead of computed:
35 // computed(() => window.v3?.customize?.value) returns the SAME object reference every
36 // time the tracker fires (because Pinia mutates in-place). Newer Vue 3 versions cache
37 // computed results by reference equality, so dependents never see "a change" and
38 // templates never re-render.
39 //
40 // By spreading two levels deep we guarantee:
41 // • amCustomize.value itself → new object (shallowRef detects change)
42 // • amCustomize.value[pageRenderKey] → new object (downstream computed sees change)
43 // The spread reads current (post-mutation) property values from the Pinia reactive
44 // proxy, so all nested scalars (e.g. `required`, `visibility`) reflect the update.
45 const amCustomize = shallowRef(null)
46 watch(
47 globalWindowV3Tracker,
48 () => {
49 if (typeof window === 'undefined') {
50 amCustomize.value = null
51 return
52 }
53
54 const raw = window.v3?.customize?.value
55 if (!raw) {
56 amCustomize.value = null
57 return
58 }
59 // Two-level spread: top-level keys get new objects, so every consumer that holds a
60 // reference to amCustomize.value[someKey] sees a changed value on next access.
61 amCustomize.value = Object.fromEntries(
62 Object.entries(raw).map(([k, v]) => [
63 k,
64 Array.isArray(v) ? [...v] : v && typeof v === 'object' ? { ...v } : v,
65 ]),
66 )
67 },
68 { immediate: true },
69 )
70
71 const stepIndex = computed(() => {
72 globalWindowV3Tracker.value
73 return window.v3?.stepIndex !== undefined ? window.v3.stepIndex.value : 0
74 })
75
76 const stepName = computed(() => {
77 globalWindowV3Tracker.value
78 return window.v3?.stepName !== undefined ? window.v3.stepName.value : ''
79 })
80
81 const amTranslations = computed(() => {
82 globalWindowV3Tracker.value
83 return window.v3?.translations?.value
84 })
85
86 const flowLayout = computed(() => {
87 globalWindowV3Tracker.value
88 return window.v3?.flowLayout?.value
89 })
90
91 const subStepName = computed(() => {
92 globalWindowV3Tracker.value
93 return window.v3?.subStepName !== undefined ? window.v3.subStepName.value : ''
94 })
95
96 const bookableType = computed(() => {
97 globalWindowV3Tracker.value
98 return window.v3?.bookableType !== undefined ? window.v3.bookableType.value : ''
99 })
100
101 const langKey = computed(() => {
102 globalWindowV3Tracker.value
103 return window.v3?.langKey !== undefined ? window.v3.langKey.value : ''
104 })
105
106 const pagesType = computed(() => {
107 globalWindowV3Tracker.value
108 return window.v3?.pagesType !== undefined ? window.v3.pagesType.value : ''
109 })
110
111 const features = computed(() => {
112 globalWindowV3Tracker.value
113 return window.v3?.features !== undefined ? window.v3.features.value : {}
114 })
115
116 return {
117 amCustomize,
118 stepIndex,
119 stepName,
120 amTranslations,
121 flowLayout,
122 subStepName,
123 bookableType,
124 langKey,
125 pagesType,
126 features,
127 globalWindowV3Tracker, // Expose tracker for triggering reactivity
128 }
129 }
130