fix(cropper): responsive editor layout
Two-column editor at >=900px (crop pad + cleaned preview left, cleaning/ review controls right), single column below; panel headers wrap their hints onto their own line on narrow screens.
This commit is contained in:
@@ -78,79 +78,81 @@
|
|||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<div class="finger-grid mt-2">
|
<div class="editor-grid mt-2">
|
||||||
<div>
|
<div class="editor-media">
|
||||||
<label>Original — crop it</label>
|
<label>Original — crop it</label>
|
||||||
<div class="croppa-pad mt-1">
|
<div class="croppa-pad mt-1">
|
||||||
<croppa v-model="myCroppa" :show-remove-button="false" :width="250" :height="267.44" :quality="2">
|
<croppa v-model="myCroppa" :show-remove-button="false" :width="250" :height="267.44" :quality="2">
|
||||||
<img slot="initial" crossOrigin="anonymous" :src="message.message.file_url" style="width: 300px">
|
<img slot="initial" crossOrigin="anonymous" :src="message.message.file_url" style="width: 300px">
|
||||||
</croppa>
|
</croppa>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="cleanedImage">
|
||||||
|
<label style="display:block;" class="mt-2">Cleaned preview</label>
|
||||||
|
<div class="croppa-pad mt-1">
|
||||||
|
<img :src="cleanedImage" alt="cleaned preview">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-danger-ghost mt-1" v-on:click="cleanedImage = null">Remove cleaned image</button>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="cleanedImage">
|
|
||||||
<label>Cleaned preview</label>
|
<div class="editor-controls">
|
||||||
<div class="croppa-pad mt-1">
|
<div class="row-gap" style="align-items:flex-end;">
|
||||||
<img :src="cleanedImage" alt="cleaned preview">
|
<div style="min-width:200px;">
|
||||||
|
<label for="requiresCleaning">Requires cleaning?</label>
|
||||||
|
<select id="requiresCleaning" class="form-control" v-model="requiresCleaning">
|
||||||
|
<option value="">--Choose--</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
<option value="No">No</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" v-if="requiresCleaning === 'Yes'" v-bind:disabled="busy.clean" v-on:click="cleanImage">
|
||||||
|
<span v-if="!busy.clean">Clean background</span>
|
||||||
|
<span v-else>Cleaning…</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row-gap mt-2" style="align-items:flex-end;">
|
<hr class="mt-2">
|
||||||
<div style="min-width:200px;">
|
|
||||||
<label for="requiresCleaning">Requires cleaning?</label>
|
|
||||||
<select id="requiresCleaning" class="form-control" v-model="requiresCleaning">
|
|
||||||
<option value="">--Choose--</option>
|
|
||||||
<option value="Yes">Yes</option>
|
|
||||||
<option value="No">No</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-primary" v-if="requiresCleaning === 'Yes'" v-bind:disabled="busy.clean" v-on:click="cleanImage">
|
|
||||||
<span v-if="!busy.clean">Clean background</span>
|
|
||||||
<span v-else>Cleaning…</span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-danger-ghost" v-if="cleanedImage" v-on:click="cleanedImage = null">Remove cleaned image</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr class="mt-2">
|
<div class="row-gap" v-if="!reviewed">
|
||||||
|
<div class="btn btn-success" style="border-radius:10px; padding:0.55rem 1.1rem; font-weight:600; cursor:pointer;" v-on:click="reviewed = true; accepted = true">Accept picture</div>
|
||||||
<div class="row-gap" v-if="!reviewed">
|
<div class="btn btn-danger" style="border-radius:10px; padding:0.55rem 1.1rem; font-weight:600; cursor:pointer;" v-on:click="reviewed = true; accepted = false">Reject picture</div>
|
||||||
<div class="btn btn-success" style="border-radius:10px; padding:0.55rem 1.1rem; font-weight:600; cursor:pointer;" v-on:click="reviewed = true; accepted = true">Accept picture</div>
|
|
||||||
<div class="btn btn-danger" style="border-radius:10px; padding:0.55rem 1.1rem; font-weight:600; cursor:pointer;" v-on:click="reviewed = true; accepted = false">Reject picture</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="reviewed && accepted" class="mt-2">
|
|
||||||
<div class="row-gap" style="align-items:flex-end;">
|
|
||||||
<div style="min-width:150px; max-width:200px;">
|
|
||||||
<label for="otpSave">OTP</label>
|
|
||||||
<input id="otpSave" class="form-control" v-model.trim="otp" autocomplete="one-time-code">
|
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary" v-bind:disabled="!canSave" v-on:click="saveChanges">
|
|
||||||
<span v-if="!busy.save">Crop and upload picture</span>
|
|
||||||
<span v-else>Saving…</span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-ghost" v-on:click="reviewed = false">Change review</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="reviewed && !accepted" class="mt-2">
|
<div v-if="reviewed && accepted" class="mt-2">
|
||||||
<label>Please state why this picture is not good enough.</label>
|
<div class="row-gap" style="align-items:flex-end;">
|
||||||
<div class="review-options mt-1">
|
<div style="min-width:150px; max-width:200px;">
|
||||||
<div class="form-check" v-for="item in reviewOptions" v-bind:key="item.index">
|
<label for="otpSave">OTP</label>
|
||||||
<input class="form-check-input" type="checkbox" v-model="selectedReviewOptions" v-bind:id="'reason-' + item.index" v-bind:value="item.name">
|
<input id="otpSave" class="form-control" v-model.trim="otp" autocomplete="one-time-code">
|
||||||
<label class="form-check-label" v-bind:for="'reason-' + item.index">{{ item.name }}</label>
|
</div>
|
||||||
|
<button class="btn btn-primary" v-bind:disabled="!canSave" v-on:click="saveChanges">
|
||||||
|
<span v-if="!busy.save">Crop and upload picture</span>
|
||||||
|
<span v-else>Saving…</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-ghost" v-on:click="reviewed = false">Change review</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="reviewed && !accepted" class="mt-2">
|
||||||
|
<label>Please state why this picture is not good enough.</label>
|
||||||
|
<div class="review-options mt-1">
|
||||||
|
<div class="form-check" v-for="item in reviewOptions" v-bind:key="item.index">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="selectedReviewOptions" v-bind:id="'reason-' + item.index" v-bind:value="item.name">
|
||||||
|
<label class="form-check-label" v-bind:for="'reason-' + item.index">{{ item.name }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-1" style="max-width:480px;">
|
||||||
|
<label for="otherReviewOption">Others (please specify).</label>
|
||||||
|
<textarea id="otherReviewOption" rows="3" class="form-control" v-model="otherReviewOption"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="row-gap mt-2">
|
||||||
|
<button class="btn btn-primary" v-bind:disabled="!canReview" v-on:click="reviewPicture()">
|
||||||
|
<span v-if="!busy.review">Send review</span>
|
||||||
|
<span v-else>Sending…</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-danger-ghost" v-if="!message.message.process" v-bind:disabled="!canReview" v-on:click="removeFromQueue">Dequeue only</button>
|
||||||
|
<button class="btn btn-ghost" v-on:click="reviewed = false">Change review</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="mt-1" style="max-width:480px;">
|
|
||||||
<label for="otherReviewOption">Others (please specify).</label>
|
|
||||||
<textarea id="otherReviewOption" rows="3" class="form-control" v-model="otherReviewOption"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="row-gap mt-2">
|
|
||||||
<button class="btn btn-primary" v-bind:disabled="!canReview" v-on:click="reviewPicture()">
|
|
||||||
<span v-if="!busy.review">Send review</span>
|
|
||||||
<span v-else>Sending…</span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-danger-ghost" v-if="!message.message.process" v-bind:disabled="!canReview" v-on:click="removeFromQueue">Dequeue only</button>
|
|
||||||
<button class="btn btn-ghost" v-on:click="reviewed = false">Change review</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -169,6 +169,17 @@ select.form-control { appearance: auto; }
|
|||||||
/* ── alerts ─────────────────────────────────────────────── */
|
/* ── alerts ─────────────────────────────────────────────── */
|
||||||
.alert-soft { border-radius: 10px; border: 1px solid var(--err-soft); background: var(--err-soft); color: var(--err); font-size: 13.5px; padding: 0.6rem 0.9rem; }
|
.alert-soft { border-radius: 10px; border: 1px solid var(--err-soft); background: var(--err-soft); color: var(--err); font-size: 13.5px; padding: 0.6rem 0.9rem; }
|
||||||
|
|
||||||
|
/* ── editor grid (cropper) ──────────────────────────────── */
|
||||||
|
.editor-grid { display: grid; grid-template-columns: 1fr; gap: 1.25rem; }
|
||||||
|
@media (min-width: 900px) {
|
||||||
|
.editor-grid { grid-template-columns: 300px 1fr; align-items: start; }
|
||||||
|
}
|
||||||
|
.panel-head { flex-wrap: wrap; }
|
||||||
|
.panel-head .hint { flex-basis: 100%; margin-left: 0; }
|
||||||
|
@media (min-width: 720px) {
|
||||||
|
.panel-head .hint { flex-basis: auto; margin-left: auto; }
|
||||||
|
}
|
||||||
|
|
||||||
/* ── footer ─────────────────────────────────────────────── */
|
/* ── footer ─────────────────────────────────────────────── */
|
||||||
footer.foot { flex-shrink: 0; padding: 1.4rem 0 1.8rem; color: var(--faint); font-size: 12.5px; }
|
footer.foot { flex-shrink: 0; padding: 1.4rem 0 1.8rem; color: var(--faint); font-size: 12.5px; }
|
||||||
footer.foot .container { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
|
footer.foot .container { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
|
||||||
|
|||||||
Reference in New Issue
Block a user