+ <%= t('text_view_disclaimer') %> +
+ <% schema.each do |item| %> + <% doc = @submission.schema_documents.find { |a| a.uuid == item['attachment_uuid'] } %> + <% (doc.blob.metadata.dig('pdf', 'pages_text') || {}).each do |page_index, page_text| %> +diff --git a/app/javascript/application.js b/app/javascript/application.js index 49cd9516..bbe5e69f 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -53,6 +53,7 @@ import GoogleDriveFilePicker from './elements/google_drive_file_picker' import OpenModal from './elements/open_modal' import BarChart from './elements/bar_chart' import FieldCondition from './elements/field_condition' +import DocumentTabs from './elements/document_tabs' import * as TurboInstantClick from './lib/turbo_instant_click' @@ -144,6 +145,7 @@ safeRegisterElement('google-drive-file-picker', GoogleDriveFilePicker) safeRegisterElement('open-modal', OpenModal) safeRegisterElement('bar-chart', BarChart) safeRegisterElement('field-condition', FieldCondition) +safeRegisterElement('document-tabs', DocumentTabs) safeRegisterElement('template-builder', class extends HTMLElement { connectedCallback () { diff --git a/app/javascript/elements/document_tabs.js b/app/javascript/elements/document_tabs.js new file mode 100644 index 00000000..f7aa6cca --- /dev/null +++ b/app/javascript/elements/document_tabs.js @@ -0,0 +1,45 @@ +export default class extends HTMLElement { + connectedCallback () { + this._tabs = Array.from(this.querySelectorAll('[role="tab"]')) + this._panels = Array.from(this.querySelectorAll('[role="tabpanel"]')) + + this._tabs.forEach((tab) => { + tab.addEventListener('click', () => this._selectTab(tab)) + tab.addEventListener('keydown', (e) => this._onKeydown(e)) + }) + + const saved = localStorage.getItem('docuseal_document_view') + const savedTab = saved && this._tabs.find((t) => t.id === saved) + this._selectTab(savedTab || this._tabs[0], false) + } + + _selectTab (selectedTab, save = true) { + this._tabs.forEach((tab) => { + const isSelected = tab === selectedTab + tab.setAttribute('aria-selected', isSelected ? 'true' : 'false') + tab.setAttribute('tabindex', isSelected ? '0' : '-1') + tab.classList.toggle('border-primary', isSelected) + tab.classList.toggle('text-primary', isSelected) + tab.classList.toggle('border-transparent', !isSelected) + tab.classList.toggle('text-base-content/60', !isSelected) + }) + this._panels.forEach((panel) => { + panel.hidden = panel.id !== selectedTab.getAttribute('aria-controls') + }) + if (save) localStorage.setItem('docuseal_document_view', selectedTab.id) + } + + _onKeydown (e) { + const tabs = this._tabs + const idx = tabs.indexOf(e.currentTarget) + let next + if (e.key === 'ArrowRight') next = tabs[(idx + 1) % tabs.length] + else if (e.key === 'ArrowLeft') next = tabs[(idx - 1 + tabs.length) % tabs.length] + else if (e.key === 'Home') next = tabs[0] + else if (e.key === 'End') next = tabs[tabs.length - 1] + else return + e.preventDefault() + this._selectTab(next) + next.focus() + } +} diff --git a/app/javascript/template_builder/page.vue b/app/javascript/template_builder/page.vue index 377618e7..c732f6cc 100644 --- a/app/javascript/template_builder/page.vue +++ b/app/javascript/template_builder/page.vue @@ -18,9 +18,10 @@
+ <%= t('text_view_disclaimer') %> +
+ <% schema.each do |item| %> + <% doc = @submission.schema_documents.find { |a| a.uuid == item['attachment_uuid'] } %> + <% (doc.blob.metadata.dig('pdf', 'pages_text') || {}).each do |page_index, page_text| %> ++ <%= t('text_view_disclaimer') %> <%= t('signing_fields_below') %> +
+#{ERB::Util.html_escape(stripped)}
) + nil + end + end + + def numbered_heading?(line) + line.length <= 80 && line.match?(/\A\d+\.\s+[A-Z]/) && !line.match?(/[.!?,;]\z/) + end + + def all_caps_heading?(line) + line.length >= 3 && !line.match?(/[.!?,;]\z/) && + line == line.upcase && line.match?(/[A-Z]/) + end + + def close_list(current_list) + case current_list + when :ol then '' + when :ul then '