feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
85
modules/backend/formwidgets/repeater/assets/css/repeater.css
Normal file
85
modules/backend/formwidgets/repeater/assets/css/repeater.css
Normal file
@@ -0,0 +1,85 @@
|
||||
.field-repeater{padding-top:5px}
|
||||
.field-repeater .field-repeater-items{counter-reset:repeater-index-counter}
|
||||
.field-repeater ul.field-repeater-items,
|
||||
.field-repeater li.field-repeater-item{padding:0;margin:0;list-style:none}
|
||||
.field-repeater ul.field-repeater-items>li.dragged{opacity:0.7;position:absolute;padding-top:15px;padding-right:15px;z-index:2000;background-color:#f9f9f9;border:1px dashed #dbdee0}
|
||||
.field-repeater ul.field-repeater-items>li.dragged .repeater-item-remove{opacity:0}
|
||||
.field-repeater ul.field-repeater-items>li.dragged .repeater-item-collapsed-title{top:5px}
|
||||
.field-repeater ul.field-repeater-items>li.placeholder{display:block;position:relative;height:25px;margin-bottom:5px}
|
||||
.field-repeater ul.field-repeater-items>li.placeholder:before{display:block;position:absolute;font-family:"Font Awesome 6 Free";font-weight:900;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-style:normal;font-variant:normal;text-rendering:auto;content:"\f054";color:#d35714;left:-10px;top:8px;z-index:2000}
|
||||
.field-repeater li.field-repeater-item{position:relative;margin:0 0 1em 1em !important;padding:1.5em 1.25em 0 1.25em !important;background:#f5f5f5;border:1px solid #d1d6d9;border-radius:3px;box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);min-height:30px}
|
||||
.field-repeater li.field-repeater-item:before{color:#bdc3c7;font-family:"Font Awesome 6 Free";font-weight:900;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-style:normal;font-variant:normal;text-rendering:auto;content:"\f111";font-size:8px;position:absolute;left:-18px;top:-2px}
|
||||
.field-repeater li.field-repeater-item:after{counter-increment:repeater-index-counter;content:counter(repeater-index-counter);display:block;position:absolute;font-size:12px;left:-27px;color:#bdc3c7;top:10px;width:24px;text-align:center}
|
||||
.field-repeater li.field-repeater-item.collapsed .field-repeater-form{display:none}
|
||||
.field-repeater li.field-repeater-item.collapsed .repeater-item-collapse .repeater-item-collapse-one{-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}
|
||||
.field-repeater li.field-repeater-item.collapsed .repeater-item-collapsed-title{display:block}
|
||||
.field-repeater li.field-repeater-item .repeater-item-collapsed-title{display:none;position:absolute;font-size:13px;top:50%;left:10px;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%)}
|
||||
.field-repeater li.field-repeater-item .field-repeater-form{position:relative;top:-7px}
|
||||
.field-repeater li.field-repeater-item .field-repeater-form:before,
|
||||
.field-repeater li.field-repeater-item .field-repeater-form:after{content:" ";display:table}
|
||||
.field-repeater li.field-repeater-item .field-repeater-form:after{clear:both}
|
||||
.field-repeater li.field-repeater-item .repeater-item-handle{position:absolute;top:-6px;left:-25px;color:#bdc3c7;background:#f9f9f9;cursor:move;opacity:0;border-radius:999px;-webkit-transition:opacity 0.5s;transition:opacity 0.5s}
|
||||
.field-repeater li.field-repeater-item .repeater-item-handle:hover{color:#999}
|
||||
.field-repeater li.field-repeater-item .repeater-item-collapse{position:absolute;top:5px;right:30px;z-index:90}
|
||||
.field-repeater li.field-repeater-item .repeater-item-collapse a,
|
||||
.field-repeater li.field-repeater-item .repeater-item-collapse button{-webkit-transition:transform 0.3s;transition:transform 0.3s;color:#bdc3c7;line-height:20px;display:block;font-size:12px}
|
||||
.field-repeater li.field-repeater-item .repeater-item-collapse a:hover,
|
||||
.field-repeater li.field-repeater-item .repeater-item-collapse button:hover,
|
||||
.field-repeater li.field-repeater-item .repeater-item-collapse a:focus,
|
||||
.field-repeater li.field-repeater-item .repeater-item-collapse button:focus{color:#999;text-decoration:none}
|
||||
.field-repeater li.field-repeater-item .repeater-item-remove{position:absolute;top:4px;right:5px;z-index:90}
|
||||
.field-repeater li.field-repeater-item .repeater-item-remove.disabled{display:none}
|
||||
.field-repeater li.field-repeater-item .repeater-item-remove.disabled + .repeater-item-collapse{right:7px}
|
||||
.field-repeater li.field-repeater-item .repeater-item-remove .close{float:none;display:inline-block}
|
||||
.field-repeater li.field-repeater-item .repeater-item-collapse,
|
||||
.field-repeater li.field-repeater-item .repeater-item-handle,
|
||||
.field-repeater li.field-repeater-item .repeater-item-remove{width:20px;height:20px;text-align:center}
|
||||
.field-repeater li.field-repeater-item:hover .repeater-item-collapse,
|
||||
.field-repeater li.field-repeater-item:active .repeater-item-collapse,
|
||||
.field-repeater li.field-repeater-item:hover .repeater-item-handle,
|
||||
.field-repeater li.field-repeater-item:active .repeater-item-handle,
|
||||
.field-repeater li.field-repeater-item:hover .repeater-item-remove,
|
||||
.field-repeater li.field-repeater-item:active .repeater-item-remove{opacity:1}
|
||||
.field-repeater .field-repeater-add-item{position:relative;margin-top:10px;margin-left:20px;border:2px dotted #e0e0e0;border-radius:5px}
|
||||
.field-repeater .field-repeater-add-item:before{color:#bdc3c7;font-family:"Font Awesome 6 Free";font-weight:900;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-style:normal;font-variant:normal;text-rendering:auto;content:"\2b";font-size:16px;position:absolute;left:-23px;top:-11px}
|
||||
.field-repeater .field-repeater-add-item>a{color:#bdc3c7;text-align:center;display:block;text-decoration:none;padding:13px 15px;text-transform:uppercase;font-weight:600;font-size:12px}
|
||||
.field-repeater .field-repeater-add-item:hover,
|
||||
.field-repeater .field-repeater-add-item:focus{background-color:#48b2ce;border-color:transparent}
|
||||
.field-repeater .field-repeater-add-item:hover:before,
|
||||
.field-repeater .field-repeater-add-item:focus:before{color:#999}
|
||||
.field-repeater .field-repeater-add-item:hover>a,
|
||||
.field-repeater .field-repeater-add-item:focus>a{color:#fff}
|
||||
.field-repeater .field-repeater-add-item:active{background:#6cc551;border-color:transparent}
|
||||
.field-repeater .field-repeater-add-item:active>a{color:#fff}
|
||||
.field-repeater .field-repeater-add-item.in-progress{border-color:#e0e0e0 !important;background:transparent !important}
|
||||
.field-repeater[data-mode="grid"]>ul.field-repeater-items{display:grid;gap:20px}
|
||||
.field-repeater[data-mode="grid"]>ul.field-repeater-items>.field-repeater-item{margin-bottom:0 !important}
|
||||
.field-repeater[data-mode="grid"]>ul.field-repeater-items>.field-repeater-add-item{margin-top:0}
|
||||
.field-repeater[data-mode="grid"]>ul.field-repeater-items>.field-repeater-add-item>a{display:flex;flex-direction:column;justify-content:center;height:100%}
|
||||
.field-repeater[data-mode="grid"]>ul.field-repeater-items>.field-repeater-add-item:before{display:none}
|
||||
.field-repeater[data-mode="grid"][data-columns="2"] ul.field-repeater-items{grid-template-columns:repeat(2,1fr)}
|
||||
.field-repeater[data-mode="grid"][data-columns="3"] ul.field-repeater-items{grid-template-columns:repeat(3,1fr)}
|
||||
.field-repeater[data-mode="grid"][data-columns="4"] ul.field-repeater-items{grid-template-columns:repeat(4,1fr)}
|
||||
@media (max-width:1600px){.field-repeater[data-mode="grid"][data-columns="4"] ul.field-repeater-items{grid-template-columns:repeat(3,1fr)}}
|
||||
.field-repeater[data-mode="grid"][data-columns="5"] ul.field-repeater-items{grid-template-columns:repeat(5,1fr)}
|
||||
@media (max-width:1600px){.field-repeater[data-mode="grid"][data-columns="5"] ul.field-repeater-items{grid-template-columns:repeat(4,1fr)}}
|
||||
.field-repeater[data-mode="grid"][data-columns="6"] ul.field-repeater-items{grid-template-columns:repeat(6,1fr)}
|
||||
@media (max-width:1600px){.field-repeater[data-mode="grid"][data-columns="6"] ul.field-repeater-items{grid-template-columns:repeat(4,1fr)}}
|
||||
@media (min-width:768px) and (max-width:1199px){.field-repeater[data-mode="grid"]>ul.field-repeater-items{grid-template-columns:repeat(2,1fr) !important}}
|
||||
@media (max-width:767px){.field-repeater[data-mode="grid"]>ul.field-repeater-items{grid-template-columns:1fr !important}.field-repeater[data-mode="grid"]>ul.field-repeater-items>.field-repeater-item,.field-repeater[data-mode="grid"]>ul.field-repeater-items>.field-repeater-add-item{min-height:0 !important}.field-repeater[data-mode="grid"]>ul.field-repeater-items>.field-repeater-add-item{margin-top:10px}.field-repeater[data-mode="grid"]>ul.field-repeater-items>.field-repeater-add-item::before{display:block}}
|
||||
.repeater-group-search-container{padding:10px 15px;border-bottom:1px solid #e0e0e0}
|
||||
.repeater-group-search-container>div{position:relative}
|
||||
.repeater-group-search-container .icon-search{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:#999;pointer-events:none;z-index:10}
|
||||
.repeater-group-search-container .repeater-group-search{padding-left:32px;padding-right:32px;width:100%}
|
||||
.repeater-group-search-container .repeater-group-search-clear{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:transparent;border:none;padding:4px 6px;cursor:pointer;display:none;z-index:10;color:#999}
|
||||
.repeater-group-search-container .repeater-group-search-clear i{font-size:14px}
|
||||
.repeater-group-search-container .repeater-group-search-clear:hover{color:#666}
|
||||
.repeater-group-no-results{display:none;padding:20px;text-align:center;color:#999}
|
||||
.repeater-group-items-container{min-width:500px;max-width:800px}
|
||||
.repeater-group-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:8px;padding:15px}
|
||||
.repeater-group-grid .repeater-group-item a{display:flex;height:100%;align-items:center;gap:16px;padding:12px 12px 12px 16px;border:1px solid #e0e0e0;border-radius:3px;text-decoration:none;color:inherit;transition:all 0.2s}
|
||||
.repeater-group-grid .repeater-group-item a i{width:1em;text-align:center;font-size:24px;color:#666;margin-top:2px;flex-shrink:0}
|
||||
.repeater-group-grid .repeater-group-item a>div{flex:1;min-width:0}
|
||||
.repeater-group-grid .repeater-group-item a>div .title{font-weight:600;font-size:13px;display:block;margin-bottom:2px}
|
||||
.repeater-group-grid .repeater-group-item a>div .description{font-size:11px;color:#999;display:block;line-height:1.3}
|
||||
.repeater-group-grid .repeater-group-item a:hover{border-color:#2da7c7;box-shadow:0 2px 8px rgba(0,0,0,0.1);transform:translateY(-2px)}
|
||||
424
modules/backend/formwidgets/repeater/assets/js/repeater.js
Normal file
424
modules/backend/formwidgets/repeater/assets/js/repeater.js
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Field Repeater plugin
|
||||
*
|
||||
* Data attributes:
|
||||
* - data-control="fieldrepeater" - enables the plugin on an element
|
||||
* - data-option="value" - an option with a value
|
||||
*
|
||||
* JavaScript API:
|
||||
* $('a#someElement').fieldRepeater({...})
|
||||
*/
|
||||
|
||||
+function ($) { "use strict";
|
||||
|
||||
var Base = $.wn.foundation.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
// FIELD REPEATER CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Repeater = function(element, options) {
|
||||
this.options = options
|
||||
this.$el = $(element)
|
||||
if (this.options.sortable) {
|
||||
this.$sortable = $(options.sortableContainer, this.$el)
|
||||
}
|
||||
|
||||
$.wn.foundation.controlUtils.markDisposable(element)
|
||||
Base.call(this)
|
||||
this.init()
|
||||
}
|
||||
|
||||
Repeater.prototype = Object.create(BaseProto)
|
||||
Repeater.prototype.constructor = Repeater
|
||||
|
||||
Repeater.DEFAULTS = {
|
||||
sortableHandle: '.repeater-item-handle',
|
||||
sortableContainer: 'ul.field-repeater-items',
|
||||
titleFrom: null,
|
||||
minItems: null,
|
||||
maxItems: null,
|
||||
sortable: false,
|
||||
mode: 'list',
|
||||
style: 'default',
|
||||
}
|
||||
|
||||
Repeater.prototype.init = function() {
|
||||
if (this.options.sortable) {
|
||||
this.bindSorting()
|
||||
}
|
||||
|
||||
this.$el.on('ajaxDone', '> .field-repeater-items > .field-repeater-item > .repeater-item-remove > [data-repeater-remove]', this.proxy(this.onRemoveItemSuccess))
|
||||
this.$el.on('ajaxDone', '> .field-repeater-items > .field-repeater-add-item > [data-repeater-add]', this.proxy(this.onAddItemSuccess))
|
||||
this.$el.on('click', '> ul > li > .repeater-item-collapse .repeater-item-collapse-one', this.proxy(this.toggleCollapse))
|
||||
this.$el.on('click', '> .field-repeater-items > .field-repeater-add-item > [data-repeater-add-group]', this.proxy(this.clickAddGroupButton))
|
||||
|
||||
this.$el.find('> ul > li > .repeater-item-collapsed-title')
|
||||
.css({'cursor': 'pointer', 'width': '100%'})
|
||||
.on('click', this.proxy(this.toggleCollapse))
|
||||
|
||||
this.$el.find('> ul > li > .field-repeater-form > .form-group > label')
|
||||
.css({'cursor': 'pointer', 'width': '100%'})
|
||||
.on('click', this.proxy(this.toggleCollapse))
|
||||
|
||||
this.$el.one('dispose-control', this.proxy(this.dispose))
|
||||
|
||||
this.togglePrompt()
|
||||
this.applyStyle()
|
||||
}
|
||||
|
||||
Repeater.prototype.dispose = function() {
|
||||
if (this.options.sortable) {
|
||||
this.$sortable.sortable('destroy')
|
||||
}
|
||||
|
||||
this.$el.off('ajaxDone', '> .field-repeater-items > .field-repeater-item > .repeater-item-remove > [data-repeater-remove]', this.proxy(this.onRemoveItemSuccess))
|
||||
this.$el.off('ajaxDone', '> .field-repeater-items > .field-repeater-add-item > [data-repeater-add]', this.proxy(this.onAddItemSuccess))
|
||||
this.$el.off('click', '> ul > li > .repeater-item-collapse .repeater-item-collapse-one', this.proxy(this.toggleCollapse))
|
||||
this.$el.off('click', '> ul > li > .repeater-item-collapsed-title', this.proxy(this.toggleCollapse))
|
||||
this.$el.off('click', '> ul > li > .field-repeater-form > .form-group > label', this.proxy(this.toggleCollapse))
|
||||
this.$el.off('click', '> .field-repeater-items > .field-repeater-add-item > [data-repeater-add-group]', this.proxy(this.clickAddGroupButton))
|
||||
|
||||
this.$el.off('dispose-control', this.proxy(this.dispose))
|
||||
this.$el.removeData('oc.repeater')
|
||||
|
||||
this.$el = null
|
||||
this.$sortable = null
|
||||
this.options = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
Repeater.prototype.unbind = function() {
|
||||
this.dispose()
|
||||
}
|
||||
|
||||
Repeater.prototype.bindSorting = function() {
|
||||
var sortableOptions = {
|
||||
handle: this.options.sortableHandle,
|
||||
nested: false,
|
||||
vertical: this.options.mode === 'list',
|
||||
}
|
||||
|
||||
this.$sortable.sortable(sortableOptions)
|
||||
}
|
||||
|
||||
Repeater.prototype.clickAddGroupButton = function(ev) {
|
||||
var $self = this
|
||||
var templateHtml = $('> [data-group-palette-template]', this.$el).html(),
|
||||
$target = $(ev.target),
|
||||
$form = this.$el.closest('form'),
|
||||
$loadContainer = $target.closest('.loading-indicator-container')
|
||||
|
||||
$target.ocPopover({
|
||||
content: templateHtml
|
||||
})
|
||||
|
||||
var $container = $target.data('oc.popover').$container
|
||||
|
||||
// Initialize the scrollpad control in the popup
|
||||
$container.trigger('render')
|
||||
|
||||
$container
|
||||
.on('click', 'a', function (ev) {
|
||||
setTimeout(function() { $(ev.target).trigger('close.oc.popover') }, 1)
|
||||
})
|
||||
.on('ajaxPromise', '[data-repeater-add]', function(ev, context) {
|
||||
$loadContainer.loadIndicator()
|
||||
|
||||
$(window).one('ajaxUpdateComplete', function() {
|
||||
$loadContainer.loadIndicator('hide')
|
||||
$self.togglePrompt()
|
||||
$($self.$el).find('.field-repeater-items > .field-repeater-add-item').each(function () {
|
||||
if ($(this).children().length === 0) {
|
||||
$(this).remove()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
$('[data-repeater-add]', $container).data('request-form', $form)
|
||||
|
||||
// Setup search functionality
|
||||
var $searchInput = $('.repeater-group-search', $container)
|
||||
var $clearButton = $('.repeater-group-search-clear', $container)
|
||||
var $noResults = $('.repeater-group-no-results', $container)
|
||||
var $itemsContainer = $('.repeater-group-items-container', $container)
|
||||
var $listItems = $('.repeater-group-item', $container)
|
||||
|
||||
// Add hover effects to grid items
|
||||
$listItems.on('mouseenter', 'a', function() {
|
||||
$(this).css({
|
||||
'border-color': '#3498db',
|
||||
'box-shadow': '0 2px 8px rgba(0,0,0,0.1)',
|
||||
'transform': 'translateY(-2px)'
|
||||
})
|
||||
}).on('mouseleave', 'a', function() {
|
||||
$(this).css({
|
||||
'border-color': '#e0e0e0',
|
||||
'box-shadow': 'none',
|
||||
'transform': 'translateY(0)'
|
||||
})
|
||||
})
|
||||
|
||||
$searchInput.on('input', function() {
|
||||
var searchTerm = $(this).val().toLowerCase().trim()
|
||||
|
||||
// Show/hide clear button
|
||||
$clearButton.toggle(searchTerm.length > 0)
|
||||
|
||||
if (searchTerm === '') {
|
||||
// Show all items
|
||||
$listItems.show()
|
||||
$noResults.hide()
|
||||
$itemsContainer.show()
|
||||
return
|
||||
}
|
||||
|
||||
var visibleCount = 0
|
||||
|
||||
// Filter items
|
||||
$listItems.each(function() {
|
||||
var $item = $(this)
|
||||
var title = $('.title', $item).text().toLowerCase()
|
||||
var description = $('.description', $item).text().toLowerCase()
|
||||
|
||||
if (title.indexOf(searchTerm) !== -1 || description.indexOf(searchTerm) !== -1) {
|
||||
$item.show()
|
||||
visibleCount++
|
||||
} else {
|
||||
$item.hide()
|
||||
}
|
||||
})
|
||||
|
||||
// Show/hide no results message and adjust container height
|
||||
if (visibleCount === 0) {
|
||||
$noResults.show()
|
||||
$itemsContainer.hide()
|
||||
} else {
|
||||
$noResults.hide()
|
||||
$itemsContainer.show()
|
||||
}
|
||||
})
|
||||
|
||||
// Clear search functionality
|
||||
$clearButton.on('click', function() {
|
||||
$searchInput.val('').trigger('input').focus()
|
||||
})
|
||||
|
||||
// Focus search input when popover opens
|
||||
setTimeout(function() {
|
||||
$searchInput.focus()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
Repeater.prototype.onRemoveItemSuccess = function(ev) {
|
||||
var $target = $(ev.target)
|
||||
|
||||
// Allow any widgets inside a deleted item to be disposed
|
||||
$target.closest('.field-repeater-item').find('[data-disposable]').each(function () {
|
||||
var $elem = $(this),
|
||||
control = $elem.data('control'),
|
||||
widget = $elem.data('oc.' + control)
|
||||
|
||||
if (widget && typeof widget['dispose'] === 'function') {
|
||||
widget.dispose()
|
||||
}
|
||||
})
|
||||
|
||||
$target.closest('[data-field-name]').trigger('change.oc.formwidget')
|
||||
$target.closest('.field-repeater-item').remove()
|
||||
this.togglePrompt()
|
||||
}
|
||||
|
||||
Repeater.prototype.onAddItemSuccess = function(ev) {
|
||||
window.requestAnimationFrame(() => {
|
||||
this.togglePrompt()
|
||||
$(ev.target).closest('[data-field-name]').trigger('change.oc.formwidget')
|
||||
$(this.$el).find('.field-repeater-items > .field-repeater-add-item').each(function () {
|
||||
if ($(this).children().length === 0) {
|
||||
$(this).remove()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Repeater.prototype.togglePrompt = function () {
|
||||
if (this.options.minItems && this.options.minItems > 0) {
|
||||
var repeatedItems = this.$el.find('> .field-repeater-items > .field-repeater-item').length,
|
||||
$removeItemBtn = this.$el.find('> .field-repeater-items > .field-repeater-item > .repeater-item-remove')
|
||||
|
||||
$removeItemBtn.toggleClass('disabled', !(repeatedItems > this.options.minItems))
|
||||
}
|
||||
|
||||
if (this.options.maxItems && this.options.maxItems > 0) {
|
||||
var repeatedItems = this.$el.find('> .field-repeater-items > .field-repeater-item').length,
|
||||
$addItemBtn = this.$el.find('> .field-repeater-items > .field-repeater-add-item')
|
||||
|
||||
$addItemBtn.toggle(repeatedItems < this.options.maxItems)
|
||||
}
|
||||
}
|
||||
|
||||
Repeater.prototype.toggleCollapse = function(ev) {
|
||||
var $item = $(ev.target).closest('.field-repeater-item'),
|
||||
isCollapsed = $item.hasClass('collapsed')
|
||||
|
||||
ev.preventDefault()
|
||||
|
||||
if (this.getStyle() === 'accordion') {
|
||||
if (isCollapsed) {
|
||||
this.expand($item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (ev.ctrlKey || ev.metaKey) {
|
||||
isCollapsed ? this.expandAll() : this.collapseAll()
|
||||
}
|
||||
else {
|
||||
isCollapsed ? this.expand($item) : this.collapse($item)
|
||||
}
|
||||
}
|
||||
|
||||
Repeater.prototype.collapseAll = function() {
|
||||
var self = this,
|
||||
items = $(this.$el).children('.field-repeater-items').children('.field-repeater-item')
|
||||
|
||||
$.each(items, function(key, item){
|
||||
self.collapse($(item))
|
||||
})
|
||||
}
|
||||
|
||||
Repeater.prototype.expandAll = function() {
|
||||
var self = this,
|
||||
items = $(this.$el).children('.field-repeater-items').children('.field-repeater-item')
|
||||
|
||||
$.each(items, function(key, item){
|
||||
self.expand($(item))
|
||||
})
|
||||
}
|
||||
|
||||
Repeater.prototype.collapse = function($item) {
|
||||
$item.addClass('collapsed')
|
||||
$('.repeater-item-collapsed-title', $item).text(this.getCollapseTitle($item))
|
||||
}
|
||||
|
||||
Repeater.prototype.expand = function($item) {
|
||||
if (this.getStyle() === 'accordion') {
|
||||
this.collapseAll()
|
||||
}
|
||||
$item.removeClass('collapsed')
|
||||
}
|
||||
|
||||
Repeater.prototype.getCollapseTitle = function($item) {
|
||||
var $target,
|
||||
defaultText = '',
|
||||
explicitText = $item.data('collapse-title'),
|
||||
selector = 'input[type=text], select:first, ul:first'
|
||||
|
||||
if (explicitText) {
|
||||
return explicitText
|
||||
}
|
||||
|
||||
if (this.options.titleFrom) {
|
||||
$target = $('[data-field-name="'+this.options.titleFrom+'"]', $item)
|
||||
if ($target.length) {
|
||||
selector = 'input, select:first, ul:first'
|
||||
}
|
||||
}
|
||||
if (!$target || !$target.length) {
|
||||
$target = $item
|
||||
}
|
||||
|
||||
var $textInput = $(selector, $target).first()
|
||||
if ($textInput.length) {
|
||||
switch($textInput.prop("tagName")) {
|
||||
case 'SELECT':
|
||||
return $textInput.find('option:selected').text()
|
||||
case 'UL':
|
||||
return $textInput.find('li.active').text()
|
||||
default:
|
||||
return $textInput.val()
|
||||
}
|
||||
} else {
|
||||
var $disabledTextInput = $('.form-control:first', $target)
|
||||
if ($disabledTextInput.length) {
|
||||
return $disabledTextInput.text()
|
||||
}
|
||||
}
|
||||
|
||||
return defaultText
|
||||
}
|
||||
|
||||
Repeater.prototype.getStyle = function() {
|
||||
var style = 'default'
|
||||
|
||||
// Validate style
|
||||
if (this.options.style && ['collapsed', 'accordion'].indexOf(this.options.style) !== -1) {
|
||||
style = this.options.style
|
||||
}
|
||||
|
||||
return style
|
||||
}
|
||||
|
||||
Repeater.prototype.applyStyle = function() {
|
||||
if (this.options.mode === 'grid') {
|
||||
return
|
||||
}
|
||||
|
||||
var style = this.getStyle(),
|
||||
self = this,
|
||||
items = $(this.$el).children('.field-repeater-items').children('.field-repeater-item')
|
||||
|
||||
$.each(items, function(key, item) {
|
||||
switch (style) {
|
||||
case 'collapsed':
|
||||
self.collapse($(item))
|
||||
break
|
||||
case 'accordion':
|
||||
if (key !== 0) {
|
||||
self.collapse($(item))
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FIELD REPEATER PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
var old = $.fn.fieldRepeater
|
||||
|
||||
$.fn.fieldRepeater = function (option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1), result
|
||||
this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('oc.repeater')
|
||||
var options = $.extend({}, Repeater.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
if (!data) $this.data('oc.repeater', (data = new Repeater(this, options)))
|
||||
if (typeof option == 'string') result = data[option].apply(data, args)
|
||||
if (typeof result != 'undefined') return false
|
||||
})
|
||||
|
||||
return result ? result : this
|
||||
}
|
||||
|
||||
$.fn.fieldRepeater.Constructor = Repeater
|
||||
|
||||
// FIELD REPEATER NO CONFLICT
|
||||
// =================
|
||||
|
||||
$.fn.fieldRepeater.noConflict = function () {
|
||||
$.fn.fieldRepeater = old
|
||||
return this
|
||||
}
|
||||
|
||||
// FIELD REPEATER DATA-API
|
||||
// ===============
|
||||
|
||||
$(document).render(function() {
|
||||
$('[data-control="fieldrepeater"]').fieldRepeater()
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
442
modules/backend/formwidgets/repeater/assets/less/repeater.less
Normal file
442
modules/backend/formwidgets/repeater/assets/less/repeater.less
Normal file
@@ -0,0 +1,442 @@
|
||||
// out: false
|
||||
|
||||
@import "../../../../assets/less/core/boot.less";
|
||||
|
||||
.field-repeater {
|
||||
padding-top: 5px;
|
||||
|
||||
.field-repeater-items {
|
||||
counter-reset: repeater-index-counter;
|
||||
}
|
||||
|
||||
ul.field-repeater-items,
|
||||
li.field-repeater-item {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul.field-repeater-items > li {
|
||||
&.dragged {
|
||||
opacity: .7;
|
||||
position: absolute;
|
||||
padding-top: 15px;
|
||||
padding-right: 15px;
|
||||
z-index: 2000;
|
||||
background-color: @body-bg;
|
||||
border: 1px dashed #dbdee0;
|
||||
|
||||
.repeater-item-remove {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.repeater-item-collapsed-title {
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&.placeholder {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 25px;
|
||||
margin-bottom: 5px;
|
||||
&:before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
.icon(@chevron-right);
|
||||
color: #d35714;
|
||||
left: -10px;
|
||||
top: 8px;
|
||||
z-index: 2000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li.field-repeater-item {
|
||||
position: relative;
|
||||
margin: 0 0 1em 1em !important;
|
||||
padding: 1.5em 1.25em 0 1.25em !important;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid @input-border;
|
||||
border-radius: @border-radius-base;
|
||||
box-shadow: @input-box-shadow;
|
||||
min-height: 30px;
|
||||
|
||||
&:before {
|
||||
color: #bdc3c7;
|
||||
.icon(@circle);
|
||||
font-size: 8px;
|
||||
position: absolute;
|
||||
left: -18px;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
counter-increment: repeater-index-counter;
|
||||
content: counter(repeater-index-counter);
|
||||
display: block;
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
left: -27px;
|
||||
color: #bdc3c7;
|
||||
top: 10px;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
.field-repeater-form {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.repeater-item-collapse {
|
||||
.repeater-item-collapse-one {
|
||||
.transform(scale(1,-1));
|
||||
}
|
||||
}
|
||||
|
||||
.repeater-item-collapsed-title {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.repeater-item-collapsed-title {
|
||||
display: none;
|
||||
position: absolute;
|
||||
font-size: 13px;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
.transform(translateY(-50%));
|
||||
}
|
||||
|
||||
.field-repeater-form {
|
||||
position: relative;
|
||||
top: -7px;
|
||||
.clearfix;
|
||||
}
|
||||
|
||||
.repeater-item-handle {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: -25px;
|
||||
color: #bdc3c7;
|
||||
background: @body-bg;
|
||||
cursor: move;
|
||||
opacity: 0;
|
||||
border-radius: 999px;
|
||||
.transition(~'opacity 0.5s');
|
||||
&:hover {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.repeater-item-collapse {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 30px;
|
||||
z-index: 90;
|
||||
|
||||
a, button {
|
||||
.transition(~'transform 0.3s');
|
||||
color: #bdc3c7;
|
||||
line-height: 20px;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: #999;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.repeater-item-remove {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 5px;
|
||||
z-index: 90;
|
||||
|
||||
&.disabled {
|
||||
display: none;
|
||||
|
||||
+ .repeater-item-collapse {
|
||||
right: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.close {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.repeater-item-collapse,
|
||||
.repeater-item-handle,
|
||||
.repeater-item-remove {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:hover, &:active {
|
||||
.repeater-item-collapse,
|
||||
.repeater-item-handle,
|
||||
.repeater-item-remove {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.field-repeater-add-item {
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-left: 20px;
|
||||
border: 2px dotted #e0e0e0;
|
||||
border-radius: 5px;
|
||||
|
||||
&:before {
|
||||
color: #bdc3c7;
|
||||
.icon(@plus);
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
left: -23px;
|
||||
top: -11px;
|
||||
}
|
||||
|
||||
> a {
|
||||
color: #bdc3c7;
|
||||
text-align: center;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: 13px 15px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: @font-size-base - 2;
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: @highlight-hover-bg;
|
||||
border-color: transparent;
|
||||
|
||||
&:before {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
> a {
|
||||
color: @highlight-hover-text;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: @highlight-active-bg;
|
||||
border-color: transparent;
|
||||
> a {
|
||||
color: @highlight-active-text;
|
||||
}
|
||||
}
|
||||
|
||||
&.in-progress {
|
||||
border-color: #e0e0e0 !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-mode="grid"] {
|
||||
> ul.field-repeater-items {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
|
||||
> .field-repeater-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
> .field-repeater-add-item {
|
||||
margin-top: 0;
|
||||
|
||||
> a {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&[data-columns="2"] ul.field-repeater-items {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
&[data-columns="3"] ul.field-repeater-items {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
&[data-columns="4"] ul.field-repeater-items {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
|
||||
@media (max-width: 1600px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
&[data-columns="5"] ul.field-repeater-items {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
|
||||
@media (max-width: 1600px) {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
&[data-columns="6"] ul.field-repeater-items {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
|
||||
@media (max-width: 1600px) {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: @screen-sm-min) and (max-width: @screen-md-max) {
|
||||
> ul.field-repeater-items {
|
||||
grid-template-columns: repeat(2, 1fr) !important;;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @screen-xs-max) {
|
||||
> ul.field-repeater-items {
|
||||
grid-template-columns: 1fr !important;
|
||||
|
||||
> .field-repeater-item,
|
||||
> .field-repeater-add-item {
|
||||
min-height: 0 !important;
|
||||
}
|
||||
|
||||
> .field-repeater-add-item {
|
||||
margin-top: 10px;
|
||||
|
||||
&::before {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Grouped Repeater Palette
|
||||
//
|
||||
|
||||
.repeater-group-search-container {
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
|
||||
> div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon-search {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #999;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.repeater-group-search {
|
||||
padding-left: 32px;
|
||||
padding-right: 32px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.repeater-group-search-clear {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 4px 6px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
z-index: 10;
|
||||
color: #999;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.repeater-group-no-results {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.repeater-group-items-container {
|
||||
min-width: 500px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.repeater-group-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 8px;
|
||||
padding: 15px;
|
||||
|
||||
.repeater-group-item {
|
||||
a {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 12px 12px 12px 16px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: @border-radius-base;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: all 0.2s;
|
||||
|
||||
i {
|
||||
width: 1em;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
color: #666;
|
||||
margin-top: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
display: block;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: @link-color;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user