mirror of
https://github.com/docusealco/docuseal
synced 2026-11-04 06:42:32 -05:00
native modals
This commit is contained in:
1 parent
0609a70544
commit
27e5c2c81e
21 files changed
+725
-169
No files matched your search
@@ -194,6 +194,8 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
||||
document.addEventListener('template-builder:sync', this.onSync)
|
||||
document.addEventListener('template-builder:scroll-to', this.onScrollTo)
|
||||
document.addEventListener('template-builder:select-field', this.onSelectField)
|
||||
document.addEventListener('template-builder:select-submitter', this.onSelectSubmitter)
|
||||
document.addEventListener('template-builder:sync-custom-fields', this.onSyncCustomFields)
|
||||
document.addEventListener('template-builder:draw-field', this.onDrawField)
|
||||
document.addEventListener('turbo:before-cache', this.onBeforeCache)
|
||||
|
||||
@@ -201,6 +203,10 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
||||
|
||||
this.appElem.classList.add('md:h-screen')
|
||||
|
||||
if (this.dataset.nativeView) {
|
||||
this.appElem.classList.add('min-w-0')
|
||||
}
|
||||
|
||||
const template = reactive(JSON.parse(this.dataset.template))
|
||||
|
||||
this.app = createApp(TemplateBuilder, {
|
||||
@@ -240,7 +246,7 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
||||
acceptFileTypes: this.dataset.acceptFileTypes,
|
||||
showTourStartForm: this.dataset.showTourStartForm === 'true',
|
||||
nativePlatform: this.dataset.nativePlatform,
|
||||
view: this.dataset.view
|
||||
nativeView: this.dataset.nativeView
|
||||
})
|
||||
|
||||
this.component = this.app.mount(this.appElem)
|
||||
@@ -266,14 +272,33 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
||||
|
||||
onSync = (e) => {
|
||||
const { schema, documents, fields, submitters } = e.detail
|
||||
const selectedAreas = this.component.selectedAreasRef.value.map((area) => {
|
||||
const field = this.component.template.fields.find((f) => f.areas?.includes(area))
|
||||
|
||||
return field && { uuid: field.uuid, index: field.areas.indexOf(area) }
|
||||
})
|
||||
const selectedSubmitterUuid = this.component.selectedSubmitter?.uuid
|
||||
|
||||
Object.assign(this.component.template, { schema, documents, fields, submitters })
|
||||
|
||||
this.component.selectedAreasRef.value = selectedAreas.map((item) => item && fields.find((f) => f.uuid === item.uuid)?.areas?.[item.index]).filter(Boolean)
|
||||
this.component.selectedSubmitter = submitters.find((s) => s.uuid === selectedSubmitterUuid) || submitters[0]
|
||||
}
|
||||
|
||||
onScrollTo = (e) => {
|
||||
this.component.scrollIntoDocument(e.detail)
|
||||
}
|
||||
|
||||
onSyncCustomFields = (e) => {
|
||||
this.component.customFields.splice(0, this.component.customFields.length, ...e.detail)
|
||||
}
|
||||
|
||||
onSelectSubmitter = (e) => {
|
||||
const submitter = this.component.template.submitters.find((s) => s.uuid === e.detail.uuid)
|
||||
|
||||
if (submitter) this.component.selectedSubmitter = submitter
|
||||
}
|
||||
|
||||
onSelectField = (e) => {
|
||||
const { uuid, attachment_uuid: attachmentUuid, page, x, y } = e.detail
|
||||
const field = this.component.template.fields.find((f) => f.uuid === uuid)
|
||||
@@ -284,9 +309,16 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
||||
}
|
||||
|
||||
onDrawField = (e) => {
|
||||
const { uuid, type, option } = e.detail
|
||||
const { uuid, type, option, custom_field_uuid: customFieldUuid } = e.detail
|
||||
|
||||
if (uuid) {
|
||||
if (customFieldUuid) {
|
||||
const field = this.component.customFields.find((f) => f.uuid === customFieldUuid)
|
||||
|
||||
if (field) {
|
||||
this.component.drawCustomField = field
|
||||
this.component.showDrawField = true
|
||||
}
|
||||
} else if (uuid) {
|
||||
const field = this.component.template.fields.find((f) => f.uuid === uuid)
|
||||
|
||||
if (field) {
|
||||
@@ -338,6 +370,8 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
||||
document.removeEventListener('template-builder:sync', this.onSync)
|
||||
document.removeEventListener('template-builder:scroll-to', this.onScrollTo)
|
||||
document.removeEventListener('template-builder:select-field', this.onSelectField)
|
||||
document.removeEventListener('template-builder:select-submitter', this.onSelectSubmitter)
|
||||
document.removeEventListener('template-builder:sync-custom-fields', this.onSyncCustomFields)
|
||||
document.removeEventListener('template-builder:draw-field', this.onDrawField)
|
||||
document.removeEventListener('turbo:before-cache', this.onBeforeCache)
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
@blur="onNameBlur"
|
||||
>{{ optionIndexText }} {{ (defaultField ? (defaultField.title || field.title || field.name) : field.name) || defaultName }}</span>
|
||||
<div
|
||||
v-if="isSettingsFocus || isSelectInput || (isValueInput && field.type !== 'heading') || (isNameFocus && !['checkbox', 'phone'].includes(field.type))"
|
||||
v-if="isShowControls"
|
||||
class="flex items-center ml-1.5"
|
||||
>
|
||||
<input
|
||||
@@ -74,62 +74,80 @@
|
||||
@click.prevent="field.readonly = !(field.readonly ?? true)"
|
||||
@mousedown.prevent
|
||||
>{{ t('editable') }}</label>
|
||||
<span
|
||||
v-if="field.type !== 'payment' && !isValueInput"
|
||||
class="dropdown dropdown-end field-area-settings-dropdown"
|
||||
@mouseenter="renderDropdown = true"
|
||||
@touchstart="renderDropdown = true"
|
||||
>
|
||||
<label
|
||||
ref="settingsButton"
|
||||
tabindex="0"
|
||||
:title="t('settings')"
|
||||
class="cursor-pointer flex items-center"
|
||||
style="height: 25px"
|
||||
@focus="isSettingsFocus = true"
|
||||
@blur="maybeBlurSettings"
|
||||
>
|
||||
<IconDotsVertical class="w-5 h-5" />
|
||||
</label>
|
||||
<ul
|
||||
v-if="renderDropdown"
|
||||
ref="settingsDropdown"
|
||||
tabindex="0"
|
||||
class="dropdown-content menu menu-xs px-2 pb-2 pt-1 shadow rounded-box w-52 z-10 rounded-t-none"
|
||||
:style="{ backgroundColor: 'white' }"
|
||||
@dragstart.prevent.stop
|
||||
@click="closeDropdown"
|
||||
@focusout="maybeBlurSettings"
|
||||
>
|
||||
<FieldSettings
|
||||
v-if="isMobile"
|
||||
:field="field"
|
||||
:default-field="defaultField"
|
||||
:editable="editable"
|
||||
:background-color="'white'"
|
||||
:with-required="false"
|
||||
:with-areas="false"
|
||||
:with-signature-id="withSignatureId"
|
||||
:with-prefillable="withPrefillable"
|
||||
@click-formula="isShowFormulaModal = true"
|
||||
@click-font="isShowFontModal = true"
|
||||
@click-description="isShowDescriptionModal = true"
|
||||
@add-custom-field="$emit('add-custom-field')"
|
||||
@click-condition="isShowConditionsModal = true"
|
||||
@save="$emit('change')"
|
||||
@scroll-to="[selectedAreasRef.value = [$event], $emit('scroll-to', $event)]"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="whitespace-normal"
|
||||
>
|
||||
The dots menu is retired in favor of the field context menu. Right-click the field to access field settings. Double-click the field to set a default value.
|
||||
</div>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
v-if="isShowSettingsMenu"
|
||||
class="dropdown dropdown-end field-area-settings-dropdown"
|
||||
:class="isMobile ? '-ml-1' : { 'ml-1.5': !isShowControls }"
|
||||
@mouseenter="renderDropdown = true"
|
||||
@touchstart="renderDropdown = true"
|
||||
>
|
||||
<label
|
||||
ref="settingsButton"
|
||||
tabindex="0"
|
||||
:title="t('settings')"
|
||||
class="cursor-pointer flex items-center"
|
||||
:class="{ 'pr-0.5': isMobile }"
|
||||
style="height: 25px"
|
||||
@focus="isSettingsFocus = true"
|
||||
@blur="maybeBlurSettings"
|
||||
>
|
||||
<IconDotsVertical class="w-5 h-5" />
|
||||
</label>
|
||||
<ul
|
||||
v-if="renderDropdown"
|
||||
ref="settingsDropdown"
|
||||
tabindex="0"
|
||||
class="dropdown-content menu menu-xs px-2 pb-2 pt-1 shadow rounded-box w-52 z-10 rounded-t-none"
|
||||
:style="{ backgroundColor: 'white' }"
|
||||
@dragstart.prevent.stop
|
||||
@click="closeDropdown"
|
||||
@focusout="maybeBlurSettings"
|
||||
>
|
||||
<FieldSettings
|
||||
v-if="isMobile"
|
||||
:field="field"
|
||||
:default-field="defaultField"
|
||||
:editable="editable"
|
||||
:background-color="'white'"
|
||||
:with-required="false"
|
||||
:with-areas="false"
|
||||
:with-signature-id="withSignatureId"
|
||||
:with-prefillable="withPrefillable"
|
||||
@click-formula="openFormulaModal"
|
||||
@click-font="openFontModal"
|
||||
@click-description="openDescriptionModal"
|
||||
@add-custom-field="$emit('add-custom-field')"
|
||||
@click-condition="openConditionsModal"
|
||||
@save="$emit('change')"
|
||||
@scroll-to="[selectedAreasRef.value = [$event], $emit('scroll-to', $event)]"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="whitespace-normal"
|
||||
>
|
||||
The dots menu is retired in favor of the field context menu. Right-click the field to access field settings. Double-click the field to set a default value.
|
||||
</div>
|
||||
<li
|
||||
v-if="isMobile && editable"
|
||||
class="field-settings-remove"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
class="text-sm py-1 px-2 text-red-600"
|
||||
@click.prevent="$emit('remove')"
|
||||
>
|
||||
<IconTrashX
|
||||
:width="20"
|
||||
:stroke-width="1.6"
|
||||
/>
|
||||
{{ t('remove') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<button
|
||||
v-else-if="editable"
|
||||
v-else-if="editable && !isShowControls"
|
||||
class="pr-1"
|
||||
:title="t('remove')"
|
||||
@click.prevent="$emit('remove')"
|
||||
@@ -199,12 +217,13 @@ import FormulaModal from './formula_modal'
|
||||
import FontModal from './font_modal'
|
||||
import ConditionsModal from './conditions_modal'
|
||||
import DescriptionModal from './description_modal'
|
||||
import { IconX, IconDotsVertical } from '@tabler/icons-vue'
|
||||
import { IconX, IconDotsVertical, IconTrashX } from '@tabler/icons-vue'
|
||||
import { v4 } from 'uuid'
|
||||
|
||||
export default {
|
||||
name: 'AreaTitle',
|
||||
components: {
|
||||
IconTrashX,
|
||||
FieldType,
|
||||
FieldSettings,
|
||||
FormulaModal,
|
||||
@@ -215,7 +234,7 @@ export default {
|
||||
FieldSubmitter,
|
||||
IconX
|
||||
},
|
||||
inject: ['t'],
|
||||
inject: ['t', 'isMobile'],
|
||||
props: {
|
||||
template: {
|
||||
type: Object,
|
||||
@@ -292,6 +311,17 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isShowControls () {
|
||||
if (this.isMobile) return false
|
||||
|
||||
return this.isSettingsFocus || this.isSelectInput || (this.isValueInput && this.field.type !== 'heading') || (this.isNameFocus && !['checkbox', 'phone'].includes(this.field.type))
|
||||
},
|
||||
isShowSettingsMenu () {
|
||||
if (this.field.type === 'payment' || this.isValueInput) return false
|
||||
if (this.isMobile) return this.editable
|
||||
|
||||
return this.isShowControls
|
||||
},
|
||||
fieldNames: FieldType.computed.fieldNames,
|
||||
fieldLabels: FieldType.computed.fieldLabels,
|
||||
submitter () {
|
||||
@@ -318,6 +348,34 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openFormulaModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'formula', uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowFormulaModal = true
|
||||
}
|
||||
},
|
||||
openFontModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'font', detent: 'medium', uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowFontModal = true
|
||||
}
|
||||
},
|
||||
openConditionsModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'conditions', detent: 'medium', uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowConditionsModal = true
|
||||
}
|
||||
},
|
||||
openDescriptionModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'description', uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowDescriptionModal = true
|
||||
}
|
||||
},
|
||||
buildDefaultName: Field.methods.buildDefaultName,
|
||||
closeDropdown () {
|
||||
this.$el.getRootNode().activeElement.blur()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div
|
||||
ref="dragContainer"
|
||||
style="max-width: 1600px"
|
||||
:class="isNativeView ? (isRevisionsView ? 'px-4 pt-3 pb-6' : 'px-4') : 'mx-auto pl-3 md:pl-4 h-full'"
|
||||
:class="nativeView ? (isSheetView ? 'bg-base-100' : 'px-4') : 'mx-auto pl-3 md:pl-4 h-full'"
|
||||
@dragover="onDragover"
|
||||
@drop="isDragFile = false"
|
||||
>
|
||||
@@ -53,7 +53,7 @@
|
||||
@replace="onDocumentReplace"
|
||||
@up="moveDocument(item, -1)"
|
||||
@reorder="reorderFields"
|
||||
@edit="editModalDocumentUuid = item.attachment_uuid"
|
||||
@edit="openDocumentsEditor(item)"
|
||||
@down="moveDocument(item, 1)"
|
||||
@change="save"
|
||||
/>
|
||||
@@ -119,29 +119,36 @@
|
||||
:with-signature-id="withSignatureId"
|
||||
:with-prefillable="withPrefillable"
|
||||
:only-defined-fields="onlyDefinedFields"
|
||||
:with-sticky-submitters="!!nativeView"
|
||||
:editable="editable"
|
||||
:scroll-on-edit="false"
|
||||
@add-field="addField"
|
||||
@set-draw="onDrawerSetDraw"
|
||||
@remove-field="onRemoveField"
|
||||
@remove-submitter="onRemoveSubmitter"
|
||||
@select-submitter="selectedSubmitter = $event"
|
||||
@select-submitter="onDrawerSelectSubmitter"
|
||||
@set-draw-type="onDrawerSetDrawType"
|
||||
@set-draw-custom-field="onDrawerSetDrawCustomField"
|
||||
@set-drag="dragField = $event"
|
||||
@set-drag-placeholder="$refs.dragPlaceholder.dragPlaceholder = $event"
|
||||
@drag-end="[dragField = null, $refs.dragPlaceholder.dragPlaceholder = null]"
|
||||
@change-submitter="selectedSubmitter = $event"
|
||||
@change-submitter="onDrawerSelectSubmitter"
|
||||
@scroll-to-area="onDrawerScrollToArea"
|
||||
@rebuild-variables-schema="rebuildVariablesSchema"
|
||||
/>
|
||||
</div>
|
||||
</MobileDrawer>
|
||||
<div
|
||||
v-if="isRevisionsView && !isRevisionsModalOpen"
|
||||
class="flex justify-center py-8"
|
||||
>
|
||||
<IconInnerShadowTop class="w-6 h-6 animate-spin" />
|
||||
</div>
|
||||
<NativeModals
|
||||
v-if="isSheetView"
|
||||
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
||||
:custom-fields="customFields"
|
||||
:editable="editable"
|
||||
:authenticity-token="authenticityToken"
|
||||
:accept-file-types="acceptFileTypes"
|
||||
:base-url="baseUrl"
|
||||
:page-preview-format="pagePreviewFormat"
|
||||
@documents-modified="onDocumentsModified"
|
||||
/>
|
||||
<div
|
||||
v-if="pendingFieldAttachmentUuids.length && editable"
|
||||
class="top-1.5 sticky h-0 z-20 max-w-2xl mx-auto"
|
||||
@@ -192,7 +199,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="($slots.buttons || withTitle) && !isNativeView"
|
||||
v-if="($slots.buttons || withTitle) && !nativeView"
|
||||
id="title_container"
|
||||
class="flex justify-between py-1.5 items-center pr-4 top-0 z-10 title-container"
|
||||
:class="{ sticky: withStickySubmitters || isBreakpointLg, hidden: !!nativePlatform }"
|
||||
@@ -440,13 +447,7 @@
|
||||
:data-label="t('revisions')"
|
||||
data-icon="history"
|
||||
>
|
||||
<a
|
||||
v-if="!!nativePlatform"
|
||||
:href="`/templates/${template.id}/edit?view=revisions`"
|
||||
data-turbo-frame="modal"
|
||||
/>
|
||||
<button
|
||||
v-else
|
||||
class="flex space-x-2"
|
||||
@click.prevent="openRevisionsModal"
|
||||
@mouseenter="preloadRevisions"
|
||||
@@ -507,7 +508,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isNativeView"
|
||||
v-if="!nativeView"
|
||||
id="main_container"
|
||||
class="flex main-container"
|
||||
:class="($slots.buttons || withTitle) && !nativePlatform ? (isMobile ? 'max-h-[calc(100%_-_60px)]' : 'md:max-h-[calc(100%_-_60px)]') : (isMobile ? 'max-h-[100%]' : 'md:max-h-[100%]')"
|
||||
@@ -539,7 +540,7 @@
|
||||
@replace="onDocumentReplace"
|
||||
@up="moveDocument(item, -1)"
|
||||
@reorder="reorderFields"
|
||||
@edit="editModalDocumentUuid = item.attachment_uuid"
|
||||
@edit="openDocumentsEditor(item)"
|
||||
@down="moveDocument(item, 1)"
|
||||
@change="save"
|
||||
/>
|
||||
@@ -758,7 +759,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isNativeView"
|
||||
v-if="!nativeView"
|
||||
class="sticky bottom-0 z-10"
|
||||
>
|
||||
<native-action
|
||||
@@ -780,13 +781,13 @@
|
||||
data-view="fields"
|
||||
/>
|
||||
<native-action
|
||||
v-if="nativePlatform && drawField && (isBreakpointLg || isMobile)"
|
||||
v-if="nativePlatform && (drawField || drawCustomField) && (isBreakpointLg || isMobile)"
|
||||
data-placement="draw"
|
||||
:data-label="t('draw_field').replace('{field}', fieldNames[drawField.type])"
|
||||
:data-label="t('draw_field').replace('{field}', drawCustomField ? drawCustomField.name : fieldNames[drawField.type])"
|
||||
>
|
||||
<button
|
||||
class="hidden"
|
||||
@click="[drawField = null, drawOption = null]"
|
||||
@click="[drawField = null, drawOption = null, drawCustomField = null, showDrawField = false]"
|
||||
/>
|
||||
</native-action>
|
||||
<button
|
||||
@@ -912,9 +913,8 @@
|
||||
:template="template"
|
||||
:revisions="revisions"
|
||||
:locale="locale"
|
||||
:inline="isRevisionsView"
|
||||
@close="isRevisionsModalOpen = false"
|
||||
@apply="isRevisionsView ? postNativeEvent('template-builder:apply-revision', $event, true) : onRevisionApply($event)"
|
||||
@apply="onRevisionApply($event)"
|
||||
/>
|
||||
<DocumentsEditorModal
|
||||
v-if="editModalDocumentUuid"
|
||||
@@ -946,6 +946,7 @@ import DocumentPreview from './preview'
|
||||
import FieldSubmitter from './field_submitter'
|
||||
import RevisionsModal from './revisions_modal'
|
||||
import DocumentsEditorModal from './documents_editor_modal'
|
||||
import NativeModals from './native_modals'
|
||||
import { IconPlus, IconFiles, IconUsersPlus, IconDeviceFloppy, IconChevronDown, IconEye, IconWritingSign, IconInnerShadowTop, IconInfoCircle, IconAdjustments, IconDownload, IconHistory, IconX } from '@tabler/icons-vue'
|
||||
import { v4 } from 'uuid'
|
||||
import { ref, computed, toRaw, defineAsyncComponent } from 'vue'
|
||||
@@ -989,7 +990,8 @@ export default {
|
||||
IconDeviceFloppy,
|
||||
IconX,
|
||||
RevisionsModal,
|
||||
DocumentsEditorModal
|
||||
DocumentsEditorModal,
|
||||
NativeModals
|
||||
},
|
||||
provide () {
|
||||
return {
|
||||
@@ -1237,7 +1239,7 @@ export default {
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
view: {
|
||||
nativeView: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
@@ -1425,19 +1427,16 @@ export default {
|
||||
fieldNames: FieldType.computed.fieldNames,
|
||||
fieldIcons: FieldType.computed.fieldIcons,
|
||||
isDocumentsView () {
|
||||
return this.view === 'documents'
|
||||
return this.nativeView === 'documents'
|
||||
},
|
||||
isFieldsView () {
|
||||
return this.view === 'fields'
|
||||
return this.nativeView === 'fields'
|
||||
},
|
||||
isSheetView () {
|
||||
return this.nativeView === 'sheet'
|
||||
},
|
||||
withMobileDrawers () {
|
||||
return !this.nativePlatform && !this.isNativeView && this.sortedDocuments.length > 0
|
||||
},
|
||||
isRevisionsView () {
|
||||
return this.view === 'revisions'
|
||||
},
|
||||
isNativeView () {
|
||||
return this.isDocumentsView || this.isFieldsView || this.isRevisionsView
|
||||
return !this.nativePlatform && !this.nativeView && this.sortedDocuments.length > 0
|
||||
},
|
||||
isMobile () {
|
||||
const isMobileSafariIos = 'ontouchstart' in window && navigator.maxTouchPoints > 0 && /AppleWebKit/i.test(navigator.userAgent)
|
||||
@@ -1624,10 +1623,6 @@ export default {
|
||||
this.selectedSubmitter = this.template.submitters[0]
|
||||
},
|
||||
mounted () {
|
||||
if (this.isRevisionsView) {
|
||||
this.openRevisionsModal()
|
||||
}
|
||||
|
||||
this.undoStack = [JSON.stringify(this.template)]
|
||||
this.redoStack = []
|
||||
|
||||
@@ -2200,6 +2195,10 @@ export default {
|
||||
this.loadRevisionsPromise ||= this.baseFetch(`/templates/${this.template.id}/versions`)
|
||||
},
|
||||
openRevisionsModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
return window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'revisions', detent: 'medium' })
|
||||
}
|
||||
|
||||
this.closeDropdown()
|
||||
|
||||
this.loadRevisionsPromise ||= this.baseFetch(`/templates/${this.template.id}/versions`)
|
||||
@@ -2212,6 +2211,13 @@ export default {
|
||||
this.loadRevisionsPromise = null
|
||||
})
|
||||
},
|
||||
openDocumentsEditor (item) {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'documents', viewport: 'fixed', uuid: item.attachment_uuid })
|
||||
} else {
|
||||
this.editModalDocumentUuid = item.attachment_uuid
|
||||
}
|
||||
},
|
||||
onApplyRevisionEvent (e) {
|
||||
this.onRevisionApply(e.detail)
|
||||
},
|
||||
@@ -2427,7 +2433,7 @@ export default {
|
||||
window.webkit?.messageHandlers?.modal?.postMessage({ action: 'dispatch', name, detail: JSON.stringify(detail), dismiss: dismiss ? 'true' : 'false' })
|
||||
},
|
||||
onDrawerScrollTo (item) {
|
||||
if (this.isNativeView) {
|
||||
if (this.nativeView) {
|
||||
this.postNativeEvent('template-builder:scroll-to', { attachment_uuid: item.attachment_uuid }, true)
|
||||
} else {
|
||||
this.isDocumentsDrawerOpen = false
|
||||
@@ -2435,7 +2441,7 @@ export default {
|
||||
}
|
||||
},
|
||||
onDrawerSetDraw ({ field, option }) {
|
||||
if (this.isNativeView) {
|
||||
if (this.nativeView) {
|
||||
this.postNativeEvent('template-builder:draw-field', { uuid: field.uuid, option }, true)
|
||||
} else {
|
||||
this.isFieldsDrawerOpen = false
|
||||
@@ -2444,15 +2450,31 @@ export default {
|
||||
}
|
||||
},
|
||||
onDrawerSetDrawType (type) {
|
||||
if (this.isNativeView) {
|
||||
if (this.nativeView) {
|
||||
this.postNativeEvent('template-builder:draw-field', { type }, true)
|
||||
} else {
|
||||
this.isFieldsDrawerOpen = false
|
||||
this.startFieldDraw({ type })
|
||||
}
|
||||
},
|
||||
onDrawerSelectSubmitter (submitter) {
|
||||
this.selectedSubmitter = submitter
|
||||
|
||||
if (this.nativeView) {
|
||||
this.$nextTick(() => this.postNativeEvent('template-builder:select-submitter', { uuid: submitter.uuid }))
|
||||
}
|
||||
},
|
||||
onDrawerSetDrawCustomField (field) {
|
||||
if (this.nativeView) {
|
||||
this.postNativeEvent('template-builder:draw-field', { custom_field_uuid: field.uuid }, true)
|
||||
} else {
|
||||
this.isFieldsDrawerOpen = false
|
||||
this.drawCustomField = field
|
||||
this.showDrawField = true
|
||||
}
|
||||
},
|
||||
onDrawerScrollToArea (area) {
|
||||
if (this.isNativeView) {
|
||||
if (this.nativeView) {
|
||||
const field = this.template.fields.find((f) => (f.areas || []).includes(area))
|
||||
|
||||
this.postNativeEvent('template-builder:select-field', { uuid: field?.uuid, attachment_uuid: area.attachment_uuid, page: area.page, x: area.x, y: area.y }, true)
|
||||
@@ -2465,7 +2487,7 @@ export default {
|
||||
const { schema, documents, fields, submitters } = this.template
|
||||
const detail = JSON.stringify({ schema, documents, fields, submitters })
|
||||
|
||||
if (this.isNativeView) {
|
||||
if (this.nativeView) {
|
||||
window.webkit?.messageHandlers?.modal?.postMessage({ action: 'dispatch', name: 'template-builder:sync', detail, dismiss: 'false' })
|
||||
} else {
|
||||
window.webkit?.messageHandlers?.native?.postMessage({ type: 'drawer', op: 'sync', detail })
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<template>
|
||||
<div
|
||||
class="modal modal-open items-start !animate-none overflow-y-auto"
|
||||
:class="{ 'modal modal-open items-start !animate-none overflow-y-auto': !inline }"
|
||||
>
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="absolute top-0 bottom-0 right-0 left-0"
|
||||
@click.prevent="$emit('close')"
|
||||
/>
|
||||
<div class="modal-box pt-4 pb-6 px-6 mt-20 w-full max-w-xl">
|
||||
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||
<div :class="inline ? 'px-4 pt-3 pb-6' : 'modal-box pt-4 pb-6 px-6 mt-20 w-full max-w-xl'">
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="flex justify-between items-center border-b pb-2 mb-2 font-medium"
|
||||
>
|
||||
<span class="modal-title">
|
||||
{{ t('condition') }} - {{ (defaultField ? (defaultField.title || item.title || item.name) : item.name) || buildDefaultName(item) }}
|
||||
</span>
|
||||
@@ -183,6 +187,11 @@ export default {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => []
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['close', 'save'],
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
:field="field"
|
||||
:with-condition="false"
|
||||
:with-force-open="false"
|
||||
@click-description="isShowDescriptionModal = true"
|
||||
@click-formula="isShowFormulaModal = true"
|
||||
@click-description="openDescriptionModal"
|
||||
@click-formula="openFormulaModal"
|
||||
/>
|
||||
<span
|
||||
v-else-if="!isNew"
|
||||
@@ -61,9 +61,17 @@
|
||||
<label
|
||||
tabindex="0"
|
||||
:title="t('settings')"
|
||||
class="cursor-pointer text-transparent group-hover:text-base-content"
|
||||
class="cursor-pointer flex items-center"
|
||||
:class="[iconColorClass, { 'pr-1': isMobile }]"
|
||||
>
|
||||
<IconDotsVertical
|
||||
v-if="isMobile"
|
||||
:width="18"
|
||||
:height="18"
|
||||
:stroke-width="1.6"
|
||||
/>
|
||||
<IconSettings
|
||||
v-else
|
||||
:width="18"
|
||||
:stroke-width="1.6"
|
||||
/>
|
||||
@@ -85,11 +93,27 @@
|
||||
:with-areas="false"
|
||||
:with-copy-to-all-pages="false"
|
||||
:with-condition="false"
|
||||
@click-formula="isShowFormulaModal = true"
|
||||
@click-font="isShowFontModal = true"
|
||||
@click-description="isShowDescriptionModal = true"
|
||||
@click-formula="openFormulaModal"
|
||||
@click-font="openFontModal"
|
||||
@click-description="openDescriptionModal"
|
||||
@save="$emit('save')"
|
||||
/>
|
||||
<li
|
||||
v-if="isMobile"
|
||||
class="field-settings-remove"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
class="text-sm py-1 px-2 text-red-600"
|
||||
@click.prevent="$emit('remove', field)"
|
||||
>
|
||||
<IconTrashX
|
||||
:width="20"
|
||||
:stroke-width="1.6"
|
||||
/>
|
||||
{{ t('remove') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<button
|
||||
@@ -104,8 +128,9 @@
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-if="isNew || !isMobile || field.type === 'payment'"
|
||||
class="relative group-hover:text-base-content pr-1 field-remove-button"
|
||||
:class="isNew ? 'text-base-content' : 'text-transparent group-hover:text-base-content'"
|
||||
:class="isNew ? 'text-base-content' : iconColorClass"
|
||||
:title="t('remove')"
|
||||
@click="onRemoveClick"
|
||||
>
|
||||
@@ -164,7 +189,7 @@ import FieldSettings from './field_settings'
|
||||
import FormulaModal from './formula_modal'
|
||||
import FontModal from './font_modal'
|
||||
import DescriptionModal from './description_modal'
|
||||
import { IconTrashX, IconSettings, IconCheck } from '@tabler/icons-vue'
|
||||
import { IconTrashX, IconSettings, IconCheck, IconDotsVertical } from '@tabler/icons-vue'
|
||||
import IconDrag from './icon_drag'
|
||||
|
||||
export default {
|
||||
@@ -172,6 +197,7 @@ export default {
|
||||
components: {
|
||||
Contenteditable,
|
||||
IconSettings,
|
||||
IconDotsVertical,
|
||||
IconCheck,
|
||||
FieldSettings,
|
||||
PaymentSettings,
|
||||
@@ -182,7 +208,7 @@ export default {
|
||||
IconTrashX,
|
||||
FieldType
|
||||
},
|
||||
inject: ['backgroundColor', 't'],
|
||||
inject: ['backgroundColor', 't', 'isMobile'],
|
||||
props: {
|
||||
field: {
|
||||
type: Object,
|
||||
@@ -217,6 +243,9 @@ export default {
|
||||
dropdownBgColor () {
|
||||
return ['', null, 'transparent'].includes(this.backgroundColor) ? 'white' : this.backgroundColor
|
||||
},
|
||||
iconColorClass () {
|
||||
return this.isMobile ? 'text-base-content' : 'text-transparent group-hover:text-base-content'
|
||||
},
|
||||
modalContainerEl () {
|
||||
return this.$el.getRootNode().querySelector('#docuseal_modal_container')
|
||||
}
|
||||
@@ -230,6 +259,27 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openFormulaModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'formula', custom_field_uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowFormulaModal = true
|
||||
}
|
||||
},
|
||||
openFontModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'font', detent: 'medium', custom_field_uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowFontModal = true
|
||||
}
|
||||
},
|
||||
openDescriptionModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'description', custom_field_uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowDescriptionModal = true
|
||||
}
|
||||
},
|
||||
buildDefaultName () {
|
||||
return this.t('custom')
|
||||
},
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<template>
|
||||
<div
|
||||
class="modal modal-open items-start !animate-none overflow-y-auto"
|
||||
:class="{ 'modal modal-open items-start !animate-none overflow-y-auto': !inline }"
|
||||
>
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="absolute top-0 bottom-0 right-0 left-0"
|
||||
@click.prevent="$emit('close')"
|
||||
/>
|
||||
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
||||
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||
<div :class="inline ? 'px-4 pt-3 pb-6' : 'modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl'">
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="flex justify-between items-center border-b pb-2 mb-2 font-medium"
|
||||
>
|
||||
<span class="modal-title">
|
||||
{{ (defaultField ? (defaultField.title || field.title || field.name) : field.name) || buildDefaultName(field) }}
|
||||
</span>
|
||||
@@ -86,6 +90,11 @@ export default {
|
||||
buildDefaultName: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['close', 'save'],
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<div class="flex-none px-4 border-t py-3 md:w-56 md:border-t-0 md:border-l md:py-4 md:space-y-2 md:flex md:flex-col">
|
||||
<div class="grid grid-cols-3 gap-2 md:block md:space-y-2">
|
||||
<button
|
||||
class="btn btn-sm normal-case font-normal rounded disabled:bg-base-300 flex-col gap-1 h-auto min-h-0 py-2 px-1 text-xs leading-tight whitespace-normal md:flex-row md:gap-2 md:h-8 md:min-h-[2rem] md:py-0 md:px-3 md:text-sm md:whitespace-nowrap md:w-full md:justify-start"
|
||||
class="btn btn-sm normal-case font-normal rounded order-last disabled:bg-base-300 flex-col gap-1 h-auto min-h-0 py-2 px-1 text-xs leading-tight whitespace-normal md:flex-row md:gap-2 md:h-8 md:min-h-[2rem] md:py-0 md:px-3 md:text-sm md:whitespace-nowrap md:w-full md:justify-start"
|
||||
:disabled="!!isProcessing"
|
||||
@click.prevent="submit(true)"
|
||||
>
|
||||
@@ -64,7 +64,7 @@
|
||||
{{ t('crop_and_scan') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm normal-case font-normal rounded disabled:bg-base-300 flex-col gap-1 h-auto min-h-0 py-2 px-1 text-xs leading-tight whitespace-normal md:flex-row md:gap-2 md:h-8 md:min-h-[2rem] md:py-0 md:px-3 md:text-sm md:whitespace-nowrap md:w-full md:justify-start"
|
||||
class="btn btn-sm normal-case font-normal rounded order-last disabled:bg-base-300 flex-col gap-1 h-auto min-h-0 py-2 px-1 text-xs leading-tight whitespace-normal md:flex-row md:gap-2 md:h-8 md:min-h-[2rem] md:py-0 md:px-3 md:text-sm md:whitespace-nowrap md:w-full md:justify-start"
|
||||
:disabled="!!isProcessing"
|
||||
@click.prevent="submit(false)"
|
||||
>
|
||||
@@ -74,17 +74,15 @@
|
||||
/>
|
||||
<IconCrop
|
||||
v-else
|
||||
width="22"
|
||||
height="22"
|
||||
style="margin-left: -3px"
|
||||
class="w-4 h-4 md:w-[22px] md:h-[22px] md:-ml-[3px]"
|
||||
:stroke-width="1.5"
|
||||
/>
|
||||
<span :style="{ 'margin-left': isProcessing === 'crop' ? '0px' : '-3px' }">
|
||||
<span :class="{ 'md:-ml-[3px]': isProcessing !== 'crop' }">
|
||||
{{ t('crop') }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm normal-case font-normal rounded flex-col gap-1 h-auto min-h-0 py-2 px-1 text-xs leading-tight whitespace-normal md:flex-row md:gap-2 md:h-8 md:min-h-[2rem] md:py-0 md:px-3 md:text-sm md:whitespace-nowrap md:w-full md:justify-start"
|
||||
class="btn btn-sm normal-case font-normal rounded order-last flex-col gap-1 h-auto min-h-0 py-2 px-1 text-xs leading-tight whitespace-normal md:flex-row md:gap-2 md:h-8 md:min-h-[2rem] md:py-0 md:px-3 md:text-sm md:whitespace-nowrap md:w-full md:justify-start"
|
||||
@click.prevent="$emit('cancel')"
|
||||
>
|
||||
<IconX class="w-4 h-4" />
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
<template>
|
||||
<div
|
||||
class="modal modal-open items-center !animate-none"
|
||||
:class="inline ? 'flex flex-col h-full' : 'modal modal-open items-center !animate-none'"
|
||||
@dragover.prevent="onModalDragover"
|
||||
@dragleave="onModalDragleave"
|
||||
@drop.prevent="onModalDrop"
|
||||
>
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="absolute top-0 bottom-0 right-0 left-0"
|
||||
@click.prevent="$emit('close')"
|
||||
/>
|
||||
<div
|
||||
class="modal-box relative flex flex-col p-0 w-full h-full rounded-none md:rounded-2xl overflow-x-hidden !max-h-full md:!max-h-[92vh]"
|
||||
style="max-width: 1240px"
|
||||
:class="inline ? 'relative flex flex-col flex-1 min-h-0' : 'modal-box relative flex flex-col p-0 w-full h-full rounded-none md:rounded-2xl overflow-x-hidden !max-h-full md:!max-h-[92vh]'"
|
||||
:style="inline ? null : { maxWidth: '1240px' }"
|
||||
>
|
||||
<div class="flex justify-between items-center border-b py-2 px-6 font-medium">
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="flex justify-between items-center border-b py-2 px-6 font-medium"
|
||||
>
|
||||
<span
|
||||
class="modal-title"
|
||||
style="padding-top: 2px"
|
||||
@@ -32,6 +36,7 @@
|
||||
<CropView
|
||||
v-if="cropPageItem"
|
||||
class="absolute inset-0 z-10 bg-base-100"
|
||||
:style="inline ? { paddingTop: '70px' } : null"
|
||||
:template-id="template.id"
|
||||
:page="cropPageItem"
|
||||
:image-url="thumbUrl(cropPageItem)"
|
||||
@@ -42,6 +47,7 @@
|
||||
<RedactView
|
||||
v-else-if="redactPageItem"
|
||||
class="absolute inset-0 z-10 bg-base-100"
|
||||
:style="inline ? { paddingTop: '70px' } : null"
|
||||
:template-id="template.id"
|
||||
:page="redactPageItem"
|
||||
:image-url="thumbUrl(redactPageItem)"
|
||||
@@ -57,12 +63,14 @@
|
||||
>
|
||||
<div
|
||||
class="flex-1 overflow-y-auto overflow-x-hidden px-4 md:px-6 py-4 space-y-4"
|
||||
:style="inline ? { paddingTop: '86px' } : null"
|
||||
>
|
||||
<div
|
||||
v-for="(doc, docIndex) in layout"
|
||||
:key="doc.attachmentUuid"
|
||||
:data-uuid="doc.attachmentUuid"
|
||||
class="relative"
|
||||
:style="inline ? { scrollMarginTop: '86px' } : null"
|
||||
>
|
||||
<div
|
||||
class="flex items-center pb-2"
|
||||
@@ -80,7 +88,7 @@
|
||||
:class="{ 'rotate-90': !collapsedDocumentsIndex[doc.attachmentUuid] }"
|
||||
/>
|
||||
<span
|
||||
class="truncate"
|
||||
class="truncate min-w-0"
|
||||
:class="{ 'line-through': !doc.pages.length }"
|
||||
>{{ doc.name }}</span>
|
||||
</button>
|
||||
@@ -174,7 +182,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="(!collapsedDocumentsIndex[doc.attachmentUuid] || layout.length === 1) && doc.pages.length"
|
||||
class="grid grid-cols-2 md:grid-cols-4 gap-3"
|
||||
class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3"
|
||||
@dragover.prevent="onGridDragover(docIndex, $event)"
|
||||
@drop.prevent="onPageDrop"
|
||||
>
|
||||
@@ -183,6 +191,7 @@
|
||||
:key="page.id"
|
||||
:data-page-id="page.id"
|
||||
class="relative min-w-0 cursor-pointer group"
|
||||
:style="inline ? { scrollMarginTop: '86px' } : null"
|
||||
draggable="true"
|
||||
@click="selectedPageId = page.id"
|
||||
@mousedown="onPageMousedown(page, $event)"
|
||||
@@ -250,7 +259,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex-none px-4 border-t py-3 flex flex-col gap-3 md:w-56 md:border-t-0 md:border-l md:py-4 md:gap-0"
|
||||
class="flex-none px-4 border-t pt-3 flex flex-col gap-3 md:w-56 md:border-t-0 md:border-l md:py-4 md:gap-0"
|
||||
:class="{ 'pb-3': !inline }"
|
||||
>
|
||||
<div class="grid grid-cols-4 gap-2 md:block md:space-y-2">
|
||||
<button
|
||||
@@ -324,12 +334,10 @@
|
||||
@click.prevent="openCropView"
|
||||
>
|
||||
<IconCrop
|
||||
width="22"
|
||||
height="22"
|
||||
style="margin-left: -3px"
|
||||
class="w-4 h-4 md:w-[22px] md:h-[22px] md:-ml-[3px]"
|
||||
:stroke-width="1.5"
|
||||
/>
|
||||
<span style="margin-left: -3px">
|
||||
<span class="md:-ml-[3px]">
|
||||
{{ t('crop') }}
|
||||
</span>
|
||||
</button>
|
||||
@@ -441,6 +449,11 @@ export default {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'image/*, application/pdf'
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['close', 'saved'],
|
||||
@@ -698,12 +711,14 @@ export default {
|
||||
if (targetIndex < 0) {
|
||||
if (docIndex > 0) {
|
||||
this.layout[docIndex - 1].pages.push(page)
|
||||
this.collapsedDocumentsIndex[this.layout[docIndex - 1].attachmentUuid] = false
|
||||
} else {
|
||||
this.layout[docIndex].pages.unshift(page)
|
||||
}
|
||||
} else if (targetIndex > this.layout[docIndex].pages.length) {
|
||||
if (docIndex < this.layout.length - 1) {
|
||||
this.layout[docIndex + 1].pages.unshift(page)
|
||||
this.collapsedDocumentsIndex[this.layout[docIndex + 1].attachmentUuid] = false
|
||||
} else {
|
||||
this.layout[docIndex].pages.push(page)
|
||||
}
|
||||
@@ -712,6 +727,12 @@ export default {
|
||||
}
|
||||
|
||||
this.pushUndo()
|
||||
this.scrollIntoList(`[data-page-id="${page.id}"]`)
|
||||
},
|
||||
scrollIntoList (selector) {
|
||||
this.$nextTick(() => {
|
||||
this.$el.querySelector(selector)?.scrollIntoView({ block: 'nearest', behavior: 'smooth' })
|
||||
})
|
||||
},
|
||||
removePage () {
|
||||
const position = this.selectedPosition
|
||||
@@ -737,6 +758,7 @@ export default {
|
||||
this.layout.splice(docIndex + direction, 0, doc)
|
||||
|
||||
this.pushUndo()
|
||||
this.scrollIntoList(`[data-uuid="${doc.attachmentUuid}"]`)
|
||||
},
|
||||
mergeDocument (docIndex, direction) {
|
||||
const doc = this.layout[docIndex]
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
class="relative cursor-pointer"
|
||||
:class="iconColorClass"
|
||||
:title="t('formula')"
|
||||
@click="isShowFormulaModal = true"
|
||||
@click="openFormulaModal"
|
||||
>
|
||||
<IconMathFunction
|
||||
:width="18"
|
||||
@@ -86,7 +86,7 @@
|
||||
class="relative cursor-pointer"
|
||||
:class="iconColorClass"
|
||||
:title="t('condition')"
|
||||
@click="isShowConditionsModal = true"
|
||||
@click="openConditionsModal"
|
||||
>
|
||||
<IconRouteAltLeft
|
||||
:width="18"
|
||||
@@ -97,10 +97,10 @@
|
||||
v-if="field.type === 'payment'"
|
||||
:field="field"
|
||||
:with-custom-fields="withCustomFields"
|
||||
@click-condition="isShowConditionsModal = true"
|
||||
@click-description="isShowDescriptionModal = true"
|
||||
@click-condition="openConditionsModal"
|
||||
@click-description="openDescriptionModal"
|
||||
@add-custom-field="$emit('add-custom-field', $event)"
|
||||
@click-formula="isShowFormulaModal = true"
|
||||
@click-formula="openFormulaModal"
|
||||
@save="$emit('save')"
|
||||
/>
|
||||
<span
|
||||
@@ -150,10 +150,10 @@
|
||||
:with-prefillable="withPrefillable"
|
||||
:background-color="dropdownBgColor"
|
||||
:with-custom-fields="withCustomFields"
|
||||
@click-formula="isShowFormulaModal = true"
|
||||
@click-font="isShowFontModal = true"
|
||||
@click-description="isShowDescriptionModal = true"
|
||||
@click-condition="isShowConditionsModal = true"
|
||||
@click-formula="openFormulaModal"
|
||||
@click-font="openFontModal"
|
||||
@click-description="openDescriptionModal"
|
||||
@click-condition="openConditionsModal"
|
||||
@set-draw="$emit('set-draw', $event)"
|
||||
@add-custom-field="$emit('add-custom-field', $event)"
|
||||
@remove-area="removeArea"
|
||||
@@ -515,6 +515,34 @@ export default {
|
||||
|
||||
this.$emit('save')
|
||||
},
|
||||
openFormulaModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'formula', uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowFormulaModal = true
|
||||
}
|
||||
},
|
||||
openFontModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'font', detent: 'medium', uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowFontModal = true
|
||||
}
|
||||
},
|
||||
openConditionsModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'conditions', detent: 'medium', uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowConditionsModal = true
|
||||
}
|
||||
},
|
||||
openDescriptionModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'description', uuid: this.field.uuid })
|
||||
} else {
|
||||
this.isShowDescriptionModal = true
|
||||
}
|
||||
},
|
||||
buildDefaultName (field) {
|
||||
if (field.type === 'payment' && field.preferences?.price && !field.preferences?.formula) {
|
||||
const { price, currency } = field.preferences || {}
|
||||
|
||||
@@ -164,7 +164,10 @@
|
||||
v-if="!compact && submitters.length > 1 && editable"
|
||||
class="flex"
|
||||
>
|
||||
<div class="flex-col pr-1 flex invisible group-hover:visible -mt-1 h-0">
|
||||
<div
|
||||
class="flex-col pr-1 flex -mt-1 h-0"
|
||||
:class="{ 'invisible group-hover:visible': !isMobile }"
|
||||
>
|
||||
<button
|
||||
:title="t('up')"
|
||||
class="relative w-2"
|
||||
@@ -184,7 +187,8 @@
|
||||
</div>
|
||||
<button
|
||||
v-if="!compact && submitters.length > 1 && editable"
|
||||
class="invisible group-hover:visible px-2"
|
||||
class="px-2"
|
||||
:class="{ 'invisible group-hover:visible': !isMobile }"
|
||||
@click.prevent.stop="remove(submitter)"
|
||||
>
|
||||
<IconTrashX :width="18" />
|
||||
@@ -237,7 +241,7 @@ export default {
|
||||
IconTrashX,
|
||||
IconChevronUp
|
||||
},
|
||||
inject: ['t', 'save'],
|
||||
inject: ['t', 'save', 'isMobile'],
|
||||
props: {
|
||||
submitters: {
|
||||
type: Array,
|
||||
|
||||
@@ -172,6 +172,7 @@
|
||||
@click="setFieldsTab('custom')"
|
||||
>{{ t('custom') }}</a>
|
||||
</div>
|
||||
<div ref="addFieldsMarker" />
|
||||
<div
|
||||
v-if="!isShowVariables && showCustomTab && editable && (customFields.length || newCustomField)"
|
||||
ref="customFields"
|
||||
@@ -390,6 +391,26 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="isMobile"
|
||||
class="sticky bottom-4 h-0 z-[5]"
|
||||
>
|
||||
<Transition
|
||||
enter-active-class="transition duration-100 ease-out"
|
||||
enter-from-class="translate-y-6 opacity-0"
|
||||
leave-active-class="transition duration-100 ease-in"
|
||||
leave-to-class="translate-y-6 opacity-0"
|
||||
>
|
||||
<button
|
||||
v-if="isShowAddFieldsPill"
|
||||
class="absolute bottom-0 left-0 right-0 mx-auto w-fit btn btn-neutral btn-sm rounded-full normal-case font-normal text-white gap-1 px-4 shadow"
|
||||
@click.prevent="scrollToFieldTypes"
|
||||
>
|
||||
<IconPlus class="w-4 h-4" />
|
||||
{{ t('add_fields') }}
|
||||
</button>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -398,13 +419,14 @@ import CustomField from './custom_field'
|
||||
import FieldType from './field_type'
|
||||
import FieldSubmitter from './field_submitter'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { IconLock, IconCirclePlus, IconInnerShadowTop, IconSparkles, IconBracketsContain } from '@tabler/icons-vue'
|
||||
import { IconLock, IconCirclePlus, IconPlus, IconInnerShadowTop, IconSparkles, IconBracketsContain } from '@tabler/icons-vue'
|
||||
import IconDrag from './icon_drag'
|
||||
import { v4 } from 'uuid'
|
||||
|
||||
export default {
|
||||
name: 'TemplateFields',
|
||||
components: {
|
||||
IconPlus,
|
||||
Field,
|
||||
CustomField,
|
||||
FieldType,
|
||||
@@ -417,7 +439,7 @@ export default {
|
||||
IconBracketsContain,
|
||||
DynamicVariables: defineAsyncComponent(() => import(/* webpackChunkName: "dynamic-editor" */ './dynamic_variables'))
|
||||
},
|
||||
inject: ['save', 'backgroundColor', 'withPhone', 'withVerification', 'withKba', 'withPayment', 'nativePlatform', 't', 'fieldsDragFieldRef', 'customDragFieldRef', 'baseFetch', 'selectedAreasRef', 'getFieldTypeIndex'],
|
||||
inject: ['save', 'backgroundColor', 'withPhone', 'withVerification', 'withKba', 'withPayment', 'nativePlatform', 't', 'isMobile', 'fieldsDragFieldRef', 'customDragFieldRef', 'baseFetch', 'selectedAreasRef', 'getFieldTypeIndex'],
|
||||
props: {
|
||||
fields: {
|
||||
type: Array,
|
||||
@@ -544,6 +566,7 @@ export default {
|
||||
emits: ['add-field', 'set-draw', 'set-draw-type', 'set-draw-custom-field', 'set-drag', 'drag-end', 'scroll-to-area', 'change-submitter', 'set-drag-placeholder', 'select-submitter', 'rebuild-variables-schema', 'remove-field', 'remove-submitter'],
|
||||
data () {
|
||||
return {
|
||||
isShowAddFieldsPill: false,
|
||||
fieldPagesLoaded: null,
|
||||
analyzingProgress: 0,
|
||||
newCustomField: null,
|
||||
@@ -627,8 +650,22 @@ export default {
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
if (this.isMobile) {
|
||||
this.fieldTypesObserver = new IntersectionObserver(([entry]) => {
|
||||
this.isShowAddFieldsPill = !entry.isIntersecting
|
||||
})
|
||||
|
||||
this.fieldTypesObserver.observe(this.$refs.addFieldsMarker)
|
||||
}
|
||||
},
|
||||
unmounted () {
|
||||
this.fieldTypesObserver?.disconnect()
|
||||
},
|
||||
methods: {
|
||||
scrollToFieldTypes () {
|
||||
this.$refs.addFieldsMarker.scrollIntoView({ block: 'start', behavior: 'smooth' })
|
||||
},
|
||||
toggleVariables () {
|
||||
this.$emit('rebuild-variables-schema')
|
||||
this.isShowVariables = !this.isShowVariables
|
||||
@@ -697,6 +734,10 @@ export default {
|
||||
const fields = await resp.json()
|
||||
|
||||
this.customFields.splice(0, this.customFields.length, ...fields)
|
||||
|
||||
if (this.nativePlatform) {
|
||||
window.webkit?.messageHandlers?.modal?.postMessage({ action: 'dispatch', name: 'template-builder:sync-custom-fields', detail: JSON.stringify(fields), dismiss: 'false' })
|
||||
}
|
||||
})
|
||||
},
|
||||
buildExistingFields () {
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<template>
|
||||
<div
|
||||
class="modal modal-open items-start !animate-none overflow-y-auto"
|
||||
:class="{ 'modal modal-open items-start !animate-none overflow-y-auto': !inline }"
|
||||
>
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="absolute top-0 bottom-0 right-0 left-0"
|
||||
@click.prevent="$emit('close')"
|
||||
/>
|
||||
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
||||
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||
<div :class="inline ? 'px-4 pt-3 pb-6' : 'modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl'">
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="flex justify-between items-center border-b pb-2 mb-2 font-medium"
|
||||
>
|
||||
<span class="modal-title">
|
||||
{{ t('font') }} - {{ (defaultField ? (defaultField.title || field.title || field.name) : field.name) || buildDefaultName(field) }}
|
||||
</span>
|
||||
@@ -19,7 +23,7 @@
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div>
|
||||
<div class="flex items-center space-x-1.5">
|
||||
<div class="flex flex-wrap items-center gap-1.5">
|
||||
<span>
|
||||
<div class="dropdown modal-field-font-dropdown">
|
||||
<label
|
||||
@@ -168,7 +172,7 @@
|
||||
:class="textClasses"
|
||||
>
|
||||
<span
|
||||
contenteditable="true"
|
||||
:contenteditable="!inline"
|
||||
class="outline-none whitespace-nowrap truncate"
|
||||
>
|
||||
{{ field.default_value || field.name || buildDefaultName(field) }}
|
||||
@@ -215,6 +219,11 @@ export default {
|
||||
buildDefaultName: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['close', 'save'],
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<template>
|
||||
<div
|
||||
class="modal modal-open items-start !animate-none overflow-y-auto"
|
||||
:class="{ 'modal modal-open items-start !animate-none overflow-y-auto': !inline }"
|
||||
>
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="absolute top-0 bottom-0 right-0 left-0"
|
||||
@click.prevent="$emit('close')"
|
||||
/>
|
||||
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
||||
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||
<div :class="inline ? 'px-4 pt-3 pb-6' : 'modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl'">
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="flex justify-between items-center border-b pb-2 mb-2 font-medium"
|
||||
>
|
||||
<span class="modal-title">
|
||||
{{ t('formula') }} - {{ (defaultField ? (defaultField.title || field.title || field.name) : field.name) || buildDefaultName(field) }}
|
||||
</span>
|
||||
@@ -153,6 +157,11 @@ export default {
|
||||
buildDefaultName: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['close', 'save'],
|
||||
|
||||
@@ -200,6 +200,7 @@ const en = {
|
||||
replace: 'Replace',
|
||||
uploading_: 'Uploading...',
|
||||
add_document: 'Add Document',
|
||||
add_fields: 'Add Fields',
|
||||
none: 'None',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
@@ -443,6 +444,7 @@ const es = {
|
||||
replace: 'Reemplazar',
|
||||
uploading_: 'Subiendo...',
|
||||
add_document: 'Subir',
|
||||
add_fields: 'Agregar campos',
|
||||
any: 'Cualquier',
|
||||
drawn: 'Dibujado',
|
||||
drawn_or_typed: 'Dibujado o Escrito',
|
||||
@@ -698,6 +700,7 @@ const it = {
|
||||
replace: 'Sostituisci',
|
||||
uploading_: 'Caricamento in corso...',
|
||||
add_document: 'Aggiungi',
|
||||
add_fields: 'Aggiungi campi',
|
||||
none: 'Nessuno',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
@@ -941,6 +944,7 @@ const pt = {
|
||||
replace: 'Substituir',
|
||||
uploading_: 'Carregando...',
|
||||
add_document: 'Enviar',
|
||||
add_fields: 'Adicionar campos',
|
||||
any: 'Qualquer',
|
||||
drawn: 'Desenhado',
|
||||
drawn_or_typed: 'Desenhado ou Digitado',
|
||||
@@ -1196,6 +1200,7 @@ const fr = {
|
||||
replace: 'Remplacer',
|
||||
uploading_: 'Téléversement...',
|
||||
add_document: 'Téléverser',
|
||||
add_fields: 'Ajouter des champs',
|
||||
none: 'Aucun',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
@@ -1445,6 +1450,7 @@ const de = {
|
||||
replace: 'Ersetzen',
|
||||
uploading_: 'Hochladen...',
|
||||
add_document: 'Hochladen',
|
||||
add_fields: 'Felder hinzufügen',
|
||||
none: 'Keine',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
@@ -1694,6 +1700,7 @@ const nl = {
|
||||
replace: 'Vervangen',
|
||||
uploading_: 'Bezig met uploaden...',
|
||||
add_document: 'Doc. toevoegen',
|
||||
add_fields: 'Velden toevoegen',
|
||||
none: 'Geen',
|
||||
ssn: 'SSN',
|
||||
ein: 'EIN',
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<FormulaModal
|
||||
v-if="modal?.name === 'formula' && field"
|
||||
:field="field"
|
||||
:editable="editable && !defaultField"
|
||||
:default-field="defaultField"
|
||||
:build-default-name="buildDefaultName"
|
||||
:inline="true"
|
||||
@save="onSave"
|
||||
@close="close"
|
||||
/>
|
||||
<FontModal
|
||||
v-if="modal?.name === 'font' && field"
|
||||
:field="field"
|
||||
:editable="editable && !defaultField"
|
||||
:default-field="defaultField"
|
||||
:build-default-name="buildDefaultName"
|
||||
:inline="true"
|
||||
@save="onSave"
|
||||
@close="close"
|
||||
/>
|
||||
<ConditionsModal
|
||||
v-if="modal?.name === 'conditions' && item"
|
||||
:item="item"
|
||||
:default-field="defaultField"
|
||||
:build-default-name="buildDefaultName"
|
||||
:inline="true"
|
||||
@save="onSave"
|
||||
@close="close"
|
||||
/>
|
||||
<DescriptionModal
|
||||
v-if="modal?.name === 'description' && field"
|
||||
:field="field"
|
||||
:editable="editable && !defaultField"
|
||||
:default-field="defaultField"
|
||||
:build-default-name="buildDefaultName"
|
||||
:inline="true"
|
||||
@save="onSave"
|
||||
@close="close"
|
||||
/>
|
||||
<div
|
||||
v-if="modal?.name === 'documents'"
|
||||
class="h-screen flex flex-col"
|
||||
>
|
||||
<DocumentsEditorModal
|
||||
:template="template"
|
||||
:authenticity-token="authenticityToken"
|
||||
:accept-file-types="acceptFileTypes"
|
||||
:base-url="baseUrl"
|
||||
:page-preview-format="pagePreviewFormat"
|
||||
:scroll-to-attachment-uuid="modal.uuid"
|
||||
:inline="true"
|
||||
@saved="onDocumentsSaved"
|
||||
@close="close"
|
||||
/>
|
||||
</div>
|
||||
<RevisionsModal
|
||||
v-if="modal?.name === 'revisions' && revisions"
|
||||
:template="template"
|
||||
:revisions="revisions"
|
||||
:locale="locale"
|
||||
:inline="true"
|
||||
@close="close"
|
||||
@apply="applyRevision"
|
||||
/>
|
||||
<div
|
||||
v-else-if="modal?.name === 'revisions'"
|
||||
class="flex justify-center py-8"
|
||||
>
|
||||
<IconInnerShadowTop class="w-6 h-6 animate-spin" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormulaModal from './formula_modal'
|
||||
import FontModal from './font_modal'
|
||||
import ConditionsModal from './conditions_modal'
|
||||
import DescriptionModal from './description_modal'
|
||||
import DocumentsEditorModal from './documents_editor_modal'
|
||||
import RevisionsModal from './revisions_modal'
|
||||
import Field from './field'
|
||||
import FieldType from './field_type'
|
||||
import { IconInnerShadowTop } from '@tabler/icons-vue'
|
||||
|
||||
export default {
|
||||
name: 'NativeModals',
|
||||
components: {
|
||||
FormulaModal,
|
||||
FontModal,
|
||||
ConditionsModal,
|
||||
DescriptionModal,
|
||||
DocumentsEditorModal,
|
||||
RevisionsModal,
|
||||
IconInnerShadowTop
|
||||
},
|
||||
inject: ['t', 'template', 'locale', 'baseFetch', 'getFieldTypeIndex', 'save'],
|
||||
props: {
|
||||
defaultFields: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => []
|
||||
},
|
||||
customFields: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => []
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
authenticityToken: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
acceptFileTypes: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'image/*, application/pdf'
|
||||
},
|
||||
baseUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
pagePreviewFormat: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '.jpg'
|
||||
}
|
||||
},
|
||||
emits: ['documents-modified'],
|
||||
data () {
|
||||
return {
|
||||
modal: null,
|
||||
revisions: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fieldNames: FieldType.computed.fieldNames,
|
||||
fieldLabels: FieldType.computed.fieldLabels,
|
||||
field () {
|
||||
if (this.modal?.custom_field_uuid) {
|
||||
return this.customFields.find((f) => f.uuid === this.modal.custom_field_uuid)
|
||||
}
|
||||
|
||||
return this.template.fields.find((f) => f.uuid === this.modal?.uuid)
|
||||
},
|
||||
item () {
|
||||
if (this.modal?.attachment_uuid) {
|
||||
return this.template.schema.find((item) => item.attachment_uuid === this.modal.attachment_uuid)
|
||||
}
|
||||
|
||||
return this.field
|
||||
},
|
||||
defaultField () {
|
||||
if (this.modal?.custom_field_uuid) return null
|
||||
|
||||
return this.field && this.defaultFields.find((f) => f.name === this.field.name)
|
||||
},
|
||||
title () {
|
||||
if (!this.modal) return ''
|
||||
if (this.modal.name === 'documents') return this.t('edit_documents')
|
||||
if (this.modal.name === 'revisions') return this.t('revisions')
|
||||
|
||||
const item = this.item
|
||||
const itemName = item ? ((this.defaultField ? (this.defaultField.title || item.title || item.name) : item.name) || this.buildDefaultName(item)) : ''
|
||||
|
||||
if (this.modal.name === 'description') return itemName
|
||||
|
||||
const prefix = { formula: this.t('formula'), font: this.t('font'), conditions: this.t('condition') }[this.modal.name]
|
||||
|
||||
return itemName ? `${prefix} - ${itemName}` : prefix
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
document.title = ''
|
||||
document.addEventListener('template-builder:open-modal', this.onOpen)
|
||||
document.addEventListener('template-builder:close-modal', this.onClose)
|
||||
},
|
||||
unmounted () {
|
||||
document.removeEventListener('template-builder:open-modal', this.onOpen)
|
||||
document.removeEventListener('template-builder:close-modal', this.onClose)
|
||||
},
|
||||
methods: {
|
||||
buildDefaultName: Field.methods.buildDefaultName,
|
||||
onOpen (e) {
|
||||
this.modal = e.detail
|
||||
|
||||
document.title = this.title
|
||||
document.body.style.overflow = e.detail.name === 'documents' ? 'hidden' : ''
|
||||
|
||||
if (e.detail.name === 'revisions') {
|
||||
this.loadRevisions()
|
||||
}
|
||||
},
|
||||
onClose () {
|
||||
this.modal = null
|
||||
this.revisions = null
|
||||
|
||||
document.body.style.overflow = ''
|
||||
},
|
||||
close () {
|
||||
window.webkit?.messageHandlers?.modal?.postMessage({ action: 'closeSheet' })
|
||||
},
|
||||
onSave () {
|
||||
if (this.modal?.custom_field_uuid) {
|
||||
this.saveCustomFields()
|
||||
} else {
|
||||
this.save()
|
||||
}
|
||||
},
|
||||
saveCustomFields () {
|
||||
return this.baseFetch('/account_custom_fields', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ value: this.customFields }),
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}).then(async (resp) => {
|
||||
const fields = await resp.json()
|
||||
|
||||
this.customFields.splice(0, this.customFields.length, ...fields)
|
||||
|
||||
window.webkit?.messageHandlers?.modal?.postMessage({ action: 'dispatch', name: 'template-builder:sync-custom-fields', detail: JSON.stringify(fields), dismiss: 'false' })
|
||||
})
|
||||
},
|
||||
loadRevisions () {
|
||||
this.revisions = null
|
||||
|
||||
this.baseFetch(`/templates/${this.template.id}/versions`).then(async (resp) => {
|
||||
this.revisions = await resp.json()
|
||||
})
|
||||
},
|
||||
applyRevision (revision) {
|
||||
window.webkit?.messageHandlers?.modal?.postMessage({ action: 'dispatch', name: 'template-builder:apply-revision', detail: JSON.stringify(revision), dismiss: 'true' })
|
||||
},
|
||||
onDocumentsSaved (data) {
|
||||
this.$emit('documents-modified', data)
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="relative select-none mb-4 before:border before:rounded before:top-0 before:bottom-0 before:left-0 before:right-0 before:absolute"
|
||||
:class="{ 'cursor-crosshair': allowDraw && editable && !isSelectMode, 'touch-none': !!drawField }"
|
||||
:class="{ 'cursor-crosshair': allowDraw && editable && !isSelectMode, 'touch-none': !!(drawField || drawCustomField) }"
|
||||
style="container-type: size"
|
||||
:style="{ aspectRatio: `${width} / ${height}`}"
|
||||
>
|
||||
@@ -115,11 +115,11 @@
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-show="resizeDirection || isDrag || showMask || (drawField && isMobile) || fieldsDragFieldRef.value || customDragFieldRef?.value || selectionRect"
|
||||
v-show="resizeDirection || isDrag || showMask || ((drawField || drawCustomField) && isMobile) || fieldsDragFieldRef.value || customDragFieldRef?.value || selectionRect"
|
||||
id="mask"
|
||||
ref="mask"
|
||||
class="top-0 bottom-0 left-0 right-0 absolute"
|
||||
:class="{ 'z-10': !isMobile, 'cursor-grab': isDrag, 'cursor-nwse-resize': drawField && !isSelectMode, [resizeDirectionClasses[resizeDirection]]: !!resizeDirectionClasses }"
|
||||
:class="{ 'z-10': !isMobile, 'cursor-grab': isDrag, 'cursor-nwse-resize': (drawField || drawCustomField) && !isSelectMode, [resizeDirectionClasses[resizeDirection]]: !!resizeDirectionClasses }"
|
||||
@pointermove="onPointermove"
|
||||
@pointerdown="onStartDraw"
|
||||
@contextmenu="openContextMenu"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<button
|
||||
v-if="item.conditions?.length"
|
||||
class="btn border-gray-300 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors p-0 document-control-button"
|
||||
@click.stop="isShowConditionsModal = true"
|
||||
@click.stop="openConditionsModal"
|
||||
>
|
||||
<IconRouteAltLeft
|
||||
:width="14"
|
||||
@@ -84,7 +84,7 @@
|
||||
<li>
|
||||
<button
|
||||
class="w-full px-2 py-1 rounded-md hover:bg-neutral-100 flex items-center justify-between text-sm"
|
||||
@click.stop="isShowConditionsModal = true; closeDropdown()"
|
||||
@click.stop="openConditionsModal(); closeDropdown()"
|
||||
>
|
||||
<span class="flex items-center space-x-2">
|
||||
<IconRouteAltLeft class="w-4 h-4" />
|
||||
@@ -295,6 +295,13 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openConditionsModal () {
|
||||
if (window.webkit?.messageHandlers?.modal) {
|
||||
window.webkit.messageHandlers.modal.postMessage({ action: 'sheet', name: 'conditions', detent: 'medium', attachment_uuid: this.item.attachment_uuid })
|
||||
} else {
|
||||
this.isShowConditionsModal = true
|
||||
}
|
||||
},
|
||||
upload: Upload.methods.upload,
|
||||
buildDefaultName: Field.methods.buildDefaultName,
|
||||
closeDropdown () {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
class="absolute top-0 bottom-0 right-0 left-0"
|
||||
@click.prevent="$emit('close')"
|
||||
/>
|
||||
<div :class="{ 'modal-box pt-4 pb-6 mt-20 w-full': !inline }">
|
||||
<div :class="inline ? 'px-4 pt-3 pb-6' : 'modal-box pt-4 pb-6 mt-20 w-full'">
|
||||
<div
|
||||
v-if="!inline"
|
||||
class="flex justify-between items-center border-b pb-2 mb-3 font-medium"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="bg-base-200 rounded-2xl flex flex-col sm:flex-row" data-native-title="<%= submission.name.presence || (local_assigns[:template] || submission.template)&.name if turbo_native_app? %>">
|
||||
<% if local_assigns[:with_template] %>
|
||||
<% template = submission.template %>
|
||||
<a href="<%= template ? template_path(template) : submission_path(submission) %>" class="px-5 sm:pr-3 py-3 group sm:rounded-l-2xl sm:rounded-tr-none rounded-t-2xl flex sm:flex-col justify-between sm:w-52 w-full flex-shrink-0 bg-base-300/60 space-x-2 sm:space-x-0">
|
||||
<a href="<%= template ? template_path(template) : submission_path(submission) %>" data-native-title="<%= template&.name %>" class="px-5 sm:pr-3 py-3 group sm:rounded-l-2xl sm:rounded-tr-none rounded-t-2xl flex sm:flex-col justify-between sm:w-52 w-full flex-shrink-0 bg-base-300/60 space-x-2 sm:space-x-0">
|
||||
<div>
|
||||
<div class="font-medium items-start w-full group-hover:link text-sm flex space-x-1">
|
||||
<%= svg_icon('file_text', class: 'w-4 h-4 mt-0.5 flex-shrink-0') %>
|
||||
|
||||
@@ -115,6 +115,11 @@
|
||||
<a href="<%= new_template_submissions_export_path(template, params.permit(:q, *Submissions::Filter::ALLOWED_PARAMS)) %>" data-turbo-frame="modal"></a>
|
||||
</native-action>
|
||||
<% end %>
|
||||
<% if !template.archived_at? && can?(:update, template) && @template.account_id == current_account.id %>
|
||||
<native-action data-section="edit" data-icon="folder" data-label="<%= t('move') %>">
|
||||
<a href="<%= edit_template_folder_path(template.id, subfolder: @template.folder.parent_folder.present?) %>" data-turbo-frame="modal"></a>
|
||||
</native-action>
|
||||
<% end %>
|
||||
<% if can?(:destroy, template) %>
|
||||
<% if template.archived_at? %>
|
||||
<native-action data-section="edit" data-icon="rotate" data-label="<%= t('restore') %>">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<% content_for(:native_title, params[:view] == 'revisions' ? t('revisions') : @template.name) if turbo_native_app? %>
|
||||
<% content_for(:native_title, @template.name) if turbo_native_app? %>
|
||||
<% if !turbo_native_app? && (current_user.created_at > 2.weeks.ago || params[:tour] == 'true') %>
|
||||
<% user_config = current_user.user_configs.find_or_initialize_by(key: UserConfig::SHOW_APP_TOUR) %>
|
||||
<% @show_tour_start_form = user_config.new_record? && !params.key?(:tour) %>
|
||||
@@ -10,4 +10,4 @@
|
||||
<% account_configs = current_account.account_configs.where(key: [AccountConfig::TEMPLATE_CUSTOM_FIELDS_KEY, AccountConfig::TEMPLATE_DATE_FORMATS_KEY]).to_a %>
|
||||
<% custom_fields = account_configs.find { |e| e.key == AccountConfig::TEMPLATE_CUSTOM_FIELDS_KEY }&.value || [] %>
|
||||
<% date_formats = account_configs.find { |e| e.key == AccountConfig::TEMPLATE_DATE_FORMATS_KEY }&.value || [] %>
|
||||
<template-builder data-view="<%= params[:view] %>" data-native-platform="<%= turbo_native_ios? ? 'ios' : (turbo_native_android? ? 'android' : '') %>" class="grid" data-template="<%= @template_data.to_json %>" data-custom-fields="<%= custom_fields.to_json %>" data-date-formats="<%= date_formats.to_json %>" data-with-sign-yourself-button="<%= !@template.archived_at? %>" data-with-fields-detection="true" data-with-send-button="<%= !@template.archived_at? && can?(:create, @template.submissions.new(account: current_account)) %>" data-with-revisions-menu="<%= @template.template_versions.exists? %>" data-locale="<%= I18n.locale %>" data-show-tour-start-form="<%= @show_tour_start_form %>"></template-builder>
|
||||
<template-builder data-native-platform="<%= turbo_native_ios? ? 'ios' : (turbo_native_android? ? 'android' : '') %>" class="grid" data-template="<%= @template_data.to_json %>" data-custom-fields="<%= custom_fields.to_json %>" data-date-formats="<%= date_formats.to_json %>" data-with-sign-yourself-button="<%= !@template.archived_at? %>" data-with-fields-detection="true" data-with-send-button="<%= !@template.archived_at? && can?(:create, @template.submissions.new(account: current_account)) %>" data-with-revisions-menu="<%= @template.template_versions.exists? %>" data-locale="<%= I18n.locale %>" data-show-tour-start-form="<%= @show_tour_start_form %>"></template-builder>
|
||||
Reference in new issue
Block a user