PluginProbe
The Innovative Form Builder – IvyForms / 0.9.1
The Innovative Form Builder – IvyForms v0.9.1
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / assets / js / admin / admin.ts

admin.ts in The Innovative Form Builder – IvyForms 0.9.1, at frontend/src/assets/js/admin/admin.ts

234 lines 7.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* eslint no-unused-vars: "off" */
2 import type { Component } from 'vue'
3 import * as Vue from 'vue'
4 import { createApp } from 'vue'
5 import { createPinia } from 'pinia'
6 import App from '../../../views/admin/App.vue'
7 import router from '@/router'
8 import '@/assets/scss/main.scss'
9 import 'element-plus/dist/index.css'
10 import { useLabelsStore } from '@/stores/labels'
11 import { useLabels } from '@/composables/useLabels'
12 import api, { dispatchIvyReady, installIvyGlobals } from '@/composables/IvyFormAPI'
13 import { useProFeatures } from '@/composables/useProFeatures'
14 import { useFormBuilder } from '@/stores/useFormBuilder'
15 import { registerLiteComponents } from '@/composables/registerLiteComponents'
16 import { registerLiteFieldOptions } from '@/views/admin/form-builder/form-builder-page/form-builder/panel/tabs/field-options/registerLiteFieldOptions'
17 import * as LiteTypes from '@/types/index'
18 import type { IvyProFeatures } from '@/types/global'
19 import { useWcagColors } from '@/composables/useWcagColors'
20 import { useSettingsStore } from '@/stores/useSettingsStore'
21 import IvyMessage from '@/views/_components/message/ivyMessage'
22 import { useApiClient } from '@/composables/useApiClient'
23
24 // Note: Window interface extensions are now defined in src/types/global.d.ts
25 // No need to duplicate declarations here
26
27 const app = createApp(App)
28 const pinia = createPinia()
29 app.use(pinia)
30 app.use(router)
31
32 // Initialize the labels store with WordPress data
33 const labelsStore = useLabelsStore()
34 labelsStore.initialize()
35
36 // Make labels globally available to all components
37 app.config.globalProperties.$labels = useLabels()
38
39 // Expose IvyFormsApi and Lite globals for extenders
40 ;(function exposeGlobals() {
41 // Ensure the API is present on window
42 installIvyGlobals()
43 const w = window
44 const { getLabel } = useLabels()
45 const formBuilder = useFormBuilder()
46
47 // Proxy to resolve labels dynamically via getLabel
48 const labelsProxy = new Proxy(
49 {},
50 {
51 get: (_t, key: string | symbol) => {
52 try {
53 return getLabel(String(key))
54 } catch {
55 return String(key)
56 }
57 },
58 },
59 )
60
61 w.IvyForms = w.IvyForms || { api, hooks: api.hooks, fields: api.fields }
62 w.IvyForms.api = api
63 // Expose hooks & fields for external (Pro / add-ons) integration.
64 // These were previously commented out which prevented Pro from registering filters via IvyForms.hooks.*
65 // Keep backward compatibility by only setting if not already provided.
66 if (!w.IvyForms.hooks) {
67 w.IvyForms.hooks = api.hooks
68 }
69
70 // Add triggerUpdate method for Pro plugins to notify when they add hooks
71 if (!w.IvyForms.hooks.triggerUpdate) {
72 w.IvyForms.hooks.triggerUpdate = () => {
73 // This is a no-op by default, but components can override it
74 // to trigger re-computation when Pro plugins register
75 }
76 }
77
78 if (!w.IvyForms.fields) {
79 w.IvyForms.fields = api.fields
80 }
81 // Expose label helpers for Pro plugin and other extenders
82 w.IvyForms.getLabel = getLabel
83 w.IvyForms.labels = labelsProxy
84 // Set WordPress admin URL
85 w.IvyForms.adminUrl = w.wpIvyApiSettings?.root?.replace(/\/wp-json\/?$/, '') + '/wp-admin/'
86 // Expose Vue for pro plugins with guaranteed compatibility
87 w.IvyForms.Vue = Vue
88 //w.Vue = w.Vue || Vue // Global fallback for compatibility with other plugins
89 // Expose IvyMessage utility for Pro plugin
90 w.IvyForms.IvyMessage = IvyMessage
91 // Expose useApiClient for Pro plugin (call it once to prevent tree-shaking)
92 w.IvyForms.useApiClient = useApiClient
93 // Expose the form builder store so external components can read/write fields
94 w.IvyForms.formBuilder = formBuilder
95 // Expose shared types for Pro plugin and other extenders
96 w.IvyForms.types = LiteTypes
97 // Create a callable components registry:
98 // - Use as function: w.IvyForms.components('MyComp', Comp)
99 // - Or as object map: w.IvyForms.components.MyComp
100 // - Or helper alias: w.IvyForms.registerComponent('MyComp', Comp)
101 type ComponentRegistry = {
102 (name?: string, component?: Component): Component | ComponentRegistry
103 [key: string]: Component | unknown
104 }
105 const componentsRegistry = ((name?: string, component?: Component) => {
106 if (name && component) {
107 app.component(name, component)
108 ;(componentsRegistry as Record<string, Component>)[name] = component
109 return component
110 }
111 return componentsRegistry
112 }) as ComponentRegistry
113
114 // Register all Lite components for Pro plugin access
115 registerLiteComponents(componentsRegistry, app)
116
117 w.IvyForms.components = componentsRegistry
118 w.IvyForms.registerComponent = (name: string, component: Component) =>
119 componentsRegistry(name, component)
120
121 // Expose router for Pro plugins to add routes dynamically
122 w.IvyForms._router = router
123
124 // Initialize pro features bridge if not already provided by Pro plugin
125 if (!w.IvyForms.pro) {
126 const pro = useProFeatures()
127 // Fire fetch; backend route may 404 in Lite-only context which is fine.
128 pro
129 .fetch()
130 .then(() => {
131 window.dispatchEvent(new CustomEvent('ivyforms:pro_features_loaded'))
132 })
133 .catch(() => {})
134 w.IvyForms.pro = {
135 get loaded() {
136 return pro.loaded.value
137 },
138 get loading() {
139 return pro.loading.value
140 },
141 get plan() {
142 return pro.plan.value
143 },
144 get upgradeMap() {
145 return pro.upgradeMap.value
146 },
147 get groups() {
148 return pro.groups.value
149 },
150 get license() {
151 return pro.license.value
152 },
153 get features(): Record<string, boolean> {
154 const rawFeatures =
155 (pro as { data?: { value?: { features?: Record<string, unknown> } } }).data?.value
156 ?.features || {}
157 // Convert unknown to boolean
158 const booleanFeatures: Record<string, boolean> = {}
159 for (const key in rawFeatures) {
160 booleanFeatures[key] = !!rawFeatures[key]
161 }
162 return booleanFeatures
163 },
164 hasFeature: (slug: string) => pro.hasFeature(slug),
165 fetch: (opts?: Record<string, unknown>) => pro.fetch(opts),
166 refresh: () => pro.refresh(),
167 }
168 }
169
170 // Simple onProReady queue so other scripts can safely register logic dependent on features
171 if (!w.IvyForms.onProReady) {
172 w.IvyForms._proReadyQueue = []
173 w.IvyForms.onProReady = (cb: (pro: IvyProFeatures) => void) => {
174 try {
175 if (w.IvyForms.pro?.loaded) return cb(w.IvyForms.pro)
176 w.IvyForms._proReadyQueue?.push(cb)
177 } catch {
178 /* noop */
179 }
180 }
181 window.addEventListener('ivyforms:pro_features_loaded', () => {
182 const q = Array.isArray(w.IvyForms._proReadyQueue) ? w.IvyForms._proReadyQueue : []
183 while (q.length) {
184 const fn = q.shift()
185 try {
186 if (fn) {
187 fn(w.IvyForms.pro)
188 }
189 } catch {
190 /* ignore */
191 }
192 }
193 })
194 }
195
196 // Convenience global helper (returns boolean) – safe to call anytime
197 if (!w.IvyForms.hasProFeature) {
198 w.IvyForms.hasProFeature = (slug: string): boolean => {
199 try {
200 if (!w.IvyForms.pro) return false
201 if (!w.IvyForms.pro.loaded) return false // treat not-yet-loaded as false for simplicity
202 return !!w.IvyForms.pro.hasFeature?.(slug)
203 } catch {
204 return false
205 }
206 }
207 }
208 })()
209
210 // Register Lite field options
211 registerLiteFieldOptions()
212
213 app.mount('#ivyforms-app')
214
215 // Initialize settings store and load settings before starting WCAG watcher
216 const settingsStore = useSettingsStore()
217
218 // Load settings from backend first, then initialize WCAG colors
219 settingsStore
220 .loadAllSettings()
221 .then(() => {
222 // Initialize WCAG colors watcher globally after settings are loaded
223 const { startWatching } = useWcagColors()
224 startWatching()
225 })
226 .catch(() => {
227 // Still start watcher even if settings fail to load (will use default value)
228 const { startWatching } = useWcagColors()
229 startWatching()
230 })
231
232 // Signal readiness for any external extenders waiting on Lite
233 dispatchIvyReady({ scope: 'admin' })
234