PluginProbe
The Innovative Form Builder – IvyForms / 0.5
The Innovative Form Builder – IvyForms v0.5
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.5, at frontend/src/assets/js/admin/admin.ts

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