mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-19 05:33:31 +01:00
Responsive editor improvements (#5449)
This commit is contained in:
parent
c15f02ddbf
commit
2326894a2b
@ -38,6 +38,10 @@
|
||||
#crowdfund-body-description {
|
||||
font-size: 16px;
|
||||
}
|
||||
.perk.card .card-img-top{
|
||||
max-height: 210px;
|
||||
object-fit: scale-down;
|
||||
}
|
||||
</style>
|
||||
<vc:ui-extension-point location="crowdfund-head" model="@Model"></vc:ui-extension-point>
|
||||
</head>
|
||||
|
@ -166,8 +166,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="perks">
|
||||
<partial name="TemplateEditor" model="@(nameof(Model.PerksTemplate), Model.PerksTemplate, "Perks", Model.TargetCurrency ?? Model.StoreDefaultCurrency)" />
|
||||
<div id="perks" class="row">
|
||||
<div class="col-xxl-constrain">
|
||||
<partial name="TemplateEditor" model="@(nameof(Model.PerksTemplate), Model.PerksTemplate, "Perks", Model.TargetCurrency ?? Model.StoreDefaultCurrency)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-8 col-xxl-constrain">
|
||||
@ -329,10 +331,10 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="d-flex gap-3 mt-3">
|
||||
<div class="d-grid d-sm-flex flex-wrap gap-3 mt-3">
|
||||
<a class="btn btn-secondary" asp-action="ListInvoices" asp-controller="UIInvoice" asp-route-storeId="@Model.StoreId" asp-route-searchterm="@Model.SearchTerm">Invoices</a>
|
||||
<form method="post" asp-controller="UIApps" asp-action="ToggleArchive" asp-route-appId="@Model.AppId">
|
||||
<button type="submit" class="btn btn-outline-secondary" id="btn-archive-toggle">
|
||||
<button type="submit" class="w-100 btn btn-outline-secondary" id="btn-archive-toggle">
|
||||
@if (Model.Archived)
|
||||
{
|
||||
<span class="text-nowrap">Unarchive this app</span>
|
||||
|
@ -93,8 +93,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="products">
|
||||
<partial name="TemplateEditor" model="@(nameof(Model.Template), Model.Template, "Products", Model.Currency ?? Model.StoreDefaultCurrency)" />
|
||||
<div id="products" class="row">
|
||||
<div class="col-xxl-constrain">
|
||||
<partial name="TemplateEditor" model="@(nameof(Model.Template), Model.Template, "Products", Model.Currency ?? Model.StoreDefaultCurrency)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div class="col-sm-10 col-md-9 col-xl-7 col-xxl-6">
|
||||
@ -282,10 +284,10 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="d-flex gap-3 mt-3">
|
||||
<div class="d-grid d-sm-flex flex-wrap gap-3 mt-3">
|
||||
<a class="btn btn-secondary" asp-action="ListInvoices" asp-controller="UIInvoice" asp-route-storeId="@Model.StoreId" asp-route-searchterm="@Model.SearchTerm">Invoices</a>
|
||||
<form method="post" asp-controller="UIApps" asp-action="ToggleArchive" asp-route-appId="@Model.Id">
|
||||
<button type="submit" class="btn btn-outline-secondary" id="btn-archive-toggle">
|
||||
<button type="submit" class="w-100 btn btn-outline-secondary" id="btn-archive-toggle">
|
||||
@if (Model.Archived)
|
||||
{
|
||||
<span class="text-nowrap">Unarchive this app</span>
|
||||
|
@ -76,7 +76,7 @@
|
||||
</div>
|
||||
<vc:ui-extension-point location="app-template-editor-item-detail" model="Model"></vc:ui-extension-point>
|
||||
<div class="text-danger mb-3" v-for="error of errors">{{error}}</div>
|
||||
<button class="btn btn-primary" type="button" id="ApplyItemChanges" v-on:click="apply">Apply</button>
|
||||
<button class="btn btn-primary d-none d-xl-inline-block" type="button" id="ApplyItemChanges" v-on:click="apply">Apply</button>
|
||||
</div>
|
||||
<div v-if="!editingItem">Select an item to edit</div>
|
||||
</div>
|
||||
@ -84,27 +84,28 @@
|
||||
|
||||
<template id="items-editor">
|
||||
<div>
|
||||
<div class="items list-group list-group-horizontal flex-wrap" v-sortable="{ handle: '.drag', onUpdate (event) { $emit('sort-items', event) } }">
|
||||
<div v-for="(item, index) of items" class="d-inline-flex align-items-start gap-2 list-group-item" style="flex: 0 1 375px" v-bind:key="item.id" :class="{ active: item === selectedItem }" v-on:click.stop="$emit('select-item', $event, index)">
|
||||
<div class="items list-group list-group-flush" v-sortable="{ handle: '.drag', onUpdate (event) { $emit('sort-items', event) } }">
|
||||
<div v-for="(item, index) of items" class="d-inline-flex align-items-center gap-3 list-group-item" :key="item.id" :class="{ active: item === selectedItem }" v-on:click.stop="$emit('select-item', $event, index)">
|
||||
<button type="button" class="btn b-0 control drag" :disabled="items.length === 1">
|
||||
<vc:icon symbol="drag" />
|
||||
</button>
|
||||
<div class="card template-item bg-transparent w-100 h-100">
|
||||
<img class="card-img-top" :src="getImage(item)" :alt="item.title" :style="(item.image ? null : { opacity: 0.5 })">
|
||||
<div class="card-body p-3 d-flex flex-column gap-2">
|
||||
<div class="template-item d-flex align-items-start w-100 gap-3">
|
||||
<div class="img">
|
||||
<img :src="getImage(item)" :alt="item.title" :style="(item.image ? null : { opacity: 0.5 })">
|
||||
</div>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<h5 class="card-title m-0" v-html="item.title"></h5>
|
||||
<div class="d-flex gap-2 align-items-center">
|
||||
<span class="fw-semibold badge text-bg-info" v-if="item.priceType === 'Topup' || item.price == 0">{{ item.priceType === 'Topup' ? 'Any amount' : 'Free' }}</span>
|
||||
<span class="fw-semibold" v-else>{{ item.price }} @Model.currency{{ item.priceType === 'Minimum' ? ' minimum' : '' }}</span>
|
||||
<span class="fw-semibold text-muted" v-else>{{ item.price }} @Model.currency{{ item.priceType === 'Minimum' ? ' minimum' : '' }}</span>
|
||||
<span class="badge text-bg-warning" v-if="item.inventory">
|
||||
{{ item.inventory > 0 ? `${item.inventory} left` : 'Sold out' }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="card-text" v-if="item.description">{{ item.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn b-0 control remove" v-on:click="$emit('remove-item', $event, index)">
|
||||
<vc:icon symbol="trash" />
|
||||
<vc:icon symbol="remove" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -138,7 +139,7 @@
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="EditorTabPane" role="tabpanel" aria-labelledby="EditorTabButton" tabindex="0">
|
||||
<div class="row align-items-start">
|
||||
<div class="col-lg-7 mb-4 mb-lg-0">
|
||||
<div class="col-12 col-xl-7">
|
||||
<items-editor :items="items"
|
||||
:selected-item="selectedItem"
|
||||
v-on:add-item="addItem"
|
||||
@ -148,8 +149,20 @@
|
||||
:class="{ 'pt-2': (!items || items.length === 0) }"
|
||||
class="bg-tile pb-2 rounded" />
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<item-editor :item="selectedItem" class="bg-tile p-4 rounded" />
|
||||
<div class="col-xl-5 offcanvas-xl offcanvas-end" tabindex="-1" ref="editorOffcanvas">
|
||||
<div class="offcanvas-header p-3">
|
||||
<h5 class="offcanvas-title">Edit Item</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" v-on:click="hideOffcanvas">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="offcanvas-body p-3 p-xl-0">
|
||||
<item-editor ref="itemEditor" :item="selectedItem" class="bg-tile w-100 p-xl-4 rounded" />
|
||||
</div>
|
||||
<div class="offcanvas-header p-3">
|
||||
<button class="btn btn-primary" type="button" v-on:click="() => { $refs.itemEditor.apply(); hideOffcanvas() }">Apply and close</button>
|
||||
<button class="btn btn-secondary" type="button" v-on:click="hideOffcanvas">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -64,7 +64,7 @@
|
||||
<input :id="`field-option-text-${index}`" class="form-control" v-model="option.text" />
|
||||
</div>
|
||||
<button type="button" class="btn b-0 control remove" v-on:click="removeOption($event, index)">
|
||||
<vc:icon symbol="trash" />
|
||||
<vc:icon symbol="remove" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -101,7 +101,7 @@
|
||||
<input :id="`field-valuemap-mapped-${index}`" class="form-control" placeholder="Value to set" :value="v" v-on:change="updateValueMap(k, k, $event.target.value)" />
|
||||
</div>
|
||||
<button type="button" class="btn b-0 control remove" v-on:click="removeValueMap($event, k)">
|
||||
<vc:icon symbol="trash" />
|
||||
<vc:icon symbol="remove" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -138,7 +138,7 @@
|
||||
<component :is="getFieldComponent(field.type)" v-bind="field" :path="path.concat(field.name)" :selected-field="selectedField" v-on="$listeners" />
|
||||
</div>
|
||||
<button type="button" class="btn b-0 control remove" v-on:click="$emit('remove-field', $event, path, index)">
|
||||
<vc:icon symbol="trash" />
|
||||
<vc:icon symbol="remove" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -247,7 +247,7 @@
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="EditorTabPane" role="tabpanel" aria-labelledby="EditorTabButton" tabindex="0">
|
||||
<div class="row align-items-start">
|
||||
<div class="col-lg-7 mb-4 mb-lg-0">
|
||||
<div class="col-12 col-xl-7">
|
||||
<fields-editor :path="[]"
|
||||
:fields="fields"
|
||||
:selected-field="selectedField"
|
||||
@ -258,8 +258,16 @@
|
||||
:class="{ 'pt-2': (!fields || fields.length === 0) }"
|
||||
class="bg-tile pb-2 rounded" />
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<field-editor :field="selectedField" class="bg-tile p-4 rounded" />
|
||||
<div class="col-xl-5 offcanvas-xl offcanvas-end" tabindex="-1" ref="editorOffcanvas">
|
||||
<div class="offcanvas-header p-3">
|
||||
<h5 class="offcanvas-title">Edit Field</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" v-on:click="hideOffcanvas">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="offcanvas-body p-3 p-xl-0">
|
||||
<field-editor :field="selectedField" class="bg-tile w-100 p-xl-4 rounded" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -152,7 +152,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
data () {
|
||||
return {
|
||||
config,
|
||||
selectedField: null
|
||||
selectedField: null,
|
||||
editorOffcanvas: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -192,10 +193,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const index = fields.length + 1
|
||||
const length = fields.push({ type: 'text', name: `newField${index}`, label: `New field ${index}`, fields: [], options: [] })
|
||||
this.selectedField = fields[length - 1]
|
||||
this.showOffcanvas()
|
||||
},
|
||||
selectField(event, path, index) {
|
||||
const fields = this.getFieldsForPath(path)
|
||||
this.selectedField = fields[index]
|
||||
this.showOffcanvas()
|
||||
},
|
||||
removeField(event, path, index) {
|
||||
const fields = this.getFieldsForPath(path)
|
||||
@ -217,12 +220,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
fields = field.fields
|
||||
}
|
||||
return fields
|
||||
},
|
||||
showOffcanvas() {
|
||||
if (window.getComputedStyle(this.$refs.editorOffcanvas).visibility === 'hidden')
|
||||
this.editorOffcanvas.show();
|
||||
},
|
||||
hideOffcanvas() {
|
||||
this.editorOffcanvas.hide();
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (!this.config.fields || this.config.fields.length === 0) {
|
||||
this.addField(null,[])
|
||||
}
|
||||
this.editorOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(this.$refs.editorOffcanvas);
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -229,7 +229,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
data () {
|
||||
return {
|
||||
items,
|
||||
selectedItem: null
|
||||
selectedItem: null,
|
||||
editorOffcanvas: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -263,9 +264,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
disabled: false
|
||||
})
|
||||
this.selectedItem = this.items[length - 1]
|
||||
this.showOffcanvas()
|
||||
},
|
||||
selectItem(event, index) {
|
||||
this.selectedItem = this.items[index]
|
||||
this.showOffcanvas()
|
||||
},
|
||||
removeItem(event, index) {
|
||||
this.items.splice(index, 1)
|
||||
@ -274,10 +277,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
sortItems(event) {
|
||||
const { newIndex, oldIndex } = event
|
||||
this.items.splice(newIndex, 0, this.items.splice(oldIndex, 1)[0])
|
||||
},
|
||||
showOffcanvas() {
|
||||
if (window.getComputedStyle(this.$refs.editorOffcanvas).visibility === 'hidden')
|
||||
this.editorOffcanvas.show();
|
||||
},
|
||||
hideOffcanvas() {
|
||||
this.editorOffcanvas.hide();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (!this.items) this.items = []
|
||||
this.editorOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(this.$refs.editorOffcanvas);
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -1,9 +1,3 @@
|
||||
.editor .card-img-top {
|
||||
max-height: 210px;
|
||||
object-fit: scale-down;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.editor .nav-pills .nav-link {
|
||||
background: none;
|
||||
padding: 0;
|
||||
@ -20,6 +14,7 @@
|
||||
}
|
||||
|
||||
.editor .list-group-item {
|
||||
--image-size: 3rem;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: var(--btcpay-space-m) var(--btcpay-space-m) var(--btcpay-space-m) var(--btcpay-space-s) !important;
|
||||
@ -30,6 +25,18 @@
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.editor .list-group-item .img img {
|
||||
width: var(--image-size);
|
||||
height: var(--image-size);
|
||||
}
|
||||
|
||||
.editor .list-group-item .img img {
|
||||
max-width: var(--image-size);
|
||||
max-height: var(--image-size);
|
||||
object-fit: scale-down;
|
||||
border-radius: var(--btcpay-border-radius);
|
||||
}
|
||||
|
||||
.editor fieldset .list-group-item {
|
||||
padding: var(--btcpay-space-m) 0 var(--btcpay-space-m);
|
||||
}
|
||||
@ -52,6 +59,10 @@
|
||||
color: var(--btcpay-danger);
|
||||
}
|
||||
|
||||
.editor .control.remove .icon {
|
||||
--btn-icon-size: 1.75rem;
|
||||
}
|
||||
|
||||
.editor .field .form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user