Auto-resize iframes, fix custom code, fix create form initial properties
This commit is contained in:
parent
de3e2d69c0
commit
a650228a67
|
@ -98,7 +98,7 @@
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_1027_7292">
|
<clipPath id="clip0_1027_7292">
|
||||||
<rect width="24" height="24" fill="white" />
|
<rect width="24" height="24" fill="white"/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -138,7 +138,7 @@
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_1027_7210">
|
<clipPath id="clip0_1027_7210">
|
||||||
<rect width="24" height="24" fill="white" />
|
<rect width="24" height="24" fill="white"/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -172,7 +172,7 @@ import clonedeep from 'clone-deep'
|
||||||
import EditableDiv from '~/components/global/EditableDiv.vue'
|
import EditableDiv from '~/components/global/EditableDiv.vue'
|
||||||
import VButton from '~/components/global/VButton.vue'
|
import VButton from '~/components/global/VButton.vue'
|
||||||
|
|
||||||
draggable.compatConfig = { MODE: 3 }
|
draggable.compatConfig = {MODE: 3}
|
||||||
export default {
|
export default {
|
||||||
name: 'FormFieldsEditor',
|
name: 'FormFieldsEditor',
|
||||||
components: {
|
components: {
|
||||||
|
@ -182,7 +182,7 @@ export default {
|
||||||
EditableDiv
|
EditableDiv
|
||||||
},
|
},
|
||||||
|
|
||||||
setup () {
|
setup() {
|
||||||
const workingFormStore = useWorkingFormStore()
|
const workingFormStore = useWorkingFormStore()
|
||||||
return {
|
return {
|
||||||
route: useRoute(),
|
route: useRoute(),
|
||||||
|
@ -191,21 +191,21 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
removing: null
|
removing: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted() {
|
||||||
this.init()
|
this.init()
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onChangeName (field, newName) {
|
onChangeName(field, newName) {
|
||||||
field.name = newName
|
field.name = newName
|
||||||
},
|
},
|
||||||
toggleHidden (field) {
|
toggleHidden(field) {
|
||||||
field.hidden = !field.hidden
|
field.hidden = !field.hidden
|
||||||
if (field.hidden) {
|
if (field.hidden) {
|
||||||
field.required = false
|
field.required = false
|
||||||
|
@ -214,69 +214,27 @@ export default {
|
||||||
field.generates_auto_increment_id = false
|
field.generates_auto_increment_id = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleRequired (field) {
|
toggleRequired(field) {
|
||||||
field.required = !field.required
|
field.required = !field.required
|
||||||
if (field.required) {
|
if (field.required) {
|
||||||
field.hidden = false
|
field.hidden = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getDefaultFields () {
|
init() {
|
||||||
return [
|
if (!this.form.properties) {
|
||||||
{
|
return
|
||||||
name: 'Name',
|
|
||||||
type: 'text',
|
|
||||||
hidden: false,
|
|
||||||
required: true,
|
|
||||||
id: this.generateUUID()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
hidden: false,
|
|
||||||
id: this.generateUUID()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Message',
|
|
||||||
type: 'text',
|
|
||||||
hidden: false,
|
|
||||||
multi_lines: true,
|
|
||||||
id: this.generateUUID()
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
init () {
|
|
||||||
if (this.route.name === 'forms-create' || this.route.name === 'forms-create-guest') { // Set Default fields
|
|
||||||
if (!this.form.properties || this.form.properties.length===0) {
|
|
||||||
this.form.properties = this.getDefaultFields()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.form.properties = this.form.properties.map((field) => {
|
|
||||||
// Add more field properties
|
|
||||||
field.placeholder = field.placeholder || null
|
|
||||||
field.prefill = field.prefill || null
|
|
||||||
field.help = field.help || null
|
|
||||||
field.help_position = field.help_position || 'below_input'
|
|
||||||
|
|
||||||
return field
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
this.form.properties = this.form.properties.map((field) => {
|
||||||
generateUUID () {
|
// Add more field properties
|
||||||
let d = new Date().getTime()// Timestamp
|
field.placeholder = field.placeholder || null
|
||||||
let d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0// Time in microseconds since page-load or 0 if unsupported
|
field.prefill = field.prefill || null
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
field.help = field.help || null
|
||||||
let r = Math.random() * 16// random number between 0 and 16
|
field.help_position = field.help_position || 'below_input'
|
||||||
if (d > 0) { // Use timestamp until depleted
|
|
||||||
r = (d + r) % 16 | 0
|
return field
|
||||||
d = Math.floor(d / 16)
|
|
||||||
} else { // Use microseconds since page-load if supported
|
|
||||||
r = (d2 + r) % 16 | 0
|
|
||||||
d2 = Math.floor(d2 / 16)
|
|
||||||
}
|
|
||||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
formatType (field) {
|
formatType(field) {
|
||||||
let type = field.type.replace('_', ' ')
|
let type = field.type.replace('_', ' ')
|
||||||
if (!type.startsWith('nf')) {
|
if (!type.startsWith('nf')) {
|
||||||
type = type + ' Input'
|
type = type + ' Input'
|
||||||
|
@ -288,17 +246,17 @@ export default {
|
||||||
}
|
}
|
||||||
return type
|
return type
|
||||||
},
|
},
|
||||||
editOptions (index) {
|
editOptions(index) {
|
||||||
this.workingFormStore.openSettingsForField(index)
|
this.workingFormStore.openSettingsForField(index)
|
||||||
},
|
},
|
||||||
removeBlock (blockIndex) {
|
removeBlock(blockIndex) {
|
||||||
this.form.properties.splice(blockIndex, 1)
|
this.form.properties.splice(blockIndex, 1)
|
||||||
this.closeSidebar()
|
this.closeSidebar()
|
||||||
},
|
},
|
||||||
closeSidebar () {
|
closeSidebar() {
|
||||||
this.workingFormStore.closeEditFieldSidebar()
|
this.workingFormStore.closeEditFieldSidebar()
|
||||||
},
|
},
|
||||||
openAddFieldSidebar () {
|
openAddFieldSidebar() {
|
||||||
this.workingFormStore.openAddFieldSidebar(null)
|
this.workingFormStore.openAddFieldSidebar(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="state=='default'" class="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-8">
|
<div v-if="state=='default'" class="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-8">
|
||||||
<div v-track.select_form_base="{base:'contact-form'}"
|
<div v-track.select_form_base="{base:'contact-form'}" role="button"
|
||||||
class="rounded-md border p-4 flex flex-col items-center cursor-pointer hover:bg-gray-50" @click="$emit('close')"
|
class="rounded-md border p-4 flex flex-col items-center cursor-pointer hover:bg-gray-50" @click="$emit('close')"
|
||||||
>
|
>
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="aiFeaturesEnabled" v-track.select_form_base="{base:'ai'}"
|
<div v-if="aiFeaturesEnabled" v-track.select_form_base="{base:'ai'}"
|
||||||
class="rounded-md border p-4 flex flex-col items-center cursor-pointer hover:bg-gray-50" @click="state='ai'"
|
class="rounded-md border p-4 flex flex-col items-center cursor-pointer hover:bg-gray-50" role="button" @click="state='ai'"
|
||||||
>
|
>
|
||||||
<div class="p-4 relative">
|
<div class="p-4 relative">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-8 h-8 text-blue-500">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-8 h-8 text-blue-500">
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="state=='ai'">
|
<div v-else-if="state=='ai'">
|
||||||
<a class="absolute top-4 left-4" href="#" @click.prevent="state='default'">
|
<a class="absolute top-4 left-4" href="#" role="button" @click.prevent="state='default'">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4 inline -mt-1">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4 inline -mt-1">
|
||||||
<path fill-rule="evenodd" d="M7.72 12.53a.75.75 0 010-1.06l7.5-7.5a.75.75 0 111.06 1.06L9.31 12l6.97 6.97a.75.75 0 11-1.06 1.06l-7.5-7.5z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M7.72 12.53a.75.75 0 010-1.06l7.5-7.5a.75.75 0 111.06 1.06L9.31 12l6.97 6.97a.75.75 0 11-1.06 1.06l-7.5-7.5z" clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<copy-content :content="embedCode" buttonText="Copy Code">
|
<copy-content :content="embedCode" buttonText="Copy Code">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<svg class="h-4 w-4 -mt-1 text-blue-600 inline mr-1" viewBox="0 0 18 18" fill="none"
|
<svg class="h-4 w-4 -mt-1 text-blue-600 inline mr-1" viewBox="0 0 18 18" fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
d="M11.0833 11.5L13.5833 9L11.0833 6.5M6.91667 6.5L4.41667 9L6.91667 11.5M5.5 16.5H12.5C13.9001 16.5 14.6002 16.5 15.135 16.2275C15.6054 15.9878 15.9878 15.6054 16.2275 15.135C16.5 14.6002 16.5 13.9001 16.5 12.5V5.5C16.5 4.09987 16.5 3.3998 16.2275 2.86502C15.9878 2.39462 15.6054 2.01217 15.135 1.77248C14.6002 1.5 13.9001 1.5 12.5 1.5H5.5C4.09987 1.5 3.3998 1.5 2.86502 1.77248C2.39462 2.01217 2.01217 2.39462 1.77248 2.86502C1.5 3.3998 1.5 4.09987 1.5 5.5V12.5C1.5 13.9001 1.5 14.6002 1.77248 15.135C2.01217 15.6054 2.39462 15.9878 2.86502 16.2275C3.3998 16.5 4.09987 16.5 5.5 16.5Z"
|
d="M11.0833 11.5L13.5833 9L11.0833 6.5M6.91667 6.5L4.41667 9L6.91667 11.5M5.5 16.5H12.5C13.9001 16.5 14.6002 16.5 15.135 16.2275C15.6054 15.9878 15.9878 15.6054 16.2275 15.135C16.5 14.6002 16.5 13.9001 16.5 12.5V5.5C16.5 4.09987 16.5 3.3998 16.2275 2.86502C15.9878 2.39462 15.6054 2.01217 15.135 1.77248C14.6002 1.5 13.9001 1.5 12.5 1.5H5.5C4.09987 1.5 3.3998 1.5 2.86502 1.77248C2.39462 2.01217 2.01217 2.39462 1.77248 2.86502C1.5 3.3998 1.5 4.09987 1.5 5.5V12.5C1.5 13.9001 1.5 14.6002 1.77248 15.135C2.01217 15.6054 2.39462 15.9878 2.86502 16.2275C3.3998 16.5 4.09987 16.5 5.5 16.5Z"
|
||||||
stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
@ -15,41 +15,40 @@
|
||||||
</copy-content>
|
</copy-content>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CopyContent from '../../../open/forms/components/CopyContent.vue'
|
import CopyContent from '../../../open/forms/components/CopyContent.vue'
|
||||||
|
import {appUrl} from "~/lib/utils.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'EmbedCode',
|
name: 'EmbedCode',
|
||||||
components: { CopyContent },
|
components: {CopyContent},
|
||||||
props: {
|
props: {
|
||||||
form: { type: Object, required: true },
|
form: {type: Object, required: true},
|
||||||
extraQueryParam: { type: String, default: '' }
|
extraQueryParam: {type: String, default: ''}
|
||||||
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
autoresizeIframe: false
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
embedCode() {
|
||||||
|
return `
|
||||||
|
<script type="text/javascript" src="${appUrl('/widgets/iframeResize.min.js')}"><\/script>
|
||||||
|
${this.iframeCode}
|
||||||
|
<script type="text/javascript">iFrameResize({log: false, checkOrigin: false}, "#${this.iframeId}");<\/script>
|
||||||
|
`
|
||||||
},
|
},
|
||||||
|
iframeCode() {
|
||||||
data: () => ({
|
const share_url = (this.extraQueryParam) ? this.form.share_url + "?" + this.extraQueryParam : this.form.share_url + this.extraQueryParam
|
||||||
|
return '<iframe style="border:none;width:100%;" frameborder="0" width="100%" frameborder="0" id="' + this.iframeId + '" src="' + share_url + '"></iframe>'
|
||||||
}),
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
embedCode() {
|
|
||||||
const share_url = (this.extraQueryParam) ? this.form.share_url + "?" + this.extraQueryParam : this.form.share_url + this.extraQueryParam
|
|
||||||
return '<iframe style="border:none;width:100%;" height="' + this.formHeight + 'px" src="' + share_url + '"></iframe>'
|
|
||||||
},
|
|
||||||
formHeight() {
|
|
||||||
let height = 200
|
|
||||||
if (!this.form.hide_title && !this.extraQueryParam) {
|
|
||||||
height += 60
|
|
||||||
}
|
|
||||||
height += this.form.properties.filter((property) => {
|
|
||||||
return !property.hidden
|
|
||||||
}).length * 70
|
|
||||||
|
|
||||||
return height
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
iframeId() {
|
||||||
|
return 'form-' + this.form.slug
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {}
|
methods: {}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
|
import {generateUUID} from "~/lib/utils.js";
|
||||||
|
|
||||||
export const initForm = (defaultValue = {}) => {
|
export const initForm = (defaultValue = {}, withDefaultProperties = false) => {
|
||||||
return useForm({
|
return useForm({
|
||||||
title: 'My Form',
|
title: 'My Form',
|
||||||
description: null,
|
description: null,
|
||||||
visibility: 'public',
|
visibility: 'public',
|
||||||
workspace_id: null,
|
workspace_id: null,
|
||||||
properties: [],
|
properties: withDefaultProperties ? getDefaultProperties() :[],
|
||||||
|
|
||||||
notifies: false,
|
notifies: false,
|
||||||
slack_notifies: false,
|
slack_notifies: false,
|
||||||
|
@ -52,3 +53,28 @@ export const initForm = (defaultValue = {}) => {
|
||||||
...defaultValue
|
...defaultValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDefaultProperties () {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'Name',
|
||||||
|
type: 'text',
|
||||||
|
hidden: false,
|
||||||
|
required: true,
|
||||||
|
id: generateUUID()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Email',
|
||||||
|
type: 'email',
|
||||||
|
hidden: false,
|
||||||
|
id: generateUUID()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Message',
|
||||||
|
type: 'text',
|
||||||
|
hidden: false,
|
||||||
|
multi_lines: true,
|
||||||
|
id: generateUUID()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,22 @@ export const hash = (str, seed = 0) => {
|
||||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const generateUUID = () => {
|
||||||
|
let d = new Date().getTime()// Timestamp
|
||||||
|
let d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0// Time in microseconds since page-load or 0 if unsupported
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
|
let r = Math.random() * 16// random number between 0 and 16
|
||||||
|
if (d > 0) { // Use timestamp until depleted
|
||||||
|
r = (d + r) % 16 | 0
|
||||||
|
d = Math.floor(d / 16)
|
||||||
|
} else { // Use microseconds since page-load if supported
|
||||||
|
r = (d2 + r) % 16 | 0
|
||||||
|
d2 = Math.floor(d2 / 16)
|
||||||
|
}
|
||||||
|
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Url and domain related utils
|
* Url and domain related utils
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -111,13 +111,6 @@ const loadForm = async (setup=false) => {
|
||||||
// Adapt page to form: colors, custom code etc
|
// Adapt page to form: colors, custom code etc
|
||||||
handleDarkMode(form.value.dark_mode)
|
handleDarkMode(form.value.dark_mode)
|
||||||
handleTransparentMode(form.value.transparent_background)
|
handleTransparentMode(form.value.transparent_background)
|
||||||
|
|
||||||
if (process.server) return
|
|
||||||
if (form.value.custom_code) {
|
|
||||||
const scriptEl = document.createRange().createContextualFragment(form.value.custom_code)
|
|
||||||
document.head.append(scriptEl)
|
|
||||||
}
|
|
||||||
if (!isIframe) focusOnFirstFormElement()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadForm(true)
|
await loadForm(true)
|
||||||
|
@ -127,6 +120,14 @@ onMounted(() => {
|
||||||
if (form.value) {
|
if (form.value) {
|
||||||
handleDarkMode(form.value?.dark_mode)
|
handleDarkMode(form.value?.dark_mode)
|
||||||
handleTransparentMode(form.value?.transparent_background)
|
handleTransparentMode(form.value?.transparent_background)
|
||||||
|
|
||||||
|
if (process.client) {
|
||||||
|
if (form.value.custom_code) {
|
||||||
|
const scriptEl = document.createRange().createContextualFragment(form.value.custom_code)
|
||||||
|
document.head.append(scriptEl)
|
||||||
|
}
|
||||||
|
if (!isIframe) focusOnFirstFormElement()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -165,6 +166,9 @@ useHead({
|
||||||
return titleChunk
|
return titleChunk
|
||||||
}
|
}
|
||||||
return titleChunk ? `${titleChunk} - OpnForm` : 'OpnForm';
|
return titleChunk ? `${titleChunk} - OpnForm` : 'OpnForm';
|
||||||
}
|
},
|
||||||
|
... form.value.custom_code ? {
|
||||||
|
script: [ { src: '/widgets/iframeResizer.contentWindow.min.js' } ]
|
||||||
|
} : {}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -75,7 +75,7 @@ onMounted(() => {
|
||||||
is_pro: false
|
is_pro: false
|
||||||
}])
|
}])
|
||||||
|
|
||||||
form.value = initForm()
|
form.value = initForm({}, true)
|
||||||
if (route.query.template !== undefined && route.query.template) {
|
if (route.query.template !== undefined && route.query.template) {
|
||||||
const template = templatesStore.getByKey(route.query.template)
|
const template = templatesStore.getByKey(route.query.template)
|
||||||
if (template && template.structure) {
|
if (template && template.structure) {
|
||||||
|
|
|
@ -92,7 +92,7 @@ onMounted(() => {
|
||||||
formStore.loadAll(workspace.value.id)
|
formStore.loadAll(workspace.value.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
form.value = initForm({workspace_id: workspace.value?.id})
|
form.value = initForm({workspace_id: workspace.value?.id}, true)
|
||||||
formInitialHash.value = hash(JSON.stringify(form.value.data()))
|
formInitialHash.value = hash(JSON.stringify(form.value.data()))
|
||||||
if (route.query.template !== undefined && route.query.template) {
|
if (route.query.template !== undefined && route.query.template) {
|
||||||
const template = templatesStore.getByKey(route.query.template)
|
const template = templatesStore.getByKey(route.query.template)
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue