Auto-resize iframes, fix custom code, fix create form initial properties

This commit is contained in:
Julien Nahum 2024-02-01 18:21:30 +01:00
parent de3e2d69c0
commit a650228a67
10 changed files with 132 additions and 112 deletions

View File

@ -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)
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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()
}
]
}

16
client/lib/utils.js vendored
View File

@ -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
*/

View File

@ -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>

View File

@ -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) {

View File

@ -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