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,42 +214,16 @@ 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.form.properties) {
return
}
]
},
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
@ -259,24 +233,8 @@ export default {
return field
})
}
},
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)
})
},
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

@ -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 },
components: {CopyContent},
props: {
form: { type: Object, required: true },
extraQueryParam: { type: String, default: '' }
form: {type: Object, required: true},
extraQueryParam: {type: String, default: ''}
},
data: () => ({
autoresizeIframe: false
}),
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>'
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>
`
},
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: {}
}
</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