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>
|
||||
<defs>
|
||||
<clipPath id="clip0_1027_7292">
|
||||
<rect width="24" height="24" fill="white" />
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
@ -138,7 +138,7 @@
|
|||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1027_7210">
|
||||
<rect width="24" height="24" fill="white" />
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
@ -172,7 +172,7 @@ import clonedeep from 'clone-deep'
|
|||
import EditableDiv from '~/components/global/EditableDiv.vue'
|
||||
import VButton from '~/components/global/VButton.vue'
|
||||
|
||||
draggable.compatConfig = { MODE: 3 }
|
||||
draggable.compatConfig = {MODE: 3}
|
||||
export default {
|
||||
name: 'FormFieldsEditor',
|
||||
components: {
|
||||
|
@ -182,7 +182,7 @@ export default {
|
|||
EditableDiv
|
||||
},
|
||||
|
||||
setup () {
|
||||
setup() {
|
||||
const workingFormStore = useWorkingFormStore()
|
||||
return {
|
||||
route: useRoute(),
|
||||
|
@ -191,21 +191,21 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
removing: null
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
|
||||
methods: {
|
||||
onChangeName (field, newName) {
|
||||
onChangeName(field, newName) {
|
||||
field.name = newName
|
||||
},
|
||||
toggleHidden (field) {
|
||||
toggleHidden(field) {
|
||||
field.hidden = !field.hidden
|
||||
if (field.hidden) {
|
||||
field.required = false
|
||||
|
@ -214,69 +214,27 @@ export default {
|
|||
field.generates_auto_increment_id = false
|
||||
}
|
||||
},
|
||||
toggleRequired (field) {
|
||||
toggleRequired(field) {
|
||||
field.required = !field.required
|
||||
if (field.required) {
|
||||
field.hidden = false
|
||||
}
|
||||
},
|
||||
getDefaultFields () {
|
||||
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
|
||||
})
|
||||
init() {
|
||||
if (!this.form.properties) {
|
||||
return
|
||||
}
|
||||
},
|
||||
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)
|
||||
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
|
||||
})
|
||||
},
|
||||
formatType (field) {
|
||||
formatType(field) {
|
||||
let type = field.type.replace('_', ' ')
|
||||
if (!type.startsWith('nf')) {
|
||||
type = type + ' Input'
|
||||
|
@ -288,17 +246,17 @@ export default {
|
|||
}
|
||||
return type
|
||||
},
|
||||
editOptions (index) {
|
||||
editOptions(index) {
|
||||
this.workingFormStore.openSettingsForField(index)
|
||||
},
|
||||
removeBlock (blockIndex) {
|
||||
removeBlock(blockIndex) {
|
||||
this.form.properties.splice(blockIndex, 1)
|
||||
this.closeSidebar()
|
||||
},
|
||||
closeSidebar () {
|
||||
closeSidebar() {
|
||||
this.workingFormStore.closeEditFieldSidebar()
|
||||
},
|
||||
openAddFieldSidebar () {
|
||||
openAddFieldSidebar() {
|
||||
this.workingFormStore.openAddFieldSidebar(null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</template>
|
||||
</template>
|
||||
<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')"
|
||||
>
|
||||
<div class="p-4">
|
||||
|
@ -41,7 +41,7 @@
|
|||
</p>
|
||||
</div>
|
||||
<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">
|
||||
<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 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">
|
||||
<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>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<copy-content :content="embedCode" buttonText="Copy Code">
|
||||
<template #icon>
|
||||
<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
|
||||
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"/>
|
||||
|
@ -18,38 +18,37 @@
|
|||
|
||||
<script>
|
||||
import CopyContent from '../../../open/forms/components/CopyContent.vue'
|
||||
import {appUrl} from "~/lib/utils.js";
|
||||
|
||||
export default {
|
||||
name: 'EmbedCode',
|
||||
components: { CopyContent },
|
||||
props: {
|
||||
form: { type: Object, required: true },
|
||||
extraQueryParam: { type: String, default: '' }
|
||||
name: 'EmbedCode',
|
||||
components: {CopyContent},
|
||||
props: {
|
||||
form: {type: Object, required: true},
|
||||
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>
|
||||
`
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
|
||||
}),
|
||||
|
||||
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
|
||||
}
|
||||
iframeCode() {
|
||||
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>'
|
||||
},
|
||||
iframeId() {
|
||||
return 'form-' + this.form.slug
|
||||
}
|
||||
},
|
||||
|
||||
methods: {}
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import {generateUUID} from "~/lib/utils.js";
|
||||
|
||||
export const initForm = (defaultValue = {}) => {
|
||||
export const initForm = (defaultValue = {}, withDefaultProperties = false) => {
|
||||
return useForm({
|
||||
title: 'My Form',
|
||||
description: null,
|
||||
visibility: 'public',
|
||||
workspace_id: null,
|
||||
properties: [],
|
||||
properties: withDefaultProperties ? getDefaultProperties() :[],
|
||||
|
||||
notifies: false,
|
||||
slack_notifies: false,
|
||||
|
@ -52,3 +53,28 @@ export const initForm = (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);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -111,13 +111,6 @@ const loadForm = async (setup=false) => {
|
|||
// Adapt page to form: colors, custom code etc
|
||||
handleDarkMode(form.value.dark_mode)
|
||||
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)
|
||||
|
@ -127,6 +120,14 @@ onMounted(() => {
|
|||
if (form.value) {
|
||||
handleDarkMode(form.value?.dark_mode)
|
||||
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 ? `${titleChunk} - OpnForm` : 'OpnForm';
|
||||
}
|
||||
},
|
||||
... form.value.custom_code ? {
|
||||
script: [ { src: '/widgets/iframeResizer.contentWindow.min.js' } ]
|
||||
} : {}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -75,7 +75,7 @@ onMounted(() => {
|
|||
is_pro: false
|
||||
}])
|
||||
|
||||
form.value = initForm()
|
||||
form.value = initForm({}, true)
|
||||
if (route.query.template !== undefined && route.query.template) {
|
||||
const template = templatesStore.getByKey(route.query.template)
|
||||
if (template && template.structure) {
|
||||
|
|
|
@ -92,7 +92,7 @@ onMounted(() => {
|
|||
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()))
|
||||
if (route.query.template !== undefined && 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