Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
/public/packs
/public/packs-test

# Vue i18n messages generated at boot (cf. config/initializers/vue_i18n.rb)
/public/vue/*.json

# Ignore some JS stuff
/node_modules
/yarn-error.log
Expand Down
7 changes: 0 additions & 7 deletions app/controllers/admin/vue_i18n_controller.rb

This file was deleted.

19 changes: 7 additions & 12 deletions app/javascript/apps/blocks-editor/BlocksEditorApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ export default {
loading: true,
csrfToken: '',
url: {
i18n: '',
new: '',
reorder: '',
data: '',
},
i18n: {},
data: {},
planMode: false,
offcanvasState: 'closed', // closed, picking, editing
Expand All @@ -26,19 +24,17 @@ export default {
beforeMount() {
this.csrfToken = document.querySelector('[name="csrf-token"]').content;
const dataset = document.getElementById('blocks-editor-app').dataset;
this.url.i18n = dataset.i18nUrl;
this.url.data = dataset.dataUrl;
this.url.new = dataset.newUrl;
this.url.reorder = dataset.reorderUrl;
this.loadJson(this.url.i18n, 'i18n');
this.refresh();
},
methods: {
async loadJson(url, target) {
const res = await fetch(url, { headers: { Accept: 'application/json' } });
if (!res.ok) return;
this[target] = await res.json();
if (this.i18n.blocksEditor && this.data.blocks) {
if (this.data.blocks) {
this.loading = false;
}
},
Expand Down Expand Up @@ -77,7 +73,7 @@ export default {
notyf.open({
type: 'success',
position: { x: 'left', y: 'bottom' },
message: this.i18n.blocksEditor.confirm.copy,
message: this.$t('blocksEditor.confirm.copy'),
duration: 9000,
ripple: true,
dismissible: true,
Expand Down Expand Up @@ -137,19 +133,18 @@ export default {
<a
class="btn btn-lg btn-dark me-2 mb-2"
@click="onAdd">
{{ i18n.blocksEditor.actions.addBlock }}</a>
{{ $t('blocksEditor.actions.addBlock') }}</a>
<a
class="btn btn-sm float-md-end"
:class="{'btn-success': planMode, 'btn-light': !planMode}"
@click="planMode = !planMode"
v-show="data.blocks.length > 2">
<i class="fas fa-list"></i>
{{ i18n.blocksEditor.planMode.button }}</a>
{{ $t('blocksEditor.planMode.button') }}</a>
</div>
</div>
<Blocks
v-model="data.blocks"
:i18n="i18n"
@edit="onEdit"
@delete="onDelete"
@copy="onCopy"
Expand All @@ -162,13 +157,13 @@ export default {
<a
class="btn btn-lg btn-dark"
@click="onAdd">
{{ i18n.blocksEditor.actions.addBlock }}</a>
{{ $t('blocksEditor.actions.addBlock') }}</a>
</div>
</div>
<OffcanvasShell
:open="offcanvasState !== 'closed'"
:title="i18n.blocksEditor.offcanvas.title"
:close-label="i18n.blocksEditor.offcanvas.close"
:title="$t('blocksEditor.offcanvas.title')"
:close-label="$t('blocksEditor.offcanvas.close')"
@close="closeOffcanvas">
<TemplatePicker
v-if="offcanvasState === 'picking'"
Expand Down
15 changes: 7 additions & 8 deletions app/javascript/apps/blocks-editor/components/Blocks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export default {
},
props: [
'modelValue',
'i18n',
],
emits: [
'update:modelValue',
Expand Down Expand Up @@ -37,7 +36,7 @@ export default {
},
onDelete(event, block) {
event.preventDefault();
if (!confirm(this.i18n.blocksEditor.confirm.deletion)) return;
if (!confirm(this.$t('blocksEditor.confirm.deletion'))) return;
this.$emit('delete', block);
},
onCopy(event, block) {
Expand All @@ -46,7 +45,7 @@ export default {
},
onDuplicate(event, block) {
event.preventDefault();
if (!confirm(this.i18n.blocksEditor.confirm.duplication)) return;
if (!confirm(this.$t('blocksEditor.confirm.duplication'))) return;
this.$emit('duplicate', block);
},
}
Expand Down Expand Up @@ -83,31 +82,31 @@ export default {
<span class="handle">
<i class="fas fa-sort"></i>
<span class="small">
 {{ i18n.blocksEditor.actions.move }}
 {{ $t('blocksEditor.actions.move') }}
</span>
</span>
</span>
<a
href="#"
class="action text-danger ms-2"
@click="onDelete($event, block)">
{{ i18n.blocksEditor.actions.delete }}</a>
{{ $t('blocksEditor.actions.delete') }}</a>
<a
href="#"
class="action ms-2"
@click="onCopy($event, block)">
{{ i18n.blocksEditor.actions.copy }}</a>
{{ $t('blocksEditor.actions.copy') }}</a>
<a
href="#"
class="action ms-2"
@click="onDuplicate($event, block)">
{{ i18n.blocksEditor.actions.duplicate }}</a>
{{ $t('blocksEditor.actions.duplicate') }}</a>
</span>
<a
href="#"
class="action ms-2"
@click="onEdit($event, block)">
{{ i18n.blocksEditor.actions.edit }}</a>
{{ $t('blocksEditor.actions.edit') }}</a>
</div>
<div
class="vue__blocks-editor__elements__preview"
Expand Down
7 changes: 3 additions & 4 deletions app/javascript/apps/components/Changes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
export default {
props: [
'modelValue',
'endpoint',
'i18n'
'endpoint'
],
emits: ['update:modelValue'],
computed: {
Expand Down Expand Up @@ -69,13 +68,13 @@ export default {
class="btn btn-light vue__changes__cancel"
@click="cancel()"
:disabled="savingInProgress">
{{ i18n.cancel }}
{{ $t('changes.cancel') }}
</button>
<button type="button"
class="btn btn-success vue__changes__save"
@click="save()"
:disabled="savingInProgress">
{{ i18n.save }}
{{ $t('changes.save') }}
</button>
</div>
</template>
13 changes: 4 additions & 9 deletions app/javascript/apps/components/CropperModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export default {
width: null,
height: null,
},
i18n: {},
}
},
methods: {
Expand Down Expand Up @@ -74,10 +73,6 @@ export default {
xhr.send(JSON.stringify(this.data));
},
},
beforeMount() {
this.dataset = document.getElementById('media-picker-app').dataset;
this.i18n = JSON.parse(this.dataset.i18n).cropperModal;
},
};

// On utilise canvas=false et check-orientation=false pour éviter les problèmes de CORS
Expand All @@ -94,7 +89,7 @@ export default {
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{i18n.title}}</h5>
<h5 class="modal-title">{{ $t('cropperModal.title') }}</h5>
<button type="button"
class="btn-close"
@click="close()">
Expand All @@ -115,7 +110,7 @@ export default {
<div class="modal-footer justify-content-between">
<button type="button"
class="btn btn-sm"
:aria-label="i18n.rotate"
:aria-label="$t('cropperModal.rotate')"
@click="rotate(90)">
<i class="bi bi-arrow-clockwise"></i>
</button>
Expand All @@ -124,13 +119,13 @@ export default {
class="btn btn-sm btn-secondary me-2"
:disabled="pending"
@click="close()">
{{ i18n.cancel }}
{{ $t('cropperModal.cancel') }}
</button>
<button type="button"
class="btn btn-sm btn-primary"
:disabled="pending"
@click="crop()">
{{ i18n.validate }}
{{ $t('cropperModal.validate') }}
</button>
</div>
</div>
Expand Down
40 changes: 29 additions & 11 deletions app/javascript/apps/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
import { createApp } from 'vue';
import { createI18n } from 'vue-i18n';
import SsoMappingApp from './sso-mapping/SsoMappingApp.vue';
import MediaPickerApp from './media-picker/MediaPickerApp.vue';
import TimeSlotsApp from './time-slots/TimeSlotsApp.vue';
import BlocksEditorApp from './blocks-editor/BlocksEditorApp.vue';

if (document.getElementById('sso-mapping-app')) {
createApp(SsoMappingApp).mount('#sso-mapping-app');
}
if (document.getElementById('media-picker-app')) {
createApp(MediaPickerApp).mount('#media-picker-app');
}
if (document.getElementById('time-slots-app')) {
createApp(TimeSlotsApp).mount('#time-slots-app');
}
if (document.getElementById('blocks-editor-app')) {
createApp(BlocksEditorApp).mount('#blocks-editor-app');
// Messages are precompiled at boot into public/vue/<locale>.json (cf.
// config/initializers/vue_i18n.rb) and fetched once as a cacheable static file,
// then shared by every app through a single vue-i18n instance.
async function boot() {
const locale = document.documentElement.lang || 'fr';
const messages = await fetch(`/vue/${locale}.json`)
.then((res) => res.json())
.catch(() => ({}));
const i18n = createI18n({
legacy: false,
globalInjection: true,
locale,
fallbackLocale: 'fr',
messages: { [locale]: messages },
});

const mount = (App, selector) => {
if (document.querySelector(selector)) {
createApp(App).use(i18n).mount(selector);
}
};

mount(SsoMappingApp, '#sso-mapping-app');
mount(MediaPickerApp, '#media-picker-app');
mount(TimeSlotsApp, '#time-slots-app');
mount(BlocksEditorApp, '#blocks-editor-app');
}

boot();
19 changes: 8 additions & 11 deletions app/javascript/apps/media-picker/MediaPickerApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export default {
data () {
return {
current: {},
i18n: {},
}
},
methods: {
Expand Down Expand Up @@ -57,7 +56,6 @@ export default {
},
beforeMount() {
this.dataset = document.getElementById('media-picker-app').dataset
this.i18n = JSON.parse(this.dataset.i18n);
this.summernoteLang = this.dataset.summernoteLang;
this.current = JSON.parse(this.dataset.current);
},
Expand All @@ -70,7 +68,7 @@ export default {
<template>
<section class="vue__media-picker">
<div class="d-lg-flex me-4 mb-0">
<label class="form-label">{{ i18n.mediaPicker.title }}</label>
<label class="form-label">{{ $t('mediaPicker.title') }}</label>
</div>
<div class="app-content">
<div v-if="!current.image.url" class="vue__media-picker__selector">
Expand All @@ -87,33 +85,32 @@ export default {
<a class="btn btn-sm text-danger pe-0"
@click="removeImage()">
<i class="<%= Icon::DELETE %>"></i>
{{ i18n.mediaPicker.remove }}
{{ $t('mediaPicker.remove') }}
</a>
</div>
<div class="mb-3">
<label class="form-label" aria-label="{{ i18n.mediaPicker.alt.label }}" for="alt">
{{ i18n.mediaPicker.alt.label }}
<label class="form-label" :aria-label="$t('mediaPicker.alt.label')" for="alt">
{{ $t('mediaPicker.alt.label') }}
</label>
<input id="alt"
class="form-control"
data-translatable="true"
v-model="current.image.alt"
type="text">
<div class="form-text">{{ i18n.mediaPicker.alt.hint }}</div>
<div class="form-text">{{ $t('mediaPicker.alt.hint') }}</div>
</div>
<div class="mb-3 summernote">
<label class="form-label" :aria-label="i18n.mediaPicker.credit.label" for="credit">
{{ i18n.mediaPicker.credit.label }}
<label class="form-label" :aria-label="$t('mediaPicker.credit.label')" for="credit">
{{ $t('mediaPicker.credit.label') }}
</label>
<Summernote id="credit"
:lang="summernoteLang"
v-model="current.image.credit" />
<div class="form-text">{{ i18n.mediaPicker.credit.hint }}</div>
<div class="form-text">{{ $t('mediaPicker.credit.hint') }}</div>
</div>
</div>
</div>
</section>
<Changes v-model="current"
:i18n="i18n.changes"
:endpoint="dataset.changesEndpoint" />
</template>
Loading
Loading