Skip to content

Commit

Permalink
♻️ rework slot exchange between django/vue
Browse files Browse the repository at this point in the history
  • Loading branch information
krmax44 committed Oct 9, 2023
1 parent 9d04c0b commit 1116df5
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 42 deletions.
8 changes: 4 additions & 4 deletions froide/foirequest/templates/foirequest/attachment/manage.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@ <h4>
<span class="visually-hidden">{% trans "Loading..." %}</span>
</div>
</div>
<template v-slot:convert-images>
<template data-slot="convert-images">
<h3>{% translate "Convert images to documents" %}</h3>
</template>
<template v-slot:documents>
<template data-slot="documents">
<h3>{% translate "Documents" %}</h3>
<p>
{% translate "Here you can approve, redact documents and also mark them as a result of your request." %}
</p>
</template>
<template v-slot:upload-header>
<template data-slot="upload-header">
<h3>{% translate "Upload" %}</h3>
<p>{% translate "Upload PDFs or pictures of documents." %}</p>
{% with extra_content_types=message.get_extra_content_types %}
Expand All @@ -86,7 +86,7 @@ <h3>{% translate "Upload" %}</h3>
{% endif %}
{% endwith %}
</template>
<template v-slot:other-files>
<template data-slot="other-files">
<h4>{% translate "Other files" %}</h4>
</template>
</document-uploader>
Expand Down
12 changes: 6 additions & 6 deletions froide/foirequest/templates/foirequest/request.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,23 @@ <h4>{{ campaign.name }}</h4>
:config="{{ js_config }}"
>

<template v-slot:messages>
<template data-slot="messages">
{% include "snippets/messages.html" %}
</template>

<template v-slot:publicbody-legend-title>
<template data-slot="publicbody-legend-title">
<legend>
{% blocktrans %}Choose a public body{% endblocktrans %}
</legend>
</template>

<template v-slot:publicbody-help-text>
<template data-slot="publicbody-help-text">
<p>
{% blocktrans %}Please type <strong>the name of the public body</strong> you'd like to request information from into the search box.{% endblocktrans %}
</p>
</template>

<template v-slot:publicbody-missing>
<template data-slot="publicbody-missing">
<h6>{% trans "Can't find what you're looking for?" %}</h6>
<p>
{% url 'publicbody-list' as pb_search_url %}
Expand All @@ -105,13 +105,13 @@ <h6>{% trans "Can't find what you're looking for?" %}</h6>
</p>
</template>

<template v-slot:request-legend-title>
<template data-slot="request-legend-title">
<legend>
{% blocktrans %}Write the request{% endblocktrans %}
</legend>
</template>

<template v-slot:request-hints>
<template data-slot="request-hints">
<div class="request-hints">
<h6>
{% blocktrans %}Important Notes:{% endblocktrans %}
Expand Down
10 changes: 6 additions & 4 deletions frontend/javascript/components/docupload/document-uploader.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="document-uploader mb-3 mt-3">
<div v-if="imageDocuments.length > 0" class="images mt-5">
<div v-html="slots['convert-images']" />
<django-slot name="convert-images" />
<image-document
v-for="doc in imageDocuments"
:key="doc.id"
Expand All @@ -16,7 +16,7 @@
@notnew="doc.new = false" />
</div>
<div v-if="pdfDocuments.length > 0" class="documents mt-5">
<div v-html="slots['documents']" />
<django-slot name="documents" />
<div class="mt-3 mb-3">
<div class="row bg-body-secondary pb-2 pt-2 mb-2 border-bottom">
<div class="col-auto me-md-auto">
Expand Down Expand Up @@ -64,7 +64,7 @@

<div v-if="otherAttachments.length > 0" class="mt-5">
<hr />
<div v-html="slots['other-files']" />
<django-slot name="other-files" />
<file-document
v-for="doc in otherAttachments"
:key="doc.id"
Expand All @@ -76,7 +76,7 @@
</div>

<div v-if="canUpload" class="upload mt-5">
<div v-html="slots['upload-header']" />
<django-slot name="upload-header" />
<file-uploader
class="mb-3 mt-2"
:config="config"
Expand All @@ -90,6 +90,7 @@
<script>
import I18nMixin from '../../lib/i18n-mixin'
import { postData } from '../../lib/api.js'
import DjangoSlot from '../../lib/django-slot.vue'
import { approveAttachment, createDocument } from './lib/document_utils'
import ImageDocument from './image-document.vue'
Expand All @@ -99,6 +100,7 @@ import FileUploader from '../upload/file-uploader.vue'
export default {
name: 'DocumentUploader',
components: {
DjangoSlot,
ImageDocument,
FileDocument,
FileUploader
Expand Down
18 changes: 10 additions & 8 deletions frontend/javascript/components/makerequest/request-page.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
:hide-publicbody-chooser="hidePublicbodyChooser" />

<div :class="{ container: !multiRequest, 'container-multi': multiRequest }">
<div v-html="slots['messages']" />
<django-slot name="messages" />

<div class="row justify-content-lg-center">
<div class="col-lg-12">
Expand All @@ -22,15 +22,15 @@
:scope="pbScope"
:config="config">
<template #publicbody-missing>
<div v-html="slots['publicbody-missing']" />
<django-slot name="publicbody-missing" />
</template>
</publicbody-multi-chooser>
</div>
<div v-else>
<div class="row">
<div class="col-lg-7">
<div v-html="slots['publicbody-legend-title']" />
<div v-html="slots['publicbody-help-text']" />
<django-slot name="publicbody-legend-title" />
<django-slot name="publicbody-help-text" />
</div>
</div>
<div class="row">
Expand All @@ -54,7 +54,7 @@
</template>
</div>
<div class="col-lg-4 small">
<div v-html="slots['publicbody-missing']" />
<django-slot name="publicbody-missing" />
</div>
</div>
</div>
Expand Down Expand Up @@ -90,10 +90,10 @@
:submitting="submitting"
@setStepSelectPublicBody="setStepSelectPublicBody">
<template #request-hints>
<div v-html="slots['request-hints']" />
<django-slot name="request-hints" />
</template>
<template #request-legend-title>
<div v-html="slots['request-legend-title']" />
<django-slot name="request-legend-title" />
</template>
</request-form>

Expand Down Expand Up @@ -175,6 +175,7 @@ import RequestForm from './request-form'
import RequestFormBreadcrumbs from './request-form-breadcrumbs'
import RequestPublic from './request-public'
import UserTerms from './user-terms'
import DjangoSlot from '../../lib/django-slot.vue'
import { Modal } from 'bootstrap'
Expand Down Expand Up @@ -215,7 +216,8 @@ export default {
RequestForm,
RequestFormBreadcrumbs,
RequestPublic,
UserTerms
UserTerms,
DjangoSlot
},
mixins: [I18nMixin, LetterMixin],
props: {
Expand Down
23 changes: 23 additions & 0 deletions frontend/javascript/lib/django-slot.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts" setup>
import { onMounted, ref, inject } from 'vue'
import type { DjangoSlots } from './vue-helper'
const props = defineProps<{
name: string
}>()
const container = ref<HTMLDivElement | undefined>()
onMounted(() => {
const djangoSlots: DjangoSlots = inject('django-slots')
const fragment: DocumentFragment | undefined = djangoSlots?.[props.name]
if (fragment !== undefined) {
container.value?.replaceWith(fragment.cloneNode(true))
}
})
</script>

<template>
<div ref="container" />
</template>
39 changes: 19 additions & 20 deletions frontend/javascript/lib/vue-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { VueElement, createApp } from 'vue'

type PropValue = string | boolean | Record<string, unknown> | Array<unknown>
type Props = Record<string, PropValue>
type Slots = Record<string, string>
export type DjangoSlots = Record<string, DocumentFragment> | undefined
interface OtherAttrs {
attrs: Record<string, string>
class: Record<string, boolean>
Expand Down Expand Up @@ -49,26 +49,22 @@ function getPropsFromElement(el: HTMLElement): Props {
return props
}

function getSlotData(el: HTMLElement): Slots {
function getSlotData(el: HTMLElement): DjangoSlots {
const slotEls = el.querySelectorAll('template')
const slots: Slots = {}
for (let i = 0; i < slotEls.length; i += 1) {
const slotEl = slotEls[i]
const attrs = slotEl.attributes
let slotName = null
for (let i = 0; i < attrs.length; i++) {
if (attrs[i].name.startsWith('v-slot:')) {
slotName = attrs[i].name.replace('v-slot:', '')
break
}
}
if (slotName === null) {
continue
const slots: DjangoSlots = {}

for (const slotEl of slotEls) {
const slotName = slotEl.dataset.slot

if (slotName) {
slots[slotName] = Object.freeze(
(slotEl.cloneNode(true) as HTMLTemplateElement).content
)
}
slots[slotName] = slotEl.innerHTML
}
return slots
}

function getOtherAttrs(el: HTMLElement): OtherAttrs {
const other: OtherAttrs = {
attrs: {},
Expand All @@ -93,8 +89,7 @@ function createAppWithProps(el: string | HTMLElement, component: VueElement) {
Fake VueJS compiler which does only the following:
- takes class and id attributes and sets them on new element
- takes other attributes of element and makes them to props object
- takes containing slot content elements and
adds them as *static* slots with their innerhtml
- takes containing slot content elements, which are accessible using <django-slot>
*/
let checkedEl: HTMLElement | null = null
if (typeof el === 'string') {
Expand All @@ -107,13 +102,17 @@ function createAppWithProps(el: string | HTMLElement, component: VueElement) {
}
const props = getPropsFromElement(checkedEl)
const slotData = getSlotData(checkedEl)

const otherAttrs = getOtherAttrs(checkedEl)

return createApp(component, {
slots: slotData,
const app = createApp(component, {
...props,
...otherAttrs
})

app.provide('django-slots', slotData)

return app
}

export { createAppWithProps }

0 comments on commit 1116df5

Please sign in to comment.