parent
e1faba8239
commit
ae18bcbb8d
|
|
@ -69,6 +69,7 @@
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:fields="form.properties"
|
:fields="form.properties"
|
||||||
:theme="theme"
|
:theme="theme"
|
||||||
|
:dark-mode="darkMode"
|
||||||
:admin-preview="adminPreview"
|
:admin-preview="adminPreview"
|
||||||
@submit="submitForm"
|
@submit="submitForm"
|
||||||
>
|
>
|
||||||
|
|
@ -127,7 +128,11 @@ export default {
|
||||||
form: { type: Object, required: true },
|
form: { type: Object, required: true },
|
||||||
creating: { type: Boolean, default: false }, // If true, fake form submit
|
creating: { type: Boolean, default: false }, // If true, fake form submit
|
||||||
adminPreview: { type: Boolean, default: false }, // If used in FormEditorPreview
|
adminPreview: { type: Boolean, default: false }, // If used in FormEditorPreview
|
||||||
submitButtonClass: { type: String, default: '' }
|
submitButtonClass: { type: String, default: '' },
|
||||||
|
darkMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
:data-form="dataForm"
|
:data-form="dataForm"
|
||||||
:data-form-value="dataFormValue"
|
:data-form-value="dataFormValue"
|
||||||
:theme="theme"
|
:theme="theme"
|
||||||
|
:dark-mode="darkMode"
|
||||||
:admin-preview="adminPreview"
|
:admin-preview="adminPreview"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -51,7 +52,7 @@
|
||||||
<!-- Captcha -->
|
<!-- Captcha -->
|
||||||
<template v-if="form.use_captcha && isLastPage">
|
<template v-if="form.use_captcha && isLastPage">
|
||||||
<div class="mb-3 px-2 mt-2 mx-auto w-max">
|
<div class="mb-3 px-2 mt-2 mx-auto w-max">
|
||||||
<vue-hcaptcha ref="hcaptcha" :sitekey="hCaptchaSiteKey" :theme="darkModeEnabled?'dark':'light'" />
|
<vue-hcaptcha ref="hcaptcha" :sitekey="hCaptchaSiteKey" :theme="darkMode?'dark':'light'" />
|
||||||
<has-error :form="dataForm" field="h-captcha-response" />
|
<has-error :form="dataForm" field="h-captcha-response" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -85,7 +86,6 @@ import VueHcaptcha from "@hcaptcha/vue3-hcaptcha"
|
||||||
import OpenFormField from './OpenFormField.vue'
|
import OpenFormField from './OpenFormField.vue'
|
||||||
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js"
|
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js"
|
||||||
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js"
|
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js"
|
||||||
import {darkModeEnabled} from "~/lib/forms/public-page.js"
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OpenForm',
|
name: 'OpenForm',
|
||||||
|
|
@ -112,7 +112,11 @@ export default {
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
defaultDataForm:{},
|
defaultDataForm:{},
|
||||||
adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview
|
adminPreview: { type: Boolean, default: false }, // If used in FormEditorPreview
|
||||||
|
darkMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup (props) {
|
setup (props) {
|
||||||
|
|
@ -124,7 +128,6 @@ export default {
|
||||||
dataForm,
|
dataForm,
|
||||||
recordsStore,
|
recordsStore,
|
||||||
workingFormStore,
|
workingFormStore,
|
||||||
darkModeEnabled: darkModeEnabled(),
|
|
||||||
pendingSubmission: pendingSubmission(props.form)
|
pendingSubmission: pendingSubmission(props.form)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js"
|
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js"
|
||||||
import { darkModeEnabled } from '~/lib/forms/public-page.js'
|
|
||||||
import { default as _has } from 'lodash/has'
|
import { default as _has } from 'lodash/has'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -83,6 +82,10 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
darkMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
field: {
|
field: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
|
@ -177,9 +180,6 @@ export default {
|
||||||
},
|
},
|
||||||
fieldSideBarOpened() {
|
fieldSideBarOpened() {
|
||||||
return this.adminPreview && (this.form && this.selectedFieldIndex !== null) ? (this.form.properties[this.selectedFieldIndex] && this.showEditFieldSidebar) : false
|
return this.adminPreview && (this.form && this.selectedFieldIndex !== null) ? (this.form.properties[this.selectedFieldIndex] && this.showEditFieldSidebar) : false
|
||||||
},
|
|
||||||
isDark () {
|
|
||||||
return this.form.dark_mode === 'dark' || this.form.dark_mode === 'auto' && darkModeEnabled()
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -250,7 +250,7 @@ export default {
|
||||||
theme: this.theme,
|
theme: this.theme,
|
||||||
maxCharLimit: (field.max_char_limit) ? parseInt(field.max_char_limit) : 2000,
|
maxCharLimit: (field.max_char_limit) ? parseInt(field.max_char_limit) : 2000,
|
||||||
showCharLimit: field.show_char_limit || false,
|
showCharLimit: field.show_char_limit || false,
|
||||||
isDark: this.isDark
|
isDark: this.darkMode
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['select', 'multi_select'].includes(field.type)) {
|
if (['select', 'multi_select'].includes(field.type)) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
<open-complete-form ref="form-preview" class="w-full mx-auto py-5 px-3" :class="{'max-w-lg': form && (form.width === 'centered')}"
|
<open-complete-form ref="form-preview" class="w-full mx-auto py-5 px-3" :class="{'max-w-lg': form && (form.width === 'centered')}"
|
||||||
:creating="creating"
|
:creating="creating"
|
||||||
:form="form"
|
:form="form"
|
||||||
|
:dark-mode="darkMode"
|
||||||
:admin-preview="true"
|
:admin-preview="true"
|
||||||
@restarted="previewFormSubmitted=false"
|
@restarted="previewFormSubmitted=false"
|
||||||
@submitted="previewFormSubmitted=true"
|
@submitted="previewFormSubmitted=true"
|
||||||
|
|
@ -61,7 +62,7 @@
|
||||||
<script>
|
<script>
|
||||||
import VSwitch from '../../../../forms/components/VSwitch.vue'
|
import VSwitch from '../../../../forms/components/VSwitch.vue'
|
||||||
import OpenCompleteForm from '../../OpenCompleteForm.vue'
|
import OpenCompleteForm from '../../OpenCompleteForm.vue'
|
||||||
import {handleDarkMode} from "~/lib/forms/public-page.js"
|
import {handleDarkMode, useDarkMode} from "~/lib/forms/public-page.js"
|
||||||
import { default as _has } from 'lodash/has'
|
import { default as _has } from 'lodash/has'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -69,8 +70,11 @@ export default {
|
||||||
props: {},
|
props: {},
|
||||||
setup () {
|
setup () {
|
||||||
const workingFormStore = useWorkingFormStore()
|
const workingFormStore = useWorkingFormStore()
|
||||||
|
const parent = ref(null)
|
||||||
return {
|
return {
|
||||||
workingFormStore
|
workingFormStore,
|
||||||
|
parent: parent,
|
||||||
|
darkMode: useDarkMode(parent)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,63 @@
|
||||||
<template>
|
<template>
|
||||||
<iframe v-if="!isDark" id="testimonialto-carousel-all-notionforms"
|
<iframe
|
||||||
|
:id="iframeId"
|
||||||
|
title="NoteForms testimonial"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
src="https://embed.testimonial.to/carousel/all/notionforms?theme=light&autoplay=on&showmore=on&one-row=on&same-height=off"
|
height="500px"
|
||||||
frameBorder="0" scrolling="no" width="100%"
|
:src="'https://embed-v2.testimonial.to/w/notionforms?theme=light&card=base&loadMore=on&initialCount=8&tag=all'"
|
||||||
|
frameBorder="0"
|
||||||
|
scrolling="no"
|
||||||
|
width="100%"
|
||||||
/>
|
/>
|
||||||
<iframe v-else id="testimonialto-carousel-all-notionforms" src="https://embed.testimonial.to/carousel/all/notionforms?theme=dark&autoplay=on&showmore=on&one-row=on&same-height=off" frameborder="0" scrolling="no" width="100%" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {darkModeEnabled} from "~/lib/forms/public-page.js"
|
import { useDarkMode } from '~/lib/forms/public-page.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
featuresOnly: {
|
featuresOnly: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data: () => ({}),
|
|
||||||
|
|
||||||
setup () {
|
data: () => ({
|
||||||
const isDark = darkModeEnabled()
|
iframeId: 'testimonialto-carousel-all-notionforms'
|
||||||
return {
|
}),
|
||||||
isDark
|
|
||||||
}
|
computed: {},
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
|
window.addEventListener('load', () => {
|
||||||
this.loadScript()
|
this.loadScript()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
loadScript () {
|
loadScript () {
|
||||||
if (import.meta.server) return
|
if (import.meta.server)
|
||||||
|
return
|
||||||
const script = document.createElement('script')
|
const script = document.createElement('script')
|
||||||
script.setAttribute('src', 'https://testimonial.to/js/iframeResizer.min.js')
|
script.setAttribute(
|
||||||
|
'src',
|
||||||
|
'https://testimonial.to/js/iframeResizer.min.js'
|
||||||
|
)
|
||||||
|
script.setAttribute('defer', 'defer')
|
||||||
document.head.appendChild(script)
|
document.head.appendChild(script)
|
||||||
script.addEventListener('load', function () {
|
script.addEventListener('load', () => {
|
||||||
window.iFrameResize({
|
console.log('resizeing')
|
||||||
|
window.iFrameResize(
|
||||||
|
{
|
||||||
log: false,
|
log: false,
|
||||||
checkOrigin: false
|
checkOrigin: false
|
||||||
}, '#testimonialto-carousel-all-notionforms')
|
},
|
||||||
|
'#' + this.iframeId
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,121 @@
|
||||||
|
|
||||||
let darkModeNodeParent = import.meta.client ? document.body : null
|
let darkModeNodeParent = import.meta.client ? document.body : null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle form public pages dark mode and transparent mode
|
* Handle form public pages dark mode and transparent mode
|
||||||
*/
|
*/
|
||||||
export function handleDarkMode (darkMode, elem = null) {
|
export function handleDarkMode (darkMode, elem = null) {
|
||||||
if (import.meta.server) return
|
if (import.meta.server)
|
||||||
|
return
|
||||||
|
|
||||||
darkModeNodeParent = elem ?? document.body
|
darkModeNodeParent = elem ?? document.body
|
||||||
|
|
||||||
// Dark mode
|
// Dark mode
|
||||||
if (['dark', 'light'].includes(darkMode)) {
|
if (['dark', 'light'].includes(darkMode))
|
||||||
return handleDarkModeToggle(darkMode === 'dark')
|
return handleDarkModeToggle(darkMode === 'dark')
|
||||||
}
|
|
||||||
|
|
||||||
// Case auto
|
// Case auto
|
||||||
handleDarkModeToggle(window.matchMedia('(prefers-color-scheme: dark)').matches)
|
handleDarkModeToggle(
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
)
|
||||||
|
|
||||||
// Create listener
|
// Create listener
|
||||||
window.matchMedia('(prefers-color-scheme: dark)')
|
window
|
||||||
|
.matchMedia('(prefers-color-scheme: dark)')
|
||||||
.addEventListener('change', handleDarkModeToggle)
|
.addEventListener('change', handleDarkModeToggle)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function darkModeEnabled() {
|
export function useClassWatcher (elem, className) {
|
||||||
if (import.meta.server) return false
|
const hasClass = ref(false)
|
||||||
return computed(() => document.body.classList.contains('dark'))
|
|
||||||
|
const updateClassPresence = () => {
|
||||||
|
hasClass.value = elem.value?.classList.contains(className) ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
let observer = null
|
||||||
|
|
||||||
|
const startObserving = () => {
|
||||||
|
if (elem.value) {
|
||||||
|
updateClassPresence()
|
||||||
|
observer = new MutationObserver(updateClassPresence)
|
||||||
|
observer.observe(elem.value, { attributes: true, attributeFilter: ['class'] })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopObserving = () => {
|
||||||
|
if (observer) {
|
||||||
|
observer.disconnect()
|
||||||
|
observer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(elem, (newElem, oldElem, onCleanup) => {
|
||||||
|
stopObserving()
|
||||||
|
startObserving()
|
||||||
|
onCleanup(stopObserving)
|
||||||
|
}, { immediate: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopObserving()
|
||||||
|
})
|
||||||
|
|
||||||
|
return computed(() => hasClass.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDarkMode (elem = ref(null)) {
|
||||||
|
// Define a computed property to handle the element reference reactively
|
||||||
|
const effectiveElem = computed(() => {
|
||||||
|
return elem.value || (process.client ? document.body : null)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Pass the computed property to useClassWatcher
|
||||||
|
return useClassWatcher(effectiveElem, 'dark')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function darkModeEnabled (elem = ref(null)) {
|
||||||
|
if (import.meta.server)
|
||||||
|
return ref(false)
|
||||||
|
|
||||||
|
// TODO: replace this with a function that returns a watcher for the given of dark class element
|
||||||
|
// Simplify this
|
||||||
|
const isDark = ref(false)
|
||||||
|
|
||||||
|
// Update isDark based on the current class
|
||||||
|
const updateIsDark = () => {
|
||||||
|
console.log(elem.value, 'test')
|
||||||
|
const finalElement = elem.value ?? document.body
|
||||||
|
isDark.value = finalElement.classList.contains('dark')
|
||||||
|
}
|
||||||
|
|
||||||
|
// MutationObserver callback to react to class changes
|
||||||
|
let observer = new MutationObserver(updateIsDark)
|
||||||
|
|
||||||
|
// Initialize and clean up the observer
|
||||||
|
const initObserver = (element) => {
|
||||||
|
if (observer) {
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
if (element) {
|
||||||
|
observer = new MutationObserver(updateIsDark)
|
||||||
|
observer.observe(element, { attributes: true, attributeFilter: ['class'] })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!import.meta.server) {
|
||||||
|
initObserver(elem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (observer) {
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Return a computed ref that depends on isDark
|
||||||
|
return computed(() => isDark.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDarkModeToggle (enabled) {
|
function handleDarkModeToggle (enabled) {
|
||||||
|
|
@ -32,20 +123,27 @@ function handleDarkModeToggle (enabled) {
|
||||||
// if we received an event
|
// if we received an event
|
||||||
enabled = enabled.matches
|
enabled = enabled.matches
|
||||||
}
|
}
|
||||||
enabled ? darkModeNodeParent.classList.add('dark') : darkModeNodeParent.classList.remove('dark')
|
enabled
|
||||||
|
? darkModeNodeParent.classList.add('dark')
|
||||||
|
: darkModeNodeParent.classList.remove('dark')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function disableDarkMode () {
|
export function disableDarkMode () {
|
||||||
if (import.meta.server) return
|
if (import.meta.server)
|
||||||
|
return
|
||||||
const body = document.body
|
const body = document.body
|
||||||
body.classList.remove('dark')
|
body.classList.remove('dark')
|
||||||
// Remove event listener
|
// Remove event listener
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', handleDarkModeToggle)
|
window
|
||||||
|
.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.removeEventListener('change', handleDarkModeToggle)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleTransparentMode (transparentModeEnabled) {
|
export function handleTransparentMode (transparentModeEnabled) {
|
||||||
if (import.meta.server) return
|
if (import.meta.server)
|
||||||
if (!useIsIframe() || !transparentModeEnabled) return
|
return
|
||||||
|
if (!useIsIframe() || !transparentModeEnabled)
|
||||||
|
return
|
||||||
|
|
||||||
const app = document.getElementById('app')
|
const app = document.getElementById('app')
|
||||||
app.classList.remove('bg-white')
|
app.classList.remove('bg-white')
|
||||||
|
|
@ -54,8 +152,11 @@ export function handleTransparentMode (transparentModeEnabled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function focusOnFirstFormElement () {
|
export function focusOnFirstFormElement () {
|
||||||
if (import.meta.server) return
|
if (import.meta.server)
|
||||||
for (const ele of document.querySelectorAll('input,button,textarea,[role="button"]')) {
|
return
|
||||||
|
for (const ele of document.querySelectorAll(
|
||||||
|
'input,button,textarea,[role="button"]'
|
||||||
|
)) {
|
||||||
if (ele.offsetWidth !== 0 || ele.offsetHeight !== 0) {
|
if (ele.offsetWidth !== 0 || ele.offsetHeight !== 0) {
|
||||||
ele.focus()
|
ele.focus()
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
<loader class="h-6 w-6 text-nt-blue mx-auto"/>
|
<loader class="h-6 w-6 text-nt-blue mx-auto"/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<open-complete-form v-show="!recordLoading" ref="openCompleteForm" :form="form" class="mb-10"
|
<open-complete-form v-show="!recordLoading" ref="openCompleteForm" :form="form" class="mb-10" :dark-mode="darkMode"
|
||||||
@password-entered="passwordEntered"
|
@password-entered="passwordEntered"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -53,12 +53,18 @@ import {computed} from 'vue'
|
||||||
import OpenCompleteForm from "~/components/open/forms/OpenCompleteForm.vue"
|
import OpenCompleteForm from "~/components/open/forms/OpenCompleteForm.vue"
|
||||||
import sha256 from 'js-sha256'
|
import sha256 from 'js-sha256'
|
||||||
import { onBeforeRouteLeave } from 'vue-router'
|
import { onBeforeRouteLeave } from 'vue-router'
|
||||||
import {disableDarkMode, handleDarkMode, handleTransparentMode, focusOnFirstFormElement} from '~/lib/forms/public-page'
|
import {
|
||||||
|
disableDarkMode,
|
||||||
|
handleDarkMode,
|
||||||
|
handleTransparentMode,
|
||||||
|
focusOnFirstFormElement,
|
||||||
|
useDarkMode
|
||||||
|
} from '~/lib/forms/public-page'
|
||||||
|
|
||||||
const crisp = useCrisp()
|
const crisp = useCrisp()
|
||||||
const formsStore = useFormsStore()
|
const formsStore = useFormsStore()
|
||||||
const recordsStore = useRecordsStore()
|
const recordsStore = useRecordsStore()
|
||||||
|
const darkMode = useDarkMode()
|
||||||
const isIframe = useIsIframe()
|
const isIframe = useIsIframe()
|
||||||
const formLoading = computed(() => formsStore.loading)
|
const formLoading = computed(() => formsStore.loading)
|
||||||
const recordLoading = computed(() => recordsStore.loading)
|
const recordLoading = computed(() => recordsStore.loading)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue