diff --git a/.eslintrc.js b/.eslintrc.js index d401f45d2..7160b45c3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -8,7 +8,8 @@ module.exports = { wiki: 'writable', Vue: 'readable', _t: 'readable', - ace: 'writable' + ace: 'writable', + toastMessage: 'readable' }, extends: [ 'airbnb-base' @@ -31,7 +32,8 @@ module.exports = { 'object-curly-newline': ['error', { multiline: true }], 'func-names': ['error', 'never'], 'space-before-function-paren': ['error', 'never'], - 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }] + 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }], + 'no-new': 'off' }, ignorePatterns: [ 'vendor/', diff --git a/README.md b/README.md index 50d377eb9..31cdc6dde 100755 --- a/README.md +++ b/README.md @@ -27,13 +27,13 @@ We are using [weblate](https://hosted.weblate.org/yeswiki) to translate our soft ## Developers -We recommand an installation through docker. +We recommend an installation through docker. ### Linters & Formatters -Please install relevant extension and enable auto formating on your editor. +Please install relevant extension and enable auto formatting on your editor. -Alternatly you can run `make lint` +Alternatively you can run `make lint` | Language | Linter/Formatter | | --------------------------- | ------------------------------------------------------------ | @@ -42,7 +42,7 @@ Alternatly you can run `make lint` | Twig | no automatic linter. Couldn't find one which is good enough. | | CSS, Yaml, JSON, Markdown.. | `prettier` | -If you use VS Codium, get yeswiki linting settings with `cp .vscode/settings.example.json .vscode/settings.json` +If you use VS Codium, get YesWiki linting settings with `cp .vscode/settings.example.json .vscode/settings.json` ## History @@ -71,4 +71,4 @@ YesWiki grew out of a French language version of [WakkaWiki](https://en.wikipedi ### YesWiki authors -See +See \ No newline at end of file diff --git a/handlers/EditIframeHandler.php b/handlers/EditIframeHandler.php index 8960583bf..853312250 100644 --- a/handlers/EditIframeHandler.php +++ b/handlers/EditIframeHandler.php @@ -35,7 +35,7 @@ public function run() } } - $this->wiki->AddJavascriptFile('tools/bazar/libs/bazar.js'); + $this->wiki->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); $output .= '' . "\n" . '
' . "\n" . '
' . "\n"; diff --git a/handlers/IframeHandler.php b/handlers/IframeHandler.php index 901946214..b48ef48f3 100644 --- a/handlers/IframeHandler.php +++ b/handlers/IframeHandler.php @@ -91,7 +91,7 @@ private function renderBazarEntry(): string { $output = ''; // si la page est une fiche bazar, alors on affiche la fiche plutot que de formater en wiki - $this->wiki->AddJavascriptFile('tools/bazar/libs/bazar.js'); + $this->wiki->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); $valjson = $this->wiki->page['body']; $tab_valeurs = json_decode($valjson, true); if (YW_CHARSET != 'UTF-8') { diff --git a/includes/migrations/20240502083251_RefactorListStruture.php b/includes/migrations/20240502083251_RefactorListStruture.php new file mode 100644 index 000000000..55c5e4da0 --- /dev/null +++ b/includes/migrations/20240502083251_RefactorListStruture.php @@ -0,0 +1,26 @@ +wiki->services->get(TripleStore::class); + $pageManager = $this->wiki->services->get(PageManager::class); + $listManager = $this->wiki->services->get(ListManager::class); + $lists = $tripleStore->getMatching(null, TripleStore::TYPE_URI, ListManager::TRIPLES_LIST_ID, '', ''); + foreach ($lists as $list) { + $tag = $list['resource']; + $page = $pageManager->getOne($tag); + $oldJson = json_decode($page['body'], true); + $newJson = $listManager->convertDataStructure($oldJson); + $pageManager->save($tag, json_encode($newJson)); + } + } +} \ No newline at end of file diff --git a/includes/migrations/20240621202127_RefactorEnumFieldPropertyName.php b/includes/migrations/20240621202127_RefactorEnumFieldPropertyName.php new file mode 100644 index 000000000..43e052082 --- /dev/null +++ b/includes/migrations/20240621202127_RefactorEnumFieldPropertyName.php @@ -0,0 +1,28 @@ +getService(FormManager::class); + $fieldFactory = $this->getService(FieldFactory::class); + $forms = $formManager->getAll(); + foreach ($forms as $form) { + $newTemplate = []; + foreach ($form['template'] as $fieldArray) { + $field = $fieldFactory->create($fieldArray); + if ($field instanceof EnumField) { + $fieldArray[EnumField::FIELD_NAME] = $field->getType() . $field->getLinkedObjectName() . $field->getName(); + } + $newTemplate[] = $fieldArray; + } + $form['bn_template'] = $formManager->encodeTemplate($newTemplate); + $formManager->update($form); + } + } +} diff --git a/javascripts/shared-components/CollapseTransition.js b/javascripts/shared-components/CollapseTransition.js new file mode 100644 index 000000000..6fa1b595e --- /dev/null +++ b/javascripts/shared-components/CollapseTransition.js @@ -0,0 +1,223 @@ +// This code come from https://github.com/BinarCode/vue2-transitions + +const BaseTransitionMixin = { + inheritAttrs: false, + props: { + /** + * Transition duration. Number for specifying the same duration for enter/leave transitions + * Object style {enter: 300, leave: 300} for specifying explicit durations for enter/leave + */ + duration: { + type: [Number, Object], + default: 200 + }, + /** + * Transition delay. Number for specifying the same delay for enter/leave transitions + * Object style {enter: 300, leave: 300} for specifying explicit durations for enter/leave + */ + delay: { + type: [Number, Object], + default: 0 + }, + /** + * Whether the component should be a `transition-group` component. + */ + group: Boolean, + /** + * Transition tag, in case the component is a `transition-group` + */ + tag: { + type: String, + default: 'span' + }, + /** + * Transform origin property https://tympanus.net/codrops/css_reference/transform-origin/. + * Can be specified with styles as well but it's shorter with this prop + */ + origin: { + type: String, + default: '' + }, + /** + * Element styles that are applied during transition. These styles are applied on @beforeEnter and @beforeLeave hooks + */ + styles: { + type: Object, + default: () => ({ + animationFillMode: 'both', + animationTimingFunction: 'ease-out' + }) + } + }, + computed: { + componentType() { + return this.group ? 'transition-group' : 'transition' + }, + hooks() { + return { + ...this.$listeners, + beforeEnter: this.beforeEnter, + afterEnter: (el) => { + this.cleanUpStyles(el) + this.$emit('after-enter', el) + }, + beforeLeave: this.beforeLeave, + leave: this.leave, + afterLeave: (el) => { + this.cleanUpStyles(el) + this.$emit('after-leave', el) + } + } + } + }, + methods: { + beforeEnter(el) { + const enterDuration = this.duration.enter ? this.duration.enter : this.duration + el.style.animationDuration = `${enterDuration}ms` + + const enterDelay = this.delay.enter ? this.delay.enter : this.delay + el.style.animationDelay = `${enterDelay}ms` + + this.setStyles(el) + this.$emit('before-enter', el) + }, + cleanUpStyles(el) { + Object.keys(this.styles).forEach((key) => { + const styleValue = this.styles[key] + if (styleValue) { + el.style[key] = '' + } + }) + el.style.animationDuration = '' + el.style.animationDelay = '' + }, + beforeLeave(el) { + const leaveDuration = this.duration.leave ? this.duration.leave : this.duration + el.style.animationDuration = `${leaveDuration}ms` + + const leaveDelay = this.delay.leave ? this.delay.leave : this.delay + el.style.animationDelay = `${leaveDelay}ms` + + this.setStyles(el) + this.$emit('before-leave', el) + }, + leave(el, done) { + this.setAbsolutePosition(el) + this.$emit('leave', el, done) + }, + setStyles(el) { + this.setTransformOrigin(el) + Object.keys(this.styles).forEach((key) => { + const styleValue = this.styles[key] + if (styleValue) { + el.style[key] = styleValue + } + }) + }, + setAbsolutePosition(el) { + if (this.group) { + el.style.position = 'absolute' + } + return this + }, + setTransformOrigin(el) { + if (this.origin) { + el.style.transformOrigin = this.origin + } + return this + } + } +} + +export default { + name: 'collapse-transition', + mixins: [BaseTransitionMixin], + methods: { + transitionStyle(duration = 300) { + const durationInSeconds = duration / 1000 + const style = `${durationInSeconds}s height ease-in-out, ${durationInSeconds}s padding-top ease-in-out, ${durationInSeconds}s padding-bottom ease-in-out` + return style + }, + beforeEnter(el) { + const enterDuration = this.duration.enter ? this.duration.enter : this.duration + el.style.transition = this.transitionStyle(enterDuration) + if (!el.dataset) el.dataset = {} + + el.dataset.oldPaddingTop = el.style.paddingTop + el.dataset.oldPaddingBottom = el.style.paddingBottom + + el.style.height = '0' + el.style.paddingTop = 0 + el.style.paddingBottom = 0 + this.setStyles(el) + }, + + enter(el) { + el.dataset.oldOverflow = el.style.overflow + if (el.scrollHeight !== 0) { + el.style.height = `${el.scrollHeight}px` + el.style.paddingTop = el.dataset.oldPaddingTop + el.style.paddingBottom = el.dataset.oldPaddingBottom + } else { + el.style.height = '' + el.style.paddingTop = el.dataset.oldPaddingTop + el.style.paddingBottom = el.dataset.oldPaddingBottom + } + + el.style.overflow = 'hidden' + }, + + afterEnter(el) { + // for safari: remove class then reset height is necessary + el.style.transition = '' + el.style.height = '' + el.style.overflow = el.dataset.oldOverflow + }, + + beforeLeave(el) { + if (!el.dataset) el.dataset = {} + el.dataset.oldPaddingTop = el.style.paddingTop + el.dataset.oldPaddingBottom = el.style.paddingBottom + el.dataset.oldOverflow = el.style.overflow + + el.style.height = `${el.scrollHeight}px` + el.style.overflow = 'hidden' + this.setStyles(el) + }, + + leave(el) { + const leaveDuration = this.duration.leave ? this.duration.leave : this.duration + if (el.scrollHeight !== 0) { + // for safari: add class after set height, or it will jump to zero height suddenly, weired + el.style.transition = this.transitionStyle(leaveDuration) + el.style.height = 0 + el.style.paddingTop = 0 + el.style.paddingBottom = 0 + } + // necessary for transition-group + this.setAbsolutePosition(el) + }, + + afterLeave(el) { + el.style.transition = '' + el.style.height = '' + el.style.overflow = el.dataset.oldOverflow + el.style.paddingTop = el.dataset.oldPaddingTop + el.style.paddingBottom = el.dataset.oldPaddingBottom + } + }, + template: ` + + + ` +} diff --git a/tools/bazar/presentation/javascripts/components/Panel.js b/javascripts/shared-components/Panel.js similarity index 69% rename from tools/bazar/presentation/javascripts/components/Panel.js rename to javascripts/shared-components/Panel.js index 9c0afb812..97ac86c1a 100644 --- a/tools/bazar/presentation/javascripts/components/Panel.js +++ b/javascripts/shared-components/Panel.js @@ -1,3 +1,5 @@ +import CollapseTransition from './CollapseTransition.js' + export default { props: { color: { @@ -13,8 +15,11 @@ export default { default: true } }, + components: { CollapseTransition }, data() { - return { internalCollapsed: true // value to work internally, name should not conflict with prop + return { + // value to work internally, name should not conflict with prop + internalCollapsed: true } }, computed: { @@ -30,7 +35,7 @@ export default { if (!this.internalCollapsed) this.$emit('opened') } }, - mounted() { + beforeMount() { this.internalCollapsed = this.collapsed }, template: ` @@ -40,10 +45,11 @@ export default { @click="headerClicked()"> -
- -
-
+ +
+ +
+
` } diff --git a/javascripts/vendor/extract-files-from-node-modules.sh b/javascripts/vendor/extract-files-from-node-modules.sh index b2bc6e9a5..8d60ce8f5 100755 --- a/javascripts/vendor/extract-files-from-node-modules.sh +++ b/javascripts/vendor/extract-files-from-node-modules.sh @@ -23,6 +23,9 @@ mkdir -p javascripts/vendor/vue-select && cp -f node_modules/vue-select/dist/vue mkdir -p styles/vendor/vue-select && cp -f node_modules/vue-select/dist/vue-select.css styles/vendor/vue-select # Vue Leaflet mkdir -p javascripts/vendor/vue2-leaflet && cp -f node_modules/vue2-leaflet/dist/vue2-leaflet.min.js javascripts/vendor/vue2-leaflet/vue2-leaflet.js +# Vue draggable +mkdir -p javascripts/vendor/sortablejs && cp -f node_modules/sortablejs/Sortable.min.js javascripts/vendor/sortablejs/sortable.js +mkdir -p javascripts/vendor/vuedraggable && cp -f node_modules/vuedraggable/dist/vuedraggable.umd.js javascripts/vendor/vuedraggable/vuedraggable.js # Leaflet mkdir -p javascripts/vendor/leaflet && cp -f node_modules/leaflet/dist/leaflet.js javascripts/vendor/leaflet/leaflet.min.js diff --git a/javascripts/vendor/sortablejs/sortable.js b/javascripts/vendor/sortablejs/sortable.js new file mode 100644 index 000000000..bb9953355 --- /dev/null +++ b/javascripts/vendor/sortablejs/sortable.js @@ -0,0 +1,2 @@ +/*! Sortable 1.15.2 - MIT | git://github.com/SortableJS/Sortable.git */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Sortable=e()}(this,function(){"use strict";function e(e,t){var n,o=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),o.push.apply(o,n)),o}function I(o){for(var t=1;tt.length)&&(e=t.length);for(var n=0,o=new Array(e);n"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}function P(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"!==e[0]||t.parentNode===n)&&p(t,e)||o&&t===n)return t}while(t!==n&&(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode))}var i;return null}var g,m=/\s+/g;function k(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(m," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(m," ")))}function R(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=!(e in o||-1!==e.indexOf("webkit"))?"-webkit-"+e:e]=n+("string"==typeof n?"":"px")}}function v(t,e){var n="";if("string"==typeof t)n=t;else do{var o=R(t,"transform")}while(o&&"none"!==o&&(n=o+" "+n),!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function b(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i=n.left-e&&i<=n.right+e,e=r>=n.top-e&&r<=n.bottom+e;return o&&e?a=t:void 0}}),a);if(e){var n,o={};for(n in t)t.hasOwnProperty(n)&&(o[n]=t[n]);o.target=o.rootEl=e,o.preventDefault=void 0,o.stopPropagation=void 0,e[K]._onDragOver(o)}}var i,r,a}function Bt(t){V&&V.parentNode[K]._isOutsideThisEl(t.target)}function Ft(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[K]=this;var n,o,i={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Pt(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Ft.supportPointer&&"PointerEvent"in window&&!u,emptyInsertThreshold:5};for(n in W.initializePlugins(this,t,i),i)n in e||(e[n]=i[n]);for(o in kt(e),this)"_"===o.charAt(0)&&"function"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!e.forceFallback&&Nt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?h(t,"pointerdown",this._onTapStart):(h(t,"mousedown",this._onTapStart),h(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(h(t,"dragover",this),h(t,"dragenter",this)),Dt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,x())}function jt(t,e,n,o,i,r,a,l){var s,c,u=t[K],d=u.options.onMove;return!window.CustomEvent||y||w?(s=document.createEvent("Event")).initEvent("move",!0,!0):s=new CustomEvent("move",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||X(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),c=d?d.call(u,s,a):c}function Ht(t){t.draggable=!1}function Lt(){Tt=!1}function Kt(t){return setTimeout(t,0)}function Wt(t){return clearTimeout(t)}Ft.prototype={constructor:Ft,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(mt=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,V):this.options.direction},_onTapStart:function(e){if(e.cancelable){var n=this,o=this.el,t=this.options,i=t.preventOnFilter,r=e.type,a=e.touches&&e.touches[0]||e.pointerType&&"touch"===e.pointerType&&e,l=(a||e).target,s=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||l,c=t.filter;if(!function(t){xt.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&xt.push(o)}}(o),!V&&!(/mousedown|pointerdown/.test(r)&&0!==e.button||t.disabled)&&!s.isContentEditable&&(this.nativeDraggable||!u||!l||"SELECT"!==l.tagName.toUpperCase())&&!((l=P(l,t.draggable,o,!1))&&l.animated||tt===l)){if(ot=j(l),rt=j(l,t.draggable),"function"==typeof c){if(c.call(this,e,l,this))return q({sortable:n,rootEl:s,name:"filter",targetEl:l,toEl:o,fromEl:o}),G("filter",n,{evt:e}),void(i&&e.cancelable&&e.preventDefault())}else if(c=c&&c.split(",").some(function(t){if(t=P(s,t.trim(),o,!1))return q({sortable:n,rootEl:t,name:"filter",targetEl:l,fromEl:o,toEl:o}),G("filter",n,{evt:e}),!0}))return void(i&&e.cancelable&&e.preventDefault());t.handle&&!P(s,t.handle,o,!1)||this._prepareDragStart(e,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,a=i.options,l=r.ownerDocument;n&&!V&&n.parentNode===r&&(o=X(n),Q=r,Z=(V=n).parentNode,J=V.nextSibling,tt=n,lt=a.group,ct={target:Ft.dragged=V,clientX:(e||t).clientX,clientY:(e||t).clientY},ft=ct.clientX-o.left,pt=ct.clientY-o.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,V.style["will-change"]="all",o=function(){G("delayEnded",i,{evt:t}),Ft.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(V.draggable=!0),i._triggerDragStart(t,e),q({sortable:i,name:"choose",originalEvent:t}),k(V,a.chosenClass,!0))},a.ignore.split(",").forEach(function(t){b(V,t.trim(),Ht)}),h(l,"dragover",Yt),h(l,"mousemove",Yt),h(l,"touchmove",Yt),h(l,"mouseup",i._onDrop),h(l,"touchend",i._onDrop),h(l,"touchcancel",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,V.draggable=!0),G("delayStart",this,{evt:t}),!a.delay||a.delayOnTouchOnly&&!e||this.nativeDraggable&&(w||y)?o():Ft.eventCanceled?this._onDrop():(h(l,"mouseup",i._disableDelayedDrag),h(l,"touchend",i._disableDelayedDrag),h(l,"touchcancel",i._disableDelayedDrag),h(l,"mousemove",i._delayedDragTouchMoveHandler),h(l,"touchmove",i._delayedDragTouchMoveHandler),a.supportPointer&&h(l,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,a.delay)))},_delayedDragTouchMoveHandler:function(t){t=t.touches?t.touches[0]:t;Math.max(Math.abs(t.clientX-this._lastX),Math.abs(t.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){V&&Ht(V),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._disableDelayedDrag),f(t,"touchend",this._disableDelayedDrag),f(t,"touchcancel",this._disableDelayedDrag),f(t,"mousemove",this._delayedDragTouchMoveHandler),f(t,"touchmove",this._delayedDragTouchMoveHandler),f(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?h(document,"pointermove",this._onTouchMove):h(document,e?"touchmove":"mousemove",this._onTouchMove):(h(V,"dragend",this),h(Q,"dragstart",this._onDragStart));try{document.selection?Kt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){var n;wt=!1,Q&&V?(G("dragStarted",this,{evt:e}),this.nativeDraggable&&h(document,"dragover",Bt),n=this.options,t||k(V,n.dragClass,!1),k(V,n.ghostClass,!0),Ft.active=this,t&&this._appendGhost(),q({sortable:this,name:"start",originalEvent:e})):this._nulling()},_emulateDragOver:function(){if(ut){this._lastX=ut.clientX,this._lastY=ut.clientY,Rt();for(var t=document.elementFromPoint(ut.clientX,ut.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(ut.clientX,ut.clientY))!==e;)e=t;if(V.parentNode[K]._isOutsideThisEl(t),e)do{if(e[K])if(e[K]._onDragOver({clientX:ut.clientX,clientY:ut.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}while(e=(t=e).parentNode);Xt()}},_onTouchMove:function(t){if(ct){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=$&&v($,!0),a=$&&r&&r.a,l=$&&r&&r.d,e=Mt&&yt&&E(yt),a=(i.clientX-ct.clientX+o.x)/(a||1)+(e?e[0]-Ct[0]:0)/(a||1),l=(i.clientY-ct.clientY+o.y)/(l||1)+(e?e[1]-Ct[1]:0)/(l||1);if(!Ft.active&&!wt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))D.right+10||S.clientY>x.bottom&&S.clientX>x.left:S.clientY>D.bottom+10||S.clientX>x.right&&S.clientY>x.top)||m.animated)){if(m&&(t=n,e=r,C=X(B((_=this).el,0,_.options,!0)),_=L(_.el,_.options,$),e?t.clientX<_.left-10||t.clientY String#at +// false -> String#codePointAt +module.exports = function (TO_STRING) { + return function (that, pos) { + var s = String(defined(that)); + var i = toInteger(pos); + var l = s.length; + var a, b; + if (i < 0 || i >= l) return TO_STRING ? '' : undefined; + a = s.charCodeAt(i); + return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff + ? TO_STRING ? s.charAt(i) : a + : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; + }; +}; + + +/***/ }), + +/***/ "0390": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var at = __webpack_require__("02f4")(true); + + // `AdvanceStringIndex` abstract operation +// https://tc39.github.io/ecma262/#sec-advancestringindex +module.exports = function (S, index, unicode) { + return index + (unicode ? at(S, index).length : 1); +}; + + +/***/ }), + +/***/ "0bfb": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +// 21.2.5.3 get RegExp.prototype.flags +var anObject = __webpack_require__("cb7c"); +module.exports = function () { + var that = anObject(this); + var result = ''; + if (that.global) result += 'g'; + if (that.ignoreCase) result += 'i'; + if (that.multiline) result += 'm'; + if (that.unicode) result += 'u'; + if (that.sticky) result += 'y'; + return result; +}; + + +/***/ }), + +/***/ "0d58": +/***/ (function(module, exports, __webpack_require__) { + +// 19.1.2.14 / 15.2.3.14 Object.keys(O) +var $keys = __webpack_require__("ce10"); +var enumBugKeys = __webpack_require__("e11e"); + +module.exports = Object.keys || function keys(O) { + return $keys(O, enumBugKeys); +}; + + +/***/ }), + +/***/ "1495": +/***/ (function(module, exports, __webpack_require__) { + +var dP = __webpack_require__("86cc"); +var anObject = __webpack_require__("cb7c"); +var getKeys = __webpack_require__("0d58"); + +module.exports = __webpack_require__("9e1e") ? Object.defineProperties : function defineProperties(O, Properties) { + anObject(O); + var keys = getKeys(Properties); + var length = keys.length; + var i = 0; + var P; + while (length > i) dP.f(O, P = keys[i++], Properties[P]); + return O; +}; + + +/***/ }), + +/***/ "214f": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +__webpack_require__("b0c5"); +var redefine = __webpack_require__("2aba"); +var hide = __webpack_require__("32e9"); +var fails = __webpack_require__("79e5"); +var defined = __webpack_require__("be13"); +var wks = __webpack_require__("2b4c"); +var regexpExec = __webpack_require__("520a"); + +var SPECIES = wks('species'); + +var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () { + // #replace needs built-in support for named groups. + // #match works fine because it just return the exec results, even if it has + // a "grops" property. + var re = /./; + re.exec = function () { + var result = []; + result.groups = { a: '7' }; + return result; + }; + return ''.replace(re, '$') !== '7'; +}); + +var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = (function () { + // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec + var re = /(?:)/; + var originalExec = re.exec; + re.exec = function () { return originalExec.apply(this, arguments); }; + var result = 'ab'.split(re); + return result.length === 2 && result[0] === 'a' && result[1] === 'b'; +})(); + +module.exports = function (KEY, length, exec) { + var SYMBOL = wks(KEY); + + var DELEGATES_TO_SYMBOL = !fails(function () { + // String methods call symbol-named RegEp methods + var O = {}; + O[SYMBOL] = function () { return 7; }; + return ''[KEY](O) != 7; + }); + + var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL ? !fails(function () { + // Symbol-named RegExp methods call .exec + var execCalled = false; + var re = /a/; + re.exec = function () { execCalled = true; return null; }; + if (KEY === 'split') { + // RegExp[@@split] doesn't call the regex's exec method, but first creates + // a new one. We need to return the patched regex when creating the new one. + re.constructor = {}; + re.constructor[SPECIES] = function () { return re; }; + } + re[SYMBOL](''); + return !execCalled; + }) : undefined; + + if ( + !DELEGATES_TO_SYMBOL || + !DELEGATES_TO_EXEC || + (KEY === 'replace' && !REPLACE_SUPPORTS_NAMED_GROUPS) || + (KEY === 'split' && !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC) + ) { + var nativeRegExpMethod = /./[SYMBOL]; + var fns = exec( + defined, + SYMBOL, + ''[KEY], + function maybeCallNative(nativeMethod, regexp, str, arg2, forceStringMethod) { + if (regexp.exec === regexpExec) { + if (DELEGATES_TO_SYMBOL && !forceStringMethod) { + // The native String method already delegates to @@method (this + // polyfilled function), leasing to infinite recursion. + // We avoid it by directly calling the native @@method method. + return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) }; + } + return { done: true, value: nativeMethod.call(str, regexp, arg2) }; + } + return { done: false }; + } + ); + var strfn = fns[0]; + var rxfn = fns[1]; + + redefine(String.prototype, KEY, strfn); + hide(RegExp.prototype, SYMBOL, length == 2 + // 21.2.5.8 RegExp.prototype[@@replace](string, replaceValue) + // 21.2.5.11 RegExp.prototype[@@split](string, limit) + ? function (string, arg) { return rxfn.call(string, this, arg); } + // 21.2.5.6 RegExp.prototype[@@match](string) + // 21.2.5.9 RegExp.prototype[@@search](string) + : function (string) { return rxfn.call(string, this); } + ); + } +}; + + +/***/ }), + +/***/ "230e": +/***/ (function(module, exports, __webpack_require__) { + +var isObject = __webpack_require__("d3f4"); +var document = __webpack_require__("7726").document; +// typeof document.createElement is 'object' in old IE +var is = isObject(document) && isObject(document.createElement); +module.exports = function (it) { + return is ? document.createElement(it) : {}; +}; + + +/***/ }), + +/***/ "23c6": +/***/ (function(module, exports, __webpack_require__) { + +// getting tag from 19.1.3.6 Object.prototype.toString() +var cof = __webpack_require__("2d95"); +var TAG = __webpack_require__("2b4c")('toStringTag'); +// ES3 wrong here +var ARG = cof(function () { return arguments; }()) == 'Arguments'; + +// fallback for IE11 Script Access Denied error +var tryGet = function (it, key) { + try { + return it[key]; + } catch (e) { /* empty */ } +}; + +module.exports = function (it) { + var O, T, B; + return it === undefined ? 'Undefined' : it === null ? 'Null' + // @@toStringTag case + : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T + // builtinTag case + : ARG ? cof(O) + // ES3 arguments fallback + : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; +}; + + +/***/ }), + +/***/ "2621": +/***/ (function(module, exports) { + +exports.f = Object.getOwnPropertySymbols; + + +/***/ }), + +/***/ "2aba": +/***/ (function(module, exports, __webpack_require__) { + +var global = __webpack_require__("7726"); +var hide = __webpack_require__("32e9"); +var has = __webpack_require__("69a8"); +var SRC = __webpack_require__("ca5a")('src'); +var $toString = __webpack_require__("fa5b"); +var TO_STRING = 'toString'; +var TPL = ('' + $toString).split(TO_STRING); + +__webpack_require__("8378").inspectSource = function (it) { + return $toString.call(it); +}; + +(module.exports = function (O, key, val, safe) { + var isFunction = typeof val == 'function'; + if (isFunction) has(val, 'name') || hide(val, 'name', key); + if (O[key] === val) return; + if (isFunction) has(val, SRC) || hide(val, SRC, O[key] ? '' + O[key] : TPL.join(String(key))); + if (O === global) { + O[key] = val; + } else if (!safe) { + delete O[key]; + hide(O, key, val); + } else if (O[key]) { + O[key] = val; + } else { + hide(O, key, val); + } +// add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative +})(Function.prototype, TO_STRING, function toString() { + return typeof this == 'function' && this[SRC] || $toString.call(this); +}); + + +/***/ }), + +/***/ "2aeb": +/***/ (function(module, exports, __webpack_require__) { + +// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) +var anObject = __webpack_require__("cb7c"); +var dPs = __webpack_require__("1495"); +var enumBugKeys = __webpack_require__("e11e"); +var IE_PROTO = __webpack_require__("613b")('IE_PROTO'); +var Empty = function () { /* empty */ }; +var PROTOTYPE = 'prototype'; + +// Create object with fake `null` prototype: use iframe Object with cleared prototype +var createDict = function () { + // Thrash, waste and sodomy: IE GC bug + var iframe = __webpack_require__("230e")('iframe'); + var i = enumBugKeys.length; + var lt = '<'; + var gt = '>'; + var iframeDocument; + iframe.style.display = 'none'; + __webpack_require__("fab2").appendChild(iframe); + iframe.src = 'javascript:'; // eslint-disable-line no-script-url + // createDict = iframe.contentWindow.Object; + // html.removeChild(iframe); + iframeDocument = iframe.contentWindow.document; + iframeDocument.open(); + iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt); + iframeDocument.close(); + createDict = iframeDocument.F; + while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]]; + return createDict(); +}; + +module.exports = Object.create || function create(O, Properties) { + var result; + if (O !== null) { + Empty[PROTOTYPE] = anObject(O); + result = new Empty(); + Empty[PROTOTYPE] = null; + // add "__proto__" for Object.getPrototypeOf polyfill + result[IE_PROTO] = O; + } else result = createDict(); + return Properties === undefined ? result : dPs(result, Properties); +}; + + +/***/ }), + +/***/ "2b4c": +/***/ (function(module, exports, __webpack_require__) { + +var store = __webpack_require__("5537")('wks'); +var uid = __webpack_require__("ca5a"); +var Symbol = __webpack_require__("7726").Symbol; +var USE_SYMBOL = typeof Symbol == 'function'; + +var $exports = module.exports = function (name) { + return store[name] || (store[name] = + USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); +}; + +$exports.store = store; + + +/***/ }), + +/***/ "2d00": +/***/ (function(module, exports) { + +module.exports = false; + + +/***/ }), + +/***/ "2d95": +/***/ (function(module, exports) { + +var toString = {}.toString; + +module.exports = function (it) { + return toString.call(it).slice(8, -1); +}; + + +/***/ }), + +/***/ "2fdb": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +// 21.1.3.7 String.prototype.includes(searchString, position = 0) + +var $export = __webpack_require__("5ca1"); +var context = __webpack_require__("d2c8"); +var INCLUDES = 'includes'; + +$export($export.P + $export.F * __webpack_require__("5147")(INCLUDES), 'String', { + includes: function includes(searchString /* , position = 0 */) { + return !!~context(this, searchString, INCLUDES) + .indexOf(searchString, arguments.length > 1 ? arguments[1] : undefined); + } +}); + + +/***/ }), + +/***/ "32e9": +/***/ (function(module, exports, __webpack_require__) { + +var dP = __webpack_require__("86cc"); +var createDesc = __webpack_require__("4630"); +module.exports = __webpack_require__("9e1e") ? function (object, key, value) { + return dP.f(object, key, createDesc(1, value)); +} : function (object, key, value) { + object[key] = value; + return object; +}; + + +/***/ }), + +/***/ "38fd": +/***/ (function(module, exports, __webpack_require__) { + +// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O) +var has = __webpack_require__("69a8"); +var toObject = __webpack_require__("4bf8"); +var IE_PROTO = __webpack_require__("613b")('IE_PROTO'); +var ObjectProto = Object.prototype; + +module.exports = Object.getPrototypeOf || function (O) { + O = toObject(O); + if (has(O, IE_PROTO)) return O[IE_PROTO]; + if (typeof O.constructor == 'function' && O instanceof O.constructor) { + return O.constructor.prototype; + } return O instanceof Object ? ObjectProto : null; +}; + + +/***/ }), + +/***/ "41a0": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var create = __webpack_require__("2aeb"); +var descriptor = __webpack_require__("4630"); +var setToStringTag = __webpack_require__("7f20"); +var IteratorPrototype = {}; + +// 25.1.2.1.1 %IteratorPrototype%[@@iterator]() +__webpack_require__("32e9")(IteratorPrototype, __webpack_require__("2b4c")('iterator'), function () { return this; }); + +module.exports = function (Constructor, NAME, next) { + Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) }); + setToStringTag(Constructor, NAME + ' Iterator'); +}; + + +/***/ }), + +/***/ "456d": +/***/ (function(module, exports, __webpack_require__) { + +// 19.1.2.14 Object.keys(O) +var toObject = __webpack_require__("4bf8"); +var $keys = __webpack_require__("0d58"); + +__webpack_require__("5eda")('keys', function () { + return function keys(it) { + return $keys(toObject(it)); + }; +}); + + +/***/ }), + +/***/ "4588": +/***/ (function(module, exports) { + +// 7.1.4 ToInteger +var ceil = Math.ceil; +var floor = Math.floor; +module.exports = function (it) { + return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); +}; + + +/***/ }), + +/***/ "4630": +/***/ (function(module, exports) { + +module.exports = function (bitmap, value) { + return { + enumerable: !(bitmap & 1), + configurable: !(bitmap & 2), + writable: !(bitmap & 4), + value: value + }; +}; + + +/***/ }), + +/***/ "4bf8": +/***/ (function(module, exports, __webpack_require__) { + +// 7.1.13 ToObject(argument) +var defined = __webpack_require__("be13"); +module.exports = function (it) { + return Object(defined(it)); +}; + + +/***/ }), + +/***/ "5147": +/***/ (function(module, exports, __webpack_require__) { + +var MATCH = __webpack_require__("2b4c")('match'); +module.exports = function (KEY) { + var re = /./; + try { + '/./'[KEY](re); + } catch (e) { + try { + re[MATCH] = false; + return !'/./'[KEY](re); + } catch (f) { /* empty */ } + } return true; +}; + + +/***/ }), + +/***/ "520a": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var regexpFlags = __webpack_require__("0bfb"); + +var nativeExec = RegExp.prototype.exec; +// This always refers to the native implementation, because the +// String#replace polyfill uses ./fix-regexp-well-known-symbol-logic.js, +// which loads this file before patching the method. +var nativeReplace = String.prototype.replace; + +var patchedExec = nativeExec; + +var LAST_INDEX = 'lastIndex'; + +var UPDATES_LAST_INDEX_WRONG = (function () { + var re1 = /a/, + re2 = /b*/g; + nativeExec.call(re1, 'a'); + nativeExec.call(re2, 'a'); + return re1[LAST_INDEX] !== 0 || re2[LAST_INDEX] !== 0; +})(); + +// nonparticipating capturing group, copied from es5-shim's String#split patch. +var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined; + +var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED; + +if (PATCH) { + patchedExec = function exec(str) { + var re = this; + var lastIndex, reCopy, match, i; + + if (NPCG_INCLUDED) { + reCopy = new RegExp('^' + re.source + '$(?!\\s)', regexpFlags.call(re)); + } + if (UPDATES_LAST_INDEX_WRONG) lastIndex = re[LAST_INDEX]; + + match = nativeExec.call(re, str); + + if (UPDATES_LAST_INDEX_WRONG && match) { + re[LAST_INDEX] = re.global ? match.index + match[0].length : lastIndex; + } + if (NPCG_INCLUDED && match && match.length > 1) { + // Fix browsers whose `exec` methods don't consistently return `undefined` + // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/ + // eslint-disable-next-line no-loop-func + nativeReplace.call(match[0], reCopy, function () { + for (i = 1; i < arguments.length - 2; i++) { + if (arguments[i] === undefined) match[i] = undefined; + } + }); + } + + return match; + }; +} + +module.exports = patchedExec; + + +/***/ }), + +/***/ "52a7": +/***/ (function(module, exports) { + +exports.f = {}.propertyIsEnumerable; + + +/***/ }), + +/***/ "5537": +/***/ (function(module, exports, __webpack_require__) { + +var core = __webpack_require__("8378"); +var global = __webpack_require__("7726"); +var SHARED = '__core-js_shared__'; +var store = global[SHARED] || (global[SHARED] = {}); + +(module.exports = function (key, value) { + return store[key] || (store[key] = value !== undefined ? value : {}); +})('versions', []).push({ + version: core.version, + mode: __webpack_require__("2d00") ? 'pure' : 'global', + copyright: '© 2019 Denis Pushkarev (zloirock.ru)' +}); + + +/***/ }), + +/***/ "5ca1": +/***/ (function(module, exports, __webpack_require__) { + +var global = __webpack_require__("7726"); +var core = __webpack_require__("8378"); +var hide = __webpack_require__("32e9"); +var redefine = __webpack_require__("2aba"); +var ctx = __webpack_require__("9b43"); +var PROTOTYPE = 'prototype'; + +var $export = function (type, name, source) { + var IS_FORCED = type & $export.F; + var IS_GLOBAL = type & $export.G; + var IS_STATIC = type & $export.S; + var IS_PROTO = type & $export.P; + var IS_BIND = type & $export.B; + var target = IS_GLOBAL ? global : IS_STATIC ? global[name] || (global[name] = {}) : (global[name] || {})[PROTOTYPE]; + var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); + var expProto = exports[PROTOTYPE] || (exports[PROTOTYPE] = {}); + var key, own, out, exp; + if (IS_GLOBAL) source = name; + for (key in source) { + // contains in native + own = !IS_FORCED && target && target[key] !== undefined; + // export native or passed + out = (own ? target : source)[key]; + // bind timers to global for call from export context + exp = IS_BIND && own ? ctx(out, global) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; + // extend global + if (target) redefine(target, key, out, type & $export.U); + // export + if (exports[key] != out) hide(exports, key, exp); + if (IS_PROTO && expProto[key] != out) expProto[key] = out; + } +}; +global.core = core; +// type bitmap +$export.F = 1; // forced +$export.G = 2; // global +$export.S = 4; // static +$export.P = 8; // proto +$export.B = 16; // bind +$export.W = 32; // wrap +$export.U = 64; // safe +$export.R = 128; // real proto method for `library` +module.exports = $export; + + +/***/ }), + +/***/ "5eda": +/***/ (function(module, exports, __webpack_require__) { + +// most Object methods by ES6 should accept primitives +var $export = __webpack_require__("5ca1"); +var core = __webpack_require__("8378"); +var fails = __webpack_require__("79e5"); +module.exports = function (KEY, exec) { + var fn = (core.Object || {})[KEY] || Object[KEY]; + var exp = {}; + exp[KEY] = exec(fn); + $export($export.S + $export.F * fails(function () { fn(1); }), 'Object', exp); +}; + + +/***/ }), + +/***/ "5f1b": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var classof = __webpack_require__("23c6"); +var builtinExec = RegExp.prototype.exec; + + // `RegExpExec` abstract operation +// https://tc39.github.io/ecma262/#sec-regexpexec +module.exports = function (R, S) { + var exec = R.exec; + if (typeof exec === 'function') { + var result = exec.call(R, S); + if (typeof result !== 'object') { + throw new TypeError('RegExp exec method returned something other than an Object or null'); + } + return result; + } + if (classof(R) !== 'RegExp') { + throw new TypeError('RegExp#exec called on incompatible receiver'); + } + return builtinExec.call(R, S); +}; + + +/***/ }), + +/***/ "613b": +/***/ (function(module, exports, __webpack_require__) { + +var shared = __webpack_require__("5537")('keys'); +var uid = __webpack_require__("ca5a"); +module.exports = function (key) { + return shared[key] || (shared[key] = uid(key)); +}; + + +/***/ }), + +/***/ "626a": +/***/ (function(module, exports, __webpack_require__) { + +// fallback for non-array-like ES3 and non-enumerable old V8 strings +var cof = __webpack_require__("2d95"); +// eslint-disable-next-line no-prototype-builtins +module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) { + return cof(it) == 'String' ? it.split('') : Object(it); +}; + + +/***/ }), + +/***/ "6762": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +// https://github.com/tc39/Array.prototype.includes +var $export = __webpack_require__("5ca1"); +var $includes = __webpack_require__("c366")(true); + +$export($export.P, 'Array', { + includes: function includes(el /* , fromIndex = 0 */) { + return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined); + } +}); + +__webpack_require__("9c6c")('includes'); + + +/***/ }), + +/***/ "6821": +/***/ (function(module, exports, __webpack_require__) { + +// to indexed object, toObject with fallback for non-array-like ES3 strings +var IObject = __webpack_require__("626a"); +var defined = __webpack_require__("be13"); +module.exports = function (it) { + return IObject(defined(it)); +}; + + +/***/ }), + +/***/ "69a8": +/***/ (function(module, exports) { + +var hasOwnProperty = {}.hasOwnProperty; +module.exports = function (it, key) { + return hasOwnProperty.call(it, key); +}; + + +/***/ }), + +/***/ "6a99": +/***/ (function(module, exports, __webpack_require__) { + +// 7.1.1 ToPrimitive(input [, PreferredType]) +var isObject = __webpack_require__("d3f4"); +// instead of the ES6 spec version, we didn't implement @@toPrimitive case +// and the second argument - flag - preferred type is a string +module.exports = function (it, S) { + if (!isObject(it)) return it; + var fn, val; + if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; + if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val; + if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; + throw TypeError("Can't convert object to primitive value"); +}; + + +/***/ }), + +/***/ "7333": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +// 19.1.2.1 Object.assign(target, source, ...) +var getKeys = __webpack_require__("0d58"); +var gOPS = __webpack_require__("2621"); +var pIE = __webpack_require__("52a7"); +var toObject = __webpack_require__("4bf8"); +var IObject = __webpack_require__("626a"); +var $assign = Object.assign; + +// should work with symbols and should have deterministic property order (V8 bug) +module.exports = !$assign || __webpack_require__("79e5")(function () { + var A = {}; + var B = {}; + // eslint-disable-next-line no-undef + var S = Symbol(); + var K = 'abcdefghijklmnopqrst'; + A[S] = 7; + K.split('').forEach(function (k) { B[k] = k; }); + return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K; +}) ? function assign(target, source) { // eslint-disable-line no-unused-vars + var T = toObject(target); + var aLen = arguments.length; + var index = 1; + var getSymbols = gOPS.f; + var isEnum = pIE.f; + while (aLen > index) { + var S = IObject(arguments[index++]); + var keys = getSymbols ? getKeys(S).concat(getSymbols(S)) : getKeys(S); + var length = keys.length; + var j = 0; + var key; + while (length > j) if (isEnum.call(S, key = keys[j++])) T[key] = S[key]; + } return T; +} : $assign; + + +/***/ }), + +/***/ "7726": +/***/ (function(module, exports) { + +// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 +var global = module.exports = typeof window != 'undefined' && window.Math == Math + ? window : typeof self != 'undefined' && self.Math == Math ? self + // eslint-disable-next-line no-new-func + : Function('return this')(); +if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef + + +/***/ }), + +/***/ "77f1": +/***/ (function(module, exports, __webpack_require__) { + +var toInteger = __webpack_require__("4588"); +var max = Math.max; +var min = Math.min; +module.exports = function (index, length) { + index = toInteger(index); + return index < 0 ? max(index + length, 0) : min(index, length); +}; + + +/***/ }), + +/***/ "79e5": +/***/ (function(module, exports) { + +module.exports = function (exec) { + try { + return !!exec(); + } catch (e) { + return true; + } +}; + + +/***/ }), + +/***/ "7f20": +/***/ (function(module, exports, __webpack_require__) { + +var def = __webpack_require__("86cc").f; +var has = __webpack_require__("69a8"); +var TAG = __webpack_require__("2b4c")('toStringTag'); + +module.exports = function (it, tag, stat) { + if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag }); +}; + + +/***/ }), + +/***/ "8378": +/***/ (function(module, exports) { + +var core = module.exports = { version: '2.6.5' }; +if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef + + +/***/ }), + +/***/ "84f2": +/***/ (function(module, exports) { + +module.exports = {}; + + +/***/ }), + +/***/ "86cc": +/***/ (function(module, exports, __webpack_require__) { + +var anObject = __webpack_require__("cb7c"); +var IE8_DOM_DEFINE = __webpack_require__("c69a"); +var toPrimitive = __webpack_require__("6a99"); +var dP = Object.defineProperty; + +exports.f = __webpack_require__("9e1e") ? Object.defineProperty : function defineProperty(O, P, Attributes) { + anObject(O); + P = toPrimitive(P, true); + anObject(Attributes); + if (IE8_DOM_DEFINE) try { + return dP(O, P, Attributes); + } catch (e) { /* empty */ } + if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); + if ('value' in Attributes) O[P] = Attributes.value; + return O; +}; + + +/***/ }), + +/***/ "9b43": +/***/ (function(module, exports, __webpack_require__) { + +// optional / simple context binding +var aFunction = __webpack_require__("d8e8"); +module.exports = function (fn, that, length) { + aFunction(fn); + if (that === undefined) return fn; + switch (length) { + case 1: return function (a) { + return fn.call(that, a); + }; + case 2: return function (a, b) { + return fn.call(that, a, b); + }; + case 3: return function (a, b, c) { + return fn.call(that, a, b, c); + }; + } + return function (/* ...args */) { + return fn.apply(that, arguments); + }; +}; + + +/***/ }), + +/***/ "9c6c": +/***/ (function(module, exports, __webpack_require__) { + +// 22.1.3.31 Array.prototype[@@unscopables] +var UNSCOPABLES = __webpack_require__("2b4c")('unscopables'); +var ArrayProto = Array.prototype; +if (ArrayProto[UNSCOPABLES] == undefined) __webpack_require__("32e9")(ArrayProto, UNSCOPABLES, {}); +module.exports = function (key) { + ArrayProto[UNSCOPABLES][key] = true; +}; + + +/***/ }), + +/***/ "9def": +/***/ (function(module, exports, __webpack_require__) { + +// 7.1.15 ToLength +var toInteger = __webpack_require__("4588"); +var min = Math.min; +module.exports = function (it) { + return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 +}; + + +/***/ }), + +/***/ "9e1e": +/***/ (function(module, exports, __webpack_require__) { + +// Thank's IE8 for his funny defineProperty +module.exports = !__webpack_require__("79e5")(function () { + return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; +}); + + +/***/ }), + +/***/ "a352": +/***/ (function(module, exports) { + +module.exports = __WEBPACK_EXTERNAL_MODULE_a352__; + +/***/ }), + +/***/ "a481": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var anObject = __webpack_require__("cb7c"); +var toObject = __webpack_require__("4bf8"); +var toLength = __webpack_require__("9def"); +var toInteger = __webpack_require__("4588"); +var advanceStringIndex = __webpack_require__("0390"); +var regExpExec = __webpack_require__("5f1b"); +var max = Math.max; +var min = Math.min; +var floor = Math.floor; +var SUBSTITUTION_SYMBOLS = /\$([$&`']|\d\d?|<[^>]*>)/g; +var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&`']|\d\d?)/g; + +var maybeToString = function (it) { + return it === undefined ? it : String(it); +}; + +// @@replace logic +__webpack_require__("214f")('replace', 2, function (defined, REPLACE, $replace, maybeCallNative) { + return [ + // `String.prototype.replace` method + // https://tc39.github.io/ecma262/#sec-string.prototype.replace + function replace(searchValue, replaceValue) { + var O = defined(this); + var fn = searchValue == undefined ? undefined : searchValue[REPLACE]; + return fn !== undefined + ? fn.call(searchValue, O, replaceValue) + : $replace.call(String(O), searchValue, replaceValue); + }, + // `RegExp.prototype[@@replace]` method + // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace + function (regexp, replaceValue) { + var res = maybeCallNative($replace, regexp, this, replaceValue); + if (res.done) return res.value; + + var rx = anObject(regexp); + var S = String(this); + var functionalReplace = typeof replaceValue === 'function'; + if (!functionalReplace) replaceValue = String(replaceValue); + var global = rx.global; + if (global) { + var fullUnicode = rx.unicode; + rx.lastIndex = 0; + } + var results = []; + while (true) { + var result = regExpExec(rx, S); + if (result === null) break; + results.push(result); + if (!global) break; + var matchStr = String(result[0]); + if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode); + } + var accumulatedResult = ''; + var nextSourcePosition = 0; + for (var i = 0; i < results.length; i++) { + result = results[i]; + var matched = String(result[0]); + var position = max(min(toInteger(result.index), S.length), 0); + var captures = []; + // NOTE: This is equivalent to + // captures = result.slice(1).map(maybeToString) + // but for some reason `nativeSlice.call(result, 1, result.length)` (called in + // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and + // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it. + for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j])); + var namedCaptures = result.groups; + if (functionalReplace) { + var replacerArgs = [matched].concat(captures, position, S); + if (namedCaptures !== undefined) replacerArgs.push(namedCaptures); + var replacement = String(replaceValue.apply(undefined, replacerArgs)); + } else { + replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue); + } + if (position >= nextSourcePosition) { + accumulatedResult += S.slice(nextSourcePosition, position) + replacement; + nextSourcePosition = position + matched.length; + } + } + return accumulatedResult + S.slice(nextSourcePosition); + } + ]; + + // https://tc39.github.io/ecma262/#sec-getsubstitution + function getSubstitution(matched, str, position, captures, namedCaptures, replacement) { + var tailPos = position + matched.length; + var m = captures.length; + var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED; + if (namedCaptures !== undefined) { + namedCaptures = toObject(namedCaptures); + symbols = SUBSTITUTION_SYMBOLS; + } + return $replace.call(replacement, symbols, function (match, ch) { + var capture; + switch (ch.charAt(0)) { + case '$': return '$'; + case '&': return matched; + case '`': return str.slice(0, position); + case "'": return str.slice(tailPos); + case '<': + capture = namedCaptures[ch.slice(1, -1)]; + break; + default: // \d\d? + var n = +ch; + if (n === 0) return match; + if (n > m) { + var f = floor(n / 10); + if (f === 0) return match; + if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1); + return match; + } + capture = captures[n - 1]; + } + return capture === undefined ? '' : capture; + }); + } +}); + + +/***/ }), + +/***/ "aae3": +/***/ (function(module, exports, __webpack_require__) { + +// 7.2.8 IsRegExp(argument) +var isObject = __webpack_require__("d3f4"); +var cof = __webpack_require__("2d95"); +var MATCH = __webpack_require__("2b4c")('match'); +module.exports = function (it) { + var isRegExp; + return isObject(it) && ((isRegExp = it[MATCH]) !== undefined ? !!isRegExp : cof(it) == 'RegExp'); +}; + + +/***/ }), + +/***/ "ac6a": +/***/ (function(module, exports, __webpack_require__) { + +var $iterators = __webpack_require__("cadf"); +var getKeys = __webpack_require__("0d58"); +var redefine = __webpack_require__("2aba"); +var global = __webpack_require__("7726"); +var hide = __webpack_require__("32e9"); +var Iterators = __webpack_require__("84f2"); +var wks = __webpack_require__("2b4c"); +var ITERATOR = wks('iterator'); +var TO_STRING_TAG = wks('toStringTag'); +var ArrayValues = Iterators.Array; + +var DOMIterables = { + CSSRuleList: true, // TODO: Not spec compliant, should be false. + CSSStyleDeclaration: false, + CSSValueList: false, + ClientRectList: false, + DOMRectList: false, + DOMStringList: false, + DOMTokenList: true, + DataTransferItemList: false, + FileList: false, + HTMLAllCollection: false, + HTMLCollection: false, + HTMLFormElement: false, + HTMLSelectElement: false, + MediaList: true, // TODO: Not spec compliant, should be false. + MimeTypeArray: false, + NamedNodeMap: false, + NodeList: true, + PaintRequestList: false, + Plugin: false, + PluginArray: false, + SVGLengthList: false, + SVGNumberList: false, + SVGPathSegList: false, + SVGPointList: false, + SVGStringList: false, + SVGTransformList: false, + SourceBufferList: false, + StyleSheetList: true, // TODO: Not spec compliant, should be false. + TextTrackCueList: false, + TextTrackList: false, + TouchList: false +}; + +for (var collections = getKeys(DOMIterables), i = 0; i < collections.length; i++) { + var NAME = collections[i]; + var explicit = DOMIterables[NAME]; + var Collection = global[NAME]; + var proto = Collection && Collection.prototype; + var key; + if (proto) { + if (!proto[ITERATOR]) hide(proto, ITERATOR, ArrayValues); + if (!proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME); + Iterators[NAME] = ArrayValues; + if (explicit) for (key in $iterators) if (!proto[key]) redefine(proto, key, $iterators[key], true); + } +} + + +/***/ }), + +/***/ "b0c5": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var regexpExec = __webpack_require__("520a"); +__webpack_require__("5ca1")({ + target: 'RegExp', + proto: true, + forced: regexpExec !== /./.exec +}, { + exec: regexpExec +}); + + +/***/ }), + +/***/ "be13": +/***/ (function(module, exports) { + +// 7.2.1 RequireObjectCoercible(argument) +module.exports = function (it) { + if (it == undefined) throw TypeError("Can't call method on " + it); + return it; +}; + + +/***/ }), + +/***/ "c366": +/***/ (function(module, exports, __webpack_require__) { + +// false -> Array#indexOf +// true -> Array#includes +var toIObject = __webpack_require__("6821"); +var toLength = __webpack_require__("9def"); +var toAbsoluteIndex = __webpack_require__("77f1"); +module.exports = function (IS_INCLUDES) { + return function ($this, el, fromIndex) { + var O = toIObject($this); + var length = toLength(O.length); + var index = toAbsoluteIndex(fromIndex, length); + var value; + // Array#includes uses SameValueZero equality algorithm + // eslint-disable-next-line no-self-compare + if (IS_INCLUDES && el != el) while (length > index) { + value = O[index++]; + // eslint-disable-next-line no-self-compare + if (value != value) return true; + // Array#indexOf ignores holes, Array#includes - not + } else for (;length > index; index++) if (IS_INCLUDES || index in O) { + if (O[index] === el) return IS_INCLUDES || index || 0; + } return !IS_INCLUDES && -1; + }; +}; + + +/***/ }), + +/***/ "c649": +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return insertNodeAt; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return camelize; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return console; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "d", function() { return removeNode; }); +/* harmony import */ var core_js_modules_es6_regexp_replace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("a481"); +/* harmony import */ var core_js_modules_es6_regexp_replace__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es6_regexp_replace__WEBPACK_IMPORTED_MODULE_0__); + + +function getConsole() { + if (typeof window !== "undefined") { + return window.console; + } + + return global.console; +} + +var console = getConsole(); + +function cached(fn) { + var cache = Object.create(null); + return function cachedFn(str) { + var hit = cache[str]; + return hit || (cache[str] = fn(str)); + }; +} + +var regex = /-(\w)/g; +var camelize = cached(function (str) { + return str.replace(regex, function (_, c) { + return c ? c.toUpperCase() : ""; + }); +}); + +function removeNode(node) { + if (node.parentElement !== null) { + node.parentElement.removeChild(node); + } +} + +function insertNodeAt(fatherNode, node, position) { + var refNode = position === 0 ? fatherNode.children[0] : fatherNode.children[position - 1].nextSibling; + fatherNode.insertBefore(node, refNode); +} + + +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__("c8ba"))) + +/***/ }), + +/***/ "c69a": +/***/ (function(module, exports, __webpack_require__) { + +module.exports = !__webpack_require__("9e1e") && !__webpack_require__("79e5")(function () { + return Object.defineProperty(__webpack_require__("230e")('div'), 'a', { get: function () { return 7; } }).a != 7; +}); + + +/***/ }), + +/***/ "c8ba": +/***/ (function(module, exports) { + +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || new Function("return this")(); +} catch (e) { + // This works if the window reference is available + if (typeof window === "object") g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; + + +/***/ }), + +/***/ "ca5a": +/***/ (function(module, exports) { + +var id = 0; +var px = Math.random(); +module.exports = function (key) { + return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); +}; + + +/***/ }), + +/***/ "cadf": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var addToUnscopables = __webpack_require__("9c6c"); +var step = __webpack_require__("d53b"); +var Iterators = __webpack_require__("84f2"); +var toIObject = __webpack_require__("6821"); + +// 22.1.3.4 Array.prototype.entries() +// 22.1.3.13 Array.prototype.keys() +// 22.1.3.29 Array.prototype.values() +// 22.1.3.30 Array.prototype[@@iterator]() +module.exports = __webpack_require__("01f9")(Array, 'Array', function (iterated, kind) { + this._t = toIObject(iterated); // target + this._i = 0; // next index + this._k = kind; // kind +// 22.1.5.2.1 %ArrayIteratorPrototype%.next() +}, function () { + var O = this._t; + var kind = this._k; + var index = this._i++; + if (!O || index >= O.length) { + this._t = undefined; + return step(1); + } + if (kind == 'keys') return step(0, index); + if (kind == 'values') return step(0, O[index]); + return step(0, [index, O[index]]); +}, 'values'); + +// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) +Iterators.Arguments = Iterators.Array; + +addToUnscopables('keys'); +addToUnscopables('values'); +addToUnscopables('entries'); + + +/***/ }), + +/***/ "cb7c": +/***/ (function(module, exports, __webpack_require__) { + +var isObject = __webpack_require__("d3f4"); +module.exports = function (it) { + if (!isObject(it)) throw TypeError(it + ' is not an object!'); + return it; +}; + + +/***/ }), + +/***/ "ce10": +/***/ (function(module, exports, __webpack_require__) { + +var has = __webpack_require__("69a8"); +var toIObject = __webpack_require__("6821"); +var arrayIndexOf = __webpack_require__("c366")(false); +var IE_PROTO = __webpack_require__("613b")('IE_PROTO'); + +module.exports = function (object, names) { + var O = toIObject(object); + var i = 0; + var result = []; + var key; + for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key); + // Don't enum bug & hidden keys + while (names.length > i) if (has(O, key = names[i++])) { + ~arrayIndexOf(result, key) || result.push(key); + } + return result; +}; + + +/***/ }), + +/***/ "d2c8": +/***/ (function(module, exports, __webpack_require__) { + +// helper for String#{startsWith, endsWith, includes} +var isRegExp = __webpack_require__("aae3"); +var defined = __webpack_require__("be13"); + +module.exports = function (that, searchString, NAME) { + if (isRegExp(searchString)) throw TypeError('String#' + NAME + " doesn't accept regex!"); + return String(defined(that)); +}; + + +/***/ }), + +/***/ "d3f4": +/***/ (function(module, exports) { + +module.exports = function (it) { + return typeof it === 'object' ? it !== null : typeof it === 'function'; +}; + + +/***/ }), + +/***/ "d53b": +/***/ (function(module, exports) { + +module.exports = function (done, value) { + return { value: value, done: !!done }; +}; + + +/***/ }), + +/***/ "d8e8": +/***/ (function(module, exports) { + +module.exports = function (it) { + if (typeof it != 'function') throw TypeError(it + ' is not a function!'); + return it; +}; + + +/***/ }), + +/***/ "e11e": +/***/ (function(module, exports) { + +// IE 8- don't enum bug keys +module.exports = ( + 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' +).split(','); + + +/***/ }), + +/***/ "f559": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +// 21.1.3.18 String.prototype.startsWith(searchString [, position ]) + +var $export = __webpack_require__("5ca1"); +var toLength = __webpack_require__("9def"); +var context = __webpack_require__("d2c8"); +var STARTS_WITH = 'startsWith'; +var $startsWith = ''[STARTS_WITH]; + +$export($export.P + $export.F * __webpack_require__("5147")(STARTS_WITH), 'String', { + startsWith: function startsWith(searchString /* , position = 0 */) { + var that = context(this, searchString, STARTS_WITH); + var index = toLength(Math.min(arguments.length > 1 ? arguments[1] : undefined, that.length)); + var search = String(searchString); + return $startsWith + ? $startsWith.call(that, search, index) + : that.slice(index, index + search.length) === search; + } +}); + + +/***/ }), + +/***/ "f6fd": +/***/ (function(module, exports) { + +// document.currentScript polyfill by Adam Miller + +// MIT license + +(function(document){ + var currentScript = "currentScript", + scripts = document.getElementsByTagName('script'); // Live NodeList collection + + // If browser needs currentScript polyfill, add get currentScript() to the document object + if (!(currentScript in document)) { + Object.defineProperty(document, currentScript, { + get: function(){ + + // IE 6-10 supports script readyState + // IE 10+ support stack trace + try { throw new Error(); } + catch (err) { + + // Find the second match for the "at" string to get file src url from stack. + // Specifically works with the format of stack traces in IE. + var i, res = ((/.*at [^\(]*\((.*):.+:.+\)$/ig).exec(err.stack) || [false])[1]; + + // For all scripts on the page, if src matches or if ready state is interactive, return the script tag + for(i in scripts){ + if(scripts[i].src == res || scripts[i].readyState == "interactive"){ + return scripts[i]; + } + } + + // If no match, return null + return null; + } + } + }); + } +})(document); + + +/***/ }), + +/***/ "f751": +/***/ (function(module, exports, __webpack_require__) { + +// 19.1.3.1 Object.assign(target, source) +var $export = __webpack_require__("5ca1"); + +$export($export.S + $export.F, 'Object', { assign: __webpack_require__("7333") }); + + +/***/ }), + +/***/ "fa5b": +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__("5537")('native-function-to-string', Function.toString); + + +/***/ }), + +/***/ "fab2": +/***/ (function(module, exports, __webpack_require__) { + +var document = __webpack_require__("7726").document; +module.exports = document && document.documentElement; + + +/***/ }), + +/***/ "fb15": +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +// ESM COMPAT FLAG +__webpack_require__.r(__webpack_exports__); + +// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js +// This file is imported into lib/wc client bundles. + +if (typeof window !== 'undefined') { + if (true) { + __webpack_require__("f6fd") + } + + var setPublicPath_i + if ((setPublicPath_i = window.document.currentScript) && (setPublicPath_i = setPublicPath_i.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))) { + __webpack_require__.p = setPublicPath_i[1] // eslint-disable-line + } +} + +// Indicate to webpack that this file can be concatenated +/* harmony default export */ var setPublicPath = (null); + +// EXTERNAL MODULE: ./node_modules/core-js/modules/es6.object.assign.js +var es6_object_assign = __webpack_require__("f751"); + +// EXTERNAL MODULE: ./node_modules/core-js/modules/es6.string.starts-with.js +var es6_string_starts_with = __webpack_require__("f559"); + +// EXTERNAL MODULE: ./node_modules/core-js/modules/web.dom.iterable.js +var web_dom_iterable = __webpack_require__("ac6a"); + +// EXTERNAL MODULE: ./node_modules/core-js/modules/es6.array.iterator.js +var es6_array_iterator = __webpack_require__("cadf"); + +// EXTERNAL MODULE: ./node_modules/core-js/modules/es6.object.keys.js +var es6_object_keys = __webpack_require__("456d"); + +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayWithHoles.js +function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; +} +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/iterableToArrayLimit.js +function _iterableToArrayLimit(arr, i) { + if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; + var _arr = []; + var _n = true; + var _d = false; + var _e = undefined; + + try { + for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i["return"] != null) _i["return"](); + } finally { + if (_d) throw _e; + } + } + + return _arr; +} +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayLikeToArray.js +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) { + arr2[i] = arr[i]; + } + + return arr2; +} +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/unsupportedIterableToArray.js + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/nonIterableRest.js +function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/slicedToArray.js + + + + +function _slicedToArray(arr, i) { + return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); +} +// EXTERNAL MODULE: ./node_modules/core-js/modules/es7.array.includes.js +var es7_array_includes = __webpack_require__("6762"); + +// EXTERNAL MODULE: ./node_modules/core-js/modules/es6.string.includes.js +var es6_string_includes = __webpack_require__("2fdb"); + +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayWithoutHoles.js + +function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); +} +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/iterableToArray.js +function _iterableToArray(iter) { + if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); +} +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/nonIterableSpread.js +function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} +// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/toConsumableArray.js + + + + +function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); +} +// EXTERNAL MODULE: external {"commonjs":"sortablejs","commonjs2":"sortablejs","amd":"sortablejs","root":"Sortable"} +var external_commonjs_sortablejs_commonjs2_sortablejs_amd_sortablejs_root_Sortable_ = __webpack_require__("a352"); +var external_commonjs_sortablejs_commonjs2_sortablejs_amd_sortablejs_root_Sortable_default = /*#__PURE__*/__webpack_require__.n(external_commonjs_sortablejs_commonjs2_sortablejs_amd_sortablejs_root_Sortable_); + +// EXTERNAL MODULE: ./src/util/helper.js +var helper = __webpack_require__("c649"); + +// CONCATENATED MODULE: ./src/vuedraggable.js + + + + + + + + + + + + +function buildAttribute(object, propName, value) { + if (value === undefined) { + return object; + } + + object = object || {}; + object[propName] = value; + return object; +} + +function computeVmIndex(vnodes, element) { + return vnodes.map(function (elt) { + return elt.elm; + }).indexOf(element); +} + +function _computeIndexes(slots, children, isTransition, footerOffset) { + if (!slots) { + return []; + } + + var elmFromNodes = slots.map(function (elt) { + return elt.elm; + }); + var footerIndex = children.length - footerOffset; + + var rawIndexes = _toConsumableArray(children).map(function (elt, idx) { + return idx >= footerIndex ? elmFromNodes.length : elmFromNodes.indexOf(elt); + }); + + return isTransition ? rawIndexes.filter(function (ind) { + return ind !== -1; + }) : rawIndexes; +} + +function emit(evtName, evtData) { + var _this = this; + + this.$nextTick(function () { + return _this.$emit(evtName.toLowerCase(), evtData); + }); +} + +function delegateAndEmit(evtName) { + var _this2 = this; + + return function (evtData) { + if (_this2.realList !== null) { + _this2["onDrag" + evtName](evtData); + } + + emit.call(_this2, evtName, evtData); + }; +} + +function isTransitionName(name) { + return ["transition-group", "TransitionGroup"].includes(name); +} + +function vuedraggable_isTransition(slots) { + if (!slots || slots.length !== 1) { + return false; + } + + var _slots = _slicedToArray(slots, 1), + componentOptions = _slots[0].componentOptions; + + if (!componentOptions) { + return false; + } + + return isTransitionName(componentOptions.tag); +} + +function getSlot(slot, scopedSlot, key) { + return slot[key] || (scopedSlot[key] ? scopedSlot[key]() : undefined); +} + +function computeChildrenAndOffsets(children, slot, scopedSlot) { + var headerOffset = 0; + var footerOffset = 0; + var header = getSlot(slot, scopedSlot, "header"); + + if (header) { + headerOffset = header.length; + children = children ? [].concat(_toConsumableArray(header), _toConsumableArray(children)) : _toConsumableArray(header); + } + + var footer = getSlot(slot, scopedSlot, "footer"); + + if (footer) { + footerOffset = footer.length; + children = children ? [].concat(_toConsumableArray(children), _toConsumableArray(footer)) : _toConsumableArray(footer); + } + + return { + children: children, + headerOffset: headerOffset, + footerOffset: footerOffset + }; +} + +function getComponentAttributes($attrs, componentData) { + var attributes = null; + + var update = function update(name, value) { + attributes = buildAttribute(attributes, name, value); + }; + + var attrs = Object.keys($attrs).filter(function (key) { + return key === "id" || key.startsWith("data-"); + }).reduce(function (res, key) { + res[key] = $attrs[key]; + return res; + }, {}); + update("attrs", attrs); + + if (!componentData) { + return attributes; + } + + var on = componentData.on, + props = componentData.props, + componentDataAttrs = componentData.attrs; + update("on", on); + update("props", props); + Object.assign(attributes.attrs, componentDataAttrs); + return attributes; +} + +var eventsListened = ["Start", "Add", "Remove", "Update", "End"]; +var eventsToEmit = ["Choose", "Unchoose", "Sort", "Filter", "Clone"]; +var readonlyProperties = ["Move"].concat(eventsListened, eventsToEmit).map(function (evt) { + return "on" + evt; +}); +var draggingElement = null; +var props = { + options: Object, + list: { + type: Array, + required: false, + default: null + }, + value: { + type: Array, + required: false, + default: null + }, + noTransitionOnDrag: { + type: Boolean, + default: false + }, + clone: { + type: Function, + default: function _default(original) { + return original; + } + }, + element: { + type: String, + default: "div" + }, + tag: { + type: String, + default: null + }, + move: { + type: Function, + default: null + }, + componentData: { + type: Object, + required: false, + default: null + } +}; +var draggableComponent = { + name: "draggable", + inheritAttrs: false, + props: props, + data: function data() { + return { + transitionMode: false, + noneFunctionalComponentMode: false + }; + }, + render: function render(h) { + var slots = this.$slots.default; + this.transitionMode = vuedraggable_isTransition(slots); + + var _computeChildrenAndOf = computeChildrenAndOffsets(slots, this.$slots, this.$scopedSlots), + children = _computeChildrenAndOf.children, + headerOffset = _computeChildrenAndOf.headerOffset, + footerOffset = _computeChildrenAndOf.footerOffset; + + this.headerOffset = headerOffset; + this.footerOffset = footerOffset; + var attributes = getComponentAttributes(this.$attrs, this.componentData); + return h(this.getTag(), attributes, children); + }, + created: function created() { + if (this.list !== null && this.value !== null) { + helper["b" /* console */].error("Value and list props are mutually exclusive! Please set one or another."); + } + + if (this.element !== "div") { + helper["b" /* console */].warn("Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"); + } + + if (this.options !== undefined) { + helper["b" /* console */].warn("Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props"); + } + }, + mounted: function mounted() { + var _this3 = this; + + this.noneFunctionalComponentMode = this.getTag().toLowerCase() !== this.$el.nodeName.toLowerCase() && !this.getIsFunctional(); + + if (this.noneFunctionalComponentMode && this.transitionMode) { + throw new Error("Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ".concat(this.getTag())); + } + + var optionsAdded = {}; + eventsListened.forEach(function (elt) { + optionsAdded["on" + elt] = delegateAndEmit.call(_this3, elt); + }); + eventsToEmit.forEach(function (elt) { + optionsAdded["on" + elt] = emit.bind(_this3, elt); + }); + var attributes = Object.keys(this.$attrs).reduce(function (res, key) { + res[Object(helper["a" /* camelize */])(key)] = _this3.$attrs[key]; + return res; + }, {}); + var options = Object.assign({}, this.options, attributes, optionsAdded, { + onMove: function onMove(evt, originalEvent) { + return _this3.onDragMove(evt, originalEvent); + } + }); + !("draggable" in options) && (options.draggable = ">*"); + this._sortable = new external_commonjs_sortablejs_commonjs2_sortablejs_amd_sortablejs_root_Sortable_default.a(this.rootContainer, options); + this.computeIndexes(); + }, + beforeDestroy: function beforeDestroy() { + if (this._sortable !== undefined) this._sortable.destroy(); + }, + computed: { + rootContainer: function rootContainer() { + return this.transitionMode ? this.$el.children[0] : this.$el; + }, + realList: function realList() { + return this.list ? this.list : this.value; + } + }, + watch: { + options: { + handler: function handler(newOptionValue) { + this.updateOptions(newOptionValue); + }, + deep: true + }, + $attrs: { + handler: function handler(newOptionValue) { + this.updateOptions(newOptionValue); + }, + deep: true + }, + realList: function realList() { + this.computeIndexes(); + } + }, + methods: { + getIsFunctional: function getIsFunctional() { + var fnOptions = this._vnode.fnOptions; + return fnOptions && fnOptions.functional; + }, + getTag: function getTag() { + return this.tag || this.element; + }, + updateOptions: function updateOptions(newOptionValue) { + for (var property in newOptionValue) { + var value = Object(helper["a" /* camelize */])(property); + + if (readonlyProperties.indexOf(value) === -1) { + this._sortable.option(value, newOptionValue[property]); + } + } + }, + getChildrenNodes: function getChildrenNodes() { + if (this.noneFunctionalComponentMode) { + return this.$children[0].$slots.default; + } + + var rawNodes = this.$slots.default; + return this.transitionMode ? rawNodes[0].child.$slots.default : rawNodes; + }, + computeIndexes: function computeIndexes() { + var _this4 = this; + + this.$nextTick(function () { + _this4.visibleIndexes = _computeIndexes(_this4.getChildrenNodes(), _this4.rootContainer.children, _this4.transitionMode, _this4.footerOffset); + }); + }, + getUnderlyingVm: function getUnderlyingVm(htmlElt) { + var index = computeVmIndex(this.getChildrenNodes() || [], htmlElt); + + if (index === -1) { + //Edge case during move callback: related element might be + //an element different from collection + return null; + } + + var element = this.realList[index]; + return { + index: index, + element: element + }; + }, + getUnderlyingPotencialDraggableComponent: function getUnderlyingPotencialDraggableComponent(_ref) { + var vue = _ref.__vue__; + + if (!vue || !vue.$options || !isTransitionName(vue.$options._componentTag)) { + if (!("realList" in vue) && vue.$children.length === 1 && "realList" in vue.$children[0]) return vue.$children[0]; + return vue; + } + + return vue.$parent; + }, + emitChanges: function emitChanges(evt) { + var _this5 = this; + + this.$nextTick(function () { + _this5.$emit("change", evt); + }); + }, + alterList: function alterList(onList) { + if (this.list) { + onList(this.list); + return; + } + + var newList = _toConsumableArray(this.value); + + onList(newList); + this.$emit("input", newList); + }, + spliceList: function spliceList() { + var _arguments = arguments; + + var spliceList = function spliceList(list) { + return list.splice.apply(list, _toConsumableArray(_arguments)); + }; + + this.alterList(spliceList); + }, + updatePosition: function updatePosition(oldIndex, newIndex) { + var updatePosition = function updatePosition(list) { + return list.splice(newIndex, 0, list.splice(oldIndex, 1)[0]); + }; + + this.alterList(updatePosition); + }, + getRelatedContextFromMoveEvent: function getRelatedContextFromMoveEvent(_ref2) { + var to = _ref2.to, + related = _ref2.related; + var component = this.getUnderlyingPotencialDraggableComponent(to); + + if (!component) { + return { + component: component + }; + } + + var list = component.realList; + var context = { + list: list, + component: component + }; + + if (to !== related && list && component.getUnderlyingVm) { + var destination = component.getUnderlyingVm(related); + + if (destination) { + return Object.assign(destination, context); + } + } + + return context; + }, + getVmIndex: function getVmIndex(domIndex) { + var indexes = this.visibleIndexes; + var numberIndexes = indexes.length; + return domIndex > numberIndexes - 1 ? numberIndexes : indexes[domIndex]; + }, + getComponent: function getComponent() { + return this.$slots.default[0].componentInstance; + }, + resetTransitionData: function resetTransitionData(index) { + if (!this.noTransitionOnDrag || !this.transitionMode) { + return; + } + + var nodes = this.getChildrenNodes(); + nodes[index].data = null; + var transitionContainer = this.getComponent(); + transitionContainer.children = []; + transitionContainer.kept = undefined; + }, + onDragStart: function onDragStart(evt) { + this.context = this.getUnderlyingVm(evt.item); + evt.item._underlying_vm_ = this.clone(this.context.element); + draggingElement = evt.item; + }, + onDragAdd: function onDragAdd(evt) { + var element = evt.item._underlying_vm_; + + if (element === undefined) { + return; + } + + Object(helper["d" /* removeNode */])(evt.item); + var newIndex = this.getVmIndex(evt.newIndex); + this.spliceList(newIndex, 0, element); + this.computeIndexes(); + var added = { + element: element, + newIndex: newIndex + }; + this.emitChanges({ + added: added + }); + }, + onDragRemove: function onDragRemove(evt) { + Object(helper["c" /* insertNodeAt */])(this.rootContainer, evt.item, evt.oldIndex); + + if (evt.pullMode === "clone") { + Object(helper["d" /* removeNode */])(evt.clone); + return; + } + + var oldIndex = this.context.index; + this.spliceList(oldIndex, 1); + var removed = { + element: this.context.element, + oldIndex: oldIndex + }; + this.resetTransitionData(oldIndex); + this.emitChanges({ + removed: removed + }); + }, + onDragUpdate: function onDragUpdate(evt) { + Object(helper["d" /* removeNode */])(evt.item); + Object(helper["c" /* insertNodeAt */])(evt.from, evt.item, evt.oldIndex); + var oldIndex = this.context.index; + var newIndex = this.getVmIndex(evt.newIndex); + this.updatePosition(oldIndex, newIndex); + var moved = { + element: this.context.element, + oldIndex: oldIndex, + newIndex: newIndex + }; + this.emitChanges({ + moved: moved + }); + }, + updateProperty: function updateProperty(evt, propertyName) { + evt.hasOwnProperty(propertyName) && (evt[propertyName] += this.headerOffset); + }, + computeFutureIndex: function computeFutureIndex(relatedContext, evt) { + if (!relatedContext.element) { + return 0; + } + + var domChildren = _toConsumableArray(evt.to.children).filter(function (el) { + return el.style["display"] !== "none"; + }); + + var currentDOMIndex = domChildren.indexOf(evt.related); + var currentIndex = relatedContext.component.getVmIndex(currentDOMIndex); + var draggedInList = domChildren.indexOf(draggingElement) !== -1; + return draggedInList || !evt.willInsertAfter ? currentIndex : currentIndex + 1; + }, + onDragMove: function onDragMove(evt, originalEvent) { + var onMove = this.move; + + if (!onMove || !this.realList) { + return true; + } + + var relatedContext = this.getRelatedContextFromMoveEvent(evt); + var draggedContext = this.context; + var futureIndex = this.computeFutureIndex(relatedContext, evt); + Object.assign(draggedContext, { + futureIndex: futureIndex + }); + var sendEvt = Object.assign({}, evt, { + relatedContext: relatedContext, + draggedContext: draggedContext + }); + return onMove(sendEvt, originalEvent); + }, + onDragEnd: function onDragEnd() { + this.computeIndexes(); + draggingElement = null; + } + } +}; + +if (typeof window !== "undefined" && "Vue" in window) { + window.Vue.component("draggable", draggableComponent); +} + +/* harmony default export */ var vuedraggable = (draggableComponent); +// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/entry-lib.js + + +/* harmony default export */ var entry_lib = __webpack_exports__["default"] = (vuedraggable); + + + +/***/ }) + +/******/ })["default"]; +}); +//# sourceMappingURL=vuedraggable.umd.js.map \ No newline at end of file diff --git a/package.json b/package.json index 5542157d7..de92198c8 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,12 @@ "minimist": "^1.2.6", "moment": "^2.29.2", "node-fetch": "^2.6.7", + "sortablejs": "^1.15.2", "spectrum-colorpicker2": "^2.0.8", "vue": "^2.6.14", "vue-select": "^3.13.0", - "vue2-leaflet": "^2.7.1" + "vue2-leaflet": "^2.7.1", + "vuedraggable": "^2.24.3" }, "devDependencies": { "eslint": "^8.24.0", diff --git a/tools/aceditor/presentation/styles/aceditor.css b/tools/aceditor/presentation/styles/aceditor.css index bc1a89c35..f5f178cfc 100644 --- a/tools/aceditor/presentation/styles/aceditor.css +++ b/tools/aceditor/presentation/styles/aceditor.css @@ -178,7 +178,7 @@ form:not(#ACEditor) .ace-container .ace_scroller { } .ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line { - background-color: #e8e8e8; + background-color: var(--neutral-light-color); } /* ================= */ diff --git a/tools/bazar/actions/BazarAction.php b/tools/bazar/actions/BazarAction.php index bb85ebdd7..0fb6c1837 100644 --- a/tools/bazar/actions/BazarAction.php +++ b/tools/bazar/actions/BazarAction.php @@ -99,7 +99,7 @@ public function run() $entryController = $this->getService(EntryController::class); // TODO put in all bazar templates - $this->wiki->AddJavascriptFile('tools/bazar/libs/bazar.js'); + $this->wiki->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); $view = $this->arguments[self::VARIABLE_VOIR]; $action = $this->arguments[self::VARIABLE_ACTION]; diff --git a/tools/bazar/actions/BazarListeAction.php b/tools/bazar/actions/BazarListeAction.php index 0b7e4773c..e82238f2b 100755 --- a/tools/bazar/actions/BazarListeAction.php +++ b/tools/bazar/actions/BazarListeAction.php @@ -255,7 +255,14 @@ public function run() ]); } else { $entries = $bazarListService->getEntries($this->arguments, $forms); - $filters = $bazarListService->formatFilters($this->arguments, $entries, $forms); + $filters = $bazarListService->getFilters($this->arguments, $entries, $forms); + + // backwardcompatibility, the structure of filters have changed in 06/2024 + $filters = array_reduce($filters, function ($carry, $filter) { + $carry[$filter['propName']] = $filter; + + return $carry; + }, []); // To handle multiple bazarlist in a same page, we need a specific ID per bazarlist // We use a global variable to count the number of bazarliste action run on this page @@ -266,7 +273,7 @@ public function run() $this->arguments['nbbazarliste'] = $GLOBALS['_BAZAR_']['nbbazarliste']; // TODO put in all bazar templates - $this->wiki->AddJavascriptFile('tools/bazar/libs/bazar.js'); + $this->wiki->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); return $this->render('@bazar/entries/index.twig', [ 'listId' => $GLOBALS['_BAZAR_']['nbbazarliste'], diff --git a/tools/bazar/actions/bazarlistecategorie.php b/tools/bazar/actions/bazarlistecategorie.php index 9d07e249e..864583a0e 100755 --- a/tools/bazar/actions/bazarlistecategorie.php +++ b/tools/bazar/actions/bazarlistecategorie.php @@ -13,7 +13,7 @@ $entryManager = $this->services->get(EntryManager::class); -$this->AddJavascriptFile('tools/bazar/libs/bazar.js'); +$this->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); // initialisation de la fonction de tri , inspiré par http://php.net/manual/fr/function.usort.php if (!function_exists('champCompare')) { diff --git a/tools/bazar/config.yaml b/tools/bazar/config.yaml index a44d5797c..3e8bf7425 100644 --- a/tools/bazar/config.yaml +++ b/tools/bazar/config.yaml @@ -58,7 +58,7 @@ parameters: # Le nombre maximum de fiches affichées par checkboxfiche sans que les champs 'Filter' et 'Cocher tout' ne soient affichés false pas de filtre BAZ_MAX_CHECKBOXLISTE_SANS_FILTRE: 6 # Le nombre maximum de fiches affichées par checkboxfuche sans que le champ 'selectall' ne soit affiché ; false : récupère le paramètre BAZ_MAX_CHECKBOXLISTE_SANS_FILTRE - BAZ_MAX_CHECKBOXENTRY_WITHOUT_SELECTALL: false + BAZ_MAX_CHECKBOXENTRY_WITHOUT_SELECTALL: 7 # type d'affichage par défaut pour checkboxfiche en mode normal BAZ_MAX_CHECKBOXENTRY_DISPLAY_MODE: 'list' # Le nombre maximum de fiches affichées par checkbox sans que le champ 'Filter' ne soit affiché ; false pas de filtre @@ -66,7 +66,7 @@ parameters: # Le nombre maximum d'éléments affichées par radio sans que le champ 'Filter' ne soit affiché ; false pas de filtre BAZ_MAX_RADIO_WITHOUT_FILTER: 6 # Le nombre maximum de fiches affichées par checkbox sans que le champ 'selectall' ne soit affiché ; false : récupère le paramètre BAZ_MAX_CHECKBOXLIST_WITHOUT_FILTER - BAZ_MAX_CHECKBOXLIST_WITHOUT_SELECTALL: false + BAZ_MAX_CHECKBOXLIST_WITHOUT_SELECTALL: 7 # type d'affichage par défaut pour checkbox en mode normal BAZ_MAX_CHECKBOXLIST_DISPLAY_MODE: 'div' # Hauteur d'affichage de la liste des options dans un mode Drag & Drop : null comportement par défaut (équivalent 300px), autres valeurs par exemple '450' pour 450px diff --git a/tools/bazar/controllers/ApiController.php b/tools/bazar/controllers/ApiController.php index f1cba3eec..55e4e424d 100644 --- a/tools/bazar/controllers/ApiController.php +++ b/tools/bazar/controllers/ApiController.php @@ -272,7 +272,7 @@ public function getBazarListData() ], $forms ); - $filters = $bazarListService->formatFilters($formattedGet, $entries, $forms); + $filters = $bazarListService->getFilters($formattedGet, $entries, $forms); // Basic fields $fieldList = ['id_fiche', 'bf_titre']; @@ -283,8 +283,8 @@ public function getBazarListData() // fields for colo / icon $fieldList = array_merge($fieldList, [$_GET['colorfield'] ?? null, $_GET['iconfield'] ?? null]); // Fields for filters - foreach ($filters as $field => $config) { - $fieldList[] = $field; + foreach ($filters as $filter) { + $fieldList[] = $filter['propName']; } // Fields used to search foreach ($searchfields as $field) { diff --git a/tools/bazar/controllers/ListController.php b/tools/bazar/controllers/ListController.php index 1f0e44553..db0b145c9 100644 --- a/tools/bazar/controllers/ListController.php +++ b/tools/bazar/controllers/ListController.php @@ -3,6 +3,8 @@ namespace YesWiki\Bazar\Controller; use YesWiki\Bazar\Service\ListManager; +use YesWiki\Core\Controller\AuthController; +use YesWiki\Core\Service\AclService; use YesWiki\Core\Service\Mailer; use YesWiki\Core\YesWikiController; use YesWiki\Security\Controller\SecurityController; @@ -11,11 +13,19 @@ class ListController extends YesWikiController { protected $listManager; protected $securityController; - - public function __construct(ListManager $listManager, SecurityController $securityController) - { + protected $aclService; + protected $authController; + + public function __construct( + ListManager $listManager, + SecurityController $securityController, + AclService $aclService, + AuthController $authController + ) { $this->listManager = $listManager; $this->securityController = $securityController; + $this->aclService = $aclService; + $this->authController = $authController; } public function displayAll() @@ -23,45 +33,34 @@ public function displayAll() if (isset($_POST['imported-list'])) { foreach ($_POST['imported-list'] as $listRaw) { $list = json_decode($listRaw, true); - $this->listManager->create($list['titre_liste'], $list['label']); + $this->listManager->create($list['title'], $list['nodes']); } echo '
' . _t('BAZ_LIST_IMPORT_SUCCESSFULL') . '.
'; + echo '
' . _t('BAZ_LIST_IMPORT_SUCCESSFULL') . '.
'; } $lists = $this->listManager->getAll(); - $values = []; foreach ($lists as $key => $list) { - $values[$key]['title'] = $list['titre_liste']; - $values[$key]['options'] = $list['label']; - $values[$key]['canEdit'] = !$this->securityController->isWikiHibernated() && $this->wiki->HasAccess('write', $key); - $values[$key]['canDelete'] = !$this->securityController->isWikiHibernated() && ($this->wiki->UserIsAdmin() || $this->wiki->UserIsOwner($key)); + $lists[$key]['canEdit'] = !$this->securityController->isWikiHibernated() && $this->wiki->HasAccess('write', $key); + $lists[$key]['canDelete'] = !$this->securityController->isWikiHibernated() && ($this->wiki->UserIsAdmin() || $this->wiki->UserIsOwner($key)); } return $this->render('@bazar/lists/list_table.twig', [ - 'lists' => $values, - 'loggedUser' => $this->wiki->GetUser(), + 'lists' => $lists, + 'loggedUser' => $this->authController->getLoggedUser(), 'canCreate' => !$this->securityController->isWikiHibernated(), ]); } public function create() { - if (isset($_POST['valider'])) { - $i = 1; - $values = []; - foreach ($_POST['label'] as $label) { - if (($label != null || $label != '') && ($_POST['id'][$i] != null || $_POST['id'][$i] != '')) { - $values[$_POST['id'][$i]] = $label; - $i++; - } - } - - $listeId = $this->listManager->create($_POST['titre_liste'], $values); + if (isset($_POST['submit'])) { + $listeId = $this->listManager->create($_POST['title'], json_decode($_POST['nodes'], true)); if ($this->shouldPostMessageOnSubmit()) { return $this->render('@core/iframe_result.twig', [ - 'data' => ['msg' => 'list_created', 'id' => $listeId, 'title' => $_POST['titre_liste']], + 'data' => ['msg' => 'list_created', 'id' => $listeId, 'title' => $_POST['title']], ]); } @@ -70,7 +69,9 @@ public function create() ); } - return $this->render('@bazar/lists/list_form.twig'); + return $this->render('@bazar/lists/list_form.twig', [ + 'list' => ['title' => '', 'nodes' => []], + ]); } private function shouldPostMessageOnSubmit() @@ -82,23 +83,13 @@ public function update($id) { $list = $this->listManager->getOne($id); - if (isset($_POST['valider'])) { - if ($this->wiki->HasAccess('write', $id)) { - $i = 1; - $values = []; - - foreach ($_POST['label'] as $label) { - if (($label != null || $label != '') && ($_POST['id'][$i] != null || $_POST['id'][$i] != '')) { - $values[$_POST['id'][$i]] = $label; - } - $i++; - } - - $this->listManager->update($id, $_POST['titre_liste'], $values); + if (isset($_POST['submit'])) { + if ($this->aclService->hasAccess('write', $id)) { + $this->listManager->update($id, $_POST['title'], json_decode($_POST['nodes'], true)); if ($this->shouldPostMessageOnSubmit()) { return $this->render('@core/iframe_result.twig', [ - 'data' => ['msg' => 'list_updated', 'id' => $id, 'title' => $_POST['titre_liste']], + 'data' => ['msg' => 'list_updated', 'id' => $id, 'title' => $_POST['title']], ]); } @@ -111,9 +102,7 @@ public function update($id) } return $this->render('@bazar/lists/list_form.twig', [ - 'listId' => $id, - 'title' => $list['titre_liste'], - 'labels' => $list['label'], + 'list' => $list, ]); } diff --git a/tools/bazar/fields/CheckboxField.php b/tools/bazar/fields/CheckboxField.php index db8fa0a2d..afe119b4b 100644 --- a/tools/bazar/fields/CheckboxField.php +++ b/tools/bazar/fields/CheckboxField.php @@ -43,7 +43,6 @@ protected function renderInput($entry) ]); return $htmlReturn; - break; case 'dragndrop': return $this->render($this->dragAndDropDisplayMode, [ 'options' => $this->getOptions(), @@ -53,13 +52,21 @@ protected function renderInput($entry) 'height' => empty($GLOBALS['wiki']->config['BAZ_CHECKBOX_DRAG_AND_DROP_MAX_HEIGHT']) ? null : $GLOBALS['wiki']->config['BAZ_CHECKBOX_DRAG_AND_DROP_MAX_HEIGHT'], 'oldValue' => $this->sanitizeValues($this->getValue($entry), 'string'), ]); - break; default: + // List with multi levels + if ($this->optionsTree) { + return $this->render('@bazar/inputs/checkbox-tree.twig', [ + 'data' => $this->optionsTree, + 'values' => $this->getValues($entry), + 'displaySelectAllLimit' => $this->displaySelectAllLimit, + ]); + } + if ($this->displayFilterLimit) { // javascript additions $GLOBALS['wiki']->AddJavascriptFile('tools/bazar/libs/vendor/jquery.fastLiveFilter.js'); $script = "$(function() { $('.filter-entries').each(function() { - $(this).fastLiveFilter($(this).siblings('.list-bazar-entries,.bazar-checkbox-cols')); }); + $(this).fastLiveFilter($(this).parent().siblings('.list-bazar-entries,.bazar-checkbox-cols')); }); });"; $GLOBALS['wiki']->AddJavascript($script); } diff --git a/tools/bazar/fields/CheckboxListField.php b/tools/bazar/fields/CheckboxListField.php index 164557b2f..ae475cc3d 100644 --- a/tools/bazar/fields/CheckboxListField.php +++ b/tools/bazar/fields/CheckboxListField.php @@ -28,14 +28,50 @@ protected function renderStatic($entry) { $keys = $this->getValues($entry); $values = []; + + if (count($values) > 0) { + return ''; + } + + // List with multi levels + if ($this->optionsTree) { + return $this->render('@bazar/fields/checkbox-tree.twig', [ + 'treeValues' => $this->filterTree($this->optionsTree, $keys), + ]); + } + + // List with one level foreach ($this->getOptions() as $key => $label) { if (in_array($key, $keys)) { $values[$key] = $label; } } - return (count($values) > 0) ? $this->render('@bazar/fields/checkbox.twig', [ + return $this->render('@bazar/fields/checkbox.twig', [ 'values' => $values, - ]) : ''; + ]); + } + + // Filter the tree to keep only branches where a nodeID is checked + private function filterTree($tree, $checkedValues) + { + $filteredTree = []; + + foreach ($tree as $node) { + if (in_array($node['id'], $checkedValues)) { + $filteredNode = $node; + $filteredNode['children'] = $this->filterTree($node['children'], $checkedValues); + $filteredTree[] = $filteredNode; + } else { + $filteredChildren = $this->filterTree($node['children'], $checkedValues); + if (!empty($filteredChildren)) { + $filteredNode = $node; + $filteredNode['children'] = $filteredChildren; + $filteredTree[] = $filteredNode; + } + } + } + + return $filteredTree; } } diff --git a/tools/bazar/fields/EnumField.php b/tools/bazar/fields/EnumField.php index 80b2e866b..67ee2b5bd 100644 --- a/tools/bazar/fields/EnumField.php +++ b/tools/bazar/fields/EnumField.php @@ -13,12 +13,14 @@ abstract class EnumField extends BazarField { protected $options; protected $optionsUrls; // only for loadOptionsFromJson + protected $optionsTree = null; // only for list with multi levels - protected $listLabel; // Allows to differentiate two enums using the same list + protected $linkedObjectName; protected $keywords; protected $queries; - protected const FIELD_LIST_LABEL = 6; + protected const FIELD_LINKED_OBJECT = 1; + public const FIELD_NAME = 6; protected const FIELD_KEYWORDS = 13; protected const FIELD_QUERIES = 15; @@ -26,22 +28,38 @@ public function __construct(array $values, ContainerInterface $services) { parent::__construct($values, $services); - $this->listLabel = $values[self::FIELD_LIST_LABEL]; + $this->name = $values[self::FIELD_NAME]; + $this->linkedObjectName = $values[self::FIELD_LINKED_OBJECT]; $this->keywords = $values[self::FIELD_KEYWORDS]; $this->queries = $values[self::FIELD_QUERIES]; $this->options = []; $this->optionsUrls = []; - $this->propertyName = $this->type . $this->name . $this->listLabel; + $this->propertyName = $this->name; } public function loadOptionsFromList() { if (!empty($this->getLinkedObjectName())) { - $listValues = $this->getService(ListManager::class)->getOne($this->getLinkedObjectName()); - if (is_array($listValues)) { - $this->options = $listValues['label']; + $list = $this->getService(ListManager::class)->getOne($this->getLinkedObjectName()); + $this->options = []; + foreach ($list['nodes'] ?? [] as $node) { + $this->loadOptionsFromListNode($node); + if (isset($node['children']) && count($node['children']) > 0) { + $this->optionsTree = $list['nodes']; + } + } + } + } + + // Recursively load options from list, in case the list is a tree (with children) + private function loadOptionsFromListNode($node, $parentLabel = '') + { + $this->options[$node['id']] = $parentLabel . $node['label']; + if (!empty($node['children'])) { + foreach ($node['children'] as $childNode) { + $this->loadOptionsFromListNode($childNode, "$parentLabel {$node['label']} ➤ "); } } } @@ -137,14 +155,15 @@ public function loadOptionsFromEntries() */ protected function prepareJSONEntryField() { - $this->propertyName = $this->type . removeAccents(preg_replace('/--+/u', '-', preg_replace('/[[:punct:]]/', '-', $this->name))) . $this->listLabel; $this->loadOptionsFromJson(); - if (preg_match('/^(.*\/\??)'// catch baseUrl + if ( + preg_match('/^(.*\/\??)' // catch baseUrl . '(?:' // followed by . '\w*\/json&(?:.*)demand=entries(?:&.*)?' // json handler with demand = entries . '|api\/forms\/[0-9]*\/entries' // or api forms/{id}/entries . '|api\/entries\/[0-9]*' // or api entries/{id} - . ')/', $this->name, $matches)) { + . ')/', $this->name, $matches) + ) { $this->baseUrl = $matches[1]; } else { $this->baseUrl = $this->name; @@ -157,6 +176,11 @@ public function getOptions() return $this->options; } + public function getOptionsTree() + { + return $this->optionsTree; + } + protected function getEntriesOptions() { // load options only when needed but not at construct to prevent infinite loops @@ -171,14 +195,9 @@ protected function getEntriesOptions() return $this->options; } - public function getName() - { - return $this->listLabel; - } - public function getLinkedObjectName() { - return $this->name; + return $this->linkedObjectName; } /** diff --git a/tools/bazar/fields/FileField.php b/tools/bazar/fields/FileField.php index 09be22a5d..ae29f5366 100644 --- a/tools/bazar/fields/FileField.php +++ b/tools/bazar/fields/FileField.php @@ -58,7 +58,7 @@ protected function renderInput($entry) $wiki = $this->getWiki(); $value = $this->getValue($entry); $deletedFile = false; - $wiki->services->get(AssetsManager::class)->AddJavascriptFile('tools/bazar/presentation/javascripts/file-field.js'); + $wiki->services->get(AssetsManager::class)->AddJavascriptFile('tools/bazar/presentation/javascripts/inputs/file-field.js'); if (!empty($value)) { if (!empty($entry) && isset($_GET['delete_file']) && $_GET['delete_file'] === $value) { diff --git a/tools/bazar/fields/ImageField.php b/tools/bazar/fields/ImageField.php index b549afa8e..5bb4a7c42 100644 --- a/tools/bazar/fields/ImageField.php +++ b/tools/bazar/fields/ImageField.php @@ -48,7 +48,7 @@ protected function renderInput($entry) $value = $this->getValue($entry); // javascript pour gerer la previsualisation // si une taille maximale est indiquée, on teste - $wiki->services->get(AssetsManager::class)->AddJavascriptFile('tools/bazar/presentation/javascripts/image-field.js'); + $wiki->services->get(AssetsManager::class)->AddJavascriptFile('tools/bazar/presentation/javascripts/inputs/image-field.js'); if (isset($value) && $value != '') { if (isset($_GET['suppr_image']) && $_GET['suppr_image'] === $value) { diff --git a/tools/bazar/fields/TabsField.php b/tools/bazar/fields/TabsField.php index 88473d4aa..d0a55d64c 100644 --- a/tools/bazar/fields/TabsField.php +++ b/tools/bazar/fields/TabsField.php @@ -58,7 +58,7 @@ protected function prepareText($mode): ?string protected function renderInput($entry) { if ($this->getMoveSubmitButtonToLastTab()) { - $this->getService(AssetsManager::class)->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar-edit-tabs-field.js'); + $this->getService(AssetsManager::class)->AddJavascriptFile('tools/bazar/presentation/javascripts/inputs/tabs.js'); } $this->formText = $this->prepareText('form'); diff --git a/tools/bazar/handlers/__WidgetHandler.php b/tools/bazar/handlers/__WidgetHandler.php index fe86783ea..8e033865c 100644 --- a/tools/bazar/handlers/__WidgetHandler.php +++ b/tools/bazar/handlers/__WidgetHandler.php @@ -1,5 +1,6 @@ getService(EntryManager::class); $formManager = $this->getService(FormManager::class); + $bazarListService = $this->getService(BazarListService::class); if (!isset($_GET['id'])) { return null; } - $this->wiki->AddJavascriptFile('tools/bazar/libs/bazar.js'); + $this->wiki->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); ob_start(); echo '
'; echo '

' . _t('BAZ_WIDGET_HANDLER_TITLE') . '

' . "\n"; $entries = $entryManager->search(['formsIds' => [!empty($_GET['id']) ? strip_tags($_GET['id']) : null], 'keywords' => (!empty($_GET['q']) ? strip_tags($_GET['q']) : null)], true, true); - $facettables = $formManager->scanAllFacettable($entries); + $forms = $formManager->getAll(); + $filters = $bazarListService->getFilters(['groups' => ['all']], $entries, $forms); - $labels = []; - $showTooltip = []; - foreach ($entries as $entry) { - $form = $formManager->getOne($entry['id_typeannonce']); - foreach ($form['prepared'] as $field) { - $propName = $field->getPropertyName(); - if (in_array($propName, array_keys($facettables)) && - !in_array($propName, array_keys($labels))) { - $labels[$propName] = !empty($field->getLabel()) ? $field->getLabel() : - ($facettables[$propName]['source'] ?? $propName); - $showTooltip[$propName] = false; - if (!isset($facettables[$propName]['label'])) { - $facettables[$propName]['label'] = $labels[$propName]; - } - } - } + // Reproduce the sames variables from the new $filters, so the view does not need to be refactored + $labels = $facettes = $showTooltip = []; + foreach ($filters as $filter) { + $labels[$filter['propName']] = $filter['title']; + $facettes[$filter['propName']] = [ + 'label' => $filter['title'], + 'source' => $filter['propName'], + ]; + $showTooltip[$filter['propName']] = false; } $params = [ @@ -55,7 +51,7 @@ public function run() $urlParams = 'id=' . strip_tags($_GET['id']) . (isset($_GET['query']) ? '&query=' . strip_tags($_GET['query']) : '') . (!empty($q) ? '&q=' . $q : ''); echo $this->render('@bazar/widget.tpl.html', [ - 'facettes' => $facettables, + 'facettes' => $facettes, 'showtooltip' => $showTooltip, 'facettestext' => $labels, 'params' => $params, diff --git a/tools/bazar/handlers/page/__html.php b/tools/bazar/handlers/page/__html.php index 1493dd744..7b19d3295 100755 --- a/tools/bazar/handlers/page/__html.php +++ b/tools/bazar/handlers/page/__html.php @@ -9,7 +9,7 @@ $entryManager = $this->services->get(EntryManager::class); if ($entryManager->isEntry($this->GetPageTag())) { - $this->AddJavascriptFile('tools/bazar/libs/bazar.js'); + $this->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); $fiche = $entryManager->getOne($this->GetPageTag()); $this->page['body'] = '""' . baz_voir_fiche(0, $fiche) . '""'; } diff --git a/tools/bazar/handlers/page/__show.php b/tools/bazar/handlers/page/__show.php index 39d747004..51873f81d 100755 --- a/tools/bazar/handlers/page/__show.php +++ b/tools/bazar/handlers/page/__show.php @@ -19,6 +19,6 @@ $fiche = $entryManager->getOne($this->GetPageTag(), $semantic); $this->exit(json_encode($fiche)); } else { - $this->AddJavascriptFile('tools/bazar/libs/bazar.js'); + $this->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); } } diff --git a/tools/bazar/handlers/page/show__.php b/tools/bazar/handlers/page/show__.php index 7a96e2bd2..3350d6d0a 100755 --- a/tools/bazar/handlers/page/show__.php +++ b/tools/bazar/handlers/page/show__.php @@ -9,7 +9,7 @@ $entryManager = $this->services->get(EntryManager::class); if ($entryManager->isEntry($this->GetPageTag())) { - $this->AddJavascriptFile('tools/bazar/libs/bazar.js'); + $this->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); $fiche = $entryManager->getOne($this->GetPageTag()); diff --git a/tools/bazar/lang/bazar_ca.inc.php b/tools/bazar/lang/bazar_ca.inc.php index fdc54cf74..c0485ddab 100755 --- a/tools/bazar/lang/bazar_ca.inc.php +++ b/tools/bazar/lang/bazar_ca.inc.php @@ -200,11 +200,8 @@ 'IMAGE_VALIDE_REQUIS' => 'És obligatori afegir una imatge a aquesta fitxa', 'BAZ_LE_FICHIER_A_ETE_EFFACE' => 'El fitxer {file} s\'ha eliminat.', 'BAZ_PAS_DE_FICHES_SAUVEES_EN_VOTRE_NOM' => 'No teniu cap fitxa fins al moment.', - 'BAZ_DEPLACER_L_ELEMENT' => 'Desplaça l\'element', 'BAZ_LIST_NAME' => 'Nom de la llista', 'BAZ_SAVE' => 'Desa', - 'BAZ_KEY' => 'Clau', - 'BAZ_TEXT' => 'Text', 'BAZ_BAD_IMAGE_FILE_EXTENSION' => 'L\'extensió de la imatge no és correcta (ha de ser .jpg ou .png o .gif)', 'BAZ_IMAGE_FILE_NOT_FOUND' => 'Fitxer de la imatge no trobat', // 'BAZAR_ACTION_VALEUR' => 'Action {{valeur...}}', @@ -433,4 +430,4 @@ // services/EntryManager.php // 'BAZ_CORRESPONDANCE_ERROR' => 'action bazarliste : parametre correspondance mal rempli : il doit etre de la forme correspondance="identifiant_1=identifiant_2" ou correspondance="identifiant_1=identifiant_2, identifiant_3=identifiant_4"', // 'BAZ_CORRESPONDANCE_ERROR2' => "action bazarliste : le paramètre correspondance est mal rempli.\nIl doit être de la forme correspondance=\"identifiant_1=identifiant_2\" ou correspondance=\"identifiant_1=identifiant_2, identifiant_3=identifiant_4\"", -]; +]; \ No newline at end of file diff --git a/tools/bazar/lang/bazar_en.inc.php b/tools/bazar/lang/bazar_en.inc.php index 191d820e2..878747f44 100644 --- a/tools/bazar/lang/bazar_en.inc.php +++ b/tools/bazar/lang/bazar_en.inc.php @@ -5,8 +5,6 @@ 'BAZ_LISTES' => 'Lists', 'BAZ_NOM_LISTE' => 'Name of the list', 'BAZ_VALEURS_LISTE' => 'Values of the list', - 'BAZ_VALEURS_LISTE_HINT' => 'Key should be unique, without spaces nor any special characters', - 'BAZ_AJOUTER_LABEL_LISTE' => 'Add a new value to the list', 'BAZ_AJOUTER_CHAMPS_FORMULAIRE' => 'Add a new field to the form', 'BAZ_MODIFIER_FORMULAIRES' => 'Forms administration', 'BAZ_GESTION_LISTES' => 'Lists administration', @@ -202,11 +200,8 @@ 'IMAGE_VALIDE_REQUIS' => 'Add an image for this entry is required', 'BAZ_LE_FICHIER_A_ETE_EFFACE' => 'The file {file} was deleted.', 'BAZ_PAS_DE_FICHES_SAUVEES_EN_VOTRE_NOM' => 'No entry on your name was saved.', - 'BAZ_DEPLACER_L_ELEMENT' => 'Move this element', 'BAZ_LIST_NAME' => 'Name of the list', 'BAZ_SAVE' => 'Save', - 'BAZ_KEY' => 'Key', - 'BAZ_TEXT' => 'Text', 'BAZ_BAD_IMAGE_FILE_EXTENSION' => 'Bad file extension for an image (should be .jpg or .png or .gif)', 'BAZ_IMAGE_FILE_NOT_FOUND' => 'Image file not found', // 'BAZAR_ACTION_VALEUR' => 'Action {{valeur...}}', @@ -396,4 +391,4 @@ // 'EVENTS_WHEN_IN_MONTH' => 'A quel moment du mois ?', 'BAZ_FILEFIELD_FILE' => 'File : {filename}', -]; +]; \ No newline at end of file diff --git a/tools/bazar/lang/bazar_es.inc.php b/tools/bazar/lang/bazar_es.inc.php index ab4d33938..e069edbd1 100755 --- a/tools/bazar/lang/bazar_es.inc.php +++ b/tools/bazar/lang/bazar_es.inc.php @@ -201,11 +201,8 @@ 'IMAGE_VALIDE_REQUIS' => 'Añadir un imagen en esta ficha es obligatorio', 'BAZ_LE_FICHIER_A_ETE_EFFACE' => 'El archivo {file} ha sido borrado.', 'BAZ_PAS_DE_FICHES_SAUVEES_EN_VOTRE_NOM' => 'No tienes nignuna ficha registrada por ahora.', - 'BAZ_DEPLACER_L_ELEMENT' => 'Desplazar el elemento', 'BAZ_LIST_NAME' => 'Nombre de la lista', 'BAZ_SAVE' => 'Guardar', - 'BAZ_KEY' => 'Clave;', - 'BAZ_TEXT' => 'Texto', 'BAZ_BAD_IMAGE_FILE_EXTENSION' => 'Mala extensión para la imagen (tiene que ser .jpg ou .png ou .gif)', 'BAZ_IMAGE_FILE_NOT_FOUND' => 'Archivo imagen no encontrado', 'BAZAR_ACTION_VALEUR' => 'Acción {{valeur...}}', @@ -434,4 +431,4 @@ // services/EntryManager.php // 'BAZ_CORRESPONDANCE_ERROR' => 'action bazarliste : parametre correspondance mal rempli : il doit etre de la forme correspondance="identifiant_1=identifiant_2" ou correspondance="identifiant_1=identifiant_2, identifiant_3=identifiant_4"', // 'BAZ_CORRESPONDANCE_ERROR2' => "action bazarliste : le paramètre correspondance est mal rempli.\nIl doit être de la forme correspondance=\"identifiant_1=identifiant_2\" ou correspondance=\"identifiant_1=identifiant_2, identifiant_3=identifiant_4\"", -]; +]; \ No newline at end of file diff --git a/tools/bazar/lang/bazar_fr.inc.php b/tools/bazar/lang/bazar_fr.inc.php index 5bd9c7764..7c4ec0921 100755 --- a/tools/bazar/lang/bazar_fr.inc.php +++ b/tools/bazar/lang/bazar_fr.inc.php @@ -5,8 +5,6 @@ 'BAZ_LISTES' => 'Listes', 'BAZ_NOM_LISTE' => 'Nom de la liste', 'BAZ_VALEURS_LISTE' => 'Valeurs de la liste', - 'BAZ_VALEURS_LISTE_HINT' => 'Pour la clé pas d\'accent, de caractères spéciaux, d\'espace. Chaque clé sera unique.', - 'BAZ_AJOUTER_LABEL_LISTE' => 'Ajouter une nouvelle valeur à la liste', 'BAZ_AJOUTER_CHAMPS_FORMULAIRE' => 'Ajouter un nouveau champs au formulaire', 'BAZ_MODIFIER_FORMULAIRES' => 'Gestion des formulaires', 'BAZ_GESTION_LISTES' => 'Gestion des listes', @@ -203,11 +201,8 @@ 'IMAGE_VALIDE_REQUIS' => 'Ajouter une image à cette fiche est obligatoire', 'BAZ_LE_FICHIER_A_ETE_EFFACE' => 'Le fichier {file} a été effacé.', 'BAZ_PAS_DE_FICHES_SAUVEES_EN_VOTRE_NOM' => 'Vous n\'avez aucune fiche enregistrée pour l\'instant.', - 'BAZ_DEPLACER_L_ELEMENT' => 'Déplacer l\'élément', 'BAZ_LIST_NAME' => 'Nom de la liste', 'BAZ_SAVE' => 'Enregistrer', - 'BAZ_KEY' => 'Clé', - 'BAZ_TEXT' => 'Texte', 'BAZ_BAD_IMAGE_FILE_EXTENSION' => 'Mauvaise extension pour l\'image (doit être .jpg ou .png ou .gif)', 'BAZ_IMAGE_FILE_NOT_FOUND' => 'Fichier image non trouvé', 'BAZAR_ACTION_VALEUR' => 'Action {{valeur...}}', @@ -439,4 +434,4 @@ // services/EntryManager.php 'BAZ_CORRESPONDANCE_ERROR' => 'action bazarliste : parametre correspondance mal rempli : il doit etre de la forme correspondance="identifiant_1=identifiant_2" ou correspondance="identifiant_1=identifiant_2, identifiant_3=identifiant_4"', 'BAZ_CORRESPONDANCE_ERROR2' => "action bazarliste : le paramètre correspondance est mal rempli.\nIl doit être de la forme correspondance=\"identifiant_1=identifiant_2\" ou correspondance=\"identifiant_1=identifiant_2, identifiant_3=identifiant_4\"", -]; +]; \ No newline at end of file diff --git a/tools/bazar/lang/bazar_nl.inc.php b/tools/bazar/lang/bazar_nl.inc.php index 4a1527084..f2e47c517 100644 --- a/tools/bazar/lang/bazar_nl.inc.php +++ b/tools/bazar/lang/bazar_nl.inc.php @@ -200,11 +200,8 @@ 'IMAGE_VALIDE_REQUIS' => 'U dient een afbeelding toe te voegen aan deze fiche', 'BAZ_LE_FICHIER_A_ETE_EFFACE' => 'Het bestand {file} werd gewist.', 'BAZ_PAS_DE_FICHES_SAUVEES_EN_VOTRE_NOM' => 'U hebt momenteel geen enkele geregistreerde fiche.', - 'BAZ_DEPLACER_L_ELEMENT' => 'Element verplaatsen', 'BAZ_LIST_NAME' => 'Naam van de lijst', 'BAZ_SAVE' => 'Registreren', - 'BAZ_KEY' => 'Sleutel', - 'BAZ_TEXT' => 'Tekst', 'BAZ_BAD_IMAGE_FILE_EXTENSION' => 'Onjuiste extensie voor de afbeelding (moet .jpg of .png of .gif zijn)', 'BAZ_IMAGE_FILE_NOT_FOUND' => 'Beeldbestand niet gevonden', // 'BAZAR_ACTION_VALEUR' => 'Action {{valeur...}}', @@ -411,4 +408,4 @@ // services/EntryManager.php // 'BAZ_CORRESPONDANCE_ERROR' => 'action bazarliste : parametre correspondance mal rempli : il doit etre de la forme correspondance="identifiant_1=identifiant_2" ou correspondance="identifiant_1=identifiant_2, identifiant_3=identifiant_4"', // 'BAZ_CORRESPONDANCE_ERROR2' => "action bazarliste : le paramètre correspondance est mal rempli.\nIl doit être de la forme correspondance=\"identifiant_1=identifiant_2\" ou correspondance=\"identifiant_1=identifiant_2, identifiant_3=identifiant_4\"", -]; +]; \ No newline at end of file diff --git a/tools/bazar/lang/bazar_pt.inc.php b/tools/bazar/lang/bazar_pt.inc.php index 4e0472d4f..2a9dba962 100755 --- a/tools/bazar/lang/bazar_pt.inc.php +++ b/tools/bazar/lang/bazar_pt.inc.php @@ -200,11 +200,8 @@ 'IMAGE_VALIDE_REQUIS' => 'E obrigatório adicionar uma imagem para esta ficha', 'BAZ_LE_FICHIER_A_ETE_EFFACE' => 'O arquivo {file} foi apagado.', 'BAZ_PAS_DE_FICHES_SAUVEES_EN_VOTRE_NOM' => 'Você não salvou nenhuma ficha ainda.', - 'BAZ_DEPLACER_L_ELEMENT' => 'Mover o item', 'BAZ_LIST_NAME' => 'Nome da lista', 'BAZ_SAVE' => 'Salvar', - 'BAZ_KEY' => 'Chave', - 'BAZ_TEXT' => 'Texto', 'BAZ_BAD_IMAGE_FILE_EXTENSION' => 'Extensão errada para a imagem (deve ser. Jpg ou. PNG ou. GIF)', 'BAZ_IMAGE_FILE_NOT_FOUND' => 'Arquivo de imagem não encontrado', 'BAZAR_ACTION_VALEUR' => 'Ação {{valeur...}}', @@ -433,4 +430,4 @@ // services/EntryManager.php // 'BAZ_CORRESPONDANCE_ERROR' => 'action bazarliste : parametre correspondance mal rempli : il doit etre de la forme correspondance="identifiant_1=identifiant_2" ou correspondance="identifiant_1=identifiant_2, identifiant_3=identifiant_4"', // 'BAZ_CORRESPONDANCE_ERROR2' => "action bazarliste : le paramètre correspondance est mal rempli.\nIl doit être de la forme correspondance=\"identifiant_1=identifiant_2\" ou correspondance=\"identifiant_1=identifiant_2, identifiant_3=identifiant_4\"", -]; +]; \ No newline at end of file diff --git a/tools/bazar/lang/bazarjs_en.inc.php b/tools/bazar/lang/bazarjs_en.inc.php index f3acdcb73..9cb02d9a4 100644 --- a/tools/bazar/lang/bazarjs_en.inc.php +++ b/tools/bazar/lang/bazarjs_en.inc.php @@ -40,10 +40,7 @@ 'GEOLOCATER_GROUP_GEOLOCATIZATION' => 'Fields to use for geolocalization', 'GEOLOCATER_GROUP_GEOLOCATIZATION_HINT' => 'Provide at least one field', 'GEOLOCATER_NOT_FOUND' => 'Not foundable "{addr}" address', - // libs/bazar.edit_lists.js - // 'BAZ_EDIT_LISTS_CONFIRM_DELETE' => 'Confirmez-vous la suppression de cette valeur dans la liste ?', - // 'BAZ_EDIT_LISTS_DELETE_ERROR' => 'Le dernier élément ne peut être supprimé.', - // libs/bazar.js + // presentation/javascripts/bazar.js // 'BAZ_FORM_REQUIRED_FIELD' => 'Veuillez saisir tous les champs obligatoires (avec une asterisque rouge)', // 'BAZ_FORM_INVALID_EMAIL' => 'L\'email saisi n\'est pas valide', // 'BAZ_FORM_INVALID_TEXT' => 'Le texte n\'est pas valide', @@ -260,4 +257,8 @@ // reactions 'BAZ_ACTIVATE_REACTIONS' => 'Activate reactions on this entry?', 'FILEFIELD_TOO_LARGE_FILE' => 'The file is too large, maximum {fileMaxSize} bytes', + // list-node.js + 'LIST_DRAGDROP' => 'Move this element', + 'LIST_KEY' => 'Key', + 'LIST_TEXT' => 'Text', ]; diff --git a/tools/bazar/lang/bazarjs_fr.inc.php b/tools/bazar/lang/bazarjs_fr.inc.php index 890fd2528..1ae3a672b 100644 --- a/tools/bazar/lang/bazarjs_fr.inc.php +++ b/tools/bazar/lang/bazarjs_fr.inc.php @@ -42,10 +42,7 @@ 'GEOLOCATER_GROUP_GEOLOCATIZATION' => 'Champs à utiliser pour la géolocalisation', 'GEOLOCATER_GROUP_GEOLOCATIZATION_HINT' => 'Renseignez au moins un champ', 'GEOLOCATER_NOT_FOUND' => 'Adresse "{addr}" introuvable', - // libs/bazar.edit_lists.js - 'BAZ_EDIT_LISTS_CONFIRM_DELETE' => 'Confirmez-vous la suppression de cette valeur dans la liste ?', - 'BAZ_EDIT_LISTS_DELETE_ERROR' => 'Le dernier élément ne peut être supprimé.', - // libs/bazar.js + // presentation/javascripts/bazar.js 'BAZ_FORM_REQUIRED_FIELD' => 'Veuillez saisir tous les champs obligatoires (asterisque rouge)', 'BAZ_FORM_INVALID_EMAIL' => 'L\'email saisi n\'est pas valide', 'BAZ_FORM_INVALID_TEXT' => 'Le texte n\'est pas valide', @@ -260,4 +257,12 @@ 'BAZAR_URL_DISPLAY_VIDEO' => 'Afficher le lecteur si le lien est une vidéo ?', // templates/entries/index-dynamic-temapltes/BazarCalendar_ButtonICS.js 'BAZ_CALENDAR_EXPORT_BUTTON_TITLE' => 'Ajouter à votre calendrier', + // liste-node.js + 'LIST_DRAGDROP' => "Déplacer l'élément", + 'LIST_KEY' => 'Clé', + 'LIST_TEXT' => 'Texte', + 'LIST_ADD_NODE' => 'Ajouter une valeur', + 'LIST_ADD_CHILD_NODE' => 'Ajouter une sous valeur', + 'LIST_ERROR_MISSING_IDS' => "Certaines valeurs n'ont pas de clé associée", + 'LIST_ERROR_DUPLICATES_IDS' => 'Chaque clé doit être unique. Ces clés sont utilisées plusieurs fois : ', ]; diff --git a/tools/bazar/lang/bazarjs_ta.inc.php b/tools/bazar/lang/bazarjs_ta.inc.php index a47b13293..3c5154a09 100644 --- a/tools/bazar/lang/bazarjs_ta.inc.php +++ b/tools/bazar/lang/bazarjs_ta.inc.php @@ -45,7 +45,7 @@ // libs/bazar.edit_lists.js 'BAZ_EDIT_LISTS_CONFIRM_DELETE' => 'உறுதிப்படுத்தல்-வவுச் லா அடக்குமுறை டி செட் வாலூர் டான்ச் லா லிச்டே?', 'BAZ_EDIT_LISTS_DELETE_ERROR' => 'லு டெர்னியர் élément ne peut être supprimé.', - // libs/bazar.js + // presentation/javascripts/bazar.js 'BAZ_FORM_REQUIRED_FIELD' => 'Veuillez Saisir Tous les Samps Walligatoires (நட்சத்திரக் கயிறு)', 'BAZ_FORM_INVALID_EMAIL' => 'L\'aigral saisi n\'est pas valuide', 'BAZ_FORM_INVALID_TEXT' => 'லு டெக்ச்டே என்ச்ட் பாச் மதிப்புமிக்கது', diff --git a/tools/bazar/libs/bazar.edit_lists.js b/tools/bazar/libs/bazar.edit_lists.js deleted file mode 100755 index a709da39b..000000000 --- a/tools/bazar/libs/bazar.edit_lists.js +++ /dev/null @@ -1,126 +0,0 @@ -$(document).ready(() => { - // on rend les listes deplacables - const sortables = $('.list-sortables') - if (sortables.length > 0) { - sortables.sortable({ - handle: '.handle-listitems', - update() { - $("#bazar_form_lists .list-sortables input[name^='label']").each(function(i) { - $(this).attr('name', `label[${i + 1}]`) - .prev().attr('name', `id[${i + 1}]`) - .parent('.liste_ligne') - .attr('id', `row${i + 1}`) - .find('input:hidden') - .attr('name', `ancienlabel[${i + 1}]`) - }) - } - }) - } - - const newitem = $('#empty-new-item').html() - - // pour la gestion des listes, on peut rajouter dynamiquement des champs - $('.ajout_label_liste').on('click', () => { - const nb = $("#bazar_form_lists .list-sortables input[name^='label']").length + 1 - const nextnewitem = newitem.replace(/@nb@/gi, nb) - $('#bazar_form_lists .list-sortables').append(nextnewitem) - $(`#bazar_form_lists input[name='id[${nb}]']`).focus() - return false - }) - - // on supprime un champs pour une liste - $('#bazar_form_lists ul.list-sortables').on('click', '.suppression_label_liste', function() { - const id = `#${$(this).parent('.liste_ligne').attr('id')}` - const nb = $("#bazar_form_lists .list-sortables input[name^='label']").length - if (nb > 1) { - if (confirm(_t('BAZ_EDIT_LISTS_CONFIRM_DELETE'))) { - const nom = `a_effacer_${$(id).find('input:hidden').attr('name')}` - $(id).find('input:hidden').attr('name', nom).appendTo('#bazar_form_lists') - $(id).remove() - $("#bazar_form_lists .list-sortables input[name^='label']").each(function(i) { - $(this).attr('name', `label[${i + 1}]`) - .prev().attr('name', `id[${i + 1}]`) - .parent('.liste_ligne') - .attr('id', `row${i + 1}`) - .find('input:hidden') - .attr('name', `ancienlabel[${i + 1}]`) - }) - } - } else { - alert(_t('BAZ_EDIT_LISTS_DELETE_ERROR')) - } - - return false - }) - - // initialise le validateur - $('.btn-save-list').click(() => { - - }) - - // import de listes à partir d'un yeswiki - const btnimportlist = $('#btn-import-lists') - const resultimportlist = $('#import-lists-result') - const resultimporttable = $('#import-lists-table') - const resultimportform = $('#import-lists-form') - const listtranslations = $('#list-translations').data() - const existinglists = $('#existing-lists-table') - btnimportlist.click(() => { - // on enleve les anciens contenus - resultimportlist.html('') - resultimportform.addClass('hide') - resultimporttable.find('tbody').html('') - - // url saisie - let url = $('#url-import-lists').val() - - // expression réguliere pour trouver une url valide - const rgHttpUrl = /^(http|https):\/\/(([a-zA-Z0-9$\-_.+!*'(),;:&=]|%[0-9a-fA-F]{2})+@)?(((25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])){3})|localhost|([a-zA-Z0-9\-\u00C0-\u017F]+\.)+([a-zA-Z]{2,}))(:[0-9]+)?(\/(([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*(\/([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*)*)?(\?([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?(\#([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?)?$/ - - if (rgHttpUrl.test(url)) { - // on formate l url pour acceder au service json de yeswiki - const taburl = url.split('wakka.php') - url = `${taburl[0].replace(/\/+$/g, '')}/wakka.php?wiki=BazaR/json&demand=lists` - resultimportlist.html(`
${listtranslations.loading}... ${listtranslations.recuperation} ${url}
`) - $.ajax({ - method: 'GET', - url - }).done((data) => { - resultimportlist.html('') - let count = 0 - for (var idlist in data) { - if (data.hasOwnProperty(idlist)) { - count++ - let select = `` - for (const key in data[idlist].label) { - if (data[idlist].label.hasOwnProperty(key)) { - select += `` - } - } - - let trclass = '' - let existingmessage = '' - if (existinglists.find('td').filter(function() { - return $(this).text() === idlist - }).length > 0) { - trclass = ' class="error danger"' - existingmessage = `
${listtranslations.existingmessage}` - } - - let tablerow = `` - - tablerow += `${idlist + existingmessage}${data[idlist].titre_liste}` - resultimporttable.find('tbody').append(tablerow) - } - } - - resultimportform.removeClass('hide') - resultimportlist.prepend(`
${listtranslations.nblistsfound} : ${count}
`) - }).fail((jqXHR, textStatus, errorThrown) => { - resultimportlist.html(`
${listtranslations.noanswers}.
`) - }) - } else { - resultimportlist.html(`
${listtranslations.notvalidurl} : ${url}
`) - } - }) -}) diff --git a/tools/bazar/libs/bazar.fonct.php b/tools/bazar/libs/bazar.fonct.php index 8eb3dfd9f..fc1db0dba 100755 --- a/tools/bazar/libs/bazar.fonct.php +++ b/tools/bazar/libs/bazar.fonct.php @@ -5,6 +5,7 @@ use YesWiki\Bazar\Field\EnumField; use YesWiki\Bazar\Service\EntryManager; use YesWiki\Bazar\Service\FormManager; +use YesWiki\Bazar\Service\ListManager; function multiArraySearch($array, $key, $value) { @@ -25,10 +26,10 @@ function multiArraySearch($array, $key, $value) function baz_forms_and_lists_ids() { - foreach (baz_valeurs_liste() as $listId => $list) { - $lists[$listId] = $list['titre_liste']; - } - + $lists = $GLOBALS['wiki']->services->get(ListManager::class)->getAll(); + $lists = array_map(function ($list) { + return $list['title']; + }, $lists); $requete = 'SELECT bn_id_nature, bn_label_nature FROM ' . $GLOBALS['wiki']->config['table_prefix'] . 'nature'; $result = $GLOBALS['wiki']->LoadAll($requete); foreach ($result as $form) { @@ -45,36 +46,39 @@ function getHtmlDataAttributes($fiche, $formtab = '') $form = isset($formtab[$fiche['id_typeannonce']]) ? $formtab[$fiche['id_typeannonce']] : $GLOBALS['wiki']->services->get(FormManager::class)->getOne($fiche['id_typeannonce']); foreach ($fiche as $key => $value) { if (!empty($value)) { - if (in_array( - $key, - [ - 'bf_latitude', - 'bf_longitude', - 'id_typeannonce', - 'owner', - 'date_creation_fiche', - 'date_debut_validite_fiche', - 'date_fin_validite_fiche', - 'id_fiche', - 'statut_fiche', - 'date_maj_fiche', - ] - )) { + if ( + in_array( + $key, + [ + 'bf_latitude', + 'bf_longitude', + 'id_typeannonce', + 'owner', + 'date_creation_fiche', + 'date_debut_validite_fiche', + 'date_fin_validite_fiche', + 'id_fiche', + 'statut_fiche', + 'date_maj_fiche', + ] + ) + ) { $htmldata .= - 'data-' . htmlspecialchars($key) . '="' . - htmlspecialchars($value) . '" '; + 'data-' . htmlspecialchars($key) . '="' . + htmlspecialchars($value) . '" '; } else { if (isset($form['prepared'])) { foreach ($form['prepared'] as $field) { $propertyName = $field->getPropertyName(); if ($propertyName === $key) { - if ($field instanceof EnumField + if ( + $field instanceof EnumField || $field instanceof DateField || $field->getName() == 'scope' ) { $htmldata .= - 'data-' . htmlspecialchars($key) . '="' . - htmlspecialchars(is_array($value) ? '[' . implode(',', $value) . ']' : $value) . '" '; + 'data-' . htmlspecialchars($key) . '="' . + htmlspecialchars(is_array($value) ? '[' . implode(',', $value) . ']' : $value) . '" '; } } } diff --git a/tools/bazar/libs/bazar.fonct.retrocompatibility.php b/tools/bazar/libs/bazar.fonct.retrocompatibility.php index ffcb9109f..552449833 100644 --- a/tools/bazar/libs/bazar.fonct.retrocompatibility.php +++ b/tools/bazar/libs/bazar.fonct.retrocompatibility.php @@ -147,14 +147,6 @@ function formulaire_valeurs_template_champs($template) return $GLOBALS['wiki']->services->get(FormManager::class)->parseTemplate($template); } -/** - * @deprecated Use FormManager::scanAllFacettable - */ -function scanAllFacettable($fiches, $params, $formtab = '', $onlyLists = false) -{ - return $GLOBALS['wiki']->services->get(FormManager::class)->scanAllFacettable($fiches, $params['group'], $onlyLists); -} - /** * @deprecated Use FormManager::findNewId */ diff --git a/tools/bazar/libs/bazar.js b/tools/bazar/presentation/javascripts/bazar.js similarity index 100% rename from tools/bazar/libs/bazar.js rename to tools/bazar/presentation/javascripts/bazar.js diff --git a/tools/bazar/presentation/javascripts/components/FilterNode.js b/tools/bazar/presentation/javascripts/components/FilterNode.js new file mode 100644 index 000000000..1a08a2e18 --- /dev/null +++ b/tools/bazar/presentation/javascripts/components/FilterNode.js @@ -0,0 +1,75 @@ +import CollapseTransition from '../../../../../javascripts/shared-components/CollapseTransition.js' + +export default { + props: ['node'], + components: { CollapseTransition }, + data: () => ({ expanded: false }), + computed: { + // A parent node should be displayed if some of it's children has count > 0 + // count is the number of entries in the list have this node value + displayNode() { + return [this.node, ...this.node.descendants].some((node) => node.count > 0) + }, + someDescendantChecked() { + return this.node.descendants.some((node) => node.checked) + }, + nodeClasses() { + return { + checked: this.node.checked, + 'some-descendant-checked': this.someDescendantChecked, + expanded: this.expanded + } + } + }, + methods: { + onChecked() { + // uncheck all parents & descendants + // (this logic is not easy to understand, play with the UI to see how it works) + + // Why unchecking parents? + // Given this tree France: [ Paris, Bordeaux], Spain: [ Madrid, Barcelone ] + // Given those entries entryCapitaleFR: Paris|France, entryLyon: France + // If France is checked, I want both entry to be display + // If Paris is checked, I want only entryCapitaleFR to be displayed. + // So when clicking Paris, I need to uncheck France (otherwise both will be displayed) + + // Why unchecking descendants? + // If Paris is checked, and I check France. All entries from France will be displayed + // then no need to have Paris checked, it's misleading + this.node.descendants.forEach((node) => { node.checked = false }) + this.node.parents.forEach((node) => { node.checked = false }) + }, + labelClicked(event) { + // if has childrne, then cliking expand them, and do not trigger the checkbox + if (this.node.children.length > 0) event.preventDefault() + this.expanded = !this.expanded + } + }, + template: ` +
+ + + +
+ +
+
+
+ ` +} diff --git a/tools/bazar/presentation/javascripts/bazar-list-dynamic.js b/tools/bazar/presentation/javascripts/entries-index-dynamic.js similarity index 73% rename from tools/bazar/presentation/javascripts/bazar-list-dynamic.js rename to tools/bazar/presentation/javascripts/entries-index-dynamic.js index e68684b89..6fc285ad8 100644 --- a/tools/bazar/presentation/javascripts/bazar-list-dynamic.js +++ b/tools/bazar/presentation/javascripts/entries-index-dynamic.js @@ -1,10 +1,14 @@ -import Panel from './components/Panel.js' +import Panel from '../../../../javascripts/shared-components/Panel.js' import EntryField from './components/EntryField.js' import PopupEntryField from './components/PopupEntryField.js' import SpinnerLoader from './components/SpinnerLoader.js' import ModalEntry from './components/ModalEntry.js' import BazarSearch from './components/BazarSearch.js' -import { initEntryMaps } from './map-field-map-entry.js' +import FilterNode from './components/FilterNode.js' +import { initEntryMaps } from './fields/map-field-map-entry.js' +import { recursivelyCalculateRelations } from './utils.js' + +Vue.component('FilterNode', FilterNode) const load = (domElement) => { new Vue({ @@ -30,17 +34,20 @@ const load = (domElement) => { imagesToProcess: [], processingImage: false, search: '', - searchFormId: null, // wether to search for a particular form ID (only used when no form id is defined for the bazar list action) + // wether to search for a particular form ID (only used when no + // form id is defined for the bazar list action) + searchFormId: null, searchTimer: null // use ot debounce user input }, computed: { computedFilters() { const result = {} - for (const filterId in this.filters) { - const checkedValues = this.filters[filterId].list.filter((option) => option.checked) - .map((option) => option.value) - if (checkedValues.length > 0) result[filterId] = checkedValues - } + this.filters.forEach((filter) => { + const checkedValues = filter.flattenNodes + .filter((node) => node.checked) + .map((node) => node.value) + if (checkedValues.length > 0) result[filter.propName] = checkedValues + }) return result }, filteredEntriesCount() { @@ -48,10 +55,14 @@ const load = (domElement) => { }, pages() { if (this.pagination <= 0) return [] - const pagesCount = Math.ceil(this.filteredEntries.length / parseInt(this.pagination)) + const pagesCount = Math.ceil(this.filteredEntries.length / parseInt(this.pagination, 10)) const start = 0; const end = pagesCount - 1 - let pages = [this.currentPage - 2, this.currentPage - 1, this.currentPage, this.currentPage + 1, this.currentPage + 2] + let pages = [ + this.currentPage - 2, this.currentPage - 1, + this.currentPage, + this.currentPage + 1, this.currentPage + 2 + ] pages = pages.filter((page) => page >= start && page <= end) if (!pages.includes(start)) { if (!pages.includes(start + 1)) pages.unshift('divider') @@ -83,9 +94,8 @@ const load = (domElement) => { calculateBaseEntries() { let result = this.entries if (this.searchFormId) { - result = result.filter((entry) => // filter based on formId, when no form id is specified - entry.id_typeannonce == this.searchFormId) + result = result.filter((entry) => entry.id_typeannonce == this.searchFormId) } if (this.search && this.search.length > 2) { result = this.searchEntries(result, this.search) @@ -97,14 +107,14 @@ const load = (domElement) => { this.filterEntries() }, filterEntries() { - // Handles filters + // Handles filters let result = this.searchedEntries - for (const filterId in this.computedFilters) { + Object.entries(this.computedFilters).forEach(([propName, filter]) => { result = result.filter((entry) => { - if (!entry[filterId] || typeof entry[filterId] != 'string') return false - return entry[filterId].split(',').some((value) => this.computedFilters[filterId].includes(value)) + if (!entry[propName] || typeof entry[propName] != 'string') return false + return entry[propName].split(',').some((value) => filter.includes(value)) }) - } + }) this.filteredEntries = result this.paginateEntries() }, @@ -125,21 +135,21 @@ const load = (domElement) => { this.entriesToDisplay = this.paginatedEntries }, calculateFiltersCount() { - for (const fieldName in this.filters) { - for (const option of this.filters[fieldName].list) { - option.nb = this.searchedEntries.filter((entry) => { - let entryValues = entry[fieldName] + this.filters.forEach((filter) => { + filter.flattenNodes.forEach((node) => { + node.count = this.searchedEntries.filter((entry) => { + let entryValues = entry[filter.propName] if (!entryValues || typeof entryValues != 'string') return entryValues = entryValues.split(',') - return entryValues.some((value) => value == option.value) + return entryValues.some((value) => value == node.value) }).length - } - } + }) + }) }, resetFilters() { - for (const filterId in this.filters) { - this.filters[filterId].list.forEach((option) => option.checked = false) - } + this.filters.forEach((filter) => { + filter.flattenNodes.forEach((node) => { node.checked = false }) + }) this.search = '' }, saveFiltersIntoHash() { @@ -153,29 +163,29 @@ const load = (domElement) => { }, initFiltersFromHash(filters, hash) { hash = hash.substring(1) // remove # - for (const combinaison of hash.split('&')) { + hash.split('&').forEach((combinaison) => { const filterId = combinaison.split('=')[0] - let filterValues = combinaison.split('=')[1] + const filterValues = combinaison.split('=')[1] + const filter = this.filters.find((f) => f.propName == fieldId) if (filterId == 'q') { this.search = filterValues - } else if (filterId && filterValues && filters[filterId]) { - filterValues = filterValues.split(',') - for (const filter of filters[filterId].list) { - if (filterValues.includes(filter.value)) filter.checked = true - } + } else if (filterId && filterValues && filter) { + filter.flattenNodes.forEach((node) => { + if (filterValues.includes(node.value)) node.checked = true + }) } - } + }) // init q from GET q also if (this.search.length == 0) { let params = document.location.search params = params.substring(1) // remove ? - for (const combinaison of params.split('&')) { + params.split('&').forEach((combinaison) => { const filterId = combinaison.split('=')[0] const filterValues = combinaison.split('=')[1] if (filterId == 'q') { this.search = decodeURIComponent(filterValues) } - } + }) } return filters }, @@ -212,7 +222,7 @@ const load = (domElement) => { }).catch(() => 'error')// in case of error do nothing }, async getJSON(url, options = {}) { - return await fetch(url, options) + return fetch(url, options) .then((response) => { if (!response.ok) { throw `response not ok ; code : ${response.status} (${response.statusText})` @@ -228,10 +238,9 @@ const load = (domElement) => { }, loadBazarListDynamicIfNeeded(html) { if (html.match(/
`) + Vue.set( + entry, + 'html_render', + `` + ) }, colorIconValueFor(entry, field, mapping) { if (!entry[field] || typeof entry[field] != 'string') return null let values = entry[field].split(',') // If some filters are checked, and the entry have multiple values, we display // the value associated with the checked filter - // TODO BazarListDynamic check with users if this is expected behaviour - if (this.computedFilters[field]) values = values.filter((val) => this.computedFilters[field].includes(val)) + if (this.computedFilters[field]) { + values = values.filter((val) => this.computedFilters[field].includes(val)) + } return mapping[values[0]] }, urlImageResizedOnError(entry, fieldName, width, height, mode, token) { @@ -372,20 +386,35 @@ const load = (domElement) => { ) const savedHash = document.location.hash // don't know how, but the hash get cleared after this.params = JSON.parse(this.$el.dataset.params) - this.pagination = parseInt(this.params.pagination) + this.pagination = parseInt(this.params.pagination, 10) this.mounted = true // Retrieve data asynchronoulsy $.getJSON(wiki.url('?api/entries/bazarlist'), this.params, (data) => { - // First display filters cause entries can be a bit long to load - this.filters = this.initFiltersFromHash(data.filters || [], savedHash) + // process the filters + const filters = data.filters || [] + // Calculate the parents + filters.forEach((filter) => { + filter.nodes.forEach((rootNode) => recursivelyCalculateRelations(rootNode)) + filter.flattenNodes = filter.nodes + .map((rootNode) => [rootNode, ...rootNode.descendants]) + .flat() + // init some attributes for reactivity + filter.flattenNodes.forEach((node) => { + node.count = 0 + node.checked = false + }) + }) + // First display filters cause entries can be a bit long to load + this.filters = this.initFiltersFromHash(filters, savedHash) - // Auto adjust some params depending on entries count - if (data.entries.length > 50 && !this.pagination) this.pagination = 20 // Auto paginate if large numbers - if (data.entries.length > 1000) this.params.cluster = true // Activate cluster for map mode + // Auto paginate if large numbers + if (data.entries.length > 50 && !this.pagination) this.pagination = 20 + // Activate cluster for map mode + if (data.entries.length > 1000) this.params.cluster = true setTimeout(() => { - // Transform forms info into a list of field mapping - // { bf_titre: { type: 'text', ...}, bf_date: { type: 'listedatedeb', ... } } + // Transform forms info into a list of field mapping + // { bf_titre: { type: 'text', ...}, bf_date: { type: 'listedatedeb', ... } } Object.values(data.forms).forEach((formFields) => { Object.values(formFields).forEach((field) => { this.formFields[field.id] = field @@ -395,16 +424,36 @@ const load = (domElement) => { }) }) - this.entries = data.entries.map((array) => { + this.entries = data.entries.map((entryAsArray) => { const entry = { color: null, icon: null } - // Transform array data into object using the fieldMapping - for (const key in data.fieldMapping) { - entry[data.fieldMapping[key]] = array[key] - } + // Transform entryAsArray data into object using the fieldMapping + Object.entries(data.fieldMapping).forEach(([key, mapping]) => { + entry[mapping] = entryAsArray[key] + }) Object.entries(this.params.displayfields).forEach(([field, mappedField]) => { if (mappedField) entry[field] = entry[mappedField] }) + // In case of Tree, if an entry have only one value down the tree then add all the parent : + // filters for checkboxes: [{ value: "website", children: [ { value: "yeswiki" }] }] + // entryA { checkboxes: "yeswiki" } + // => entryA { checkboxes: "yeswiki,website" } + this.filters.forEach((filter) => { + const { propName } = filter + if (entry[propName] && typeof entry[propName] == 'string') { + const entryValues = entry[propName].split(',') + entryValues.forEach((value) => { + const correspondingNode = filter.flattenNodes.find((node) => node.value == value) + if (correspondingNode) { + correspondingNode.parents.forEach((parent) => { + if (!entryValues.includes(parent.value)) entryValues.push(parent.value) + }) + } + }) + entry[propName] = entryValues.join(',') + } + }) + return entry }) diff --git a/tools/bazar/presentation/javascripts/facette.js b/tools/bazar/presentation/javascripts/facette.js deleted file mode 100755 index 0d776f88e..000000000 --- a/tools/bazar/presentation/javascripts/facette.js +++ /dev/null @@ -1,186 +0,0 @@ -// Fonction debogage a supprimer -function dump(arr, level) { - let dumped_text = '' - if (!level) level = 0 - - // The padding given at the beginning of the line. - let level_padding = '' - for (let j = 0; j < level + 1; j++) level_padding += ' ' - - if (typeof (arr) == 'object') { // Array/Hashes/Objects - for (const item in arr) { - const value = arr[item] - - if (typeof (value) == 'object') { // If it is an array, - dumped_text += `${level_padding}'${item}' ...\n` - dumped_text += dump(value, level + 1) - } else { - dumped_text += `${level_padding}'${item}' => "${value}"\n` - } - } - } else { // Stings/Chars/Numbers etc. - dumped_text = `===>${arr}<===(${typeof (arr)})` - } - return dumped_text -} - -// Comportement : -// Par defaut, pas d'affichage des points. -// Affichage uniquement si point selectionné -// Pas de gestion du filtre "all" (le code est present au cas ou ...) - -$(() => { - $('#Grid').mixitup({ - targetSelector: '.mix', - filterSelector: '.filter', - sortSelector: '.sort', - buttonEvent: 'click', - effects: ['fade', 'scale'], - listEffects: null, - easing: 'smooth', - layoutMode: 'grid', - targetDisplayGrid: 'inline-block', - targetDisplayList: 'block', - listClass: 'list', - transitionSpeed: 600, - showOnLoad: 'none', - sortOnLoad: false, - multiFilter: true, - filterLogic: 'or', - resizeContainer: true, - minHeight: 0, - failClass: 'fail', - perspectiveDistance: '3000', - perspectiveOrigin: '50% 50%', - animateGridList: false, - onMixLoad(config) { - $.each(markers, (i, marker) => { - map.removeLayer(marker) - }) - - /* Specifique SMT */ - // Extend the Default marker class - const GereIcon = L.Icon.Default.extend({ - options: { - iconUrl: 'tools/bazar/presentation/images/marker_bleu.png', - iconSize: [12, 20], - shadowSize: [22, 20], - iconAnchor: [6, 20], - shadowAnchor: [6, 20] - } - }) - - const gereIcon = new GereIcon() - - const geres = Array() - $.each(places, (u, place) => { // Genere par bazarcato - if (place.idtypeannonce == '5') { - marker = markers[u] - marker.setIcon(gereIcon) - marker.unbindPopup() - marker.bindPopup(new L.Popup({ maxWidth: '1000' }).setContent(place.descriptionlongue)) - map.addLayer(marker) - geres[u] = marker - } - }) - /* Fin Specifique SMT */ - }, - onMixStart: null, - onMixEnd(config) { - // On se sert du rendu mixio pour afficher les points sur la carte - $('#Grid .mix').map(function() { - if ($(this).css('opacity') == '1') { - if (places[this.id.substring(6)].idtypeannonce == '5') { - marker = geres[this.id.substring(6)] - // map.removeLayer(marker); - } - map.addLayer(markers[this.id.substring(6)]) - } - }) - - $.each(places, (u, place) => { // Genere par bazarcato - if (place.idtypeannonce == '5') { - map.addLayer(markers[u]) - } - }) - } - }) - - // Gestion des filtres - - const $filters = $('#Filters').find('li') - - let filterbydimension = Array() - let filterString = '' - - const dimensions = Array() - - $.each(groups, (t, group) => { // Genere par bazarcato - dimensions[group] = '' - }) - - $filters.on('click', function() { - // Nettoyage markers - $.each(markers, (i, marker) => { - map.removeLayer(marker) - }) - - const $t = $(this) - const dimension = $t.attr('data-dimension') - const filter = $t.attr('data-filter') - if (typeof (dimensions[dimension]) == 'undefined') { - dimensions[dimension] = '' - } - filterString = dimensions[dimension] - - if (filter == 'all') { - // If "all" - if (!$t.hasClass('active')) { - // if unchecked, check "all" and uncheck all other active filters - $t.addClass('active').siblings().removeClass('active') - // Replace entire string with "all" - filterString = 'all' - } else { - // Uncheck - $t.removeClass('active') - // Emtpy string - filterString = '' - } - } else { - // Else, uncheck "all" - $t.siblings('[data-filter="all"]').removeClass('active') - // Remove "all" from string - filterString = filterString.replace(/\ball\b/, '') - - if (!$t.hasClass('active')) { - // Check checkbox - $t.addClass('active') - // Append filter to string - filterString = filterString === '' ? filter : `${filterString} ${filter}` - } else { - // Uncheck - $t.removeClass('active') - const re = new RegExp(`(\\s|^)${filter}`) - filterString = filterString.replace(re, '') - } - } - - dimensions[dimension] = filterString - - // alert(dump(dimensions)); - - filterbydimension = Array() - - $.each(groups, (w, group) => { - if (dimensions[group] !== '') { - filterbydimension[w] = dimensions[group] - } else { - filterbydimension[w] = '' - } - }) - - // alert(dump(filterbydimension)); - - $('#Grid').mixitup('filter', filterbydimension) - }) -}) diff --git a/tools/bazar/presentation/javascripts/map-field-map-entry.js b/tools/bazar/presentation/javascripts/fields/map-field-map-entry.js similarity index 100% rename from tools/bazar/presentation/javascripts/map-field-map-entry.js rename to tools/bazar/presentation/javascripts/fields/map-field-map-entry.js diff --git a/tools/bazar/presentation/javascripts/form-edit-template/fields/checkbox-group.js b/tools/bazar/presentation/javascripts/form-edit-template/fields/checkbox-group.js index 02d71c0a4..4cabff5fa 100644 --- a/tools/bazar/presentation/javascripts/form-edit-template/fields/checkbox-group.js +++ b/tools/bazar/presentation/javascripts/form-edit-template/fields/checkbox-group.js @@ -7,6 +7,7 @@ export default { attrs: { type: 'checkbox-group' }, icon: '' }, + defaultIdentifier: 'bf_checkboxes', attributes: { ...selectConf, ...{ diff --git a/tools/bazar/presentation/javascripts/form-edit-template/fields/radio-group.js b/tools/bazar/presentation/javascripts/form-edit-template/fields/radio-group.js index a6dfe67af..9fa49c85e 100644 --- a/tools/bazar/presentation/javascripts/form-edit-template/fields/radio-group.js +++ b/tools/bazar/presentation/javascripts/form-edit-template/fields/radio-group.js @@ -24,6 +24,7 @@ export default { } } }, + defaultIdentifier: 'bf_choice', advancedAttributes: ['read', 'write', 'semantic', 'queries', 'fillingMode', 'options'], // disabledAttributes: [], attributesMapping: { ...listsMapping, ...{ 7: 'fillingMode' } } diff --git a/tools/bazar/presentation/javascripts/form-edit-template/form-edit-template.js b/tools/bazar/presentation/javascripts/form-edit-template/form-edit-template.js index ca43cd89c..4036ba949 100644 --- a/tools/bazar/presentation/javascripts/form-edit-template/form-edit-template.js +++ b/tools/bazar/presentation/javascripts/form-edit-template/form-edit-template.js @@ -238,9 +238,7 @@ function initializeFormbuilder() { const fieldType = $(this).closest('.form-field').attr('type') // Make the default names easier to read - if (['radio_group', 'checkbox_group', 'select'].includes(fieldType)) { - name = '' - } else if (!name.includes('bf_')) { + if (!name.includes('bf_')) { name = defaultFieldsName[fieldType] || `bf_${fieldType}` if (existingFieldsNames.includes(name)) { // If name already exist, we add a number (bf_address, bf_address1, bf_address2...) diff --git a/tools/bazar/libs/bazar.edit_forms.js b/tools/bazar/presentation/javascripts/forms-import.js similarity index 100% rename from tools/bazar/libs/bazar.edit_forms.js rename to tools/bazar/presentation/javascripts/forms-import.js diff --git a/tools/bazar/presentation/javascripts/checkbox-drag-and-drop.js b/tools/bazar/presentation/javascripts/inputs/checkbox-drag-and-drop.js similarity index 100% rename from tools/bazar/presentation/javascripts/checkbox-drag-and-drop.js rename to tools/bazar/presentation/javascripts/inputs/checkbox-drag-and-drop.js diff --git a/tools/bazar/presentation/javascripts/bazar-tagsinput.js b/tools/bazar/presentation/javascripts/inputs/checkbox-tags.js similarity index 100% rename from tools/bazar/presentation/javascripts/bazar-tagsinput.js rename to tools/bazar/presentation/javascripts/inputs/checkbox-tags.js diff --git a/tools/bazar/presentation/javascripts/inputs/checkbox-tree.js b/tools/bazar/presentation/javascripts/inputs/checkbox-tree.js new file mode 100644 index 000000000..b7bf08d30 --- /dev/null +++ b/tools/bazar/presentation/javascripts/inputs/checkbox-tree.js @@ -0,0 +1,47 @@ +$(document).ready(() => { + // Handle chevron click (only expand/collapse) + $('.checkbox-node .checkbox-label').on('click', function(event) { + const nodeContainer = $(this).closest('.node-container') + const childrenContainer = nodeContainer.find('> .node-children') + + if (childrenContainer.find('.node-container').length === 0) return + + event.stopPropagation() + event.preventDefault() + + if (childrenContainer.is(':visible')) { + childrenContainer.slideUp(250) + nodeContainer.removeClass('expanded') + } else { + childrenContainer.slideDown(250) + nodeContainer.addClass('expanded') + } + }) + + // Handle checkbox clicks - expand/collapse & check parent uncheck children + $('.checkbox-node input[type=checkbox]').on('change', function() { + const nodeContainer = $(this).closest('.node-container') + const childrenContainer = nodeContainer.find('> .node-children') + + if ($(this).is(':checked')) { + childrenContainer.slideDown(300) + nodeContainer.addClass('expanded') + nodeContainer.parents('.node-container').each(function() { + $(this).find('> .checkbox-node input[type=checkbox]').prop('checked', true) + }) + } else { + childrenContainer.slideUp(200) + nodeContainer.removeClass('expanded') + childrenContainer.find('.node-children').hide() + childrenContainer.find('.node-container').removeClass('expanded') + childrenContainer.find('input[type=checkbox]').prop('checked', false) + } + }) + + $('.check-all').on('change', function() { + const checked = $(this).is(':checked') + $(this).closest('.check-all-container').siblings('.node-container').each(function() { + $(this).find('> .checkbox-node input[type=checkbox]').prop('checked', checked).trigger('change') + }) + }) +}) diff --git a/tools/bazar/presentation/javascripts/bazar-fields/conditionschecking.js b/tools/bazar/presentation/javascripts/inputs/conditions-checking.js similarity index 100% rename from tools/bazar/presentation/javascripts/bazar-fields/conditionschecking.js rename to tools/bazar/presentation/javascripts/inputs/conditions-checking.js diff --git a/tools/bazar/presentation/javascripts/file-field.js b/tools/bazar/presentation/javascripts/inputs/file-field.js similarity index 100% rename from tools/bazar/presentation/javascripts/file-field.js rename to tools/bazar/presentation/javascripts/inputs/file-field.js diff --git a/tools/bazar/presentation/javascripts/image-field.js b/tools/bazar/presentation/javascripts/inputs/image-field.js similarity index 100% rename from tools/bazar/presentation/javascripts/image-field.js rename to tools/bazar/presentation/javascripts/inputs/image-field.js diff --git a/tools/bazar/presentation/javascripts/map-field-autocomplete.js b/tools/bazar/presentation/javascripts/inputs/map-autocomplete.js similarity index 100% rename from tools/bazar/presentation/javascripts/map-field-autocomplete.js rename to tools/bazar/presentation/javascripts/inputs/map-autocomplete.js diff --git a/tools/bazar/presentation/javascripts/geolocationHelper.js b/tools/bazar/presentation/javascripts/inputs/map-geolocation-helper.js similarity index 100% rename from tools/bazar/presentation/javascripts/geolocationHelper.js rename to tools/bazar/presentation/javascripts/inputs/map-geolocation-helper.js diff --git a/tools/bazar/presentation/javascripts/map-field-leaflet.js b/tools/bazar/presentation/javascripts/inputs/map-leaflet.js similarity index 100% rename from tools/bazar/presentation/javascripts/map-field-leaflet.js rename to tools/bazar/presentation/javascripts/inputs/map-leaflet.js diff --git a/tools/bazar/presentation/javascripts/recurrent-event.js b/tools/bazar/presentation/javascripts/inputs/recurrent-event.js similarity index 100% rename from tools/bazar/presentation/javascripts/recurrent-event.js rename to tools/bazar/presentation/javascripts/inputs/recurrent-event.js diff --git a/tools/bazar/presentation/javascripts/bazar-edit-tabs-field.js b/tools/bazar/presentation/javascripts/inputs/tabs.js similarity index 100% rename from tools/bazar/presentation/javascripts/bazar-edit-tabs-field.js rename to tools/bazar/presentation/javascripts/inputs/tabs.js diff --git a/tools/bazar/presentation/javascripts/user-field-update-email.js b/tools/bazar/presentation/javascripts/inputs/user-field-update-email.js similarity index 100% rename from tools/bazar/presentation/javascripts/user-field-update-email.js rename to tools/bazar/presentation/javascripts/inputs/user-field-update-email.js diff --git a/tools/bazar/presentation/javascripts/list-form.js b/tools/bazar/presentation/javascripts/list-form.js new file mode 100644 index 000000000..3946b746c --- /dev/null +++ b/tools/bazar/presentation/javascripts/list-form.js @@ -0,0 +1,60 @@ +import ListNode from './list-node.js' + +new Vue({ + el: '.list-form', + components: { 'list-node': ListNode }, + data: { + title: '', + rootNode: { id: '@root@', vueRef: '@root@', children: [] }, + allIds: [], + nodeCreated: 0 + }, + mounted() { + const list = JSON.parse(this.$el.dataset.list) + this.title = list.title + const nodes = list.nodes || [] + // vueRef is used to give a unique and fixed ID to each node + nodes.forEach((node) => this.addVueRefProp(node)) + this.rootNode.children = nodes + }, + computed: { + jsonNodes() { + const data = this.rootNode.children.map((child) => this.removeVueRefProps({ ...child })) + return JSON.stringify(data) + } + }, + methods: { + onSubmit(event) { + // check for id presence and uniquness + this.allIds = [] + this.collectIds(this.rootNode) + if (this.allIds.some((id) => !id)) { + toastMessage(_t('LIST_ERROR_MISSING_IDS'), 4000, 'alert alert-danger') + event.preventDefault() + } + const duplicatesIds = this.allIds.filter((item, index) => this.allIds.indexOf(item) !== index) + if (duplicatesIds.length > 0) { + toastMessage( + _t('LIST_ERROR_DUPLICATES_IDS') + duplicatesIds.join(', '), + 8000, + 'alert alert-danger' + ) + event.preventDefault() + } + }, + collectIds(node) { + this.allIds.push(node.id) + node.children.forEach((childNode) => this.collectIds(childNode)) + }, + addVueRefProp(node) { + node.vueRef = node.id + node.children ||= [] + node.children.forEach((childNode) => this.addVueRefProp(childNode)) + }, + removeVueRefProps(node) { + delete node.vueRef + node.children = node.children.map((child) => this.removeVueRefProps({ ...child })) + return node + } + } +}) diff --git a/tools/bazar/presentation/javascripts/list-import.js b/tools/bazar/presentation/javascripts/list-import.js new file mode 100755 index 000000000..5a2594dbf --- /dev/null +++ b/tools/bazar/presentation/javascripts/list-import.js @@ -0,0 +1,98 @@ +$(document).ready(() => { + // import de listes à partir d'un yeswiki + const btnimportlist = $('#btn-import-lists') + const resultimportlist = $('#import-lists-result') + const resultimporttable = $('#import-lists-table') + const resultimportform = $('#import-lists-form') + const listtranslations = $('#list-translations').data() + const existinglists = $('#existing-lists-table') + btnimportlist.click(() => { + // on enleve les anciens contenus + resultimportlist.html('') + resultimportform.addClass('hide') + resultimporttable.find('tbody').html('') + + // url saisie + let url = $('#url-import-lists').val() + + // expression réguliere pour trouver une url valide + const rgHttpUrl = /^(http|https):\/\/(([a-zA-Z0-9$\-_.+!*'(),;:&=]|%[0-9a-fA-F]{2})+@)?(((25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])){3})|localhost|([a-zA-Z0-9\-\u00C0-\u017F]+\.)+([a-zA-Z]{2,}))(:[0-9]+)?(\/(([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*(\/([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*)*)?(\?([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?(\#([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?)?$/ + + if (rgHttpUrl.test(url)) { + // on formate l url pour acceder au service json de yeswiki + const taburl = url.split('wakka.php') + url = `${taburl[0].replace(/\/+$/g, '')}/wakka.php?wiki=BazaR/json&demand=lists` + resultimportlist.html(`
+ ${listtranslations.loading}... + ${listtranslations.recuperation} ${url} +
`) + + $.ajax({ + method: 'GET', + url + }).done((data) => { + resultimportlist.html('') + let count = 0 + + Object.entries(data).forEach(([idlist, listData]) => { + count += 1 + let list = {} + + // Convert old data structure + if (listData.titre_liste) { + list = { + title: listData.titre_liste, + nodes: [] + } + Object.entries(listData.label).forEach(([id, label]) => { + list.nodes.push({ id, label, children: [] }) + }) + } else { + list = listData + } + + let select = `` + list.nodes.forEach((node) => { + select += `` + }) + + let trclass = '' + let existingmessage = '' + if (existinglists.find('td').filter(function() { + return $(this).text() === idlist + }).length > 0) { + trclass = ' class="error danger"' + existingmessage = `
+ ${listtranslations.existingmessage}` + } + + resultimporttable.find('tbody').append( + ` + + + + ${idlist + existingmessage} + ${list.title} + + ` + ) + }) + + resultimportform.removeClass('hide') + resultimportlist.prepend(`
+ ${listtranslations.nblistsfound} : ${count} +
`) + }).fail(() => { + resultimportlist.html(`
${listtranslations.noanswers}.
`) + }) + } else { + resultimportlist.html(`
+ ${listtranslations.notvalidurl} : ${url} +
`) + } + }) +}) diff --git a/tools/bazar/presentation/javascripts/list-node.js b/tools/bazar/presentation/javascripts/list-node.js new file mode 100644 index 000000000..a842f1821 --- /dev/null +++ b/tools/bazar/presentation/javascripts/list-node.js @@ -0,0 +1,77 @@ +export default { + name: 'list-node', + props: ['node', 'depth', 'index'], + emits: ['delete'], + data() { + return { + expanded: false, + newNodeLabel: '' + } + }, + methods: { + addChildNode() { + // vueRef is used to give a unique and fixed ID to each node + this.node.children.push({ + label: this.newNodeLabel, + id: this.slugify(this.newNodeLabel), + children: [], + vueRef: Date.now() + }) + this.newNodeLabel = '' + this.expanded = true + }, + deleteChildNode(nodeToDelete) { + this.node.children = this.node.children.filter((node) => node !== nodeToDelete) + }, + slugify(val) { + return val.normalize('NFD').replace(/[\u0300-\u036f]/g, '') + .replace(/[^a-z^A-Z^_^0-9^{^}]/g, '_') + .toLowerCase() + } + }, + template: ` +
+
+ + + + + +
+ ${_t('LIST_KEY')} : +
+ + + +
+
+ + + +
+ + + +
+ +
+
+ ` +} diff --git a/tools/bazar/presentation/javascripts/utils.js b/tools/bazar/presentation/javascripts/utils.js new file mode 100644 index 000000000..8ef91d2bd --- /dev/null +++ b/tools/bazar/presentation/javascripts/utils.js @@ -0,0 +1,17 @@ +export function recursivelyCalculateRelations(node, parentChain = []) { + const allParents = [...parentChain] + const descendants = [] + + // Recursively calculate relations for children + if (node.children && node.children.length > 0) { + node.children.forEach((child) => { + const childNode = recursivelyCalculateRelations(child, [node, ...allParents]) + descendants.push(child, ...childNode.descendants) + }) + } + + node.parent = allParents[0] + node.parents = allParents + node.descendants = descendants + return node +} diff --git a/tools/bazar/presentation/styles/bazar.css b/tools/bazar/presentation/styles/bazar.css index fc7b0f736..6a3d261a7 100644 --- a/tools/bazar/presentation/styles/bazar.css +++ b/tools/bazar/presentation/styles/bazar.css @@ -1,1418 +1,49 @@ -/* --------------- */ - -/* ENTRY VIEW */ - -/* --------------- */ -.BAZ_cadre_fiche .BAZ_rubrique { - margin: 0 0 10px; -} - -/* ENTRY FOOTER */ -.BAZ_fiche_info { - font-size: 0.9rem; - display: flex; - flex-direction: row; -} - -.BAZ_fiche_info .BAZ_actions_fiche { - align-self: center; - text-align: right; - flex: 1 auto; -} - -/* --------------- */ - -/* ENTRY FORM */ - -/* --------------- */ - -/* following file is a mess, should try to refactor a bit ! */ - -#map img { - max-width: none !important; -} - -#map label { - width: auto !important; - display: inline !important; -} - -.map-entry { - width: 100%; - min-height: 220px; -} - -.required_symbol, -.symbole_obligatoire { - color: #e9322d; -} - -.bazar-entry-edit-image { - display: flex; - align-items: center; -} - -.bazar-entry-edit-image output img { - height: 150px; -} - -.bazar-entry-edit-image .image-actions { - display: flex; - flex-direction: column; - margin-left: 1rem; -} - -/* Image input when there is already an image loaded */ -.form-group.input-image:not(.file) .control-label { - position: relative; - padding: 0; - margin: 0 -15px; -} - -.form-group.input-image:not(.file) .control-label::before { - content: none; -} - -.frm-holder .form-elements .custom-hint { - border: 2px solid #c5c5c5; - padding: 5px; - margin: 0 5px 5px; - border-radius: 3px; -} - -.tab-content .tab-pane.with-padding { - padding-top: 1em; -} - -.btn-geolocate { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.bazar-entry button.panel-heading { - text-align: left; - width: 100%; -} - -/* --------------- */ - -/* BAZAR LIST */ - -/* --------------- */ - -.BAZ_menu .nav, -.bazar-search { - margin-bottom: 1em; -} - -.filters-col .bazar-search { - margin-bottom: 2em; -} - -.bazar-table { - table-layout: fixed; - word-wrap: break-word; -} - -.precsv { - height: 125px; - white-space: pre; - padding: 5px; - word-wrap: break-word; - overflow: auto; -} - -.results-col { - clear: both; -} - -.reset-filters.btn-block { - margin-top: 5px; -} - -/* accordion pane */ -.accordion { - background: transparent; - width: 100%; -} - -.pane { - display: none; - padding: 15px; - margin: 0; - border: 1px solid #666; - border-top: none; -} - -/* accordion header */ -.titre_accordeon { - margin: 0 0 1px; - padding: 5px 15px; - cursor: pointer; - background: #222 url('../images/alert-overlay.png') repeat-x; - color: #fff; - text-decoration: none; - border-radius: 5px; - box-shadow: 0 1px 3px rgb(0 0 0 / 50%); - text-shadow: 0 -1px 1px rgb(0 0 0 / 25%); - border-bottom: 1px solid rgb(0 0 0 / 25%); - font-size: 13px; - font-weight: bold; - line-height: 1; - display: block; - position: relative; -} -.liens_titre_accordeon { - position: absolute; - top: 3px; - right: 3px; -} -.liens_titre_accordeon a { - display: inline-block; - width: 20px; - height: 16px; - float: left; - margin: 0 2px 0 0; - padding: 0; -} - -/* currently active header */ -.titre_accordeon.current { - cursor: default; - background-color: #ffb515; - border-bottom: none; -} - -.titre_accordeon:hover { - background-color: #fc9200; - text-decoration: none; -} - -input:not(:placeholder-shown):invalid, -textarea:not(:placeholder-shown):invalid, -.submitted input:required:invalid, -.submitted textarea:required:invalid, -.submitted select:required:invalid, -.submitted .bootstrap-tagsinput.invalid, -.submitted .form-group.input-checkbox.invalid, -.submitted .form-group.input-radio.invalid, -.submitted .invalid > .ace-container, -.submitted .textarea.summernote.invalid { - border-color: #dd2c00; - border-color: var(--danger-color); -} - -input.form-control.error { - border-color: var(--danger-color) !important; -} -.BAZ_label { - font-weight: bold; - display: block; -} -.BAZ_texte { - display: block; -} -.titre_lien { - display: block; - font-size: 1.4em; -} - -.list-sortables { - list-style: none; - padding: 0; -} - -.list-sortables .liste_ligne { - display: table; - margin-bottom: 3px; -} - -.list-sortables .input-key { - width: 80px; -} - -.list-sortables .input-label { - width: calc(100% - 80px); -} -.input_liste_id { - display: inline; - margin: 0 2px; - width: 40px; -} -.input_liste_label { - display: inline; - margin: 0; - width: 250px; -} -ul.valeur_liste, -ul.valeur_formulaire { - padding: 0; - margin: 0; -} -ul.list-sortables li, -ul.valeur_formulaire li { - list-style-type: none; - padding: 0; - margin: 3px 0; -} -.handle-listitems { - cursor: move; -} -.suppression_label_liste { - cursor: pointer; -} - -/* .valeur_formulaire .handle {left:20px;position:absolute;} */ -.valeur_formulaire .formulaire_ligne { - background: #eaeaea; - border: 1px solid #ccc; - padding: 5px 0; -} - -#champs_formulaire { - /* must be initially hidden */ - display: none; - - /* place overlay on top of other elements */ - z-index: 10000; - - /* styling */ - background-color: #fff; - width: 675px; - min-height: 200px; - border: 1px solid #666; - padding: 20px; - - /* CSS3 styling for latest browsers */ - box-shadow: 0 0 90px 5px #000; - box-shadow: 0 0 90px #000; -} - -/* facette */ -.facette-container { - display: flex; - flex-direction: row; -} - -.facette-container .panel-heading { - cursor: pointer; -} - -.facette-container:not([data-filter-align='left']) - > .filters-col - > .bazar-search { - margin-top: 2rem; -} - -.facette-container-fullwidth { - flex-wrap: wrap; -} - -.facette-container[data-filter-align='right'] { - flex-direction: row-reverse; -} - -@media (width >= 768px) { - .facette-container:not([data-filter-align='left']) - > [class^='filters-col '][class*='col-sm-'] - > .bazar-search { - margin-top: 0; - } -} - -@media (width <= 768px) { - .facette-container, - .facette-container[data-filter-align='right'] { - flex-direction: column; - } - - .facette-container .filter-box .panel-body { - max-height: 11em; - overflow-y: scroll; - - /* for firefox custom scrollbar */ - scrollbar-color: var(--neutral-color) white; - scrollbar-width: auto; - - /* the scrollbar width is too thin, we may fix it by using a scrollbar JS library */ - } - - .facette-container .filter-box .panel-body::-webkit-scrollbar { - appearance: none; - } - - .facette-container .filter-box .panel-body:vertical { - width: 15px; - } - - .facette-container .filter-box .panel-body::-webkit-scrollbar:horizontal { - height: 15px; - } - - .facette-container .filter-box .panel-body::-webkit-scrollbar-thumb { - background-color: var(--neutral-color); - border-radius: 10px; - } -} - -.bazar-lists select { - width: 250px; - display: inline-block; -} - -.link-csv-file { - display: block; - margin: 10px 0; -} - -/* .yeswiki-checkbox { - display: inline-block; - vertical-align: top; - width: 235px; -} */ -.bazar-checkbox-cols { - /* Chrome, Safari, Opera */ /* Firefox */ - column-count: 3; -} - -.entries-list, -.list-bazar-entries { - max-height: 200px; - overflow: auto; -} -.entries-list .yeswiki-checkbox { - width: 100% !important; -} -.yeswiki-input-entries + .bootstrap-tagsinput input { - display: block; -} - -.pagination .page { - min-height: auto; -} - -/* BAZAR DATE FIELD */ -.bazar-date, -.bazar-date.form-control { - width: 133px; -} - -.form-group.input-listedatedeb .controls, -.form-group.input-listedatefin .controls, -.form-group.input-jour .controls { - display: flex; - flex-wrap: wrap; - gap: 1rem 1.5rem; -} - -.form-group.input-listedatedeb .select-time, -.form-group.input-listedatefin .select-time, -.form-group.input-jour .select-time { - margin-left: 0 !important; -} - -@media (width <= 600px) { - .form-group.input-listedatedeb .select-time, - .form-group.input-listedatefin .select-time, - .form-group.input-jour .select-time { - width: 100%; - margin: 0; - } -} - -.select-hour, -.select-minutes { - width: 76px !important; -} - -input::-webkit-calendar-picker-indicator { - display: none; -} - -input[type='date']::input-placeholder { - visibility: hidden !important; -} - -/* aceditor field */ -.form-group.wiki-textarea .btn-toolbar { - margin-left: 1em; +@import url('./entries/index.css'); +@import url('./entries/index-filters.css'); +@import url('./entries/form.css'); +@import url('./entries/view.css'); + +.throbber { + width: 56px; + height: 1.5rem; + background: + radial-gradient(circle closest-side, var(--primary-color) 90%, #0000) 0% 50%, + radial-gradient(circle closest-side, var(--primary-color) 90%, #0000) 50% + 50%, + radial-gradient(circle closest-side, var(--primary-color) 90%, #0000) 100% + 50%; + background-size: calc(100% / 3) 13.4px; + background-repeat: no-repeat; + animation: dots-7ar3yq 1s infinite linear; margin-right: 1em; } -.form-group.wiki-textarea .control-label { - top: 0.6em !important; - margin-top: -0.5em !important; -} - -/* submit button disabled */ -#formulaire .form-actions button[type='submit'].submit-disabled { - cursor: progress; -} - -/*! - * Datepicker for Bootstrap v1.7.1 (https://github.com/uxsolutions/bootstrap-datepicker) - * - * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - */ - -.datepicker { - border-radius: 4px; - direction: ltr; -} -.datepicker-inline { - width: 220px; -} -.datepicker-rtl { - direction: rtl; -} -.datepicker-rtl.dropdown-menu { - left: auto; -} -.datepicker-rtl table tr td span { - float: right; -} -.datepicker-dropdown { - top: 0; - left: 0; - padding: 4px; -} -.datepicker-dropdown::before { - content: ''; - display: inline-block; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-bottom: 7px solid rgb(0 0 0 / 15%); - border-top: 0; - border-bottom-color: rgb(0 0 0 / 20%); - position: absolute; -} -.datepicker-dropdown::after { - content: ''; - display: inline-block; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-bottom: 6px solid #fff; - border-top: 0; - position: absolute; -} -.datepicker-dropdown.datepicker-orient-left::before { - left: 6px; -} -.datepicker-dropdown.datepicker-orient-left::after { - left: 7px; -} -.datepicker-dropdown.datepicker-orient-right::before { - right: 6px; -} -.datepicker-dropdown.datepicker-orient-right::after { - right: 7px; -} -.datepicker-dropdown.datepicker-orient-bottom::before { - top: -7px; -} -.datepicker-dropdown.datepicker-orient-bottom::after { - top: -6px; -} -.datepicker-dropdown.datepicker-orient-top::before { - bottom: -7px; - border-bottom: 0; - border-top: 7px solid rgb(0 0 0 / 15%); -} -.datepicker-dropdown.datepicker-orient-top::after { - bottom: -6px; - border-bottom: 0; - border-top: 6px solid #fff; -} -.datepicker table { - margin: 0; - -webkit-touch-callout: none; - user-select: none; -} -.datepicker table tr td, -.datepicker table tr th { - text-align: center; - width: 30px; - height: 30px; - border-radius: 4px; - border: none; -} -.table-striped .datepicker table tr td, -.table-striped .datepicker table tr th { - background-color: transparent; -} -.datepicker table tr td.new, -.datepicker table tr td.old { - color: #777; -} -.datepicker table tr td.day:hover, -.datepicker table tr td.focused { - background: #eee; - cursor: pointer; -} -.datepicker table tr td.disabled, -.datepicker table tr td.disabled:hover { - background: 0 0; - color: #777; - cursor: default; -} -.datepicker table tr td.highlighted { - color: #000; - background-color: #d9edf7; - border-color: #85c5e5; - border-radius: 0; -} -.datepicker table tr td.highlighted.focus, -.datepicker table tr td.highlighted:focus { - color: #000; - background-color: #afd9ee; - border-color: #298fc2; -} -.datepicker table tr td.highlighted:hover { - color: #000; - background-color: #afd9ee; - border-color: #52addb; -} -.datepicker table tr td.highlighted.active, -.datepicker table tr td.highlighted:active { - color: #000; - background-color: #afd9ee; - border-color: #52addb; -} -.datepicker table tr td.highlighted.active.focus, -.datepicker table tr td.highlighted.active:focus, -.datepicker table tr td.highlighted.active:hover, -.datepicker table tr td.highlighted:active.focus, -.datepicker table tr td.highlighted:active:focus, -.datepicker table tr td.highlighted:active:hover { - color: #000; - background-color: #91cbe8; - border-color: #298fc2; -} -.datepicker table tr td.highlighted.disabled.focus, -.datepicker table tr td.highlighted.disabled:focus, -.datepicker table tr td.highlighted.disabled:hover, -.datepicker table tr td.highlighted[disabled].focus, -.datepicker table tr td.highlighted[disabled]:focus, -.datepicker table tr td.highlighted[disabled]:hover, -fieldset[disabled] .datepicker table tr td.highlighted.focus, -fieldset[disabled] .datepicker table tr td.highlighted:focus, -fieldset[disabled] .datepicker table tr td.highlighted:hover { - background-color: #d9edf7; - border-color: #85c5e5; -} -.datepicker table tr td.highlighted.focused { - background: #afd9ee; -} -.datepicker table tr td.highlighted.disabled, -.datepicker table tr td.highlighted.disabled:active { - background: #d9edf7; - color: #777; -} -.datepicker table tr td.today { - color: #000; - background-color: #ffdb99; - border-color: #ffb733; -} -.datepicker table tr td.today.focus, -.datepicker table tr td.today:focus { - color: #000; - background-color: #ffc966; - border-color: #b37400; -} -.datepicker table tr td.today:hover { - color: #000; - background-color: #ffc966; - border-color: #f59e00; -} -.datepicker table tr td.today.active, -.datepicker table tr td.today:active { - color: #000; - background-color: #ffc966; - border-color: #f59e00; -} -.datepicker table tr td.today.active.focus, -.datepicker table tr td.today.active:focus, -.datepicker table tr td.today.active:hover, -.datepicker table tr td.today:active.focus, -.datepicker table tr td.today:active:focus, -.datepicker table tr td.today:active:hover { - color: #000; - background-color: #ffbc42; - border-color: #b37400; -} -.datepicker table tr td.today.disabled.focus, -.datepicker table tr td.today.disabled:focus, -.datepicker table tr td.today.disabled:hover, -.datepicker table tr td.today[disabled].focus, -.datepicker table tr td.today[disabled]:focus, -.datepicker table tr td.today[disabled]:hover, -fieldset[disabled] .datepicker table tr td.today.focus, -fieldset[disabled] .datepicker table tr td.today:focus, -fieldset[disabled] .datepicker table tr td.today:hover { - background-color: #ffdb99; - border-color: #ffb733; -} -.datepicker table tr td.today.focused { - background: #ffc966; -} -.datepicker table tr td.today.disabled, -.datepicker table tr td.today.disabled:active { - background: #ffdb99; - color: #777; -} -.datepicker table tr td.range { - color: #000; - background-color: #eee; - border-color: #bbb; - border-radius: 0; -} -.datepicker table tr td.range.focus, -.datepicker table tr td.range:focus { - color: #000; - background-color: #d5d5d5; - border-color: #7c7c7c; -} -.datepicker table tr td.range:hover { - color: #000; - background-color: #d5d5d5; - border-color: #9d9d9d; -} -.datepicker table tr td.range.active, -.datepicker table tr td.range:active { - color: #000; - background-color: #d5d5d5; - border-color: #9d9d9d; -} -.datepicker table tr td.range.active.focus, -.datepicker table tr td.range.active:focus, -.datepicker table tr td.range.active:hover, -.datepicker table tr td.range:active.focus, -.datepicker table tr td.range:active:focus, -.datepicker table tr td.range:active:hover { - color: #000; - background-color: #c3c3c3; - border-color: #7c7c7c; -} -.datepicker table tr td.range.disabled.focus, -.datepicker table tr td.range.disabled:focus, -.datepicker table tr td.range.disabled:hover, -.datepicker table tr td.range[disabled].focus, -.datepicker table tr td.range[disabled]:focus, -.datepicker table tr td.range[disabled]:hover, -fieldset[disabled] .datepicker table tr td.range.focus, -fieldset[disabled] .datepicker table tr td.range:focus, -fieldset[disabled] .datepicker table tr td.range:hover { - background-color: #eee; - border-color: #bbb; -} -.datepicker table tr td.range.focused { - background: #d5d5d5; -} -.datepicker table tr td.range.disabled, -.datepicker table tr td.range.disabled:active { - background: #eee; - color: #777; -} -.datepicker table tr td.range.highlighted { - color: #000; - background-color: #e4eef3; - border-color: #9dc1d3; -} -.datepicker table tr td.range.highlighted.focus, -.datepicker table tr td.range.highlighted:focus { - color: #000; - background-color: #c1d7e3; - border-color: #4b88a6; -} -.datepicker table tr td.range.highlighted:hover { - color: #000; - background-color: #c1d7e3; - border-color: #73a6c0; -} -.datepicker table tr td.range.highlighted.active, -.datepicker table tr td.range.highlighted:active { - color: #000; - background-color: #c1d7e3; - border-color: #73a6c0; -} -.datepicker table tr td.range.highlighted.active.focus, -.datepicker table tr td.range.highlighted.active:focus, -.datepicker table tr td.range.highlighted.active:hover, -.datepicker table tr td.range.highlighted:active.focus, -.datepicker table tr td.range.highlighted:active:focus, -.datepicker table tr td.range.highlighted:active:hover { - color: #000; - background-color: #a8c8d8; - border-color: #4b88a6; -} -.datepicker table tr td.range.highlighted.disabled.focus, -.datepicker table tr td.range.highlighted.disabled:focus, -.datepicker table tr td.range.highlighted.disabled:hover, -.datepicker table tr td.range.highlighted[disabled].focus, -.datepicker table tr td.range.highlighted[disabled]:focus, -.datepicker table tr td.range.highlighted[disabled]:hover, -fieldset[disabled] .datepicker table tr td.range.highlighted.focus, -fieldset[disabled] .datepicker table tr td.range.highlighted:focus, -fieldset[disabled] .datepicker table tr td.range.highlighted:hover { - background-color: #e4eef3; - border-color: #9dc1d3; -} -.datepicker table tr td.range.highlighted.focused { - background: #c1d7e3; -} -.datepicker table tr td.range.highlighted.disabled, -.datepicker table tr td.range.highlighted.disabled:active { - background: #e4eef3; - color: #777; -} -.datepicker table tr td.range.today { - color: #000; - background-color: #f7ca77; - border-color: #f1a417; -} -.datepicker table tr td.range.today.focus, -.datepicker table tr td.range.today:focus { - color: #000; - background-color: #f4b747; - border-color: #815608; -} -.datepicker table tr td.range.today:hover { - color: #000; - background-color: #f4b747; - border-color: #bf800c; -} -.datepicker table tr td.range.today.active, -.datepicker table tr td.range.today:active { - color: #000; - background-color: #f4b747; - border-color: #bf800c; -} -.datepicker table tr td.range.today.active.focus, -.datepicker table tr td.range.today.active:focus, -.datepicker table tr td.range.today.active:hover, -.datepicker table tr td.range.today:active.focus, -.datepicker table tr td.range.today:active:focus, -.datepicker table tr td.range.today:active:hover { - color: #000; - background-color: #f2aa25; - border-color: #815608; -} -.datepicker table tr td.range.today.disabled.focus, -.datepicker table tr td.range.today.disabled:focus, -.datepicker table tr td.range.today.disabled:hover, -.datepicker table tr td.range.today[disabled].focus, -.datepicker table tr td.range.today[disabled]:focus, -.datepicker table tr td.range.today[disabled]:hover, -fieldset[disabled] .datepicker table tr td.range.today.focus, -fieldset[disabled] .datepicker table tr td.range.today:focus, -fieldset[disabled] .datepicker table tr td.range.today:hover { - background-color: #f7ca77; - border-color: #f1a417; -} -.datepicker table tr td.range.today.disabled, -.datepicker table tr td.range.today.disabled:active { - background: #f7ca77; - color: #777; -} -.datepicker table tr td.selected, -.datepicker table tr td.selected.highlighted { - color: #fff; - background-color: #777; - border-color: #555; - text-shadow: 0 -1px 0 rgb(0 0 0 / 25%); -} -.datepicker table tr td.selected.focus, -.datepicker table tr td.selected.highlighted.focus, -.datepicker table tr td.selected.highlighted:focus, -.datepicker table tr td.selected:focus { - color: #fff; - background-color: #5e5e5e; - border-color: #161616; -} -.datepicker table tr td.selected.highlighted:hover, -.datepicker table tr td.selected:hover { - color: #fff; - background-color: #5e5e5e; - border-color: #373737; -} -.datepicker table tr td.selected.active, -.datepicker table tr td.selected.highlighted.active, -.datepicker table tr td.selected.highlighted:active, -.datepicker table tr td.selected:active { - color: #fff; - background-color: #5e5e5e; - border-color: #373737; -} -.datepicker table tr td.selected.active.focus, -.datepicker table tr td.selected.active:focus, -.datepicker table tr td.selected.active:hover, -.datepicker table tr td.selected.highlighted.active.focus, -.datepicker table tr td.selected.highlighted.active:focus, -.datepicker table tr td.selected.highlighted.active:hover, -.datepicker table tr td.selected.highlighted:active.focus, -.datepicker table tr td.selected.highlighted:active:focus, -.datepicker table tr td.selected.highlighted:active:hover, -.datepicker table tr td.selected:active.focus, -.datepicker table tr td.selected:active:focus, -.datepicker table tr td.selected:active:hover { - color: #fff; - background-color: #4c4c4c; - border-color: #161616; -} -.datepicker table tr td.selected.disabled.focus, -.datepicker table tr td.selected.disabled:focus, -.datepicker table tr td.selected.disabled:hover, -.datepicker table tr td.selected.highlighted.disabled.focus, -.datepicker table tr td.selected.highlighted.disabled:focus, -.datepicker table tr td.selected.highlighted.disabled:hover, -.datepicker table tr td.selected.highlighted[disabled].focus, -.datepicker table tr td.selected.highlighted[disabled]:focus, -.datepicker table tr td.selected.highlighted[disabled]:hover, -.datepicker table tr td.selected[disabled].focus, -.datepicker table tr td.selected[disabled]:focus, -.datepicker table tr td.selected[disabled]:hover, -fieldset[disabled] .datepicker table tr td.selected.focus, -fieldset[disabled] .datepicker table tr td.selected.highlighted.focus, -fieldset[disabled] .datepicker table tr td.selected.highlighted:focus, -fieldset[disabled] .datepicker table tr td.selected.highlighted:hover, -fieldset[disabled] .datepicker table tr td.selected:focus, -fieldset[disabled] .datepicker table tr td.selected:hover { - background-color: #777; - border-color: #555; -} -.datepicker table tr td.active, -.datepicker table tr td.active.highlighted { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; - text-shadow: 0 -1px 0 rgb(0 0 0 / 25%); -} -.datepicker table tr td.active.focus, -.datepicker table tr td.active.highlighted.focus, -.datepicker table tr td.active.highlighted:focus, -.datepicker table tr td.active:focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.datepicker table tr td.active.highlighted:hover, -.datepicker table tr td.active:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.datepicker table tr td.active.active, -.datepicker table tr td.active.highlighted.active, -.datepicker table tr td.active.highlighted:active, -.datepicker table tr td.active:active { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.datepicker table tr td.active.active.focus, -.datepicker table tr td.active.active:focus, -.datepicker table tr td.active.active:hover, -.datepicker table tr td.active.highlighted.active.focus, -.datepicker table tr td.active.highlighted.active:focus, -.datepicker table tr td.active.highlighted.active:hover, -.datepicker table tr td.active.highlighted:active.focus, -.datepicker table tr td.active.highlighted:active:focus, -.datepicker table tr td.active.highlighted:active:hover, -.datepicker table tr td.active:active.focus, -.datepicker table tr td.active:active:focus, -.datepicker table tr td.active:active:hover { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.datepicker table tr td.active.disabled.focus, -.datepicker table tr td.active.disabled:focus, -.datepicker table tr td.active.disabled:hover, -.datepicker table tr td.active.highlighted.disabled.focus, -.datepicker table tr td.active.highlighted.disabled:focus, -.datepicker table tr td.active.highlighted.disabled:hover, -.datepicker table tr td.active.highlighted[disabled].focus, -.datepicker table tr td.active.highlighted[disabled]:focus, -.datepicker table tr td.active.highlighted[disabled]:hover, -.datepicker table tr td.active[disabled].focus, -.datepicker table tr td.active[disabled]:focus, -.datepicker table tr td.active[disabled]:hover, -fieldset[disabled] .datepicker table tr td.active.focus, -fieldset[disabled] .datepicker table tr td.active.highlighted.focus, -fieldset[disabled] .datepicker table tr td.active.highlighted:focus, -fieldset[disabled] .datepicker table tr td.active.highlighted:hover, -fieldset[disabled] .datepicker table tr td.active:focus, -fieldset[disabled] .datepicker table tr td.active:hover { - background-color: #337ab7; - border-color: #2e6da4; -} -.datepicker table tr td span { - display: block; - width: 23%; - height: 54px; - line-height: 54px; - float: left; - margin: 1%; - cursor: pointer; - border-radius: 4px; -} -.datepicker table tr td span.focused, -.datepicker table tr td span:hover { - background: #eee; -} -.datepicker table tr td span.disabled, -.datepicker table tr td span.disabled:hover { - background: 0 0; - color: #777; - cursor: default; -} -.datepicker table tr td span.active, -.datepicker table tr td span.active.disabled, -.datepicker table tr td span.active.disabled:hover, -.datepicker table tr td span.active:hover { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; - text-shadow: 0 -1px 0 rgb(0 0 0 / 25%); -} -.datepicker table tr td span.active.disabled.focus, -.datepicker table tr td span.active.disabled:focus, -.datepicker table tr td span.active.disabled:hover.focus, -.datepicker table tr td span.active.disabled:hover:focus, -.datepicker table tr td span.active.focus, -.datepicker table tr td span.active:focus, -.datepicker table tr td span.active:hover.focus, -.datepicker table tr td span.active:hover:focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.datepicker table tr td span.active.disabled:hover, -.datepicker table tr td span.active.disabled:hover:hover, -.datepicker table tr td span.active:hover, -.datepicker table tr td span.active:hover:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.datepicker table tr td span.active.active, -.datepicker table tr td span.active.disabled.active, -.datepicker table tr td span.active.disabled:active, -.datepicker table tr td span.active.disabled:hover.active, -.datepicker table tr td span.active.disabled:hover:active, -.datepicker table tr td span.active:active, -.datepicker table tr td span.active:hover.active, -.datepicker table tr td span.active:hover:active { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.datepicker table tr td span.active.active.focus, -.datepicker table tr td span.active.active:focus, -.datepicker table tr td span.active.active:hover, -.datepicker table tr td span.active.disabled.active.focus, -.datepicker table tr td span.active.disabled.active:focus, -.datepicker table tr td span.active.disabled.active:hover, -.datepicker table tr td span.active.disabled:active.focus, -.datepicker table tr td span.active.disabled:active:focus, -.datepicker table tr td span.active.disabled:active:hover, -.datepicker table tr td span.active.disabled:hover.active.focus, -.datepicker table tr td span.active.disabled:hover.active:focus, -.datepicker table tr td span.active.disabled:hover.active:hover, -.datepicker table tr td span.active.disabled:hover:active.focus, -.datepicker table tr td span.active.disabled:hover:active:focus, -.datepicker table tr td span.active.disabled:hover:active:hover, -.datepicker table tr td span.active:active.focus, -.datepicker table tr td span.active:active:focus, -.datepicker table tr td span.active:active:hover, -.datepicker table tr td span.active:hover.active.focus, -.datepicker table tr td span.active:hover.active:focus, -.datepicker table tr td span.active:hover.active:hover, -.datepicker table tr td span.active:hover:active.focus, -.datepicker table tr td span.active:hover:active:focus, -.datepicker table tr td span.active:hover:active:hover { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.datepicker table tr td span.active.disabled.disabled.focus, -.datepicker table tr td span.active.disabled.disabled:focus, -.datepicker table tr td span.active.disabled.disabled:hover, -.datepicker table tr td span.active.disabled.focus, -.datepicker table tr td span.active.disabled:focus, -.datepicker table tr td span.active.disabled:hover, -.datepicker table tr td span.active.disabled:hover.disabled.focus, -.datepicker table tr td span.active.disabled:hover.disabled:focus, -.datepicker table tr td span.active.disabled:hover.disabled:hover, -.datepicker table tr td span.active.disabled:hover[disabled].focus, -.datepicker table tr td span.active.disabled:hover[disabled]:focus, -.datepicker table tr td span.active.disabled:hover[disabled]:hover, -.datepicker table tr td span.active.disabled[disabled].focus, -.datepicker table tr td span.active.disabled[disabled]:focus, -.datepicker table tr td span.active.disabled[disabled]:hover, -.datepicker table tr td span.active:hover.disabled.focus, -.datepicker table tr td span.active:hover.disabled:focus, -.datepicker table tr td span.active:hover.disabled:hover, -.datepicker table tr td span.active:hover[disabled].focus, -.datepicker table tr td span.active:hover[disabled]:focus, -.datepicker table tr td span.active:hover[disabled]:hover, -.datepicker table tr td span.active[disabled].focus, -.datepicker table tr td span.active[disabled]:focus, -.datepicker table tr td span.active[disabled]:hover, -fieldset[disabled] .datepicker table tr td span.active.disabled.focus, -fieldset[disabled] .datepicker table tr td span.active.disabled:focus, -fieldset[disabled] .datepicker table tr td span.active.disabled:hover, -fieldset[disabled] .datepicker table tr td span.active.disabled:hover.focus, -fieldset[disabled] .datepicker table tr td span.active.disabled:hover:focus, -fieldset[disabled] .datepicker table tr td span.active.disabled:hover:hover, -fieldset[disabled] .datepicker table tr td span.active.focus, -fieldset[disabled] .datepicker table tr td span.active:focus, -fieldset[disabled] .datepicker table tr td span.active:hover, -fieldset[disabled] .datepicker table tr td span.active:hover.focus, -fieldset[disabled] .datepicker table tr td span.active:hover:focus, -fieldset[disabled] .datepicker table tr td span.active:hover:hover { - background-color: #337ab7; - border-color: #2e6da4; -} -.datepicker table tr td span.new, -.datepicker table tr td span.old { - color: #777; -} -.datepicker .datepicker-switch { - width: 145px; -} -.datepicker .datepicker-switch, -.datepicker .next, -.datepicker .prev, -.datepicker tfoot tr th { - cursor: pointer; -} -.datepicker .datepicker-switch:hover, -.datepicker .next:hover, -.datepicker .prev:hover, -.datepicker tfoot tr th:hover { - background: #eee; -} -.datepicker .next.disabled, -.datepicker .prev.disabled { - visibility: hidden; -} -.datepicker .cw { - font-size: 10px; - width: 12px; - padding: 0 2px 0 5px; - vertical-align: middle; -} -.input-group.date .input-group-addon { - cursor: pointer; -} -.input-daterange { - width: 100%; -} -.input-daterange input { - text-align: center; -} -.input-daterange input:first-child { - border-radius: 3px 0 0 3px; -} -.input-daterange input:last-child { - border-radius: 0 3px 3px 0; -} -.input-daterange .input-group-addon { - width: auto; - min-width: 16px; - padding: 4px 5px; - line-height: 1.42857143; - text-shadow: 0 1px 0 #fff; - border-width: 1px 0; - margin-left: -5px; - margin-right: -5px; -} - -/* loading spinner */ -@keyframes throbber { - 0% { - background: #dde2e7; - } - - 10% { - background: #6b9dc8; +@keyframes dots-7ar3yq { + 20% { + background-position: + 0% 0%, + 50% 50%, + 100% 50%; } 40% { - background: #dde2e7; + background-position: + 0% 100%, + 50% 0%, + 100% 50%; } -} -@keyframes throbber { - 0% { - background: #dde2e7; + 60% { + background-position: + 0% 50%, + 50% 100%, + 100% 0%; } - 10% { - background: #6b9dc8; - } - - 40% { - background: #dde2e7; + 80% { + background-position: + 0% 50%, + 50% 50%, + 100% 100%; } } - -@keyframes throbber { - 0% { - background: #dde2e7; - } - - 10% { - background: #6b9dc8; - } - - 40% { - background: #dde2e7; - } -} - -@keyframes throbber { - 0% { - background: #dde2e7; - } - - 10% { - background: #6b9dc8; - } - - 40% { - background: #dde2e7; - } -} - -/* :not(:required) hides these rules from IE9 and below */ -.throbber:not(:required) { - animation: throbber 2000ms 300ms infinite ease-out; - background: #dde2e7; - display: inline-block; - position: relative; - text-indent: -9999px; - width: 0.9em; - height: 1.5em; - margin: 0 1.6em; -} - -.throbber:not(:required)::before, -.throbber:not(:required)::after { - background: #dde2e7; - content: '\x200B'; - display: inline-block; - width: 0.9em; - height: 1.5em; - position: absolute; - top: 0; -} - -.throbber:not(:required)::before { - animation: throbber 2000ms 150ms infinite ease-out; - left: -1.6em; -} - -.throbber:not(:required)::after { - animation: throbber 2000ms 450ms infinite ease-out; - right: -1.6em; -} - -.pellet { - display: inline-block; - height: 15px; - width: 15px; - border-radius: 50%; - border: 2px solid #fff; -} - -/* --------------- */ - -/* RANGE FIELD */ - -/* --------------- */ - -.range-wrap { - position: relative; - width: 100%; - display: flex; -} - -.form-group.range .control-label { - margin-bottom: -1em; - position: relative !important; - left: 0 !important; - margin-top: 1em; -} - -.range-wrap output { - background: var(--primary-color); - padding: 5px 10px; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - color: var(--neutral-light-color); - min-width: 3.8em; - text-align: center; - display: flex; - place-content: center center; - align-items: center; -} - -.range-wrap input[type='range'] { - appearance: none; - height: 40px; - border-radius: 0 !important; - border-top-left-radius: 3px !important; - border-bottom-left-radius: 3px !important; - background-image: transparent; - background-repeat: no-repeat; - padding-left: 0; - padding-right: 0; - padding-top: 0; -} - -/* Input Thumb */ -.range-wrap input[type='range']::-webkit-slider-thumb { - appearance: none; - height: 38px; - width: 40px; - border-radius: 50%; - background: var(--primary-color); - cursor: ew-resize; - border: none; - transition: background 0.3s ease-in-out; -} - -.range-wrap input[type='range']::-moz-range-thumb { - appearance: none; - height: 38px; - width: 40px; - border-radius: 50%; - background: var(--primary-color); - cursor: ew-resize; - border: none; - transition: background 0.3s ease-in-out; -} - -.range-wrap input[type='range']::-ms-thumb { - appearance: none; - height: 38px; - width: 40px; - border-radius: 50%; - background: var(--primary-color); - cursor: ew-resize; - border: none; - transition: background 0.3s ease-in-out; -} - -.range-wrap input[type='range']::-webkit-slider-thumb:hover { - background: var(--secondary-color-1); -} - -.range-wrap input[type='range']::-moz-range-thumb:hover { - background: var(--secondary-color-1); -} - -.range-wrap input[type='range']::-ms-thumb:hover { - background: var(--secondary-color-1); -} - -/* Input Track */ -.range-wrap input[type='range']::-webkit-slider-runnable-track { - appearance: none; - border: none; - background: transparent; -} - -.range-wrap input[type='range']::-moz-range-track { - appearance: none; - border: none; - background: transparent; -} - -.range-wrap input[type='range']::-ms-track { - appearance: none; - border: none; - background: transparent; -} - -/* Display tabs when printing */ -@media print { - div[role='tabpanel'] { - display: block !important; - } - - a[role='tab'] { - display: none !important; - } -} - -/* DateField */ - -div.form-control.for-selector-is-recurrent { - height: auto; - max-width: 100%; -} - -div.form-control.for-selector-is-recurrent > .input-prepend.input-group { - margin-top: 0.4rem; - margin-bottom: 0.4rem; -} - -.control-group.input-listedatedeb .specific-for-recurrence, -.control-group.input-listedatefin - .specific-for-recurrence - .control-group.input-jour - .specific-for-recurrence { - align-self: flex-start; -} - -.control-group.input-listedatedeb - .specific-for-recurrence - span.add-on.input-group-addon, -.control-group.input-listedatefin - .specific-for-recurrence - span.add-on.input-group-addon, -.control-group.input-jour - .specific-for-recurrence - span.add-on.input-group-addon { - width: auto; - display: flex; -} - -.input-recur-date .specific-for-recurrence { - max-width: 100%; -} - -.input-recur-date.control-group > .controls { - gap: 1rem 1rem; - flex-wrap: wrap; -} - -.input-recur-date.control-group .for-selector-is-recurrent > .input-group { - flex-wrap: wrap; -} - -.input-recur-date.control-group - .for-selector-is-recurrent - > .input-group - select.form-control { - max-width: 80%; - width: auto; -} - -.input-recur-date:not(.ready) .specific-for-recurrence, -.input-recur-date:not(.ready) .event-container-for-datepicker { - display: none !important; -} - -.input-recur-date .event-container-for-datepicker { - display: flex; - flex-direction: column; - gap: 0.3rem; -} - -.input-recur-date .event-container-for-datepicker .limit-date-hint { - max-width: 250px; -} diff --git a/tools/bazar/presentation/styles/entries/form.css b/tools/bazar/presentation/styles/entries/form.css new file mode 100644 index 000000000..915b5e320 --- /dev/null +++ b/tools/bazar/presentation/styles/entries/form.css @@ -0,0 +1,75 @@ +#map img { + max-width: none !important; +} + +#map label { + width: auto !important; + display: inline !important; +} + +.map-entry { + width: 100%; + min-height: 220px; +} + +.required_symbol, +.symbole_obligatoire { + color: #e9322d; +} + +.bazar-entry-edit-image { + display: flex; + align-items: center; +} + +.bazar-entry-edit-image output img { + height: 150px; +} + +.bazar-entry-edit-image .image-actions { + display: flex; + flex-direction: column; + margin-left: 1rem; +} + +/* Image input when there is already an image loaded */ +.form-group.input-image:not(.file) .control-label { + position: relative; + padding: 0; + margin: 0 -15px; +} + +.form-group.input-image:not(.file) .control-label::before { + content: none; +} + +.frm-holder .form-elements .custom-hint { + border: 2px solid #c5c5c5; + padding: 5px; + margin: 0 5px 5px; + border-radius: 3px; +} + +.tab-content .tab-pane.with-padding { + padding-top: 1em; +} + +.btn-geolocate { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.filter-and-check-all-container { + display: flex; + margin: 10px 0; + align-items: center; + gap: 1rem; +} +.filter-and-check-all-container .check-all-container { + flex-shrink: 0; +} + +/* submit button disabled */ +#formulaire .form-actions button[type='submit'].submit-disabled { + cursor: progress; +} diff --git a/tools/bazar/presentation/styles/bazar-list-dynamic.css b/tools/bazar/presentation/styles/entries/index-dynamic.css similarity index 99% rename from tools/bazar/presentation/styles/bazar-list-dynamic.css rename to tools/bazar/presentation/styles/entries/index-dynamic.css index 94660409c..2a10ac358 100644 --- a/tools/bazar/presentation/styles/bazar-list-dynamic.css +++ b/tools/bazar/presentation/styles/entries/index-dynamic.css @@ -7,9 +7,7 @@ } /* ------------- */ - /* PAGINATION */ - /* ------------- */ .bazar-list-dynamic-container .results-col { @@ -49,9 +47,7 @@ } /* ------------- */ - /* ENTRY */ - /* ------------- */ .entry-container.modal-body { padding-top: 1rem; @@ -81,9 +77,7 @@ } /* ------------- */ - /* ENTRY FIELD */ - /* ------------- */ /* Helpers div to used only when needed */ @@ -99,9 +93,7 @@ } /* ------------- */ - /* LIST TEMPLATE */ - /* ------------- */ .bazar-list.dynamic .panel-heading-container { display: grid; diff --git a/tools/bazar/presentation/styles/entries/index-filters.css b/tools/bazar/presentation/styles/entries/index-filters.css new file mode 100644 index 000000000..b6b8a9fc3 --- /dev/null +++ b/tools/bazar/presentation/styles/entries/index-filters.css @@ -0,0 +1,143 @@ +.facette-container { + display: flex; + flex-direction: row; +} +.facette-container-fullwidth { + flex-wrap: wrap; +} +.facette-container[data-filter-align='right'] { + flex-direction: row-reverse; +} + +/* -------------------------------------------- */ +/* Filter Box Panels */ +/* -------------------------------------------- */ +.facette-container .filter-box .panel-heading { + cursor: pointer; +} +.filter-box .panel-body { + overflow-x: hidden; /* in case some labels are too long */ +} +@media (width <= 768px) { + .facette-container, + .facette-container[data-filter-align='right'] { + flex-direction: column; + } + + .filter-box .panel-body { + max-height: 11em; + overflow-y: scroll; + + /* for firefox custom scrollbar */ + scrollbar-color: var(--neutral-color) white; + scrollbar-width: auto; + + /* the scrollbar width is too thin, we may fix it by using a scrollbar JS library */ + } + + .filter-box .panel-body::-webkit-scrollbar { + appearance: none; + } + + .filter-box .panel-body:vertical { + width: 15px; + } + + .filter-box .panel-body::-webkit-scrollbar:horizontal { + height: 15px; + } + + .filter-box .panel-body::-webkit-scrollbar-thumb { + background-color: var(--neutral-color); + border-radius: 10px; + } +} + +/* -------------------------------------------- */ +/* Search */ +/* -------------------------------------------- */ +.facette-container:not([data-filter-align='left']) + > .filters-col + > .bazar-search { + margin-top: 2rem; +} + +@media (width >= 768px) { + .facette-container:not([data-filter-align='left']) + > [class^='filters-col '][class*='col-sm-'] + > .bazar-search { + margin-top: 0; + } +} + +/* -------------------------------------------- */ +/* Filter Nodes (for Bazar List Dynamic) */ +/* -------------------------------------------- */ +.filter-box .filter-nodes { + padding: 0.5rem 0; +} +.filter-box .filter-node-container { + display: flex; + flex-direction: column; +} +.filter-box .filter-node-container .children { + padding-left: 1.7rem; +} +/* Node */ +.filter-box .filter-node { + display: flex; + margin: 0.3em 1em; + color: var(--neutral-soft-color); +} +.filter-box .filter-node > span { + display: flex; + width: 100%; +} +.filter-box .filter-node > span > span { + display: flex; + align-items: center; + width: 100%; +} +/* Label */ +.filter-box .filter-node .filter-node-label { + flex-grow: 1; + display: flex; + align-items: center; + font-weight: normal; +} +.filter-box .filter-node .filter-node-label span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.filter-box .filter-node.checked .filter-node-label, +.filter-box .filter-node.some-descendant-checked .filter-node-label { + font-weight: bold; +} +.filter-box .filter-node > span > span:hover .filter-node-label { + color: var(--primary-color); +} +/* Chevron icon */ +.filter-box .filter-node .chevron-icon { + font-size: 0.8em; + margin-left: 0.5em; + transition: transform 0.2s; +} +.filter-box .filter-node.expanded .chevron-icon { + transform: rotate(180deg); +} +/* Count */ +.filter-box .filter-node .count { + position: absolute; + right: -1em; + padding: 0 0.8rem 0 0.5em; + background-color: var(--main-container-bg-color); +} +.filter-box .filter-node .count span { + background-color: var(--neutral-light-color); + padding: 0 0.5em; + border-radius: 0.2em; + font-weight: bold; + font-size: 0.7em; + opacity: 0.9; +} diff --git a/tools/bazar/presentation/styles/entries/index.css b/tools/bazar/presentation/styles/entries/index.css new file mode 100644 index 000000000..04d69e7d1 --- /dev/null +++ b/tools/bazar/presentation/styles/entries/index.css @@ -0,0 +1,162 @@ +.BAZ_menu .nav, +.bazar-search { + margin-bottom: 1em; +} + +.filters-col .bazar-search { + margin-bottom: 2em; +} + +.bazar-table { + table-layout: fixed; + word-wrap: break-word; +} + +.precsv { + height: 125px; + white-space: pre; + padding: 5px; + word-wrap: break-word; + overflow: auto; +} + +.results-col { + clear: both; +} + +.reset-filters.btn-block { + margin-top: 5px; +} + +/* accordion pane */ +.accordion { + background: transparent; + width: 100%; +} + +.pane { + display: none; + padding: 15px; + margin: 0; + border: 1px solid #666; + border-top: none; +} + +/* accordion header */ +.titre_accordeon { + margin: 0 0 1px; + padding: 5px 15px; + cursor: pointer; + background: #222 url('../images/alert-overlay.png') repeat-x; + color: #fff; + text-decoration: none; + border-radius: 5px; + box-shadow: 0 1px 3px rgb(0 0 0 / 50%); + text-shadow: 0 -1px 1px rgb(0 0 0 / 25%); + border-bottom: 1px solid rgb(0 0 0 / 25%); + font-size: 13px; + font-weight: bold; + line-height: 1; + display: block; + position: relative; +} +.liens_titre_accordeon { + position: absolute; + top: 3px; + right: 3px; +} +.liens_titre_accordeon a { + display: inline-block; + width: 20px; + height: 16px; + float: left; + margin: 0 2px 0 0; + padding: 0; +} + +/* currently active header */ +.titre_accordeon.current { + cursor: default; + background-color: #ffb515; + border-bottom: none; +} + +.titre_accordeon:hover { + background-color: #fc9200; + text-decoration: none; +} + +input:not(:placeholder-shown):invalid, +textarea:not(:placeholder-shown):invalid, +.submitted input:required:invalid, +.submitted textarea:required:invalid, +.submitted select:required:invalid, +.submitted .bootstrap-tagsinput.invalid, +.submitted .form-group.input-checkbox.invalid, +.submitted .form-group.input-radio.invalid, +.submitted .invalid > .ace-container, +.submitted .textarea.summernote.invalid { + border-color: #dd2c00; + border-color: var(--danger-color); +} + +input.form-control.error { + border-color: var(--danger-color) !important; +} +.BAZ_label { + font-weight: bold; + display: block; +} +.BAZ_texte { + display: block; +} +.titre_lien { + display: block; + font-size: 1.4em; +} + +/*.valeur_formulaire .handle {left:20px;position:absolute;}*/ +.valeur_formulaire .formulaire_ligne { + background: #eaeaea; + border: 1px solid #ccc; + padding: 5px 0; +} + +.bazar-lists select { + width: 250px; + display: inline-block; +} + +.link-csv-file { + display: block; + margin: 10px 0; +} + +.entries-list, +.list-bazar-entries { + max-height: 200px; + overflow: auto; +} +.entries-list .yeswiki-checkbox { + width: 100% !important; +} +.yeswiki-input-entries + .bootstrap-tagsinput input { + display: block; +} + +.pagination .page { + min-height: auto; +} + +.pellet { + display: inline-block; + height: 15px; + width: 15px; + border-radius: 50%; + border: 2px solid #fff; +} + +.bazar-entry button.panel-heading { + text-align: left; + width: 100%; +} diff --git a/tools/bazar/presentation/styles/entries/view.css b/tools/bazar/presentation/styles/entries/view.css new file mode 100644 index 000000000..56e3b2853 --- /dev/null +++ b/tools/bazar/presentation/styles/entries/view.css @@ -0,0 +1,16 @@ +.BAZ_cadre_fiche .BAZ_rubrique { + margin: 0 0 10px; +} + +/* ENTRY FOOTER */ +.BAZ_fiche_info { + font-size: 0.9rem; + display: flex; + flex-direction: row; +} + +.BAZ_fiche_info .BAZ_actions_fiche { + align-self: center; + text-align: right; + flex: 1 auto; +} diff --git a/tools/bazar/presentation/styles/checkbox-drag-and-drop.css b/tools/bazar/presentation/styles/inputs/checkbox-drag-and-drop.css similarity index 100% rename from tools/bazar/presentation/styles/checkbox-drag-and-drop.css rename to tools/bazar/presentation/styles/inputs/checkbox-drag-and-drop.css diff --git a/tools/bazar/presentation/styles/inputs/checkbox-tree.css b/tools/bazar/presentation/styles/inputs/checkbox-tree.css new file mode 100644 index 000000000..1aedcd73b --- /dev/null +++ b/tools/bazar/presentation/styles/inputs/checkbox-tree.css @@ -0,0 +1,43 @@ +.node-children { + margin-left: 2rem; + display: none; +} +.node-container { + position: relative; +} +.node-children .border { + content: ''; + position: absolute; + top: 2rem; + left: 0.5rem; + bottom: 0; + width: 1px; + background-color: var(--primary-color); + opacity: 0.3; + pointer-events: none; +} + +.node-container .chevron-icon { + transform: rotate(0deg); + padding: 5px; + margin-left: 5px; + transition: transform 0.15s; + font-size: 0.8em; +} +.checkbox-node .checkbox-label:hover .chevron-icon { + color: var(--secondary-color-1); +} + +.node-container.expanded > .checkbox-node .chevron-icon { + transform: rotate(90deg); +} + +.check-all-container { + --checkbox-color: var(--neutral-color); +} +.check-all-container span { + font-size: 0.8em !important; + font-weight: 800; + font-style: normal; + text-transform: uppercase; +} diff --git a/tools/bazar/presentation/styles/inputs/date.css b/tools/bazar/presentation/styles/inputs/date.css new file mode 100644 index 000000000..256ec6877 --- /dev/null +++ b/tools/bazar/presentation/styles/inputs/date.css @@ -0,0 +1,790 @@ +.bazar-date, +.bazar-date.form-control { + width: 133px; +} + +.form-group.input-listedatedeb .controls, +.form-group.input-listedatefin .controls, +.form-group.input-jour .controls { + display: flex; + flex-wrap: wrap; + gap: 1rem 1.5rem; +} + +.form-group.input-listedatedeb .select-time, +.form-group.input-listedatefin .select-time, +.form-group.input-jour .select-time { + margin-left: 0 !important; +} + +@media (width <= 600px) { + .form-group.input-listedatedeb .select-time, + .form-group.input-listedatefin .select-time, + .form-group.input-jour .select-time { + width: 100%; + margin: 0; + } +} + +.select-hour, +.select-minutes { + width: 76px !important; +} + +input::-webkit-calendar-picker-indicator { + display: none; +} + +input[type='date']::input-placeholder { + visibility: hidden !important; +} + +/*! + * Datepicker for Bootstrap v1.7.1 (https://github.com/uxsolutions/bootstrap-datepicker) + * + * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + +.datepicker { + border-radius: 4px; + direction: ltr; +} +.datepicker-inline { + width: 220px; +} +.datepicker-rtl { + direction: rtl; +} +.datepicker-rtl.dropdown-menu { + left: auto; +} +.datepicker-rtl table tr td span { + float: right; +} +.datepicker-dropdown { + top: 0; + left: 0; + padding: 4px; +} +.datepicker-dropdown::before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid rgb(0 0 0 / 15%); + border-top: 0; + border-bottom-color: rgb(0 0 0 / 20%); + position: absolute; +} +.datepicker-dropdown::after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #fff; + border-top: 0; + position: absolute; +} +.datepicker-dropdown.datepicker-orient-left::before { + left: 6px; +} +.datepicker-dropdown.datepicker-orient-left::after { + left: 7px; +} +.datepicker-dropdown.datepicker-orient-right::before { + right: 6px; +} +.datepicker-dropdown.datepicker-orient-right::after { + right: 7px; +} +.datepicker-dropdown.datepicker-orient-bottom::before { + top: -7px; +} +.datepicker-dropdown.datepicker-orient-bottom::after { + top: -6px; +} +.datepicker-dropdown.datepicker-orient-top::before { + bottom: -7px; + border-bottom: 0; + border-top: 7px solid rgb(0 0 0 / 15%); +} +.datepicker-dropdown.datepicker-orient-top::after { + bottom: -6px; + border-bottom: 0; + border-top: 6px solid #fff; +} +.datepicker table { + margin: 0; + -webkit-touch-callout: none; + user-select: none; +} +.datepicker table tr td, +.datepicker table tr th { + text-align: center; + width: 30px; + height: 30px; + border-radius: 4px; + border: none; +} +.table-striped .datepicker table tr td, +.table-striped .datepicker table tr th { + background-color: transparent; +} +.datepicker table tr td.new, +.datepicker table tr td.old { + color: #777; +} +.datepicker table tr td.day:hover, +.datepicker table tr td.focused { + background: #eee; + cursor: pointer; +} +.datepicker table tr td.disabled, +.datepicker table tr td.disabled:hover { + background: 0 0; + color: #777; + cursor: default; +} +.datepicker table tr td.highlighted { + color: #000; + background-color: #d9edf7; + border-color: #85c5e5; + border-radius: 0; +} +.datepicker table tr td.highlighted.focus, +.datepicker table tr td.highlighted:focus { + color: #000; + background-color: #afd9ee; + border-color: #298fc2; +} +.datepicker table tr td.highlighted:hover { + color: #000; + background-color: #afd9ee; + border-color: #52addb; +} +.datepicker table tr td.highlighted.active, +.datepicker table tr td.highlighted:active { + color: #000; + background-color: #afd9ee; + border-color: #52addb; +} +.datepicker table tr td.highlighted.active.focus, +.datepicker table tr td.highlighted.active:focus, +.datepicker table tr td.highlighted.active:hover, +.datepicker table tr td.highlighted:active.focus, +.datepicker table tr td.highlighted:active:focus, +.datepicker table tr td.highlighted:active:hover { + color: #000; + background-color: #91cbe8; + border-color: #298fc2; +} +.datepicker table tr td.highlighted.disabled.focus, +.datepicker table tr td.highlighted.disabled:focus, +.datepicker table tr td.highlighted.disabled:hover, +.datepicker table tr td.highlighted[disabled].focus, +.datepicker table tr td.highlighted[disabled]:focus, +.datepicker table tr td.highlighted[disabled]:hover, +fieldset[disabled] .datepicker table tr td.highlighted.focus, +fieldset[disabled] .datepicker table tr td.highlighted:focus, +fieldset[disabled] .datepicker table tr td.highlighted:hover { + background-color: #d9edf7; + border-color: #85c5e5; +} +.datepicker table tr td.highlighted.focused { + background: #afd9ee; +} +.datepicker table tr td.highlighted.disabled, +.datepicker table tr td.highlighted.disabled:active { + background: #d9edf7; + color: #777; +} +.datepicker table tr td.today { + color: #000; + background-color: #ffdb99; + border-color: #ffb733; +} +.datepicker table tr td.today.focus, +.datepicker table tr td.today:focus { + color: #000; + background-color: #ffc966; + border-color: #b37400; +} +.datepicker table tr td.today:hover { + color: #000; + background-color: #ffc966; + border-color: #f59e00; +} +.datepicker table tr td.today.active, +.datepicker table tr td.today:active { + color: #000; + background-color: #ffc966; + border-color: #f59e00; +} +.datepicker table tr td.today.active.focus, +.datepicker table tr td.today.active:focus, +.datepicker table tr td.today.active:hover, +.datepicker table tr td.today:active.focus, +.datepicker table tr td.today:active:focus, +.datepicker table tr td.today:active:hover { + color: #000; + background-color: #ffbc42; + border-color: #b37400; +} +.datepicker table tr td.today.disabled.focus, +.datepicker table tr td.today.disabled:focus, +.datepicker table tr td.today.disabled:hover, +.datepicker table tr td.today[disabled].focus, +.datepicker table tr td.today[disabled]:focus, +.datepicker table tr td.today[disabled]:hover, +fieldset[disabled] .datepicker table tr td.today.focus, +fieldset[disabled] .datepicker table tr td.today:focus, +fieldset[disabled] .datepicker table tr td.today:hover { + background-color: #ffdb99; + border-color: #ffb733; +} +.datepicker table tr td.today.focused { + background: #ffc966; +} +.datepicker table tr td.today.disabled, +.datepicker table tr td.today.disabled:active { + background: #ffdb99; + color: #777; +} +.datepicker table tr td.range { + color: #000; + background-color: #eee; + border-color: #bbb; + border-radius: 0; +} +.datepicker table tr td.range.focus, +.datepicker table tr td.range:focus { + color: #000; + background-color: #d5d5d5; + border-color: #7c7c7c; +} +.datepicker table tr td.range:hover { + color: #000; + background-color: #d5d5d5; + border-color: #9d9d9d; +} +.datepicker table tr td.range.active, +.datepicker table tr td.range:active { + color: #000; + background-color: #d5d5d5; + border-color: #9d9d9d; +} +.datepicker table tr td.range.active.focus, +.datepicker table tr td.range.active:focus, +.datepicker table tr td.range.active:hover, +.datepicker table tr td.range:active.focus, +.datepicker table tr td.range:active:focus, +.datepicker table tr td.range:active:hover { + color: #000; + background-color: #c3c3c3; + border-color: #7c7c7c; +} +.datepicker table tr td.range.disabled.focus, +.datepicker table tr td.range.disabled:focus, +.datepicker table tr td.range.disabled:hover, +.datepicker table tr td.range[disabled].focus, +.datepicker table tr td.range[disabled]:focus, +.datepicker table tr td.range[disabled]:hover, +fieldset[disabled] .datepicker table tr td.range.focus, +fieldset[disabled] .datepicker table tr td.range:focus, +fieldset[disabled] .datepicker table tr td.range:hover { + background-color: #eee; + border-color: #bbb; +} +.datepicker table tr td.range.focused { + background: #d5d5d5; +} +.datepicker table tr td.range.disabled, +.datepicker table tr td.range.disabled:active { + background: #eee; + color: #777; +} +.datepicker table tr td.range.highlighted { + color: #000; + background-color: #e4eef3; + border-color: #9dc1d3; +} +.datepicker table tr td.range.highlighted.focus, +.datepicker table tr td.range.highlighted:focus { + color: #000; + background-color: #c1d7e3; + border-color: #4b88a6; +} +.datepicker table tr td.range.highlighted:hover { + color: #000; + background-color: #c1d7e3; + border-color: #73a6c0; +} +.datepicker table tr td.range.highlighted.active, +.datepicker table tr td.range.highlighted:active { + color: #000; + background-color: #c1d7e3; + border-color: #73a6c0; +} +.datepicker table tr td.range.highlighted.active.focus, +.datepicker table tr td.range.highlighted.active:focus, +.datepicker table tr td.range.highlighted.active:hover, +.datepicker table tr td.range.highlighted:active.focus, +.datepicker table tr td.range.highlighted:active:focus, +.datepicker table tr td.range.highlighted:active:hover { + color: #000; + background-color: #a8c8d8; + border-color: #4b88a6; +} +.datepicker table tr td.range.highlighted.disabled.focus, +.datepicker table tr td.range.highlighted.disabled:focus, +.datepicker table tr td.range.highlighted.disabled:hover, +.datepicker table tr td.range.highlighted[disabled].focus, +.datepicker table tr td.range.highlighted[disabled]:focus, +.datepicker table tr td.range.highlighted[disabled]:hover, +fieldset[disabled] .datepicker table tr td.range.highlighted.focus, +fieldset[disabled] .datepicker table tr td.range.highlighted:focus, +fieldset[disabled] .datepicker table tr td.range.highlighted:hover { + background-color: #e4eef3; + border-color: #9dc1d3; +} +.datepicker table tr td.range.highlighted.focused { + background: #c1d7e3; +} +.datepicker table tr td.range.highlighted.disabled, +.datepicker table tr td.range.highlighted.disabled:active { + background: #e4eef3; + color: #777; +} +.datepicker table tr td.range.today { + color: #000; + background-color: #f7ca77; + border-color: #f1a417; +} +.datepicker table tr td.range.today.focus, +.datepicker table tr td.range.today:focus { + color: #000; + background-color: #f4b747; + border-color: #815608; +} +.datepicker table tr td.range.today:hover { + color: #000; + background-color: #f4b747; + border-color: #bf800c; +} +.datepicker table tr td.range.today.active, +.datepicker table tr td.range.today:active { + color: #000; + background-color: #f4b747; + border-color: #bf800c; +} +.datepicker table tr td.range.today.active.focus, +.datepicker table tr td.range.today.active:focus, +.datepicker table tr td.range.today.active:hover, +.datepicker table tr td.range.today:active.focus, +.datepicker table tr td.range.today:active:focus, +.datepicker table tr td.range.today:active:hover { + color: #000; + background-color: #f2aa25; + border-color: #815608; +} +.datepicker table tr td.range.today.disabled.focus, +.datepicker table tr td.range.today.disabled:focus, +.datepicker table tr td.range.today.disabled:hover, +.datepicker table tr td.range.today[disabled].focus, +.datepicker table tr td.range.today[disabled]:focus, +.datepicker table tr td.range.today[disabled]:hover, +fieldset[disabled] .datepicker table tr td.range.today.focus, +fieldset[disabled] .datepicker table tr td.range.today:focus, +fieldset[disabled] .datepicker table tr td.range.today:hover { + background-color: #f7ca77; + border-color: #f1a417; +} +.datepicker table tr td.range.today.disabled, +.datepicker table tr td.range.today.disabled:active { + background: #f7ca77; + color: #777; +} +.datepicker table tr td.selected, +.datepicker table tr td.selected.highlighted { + color: #fff; + background-color: #777; + border-color: #555; + text-shadow: 0 -1px 0 rgb(0 0 0 / 25%); +} +.datepicker table tr td.selected.focus, +.datepicker table tr td.selected.highlighted.focus, +.datepicker table tr td.selected.highlighted:focus, +.datepicker table tr td.selected:focus { + color: #fff; + background-color: #5e5e5e; + border-color: #161616; +} +.datepicker table tr td.selected.highlighted:hover, +.datepicker table tr td.selected:hover { + color: #fff; + background-color: #5e5e5e; + border-color: #373737; +} +.datepicker table tr td.selected.active, +.datepicker table tr td.selected.highlighted.active, +.datepicker table tr td.selected.highlighted:active, +.datepicker table tr td.selected:active { + color: #fff; + background-color: #5e5e5e; + border-color: #373737; +} +.datepicker table tr td.selected.active.focus, +.datepicker table tr td.selected.active:focus, +.datepicker table tr td.selected.active:hover, +.datepicker table tr td.selected.highlighted.active.focus, +.datepicker table tr td.selected.highlighted.active:focus, +.datepicker table tr td.selected.highlighted.active:hover, +.datepicker table tr td.selected.highlighted:active.focus, +.datepicker table tr td.selected.highlighted:active:focus, +.datepicker table tr td.selected.highlighted:active:hover, +.datepicker table tr td.selected:active.focus, +.datepicker table tr td.selected:active:focus, +.datepicker table tr td.selected:active:hover { + color: #fff; + background-color: #4c4c4c; + border-color: #161616; +} +.datepicker table tr td.selected.disabled.focus, +.datepicker table tr td.selected.disabled:focus, +.datepicker table tr td.selected.disabled:hover, +.datepicker table tr td.selected.highlighted.disabled.focus, +.datepicker table tr td.selected.highlighted.disabled:focus, +.datepicker table tr td.selected.highlighted.disabled:hover, +.datepicker table tr td.selected.highlighted[disabled].focus, +.datepicker table tr td.selected.highlighted[disabled]:focus, +.datepicker table tr td.selected.highlighted[disabled]:hover, +.datepicker table tr td.selected[disabled].focus, +.datepicker table tr td.selected[disabled]:focus, +.datepicker table tr td.selected[disabled]:hover, +fieldset[disabled] .datepicker table tr td.selected.focus, +fieldset[disabled] .datepicker table tr td.selected.highlighted.focus, +fieldset[disabled] .datepicker table tr td.selected.highlighted:focus, +fieldset[disabled] .datepicker table tr td.selected.highlighted:hover, +fieldset[disabled] .datepicker table tr td.selected:focus, +fieldset[disabled] .datepicker table tr td.selected:hover { + background-color: #777; + border-color: #555; +} +.datepicker table tr td.active, +.datepicker table tr td.active.highlighted { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; + text-shadow: 0 -1px 0 rgb(0 0 0 / 25%); +} +.datepicker table tr td.active.focus, +.datepicker table tr td.active.highlighted.focus, +.datepicker table tr td.active.highlighted:focus, +.datepicker table tr td.active:focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.datepicker table tr td.active.highlighted:hover, +.datepicker table tr td.active:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.datepicker table tr td.active.active, +.datepicker table tr td.active.highlighted.active, +.datepicker table tr td.active.highlighted:active, +.datepicker table tr td.active:active { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.datepicker table tr td.active.active.focus, +.datepicker table tr td.active.active:focus, +.datepicker table tr td.active.active:hover, +.datepicker table tr td.active.highlighted.active.focus, +.datepicker table tr td.active.highlighted.active:focus, +.datepicker table tr td.active.highlighted.active:hover, +.datepicker table tr td.active.highlighted:active.focus, +.datepicker table tr td.active.highlighted:active:focus, +.datepicker table tr td.active.highlighted:active:hover, +.datepicker table tr td.active:active.focus, +.datepicker table tr td.active:active:focus, +.datepicker table tr td.active:active:hover { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.datepicker table tr td.active.disabled.focus, +.datepicker table tr td.active.disabled:focus, +.datepicker table tr td.active.disabled:hover, +.datepicker table tr td.active.highlighted.disabled.focus, +.datepicker table tr td.active.highlighted.disabled:focus, +.datepicker table tr td.active.highlighted.disabled:hover, +.datepicker table tr td.active.highlighted[disabled].focus, +.datepicker table tr td.active.highlighted[disabled]:focus, +.datepicker table tr td.active.highlighted[disabled]:hover, +.datepicker table tr td.active[disabled].focus, +.datepicker table tr td.active[disabled]:focus, +.datepicker table tr td.active[disabled]:hover, +fieldset[disabled] .datepicker table tr td.active.focus, +fieldset[disabled] .datepicker table tr td.active.highlighted.focus, +fieldset[disabled] .datepicker table tr td.active.highlighted:focus, +fieldset[disabled] .datepicker table tr td.active.highlighted:hover, +fieldset[disabled] .datepicker table tr td.active:focus, +fieldset[disabled] .datepicker table tr td.active:hover { + background-color: #337ab7; + border-color: #2e6da4; +} +.datepicker table tr td span { + display: block; + width: 23%; + height: 54px; + line-height: 54px; + float: left; + margin: 1%; + cursor: pointer; + border-radius: 4px; +} +.datepicker table tr td span.focused, +.datepicker table tr td span:hover { + background: #eee; +} +.datepicker table tr td span.disabled, +.datepicker table tr td span.disabled:hover { + background: 0 0; + color: #777; + cursor: default; +} +.datepicker table tr td span.active, +.datepicker table tr td span.active.disabled, +.datepicker table tr td span.active.disabled:hover, +.datepicker table tr td span.active:hover { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; + text-shadow: 0 -1px 0 rgb(0 0 0 / 25%); +} +.datepicker table tr td span.active.disabled.focus, +.datepicker table tr td span.active.disabled:focus, +.datepicker table tr td span.active.disabled:hover.focus, +.datepicker table tr td span.active.disabled:hover:focus, +.datepicker table tr td span.active.focus, +.datepicker table tr td span.active:focus, +.datepicker table tr td span.active:hover.focus, +.datepicker table tr td span.active:hover:focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.datepicker table tr td span.active.disabled:hover, +.datepicker table tr td span.active.disabled:hover:hover, +.datepicker table tr td span.active:hover, +.datepicker table tr td span.active:hover:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.datepicker table tr td span.active.active, +.datepicker table tr td span.active.disabled.active, +.datepicker table tr td span.active.disabled:active, +.datepicker table tr td span.active.disabled:hover.active, +.datepicker table tr td span.active.disabled:hover:active, +.datepicker table tr td span.active:active, +.datepicker table tr td span.active:hover.active, +.datepicker table tr td span.active:hover:active { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.datepicker table tr td span.active.active.focus, +.datepicker table tr td span.active.active:focus, +.datepicker table tr td span.active.active:hover, +.datepicker table tr td span.active.disabled.active.focus, +.datepicker table tr td span.active.disabled.active:focus, +.datepicker table tr td span.active.disabled.active:hover, +.datepicker table tr td span.active.disabled:active.focus, +.datepicker table tr td span.active.disabled:active:focus, +.datepicker table tr td span.active.disabled:active:hover, +.datepicker table tr td span.active.disabled:hover.active.focus, +.datepicker table tr td span.active.disabled:hover.active:focus, +.datepicker table tr td span.active.disabled:hover.active:hover, +.datepicker table tr td span.active.disabled:hover:active.focus, +.datepicker table tr td span.active.disabled:hover:active:focus, +.datepicker table tr td span.active.disabled:hover:active:hover, +.datepicker table tr td span.active:active.focus, +.datepicker table tr td span.active:active:focus, +.datepicker table tr td span.active:active:hover, +.datepicker table tr td span.active:hover.active.focus, +.datepicker table tr td span.active:hover.active:focus, +.datepicker table tr td span.active:hover.active:hover, +.datepicker table tr td span.active:hover:active.focus, +.datepicker table tr td span.active:hover:active:focus, +.datepicker table tr td span.active:hover:active:hover { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.datepicker table tr td span.active.disabled.disabled.focus, +.datepicker table tr td span.active.disabled.disabled:focus, +.datepicker table tr td span.active.disabled.disabled:hover, +.datepicker table tr td span.active.disabled.focus, +.datepicker table tr td span.active.disabled:focus, +.datepicker table tr td span.active.disabled:hover, +.datepicker table tr td span.active.disabled:hover.disabled.focus, +.datepicker table tr td span.active.disabled:hover.disabled:focus, +.datepicker table tr td span.active.disabled:hover.disabled:hover, +.datepicker table tr td span.active.disabled:hover[disabled].focus, +.datepicker table tr td span.active.disabled:hover[disabled]:focus, +.datepicker table tr td span.active.disabled:hover[disabled]:hover, +.datepicker table tr td span.active.disabled[disabled].focus, +.datepicker table tr td span.active.disabled[disabled]:focus, +.datepicker table tr td span.active.disabled[disabled]:hover, +.datepicker table tr td span.active:hover.disabled.focus, +.datepicker table tr td span.active:hover.disabled:focus, +.datepicker table tr td span.active:hover.disabled:hover, +.datepicker table tr td span.active:hover[disabled].focus, +.datepicker table tr td span.active:hover[disabled]:focus, +.datepicker table tr td span.active:hover[disabled]:hover, +.datepicker table tr td span.active[disabled].focus, +.datepicker table tr td span.active[disabled]:focus, +.datepicker table tr td span.active[disabled]:hover, +fieldset[disabled] .datepicker table tr td span.active.disabled.focus, +fieldset[disabled] .datepicker table tr td span.active.disabled:focus, +fieldset[disabled] .datepicker table tr td span.active.disabled:hover, +fieldset[disabled] .datepicker table tr td span.active.disabled:hover.focus, +fieldset[disabled] .datepicker table tr td span.active.disabled:hover:focus, +fieldset[disabled] .datepicker table tr td span.active.disabled:hover:hover, +fieldset[disabled] .datepicker table tr td span.active.focus, +fieldset[disabled] .datepicker table tr td span.active:focus, +fieldset[disabled] .datepicker table tr td span.active:hover, +fieldset[disabled] .datepicker table tr td span.active:hover.focus, +fieldset[disabled] .datepicker table tr td span.active:hover:focus, +fieldset[disabled] .datepicker table tr td span.active:hover:hover { + background-color: #337ab7; + border-color: #2e6da4; +} +.datepicker table tr td span.new, +.datepicker table tr td span.old { + color: #777; +} +.datepicker .datepicker-switch { + width: 145px; +} +.datepicker .datepicker-switch, +.datepicker .next, +.datepicker .prev, +.datepicker tfoot tr th { + cursor: pointer; +} +.datepicker .datepicker-switch:hover, +.datepicker .next:hover, +.datepicker .prev:hover, +.datepicker tfoot tr th:hover { + background: #eee; +} +.datepicker .next.disabled, +.datepicker .prev.disabled { + visibility: hidden; +} +.datepicker .cw { + font-size: 10px; + width: 12px; + padding: 0 2px 0 5px; + vertical-align: middle; +} +.input-group.date .input-group-addon { + cursor: pointer; +} +.input-daterange { + width: 100%; +} +.input-daterange input { + text-align: center; +} +.input-daterange input:first-child { + border-radius: 3px 0 0 3px; +} +.input-daterange input:last-child { + border-radius: 0 3px 3px 0; +} +.input-daterange .input-group-addon { + width: auto; + min-width: 16px; + padding: 4px 5px; + line-height: 1.42857143; + text-shadow: 0 1px 0 #fff; + border-width: 1px 0; + margin-left: -5px; + margin-right: -5px; +} + +/* -------------------------------------------------------------------------- */ +/* DATE RECURRENT */ +/* -------------------------------------------------------------------------- */ +div.form-control.for-selector-is-recurrent { + height: auto; + max-width: 100%; +} + +div.form-control.for-selector-is-recurrent > .input-prepend.input-group { + margin-top: 0.4rem; + margin-bottom: 0.4rem; +} + +.control-group.input-listedatedeb .specific-for-recurrence, +.control-group.input-listedatefin + .specific-for-recurrence + .control-group.input-jour + .specific-for-recurrence { + align-self: flex-start; +} + +.control-group.input-listedatedeb + .specific-for-recurrence + span.add-on.input-group-addon, +.control-group.input-listedatefin + .specific-for-recurrence + span.add-on.input-group-addon, +.control-group.input-jour + .specific-for-recurrence + span.add-on.input-group-addon { + width: auto; + display: flex; +} + +.input-recur-date .specific-for-recurrence { + max-width: 100%; +} + +.input-recur-date.control-group > .controls { + gap: 1rem 1rem; + flex-wrap: wrap; +} + +.input-recur-date.control-group .for-selector-is-recurrent > .input-group { + flex-wrap: wrap; +} + +.input-recur-date.control-group + .for-selector-is-recurrent + > .input-group + select.form-control { + max-width: 80%; + width: auto; +} + +.input-recur-date:not(.ready) .specific-for-recurrence, +.input-recur-date:not(.ready) .event-container-for-datepicker { + display: none !important; +} + +.input-recur-date .event-container-for-datepicker { + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +.input-recur-date .event-container-for-datepicker .limit-date-hint { + max-width: 250px; +} diff --git a/tools/bazar/presentation/styles/inputs/range.css b/tools/bazar/presentation/styles/inputs/range.css new file mode 100644 index 000000000..8f031713b --- /dev/null +++ b/tools/bazar/presentation/styles/inputs/range.css @@ -0,0 +1,114 @@ +.range-wrap { + position: relative; + width: 100%; + display: flex; +} + +.form-group.range .control-label { + margin-bottom: -1em; + position: relative !important; + left: 0 !important; + margin-top: 1em; +} + +.range-wrap output { + background: var(--primary-color); + padding: 5px 10px; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + color: var(--neutral-light-color); + min-width: 3.8em; + text-align: center; + display: flex; + place-content: center center; + align-items: center; +} + +.range-wrap input[type='range'] { + appearance: none; + height: 40px; + border-radius: 0 !important; + border-top-left-radius: 3px !important; + border-bottom-left-radius: 3px !important; + background-image: transparent; + background-repeat: no-repeat; + padding-left: 0; + padding-right: 0; + padding-top: 0; +} + +/* Input Thumb */ +.range-wrap input[type='range']::-webkit-slider-thumb { + appearance: none; + height: 38px; + width: 40px; + border-radius: 50%; + background: var(--primary-color); + cursor: ew-resize; + border: none; + transition: background 0.3s ease-in-out; +} + +.range-wrap input[type='range']::-moz-range-thumb { + appearance: none; + height: 38px; + width: 40px; + border-radius: 50%; + background: var(--primary-color); + cursor: ew-resize; + border: none; + transition: background 0.3s ease-in-out; +} + +.range-wrap input[type='range']::-ms-thumb { + appearance: none; + height: 38px; + width: 40px; + border-radius: 50%; + background: var(--primary-color); + cursor: ew-resize; + border: none; + transition: background 0.3s ease-in-out; +} + +.range-wrap input[type='range']::-webkit-slider-thumb:hover { + background: var(--secondary-color-1); +} + +.range-wrap input[type='range']::-moz-range-thumb:hover { + background: var(--secondary-color-1); +} + +.range-wrap input[type='range']::-ms-thumb:hover { + background: var(--secondary-color-1); +} + +/* Input Track */ +.range-wrap input[type='range']::-webkit-slider-runnable-track { + appearance: none; + border: none; + background: transparent; +} + +.range-wrap input[type='range']::-moz-range-track { + appearance: none; + border: none; + background: transparent; +} + +.range-wrap input[type='range']::-ms-track { + appearance: none; + border: none; + background: transparent; +} + +/* Display tabs when printing */ +@media print { + div[role='tabpanel'] { + display: block !important; + } + + a[role='tab'] { + display: none !important; + } +} diff --git a/tools/bazar/presentation/styles/inputs/textarea.css b/tools/bazar/presentation/styles/inputs/textarea.css new file mode 100644 index 000000000..08701147f --- /dev/null +++ b/tools/bazar/presentation/styles/inputs/textarea.css @@ -0,0 +1,9 @@ +.form-group.wiki-textarea .btn-toolbar { + margin-left: 1em; + margin-right: 1em; +} + +.form-group.wiki-textarea .control-label { + top: 0.6em !important; + margin-top: -0.5em !important; +} diff --git a/tools/bazar/presentation/styles/list-form.css b/tools/bazar/presentation/styles/list-form.css new file mode 100644 index 000000000..5d1f29040 --- /dev/null +++ b/tools/bazar/presentation/styles/list-form.css @@ -0,0 +1,34 @@ +.list-form .nodes-container { + margin-top: 15px; +} +.list-form + .list-node-container:not([data-depth="1"][data-index="0"]) + > .list-node + > input { + border-top: 0; +} +.list-form .list-node .input-id { + width: 9rem; + min-width: 9rem; + max-width: 9rem; +} +.list-form .list-node .btn-expand { + width: 2.5rem; + justify-content: center; +} +.list-form .list-node-children:not(.root) { + margin-left: 2.5rem; +} +.list-form .list-new-node { + margin-bottom: 15px; +} +.list-form .list-new-node input { + border-top: 0; + max-width: 300px; + border-color: var(--neutral-color); +} +.list-form .list-new-node .btn-add-child { + background-color: var(--neutral-color); + width: 2.5rem; + justify-content: center; +} diff --git a/tools/bazar/presentation/templates/map.tpl.html b/tools/bazar/presentation/templates/map.tpl.html index 31fd42dce..eb04d4d58 100644 --- a/tools/bazar/presentation/templates/map.tpl.html +++ b/tools/bazar/presentation/templates/map.tpl.html @@ -7,7 +7,7 @@ $GLOBALS['wiki']->AddCSSFile('styles/vendor/leaflet/leaflet.css'); $GLOBALS['wiki']->AddCSSFile('tools/bazar/presentation/styles/bazarcarto.css'); - $GLOBALS['wiki']->AddJavascriptFile('tools/bazar/libs/bazar.js'); + $GLOBALS['wiki']->AddJavascriptFile('tools/bazar/presentation/javascripts/bazar.js'); $GLOBALS['wiki']->AddJavascriptFile('javascripts/vendor/leaflet/leaflet.min.js'); $GLOBALS['wiki']->AddJavascriptFile('javascripts/vendor/leaflet-providers/leaflet-providers.js'); ?> diff --git a/tools/bazar/presentation/templates/widget.tpl.html b/tools/bazar/presentation/templates/widget.tpl.html index 6d5aed489..dd5cdc676 100755 --- a/tools/bazar/presentation/templates/widget.tpl.html +++ b/tools/bazar/presentation/templates/widget.tpl.html @@ -108,11 +108,11 @@
- +
- +
@@ -177,48 +177,50 @@
-
-
Ajouter des facettes
-
-
    - - $value) : $i++ ?> -
  • -
    -
  • + +
+ +
- -
-
-
-
- - +
+
+
+
+ + +
-
+
diff --git a/tools/bazar/services/BazarListService.php b/tools/bazar/services/BazarListService.php index b53666534..6c97693ba 100644 --- a/tools/bazar/services/BazarListService.php +++ b/tools/bazar/services/BazarListService.php @@ -4,7 +4,7 @@ use Attach; use YesWiki\Bazar\Controller\EntryController; -use YesWiki\Bazar\Field\BazarField; +use YesWiki\Bazar\Field\EnumField; use YesWiki\Wiki; class BazarListService @@ -126,165 +126,168 @@ public function getEntries($options, $forms = null): array return $entries; } - public function formatFilters($options, $entries, $forms): array + // Use bazarlist options like groups, titles, groupicons, groupsexpanded + // To create a filters array to be used by the view + // Note for [old-non-dynamic-bazarlist] For old bazarlist, most of the calculation happens on the backend + // But with the new dynamic bazalist, everything is done on the front + public function getFilters($options, $entries, $forms): array { - if (empty($options['groups'])) { - return []; + // add default options + $options = array_merge([ + 'groups' => [], + 'dynamic' => true, + 'groupsexpanded' => false, + ], $options); + + $formIdsUsed = array_unique(array_column($entries, 'id_typeannonce')); + $formsUsed = array_map(function ($formId) use ($forms) { return $forms[$formId]; }, $formIdsUsed); + $allFields = array_merge(...array_column($formsUsed, 'prepared')); + + $propNames = $options['groups']; + // Special value groups=all use all available Enum fields + if (count($propNames) == 1 && $propNames[0] == 'all') { + $enumFields = array_filter($allFields, function ($field) { + return $field instanceof EnumField; + }); + $propNames = array_map(function ($field) { return $field->getPropertyName(); }, $enumFields); } - // Scanne tous les champs qui pourraient faire des filtres pour les facettes - $facettables = $this->formManager - ->scanAllFacettable($entries, $options['groups']); - - if (count($facettables) == 0) { - return []; - } - - if (!$forms) { - $forms = $this->getForms($options); - } $filters = []; - // Récupere les facettes cochees - $tabfacette = []; - if (isset($_GET['facette']) && !empty($_GET['facette'])) { - $tab = explode('|', $_GET['facette']); - //découpe la requete autour des | - foreach ($tab as $req) { - $tabdecoup = explode('=', $req, 2); - if (count($tabdecoup) > 1) { - $tabfacette[$tabdecoup[0]] = explode(',', trim($tabdecoup[1])); - } - } - } - foreach ($facettables as $id => $facettable) { - $list = []; - // Formatte la liste des resultats en fonction de la source - if (in_array($facettable['type'], ['liste', 'fiche'])) { - $field = $this->findFieldByName($forms, $facettable['source']); - if (!($field instanceof BazarField)) { - if ($this->debug) { - trigger_error('Waiting field instanceof BazarField from findFieldByName, ' . - ( - (is_null($field)) ? 'null' : ( - (gettype($field) == 'object') ? get_class($field) : gettype($field) - ) - ) . ' returned'); - } - } elseif ($facettable['type'] == 'liste') { - $list['titre_liste'] = $field->getLabel(); - $list['label'] = $field->getOptions(); - } elseif ($facettable['type'] == 'fiche') { - $formId = $field->getLinkedObjectName(); - $form = $forms[$formId]; - $list['titre_liste'] = $form['bn_label_nature']; - $list['label'] = []; - foreach ($facettable as $idfiche => $nb) { - if ($idfiche != 'source' && $idfiche != 'type') { - $f = $this->entryManager->getOne($idfiche); - if (!empty($f['bf_titre'])) { - $list['label'][$idfiche] = $f['bf_titre']; - } - } - } + foreach ($propNames as $index => $propName) { + // Create a filter object to be returned to the view + $filter = [ + 'propName' => $propName, + 'title' => '', + 'icon' => '', + 'nodes' => [], + 'collapsed' => true, + ]; + + // Check if an existing Form Field existing by this propName + foreach ($allFields as $aField) { + if ($aField->getPropertyName() == $propName) { + $field = $aField; + break; } - } elseif ($facettable['type'] == 'form') { - if ($facettable['source'] == 'id_typeannonce') { - $list['titre_liste'] = _t('BAZ_TYPE_FICHE'); - foreach ($facettable as $idf => $nb) { - if ($idf != 'source' && $idf != 'type') { - $list['label'][$idf] = $forms[$idf]['bn_label_nature'] ?? $idf; - } - } - } elseif ($facettable['source'] == 'owner') { - $list['titre_liste'] = _t('BAZ_CREATOR'); - foreach ($facettable as $idf => $nb) { - if ($idf != 'source' && $idf != 'type') { - $list['label'][$idf] = $idf; - } + } + // Depending on the propName, get the list of filter nodes + if (!empty($field) && $field instanceof EnumField) { + // ENUM FIELD + $filter['title'] = $field->getLabel(); + + if (!empty($field->getOptionsTree()) && $options['dynamic'] == true) { + // OptionsTree only supported by bazarlist dynamic + foreach ($field->getOptionsTree() as $node) { + $filter['nodes'][] = $this->recursivelyCreateNode($node); } } else { - $list['titre_liste'] = $id; - foreach ($facettable as $idf => $nb) { - if ($idf != 'source' && $idf != 'type') { - $list['label'][$idf] = $idf; - } + foreach ($field->getOptions() as $value => $label) { + $filter['nodes'][] = $this->createFilterNode($value, $label); } } + } elseif ($propName == 'id_typeannonce') { + // SPECIAL PROPNAME id_typeannonce + $filter['title'] = _t('BAZ_TYPE_FICHE'); + foreach ($formsUsed as $form) { + $filter['nodes'][] = $this->createFilterNode($form['bn_id_nature'], $form['bn_label_nature']); + } + usort($filter['nodes'], function ($a, $b) { return strcmp($a['label'], $b['label']); }); + } else { + // OTHER PROPNAME (for example a field that is not an Enum) + $filter['title'] = $propName == 'owner' ? _t('BAZ_CREATOR') : $propName; + // We collect all values + $uniqValues = array_unique(array_column($entries, $propName)); + sort($uniqValues); + foreach ($uniqValues as $value) { + $filter['nodes'][] = $this->createFilterNode($value, $value); + } } - $idkey = htmlspecialchars($id); - - $i = array_key_first(array_filter($options['groups'], function ($value) use ($idkey) { - return $value == $idkey; - })); - - $filters[$idkey]['icon'] = !empty($options['groupicons'][$i]) ? - ' ' : ''; - - $filters[$idkey]['title'] = !empty($options['titles'][$i]) ? - $options['titles'][$i] : $list['titre_liste']; + // Filter Icon + if (!empty($options['groupicons'][$index])) { + $filter['icon'] = ' '; + } + // Custom title + if (!empty($options['titles'][$index])) { + $filter['title'] = $options['titles'][$index]; + } + // Initial Collapsed state + $filter['collapsed'] = ($index != 0) && !$options['groupsexpanded']; + + // [old-non-dynamic-bazarlist] For old bazarlist, most of the calculation happens on the backend + if ($options['dynamic'] == false) { + $checkedValues = $this->parseCheckedFiltersInURLForNonDynamic(); + // Calculate the count for each filterNode + $entriesValues = array_column($entries, $propName); + // convert string values to array + $entriesValues = array_map(function ($val) { return explode(',', $val); }, $entriesValues); + // flatten the array + $entriesValues = array_merge(...$entriesValues); + $countedValues = array_count_values($entriesValues); + $adjustedNodes = []; + foreach ($filter['nodes'] as $rootNode) { + $adjustedNodes[] = $this->recursivelyInitValuesForNonDynamic($rootNode, $propName, $countedValues, $checkedValues); + } + $filter['nodes'] = $adjustedNodes; + } - $filters[$idkey]['collapsed'] = ($i != 0) && !$options['groupsexpanded']; + $filters[] = $filter; + } - $filters[$idkey]['index'] = $i; + return $filters; + } - // sort facette labels - natcasesort($list['label']); - foreach ($list['label'] as $listkey => $label) { - if (!empty($facettables[$id][$listkey])) { - $filters[$idkey]['list'][] = [ - 'id' => $idkey . $listkey, - 'name' => $idkey, - 'value' => htmlspecialchars($listkey), - 'label' => $label, - 'nb' => $facettables[$id][$listkey], - 'checked' => (isset($tabfacette[$idkey]) and in_array($listkey, $tabfacette[$idkey])) ? ' checked' : '', - ]; - } - } + // [old-non-dynamic-bazarlist] filters state in stored in URL + // ?Page&facette=field1=3,4|field2=web + // => ['field1' => ['3', '4'], 'field2' => ['web']] + private function parseCheckedFiltersInURLForNonDynamic() + { + if (empty($_GET['facette'])) { + return []; + } + $result = []; + foreach (explode('|', $_GET['facette']) as $field) { + list($key, $values) = explode('=', $field); + $result[$key] = explode(',', trim($values)); } - // reorder $filters - uasort($filters, function ($a, $b) { - if (isset($a['index']) && isset($b['index'])) { - if ($a['index'] == $b['index']) { - return 0; - } else { - return ($a['index'] < $b['index']) ? -1 : 1; - } - } elseif (isset($a['index'])) { - return 1; - } elseif (isset($b['index'])) { - return -1; - } else { - return 0; - } - }); + return $result; + } - foreach ($filters as $id => $filter) { - if (isset($filter['index'])) { - unset($filter['index']); - } + private function createFilterNode($value, $label) + { + return [ + 'value' => htmlspecialchars($value), + 'label' => $label, + 'children' => [], + ]; + } + + private function recursivelyCreateNode($node) + { + $result = $this->createFilterNode($node['id'], $node['label']); + foreach ($node['children'] as $childNode) { + $result['children'][] = $this->recursivelyCreateNode($childNode); } - return $filters; + return $result; } - /* - * Scan all forms and return the first field matching the given ID - */ - private function findFieldByName($forms, $name) + private function recursivelyInitValuesForNonDynamic($node, $propName, $countedValues, $checkedValues) { - foreach ($forms as $form) { - foreach ($form['prepared'] as $field) { - if ($field instanceof BazarField) { - if ($field->getPropertyName() === $name) { - return $field; - } - } - } + $result = array_merge($node, [ + 'id' => $propName . $node['value'], + 'name' => $propName, + 'count' => $countedValues[$node['value']] ?? 0, + 'checked' => isset($checkedValues[$propName]) && in_array($node['value'], $checkedValues[$propName]) ? ' checked' : '', + ]); + + foreach ($node['children'] as &$childNode) { + $result['children'][] = $this->recursivelyInitValuesForNonDynamic($childNode, $propName, $countedValues, $checkedValues); } + + return $result; } private function buildFieldSorter($ordre, $champ): callable diff --git a/tools/bazar/services/FormManager.php b/tools/bazar/services/FormManager.php index b116603d0..e5dcf30a6 100644 --- a/tools/bazar/services/FormManager.php +++ b/tools/bazar/services/FormManager.php @@ -5,7 +5,6 @@ use Attach; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use YesWiki\Bazar\Field\BazarField; -use YesWiki\Bazar\Field\EnumField; use YesWiki\Core\Service\DbService; use YesWiki\Security\Controller\SecurityController; use YesWiki\Wiki; @@ -426,105 +425,6 @@ public function prepareData($form) return $prepared; } - public function scanAllFacettable($entries, $groups = ['all'], $onlyLists = false) - { - $facetteValue = $fields = []; - - foreach ($entries as $entry) { - $form = $this->getOne($entry['id_typeannonce']); - - // on filtre pour n'avoir que les liste, checkbox, listefiche ou checkboxfiche - if (!isset($fields[$entry['id_typeannonce']])) { - $fields[$entry['id_typeannonce']] = (empty($form['prepared'])) - ? [] - : $this->filterFieldsByPropertyName($form['prepared'], $groups); - } - - foreach ($entry as $key => $value) { - $facetteasked = (isset($groups[0]) && $groups[0] == 'all') || in_array($key, $groups); - - if (!empty($value) and is_array($fields[$entry['id_typeannonce']]) && $facetteasked) { - if (in_array($key, ['id_typeannonce', 'owner'])) { - $fieldPropName = $key; - $field = null; - } else { - $filteredFields = $this->filterFieldsByPropertyName($fields[$entry['id_typeannonce']], [$key]); - $field = array_pop($filteredFields); - - $fieldPropName = null; - if ($field instanceof BazarField) { - $fieldPropName = $field->getPropertyName(); - $fieldType = $field->getType(); - } - } - - if ($fieldPropName) { - if ($field instanceof EnumField) { - $facetteValue[$fieldPropName]['type'] = ($field->isEnumEntryField()) ? 'fiche' : 'liste'; - - $facetteValue[$fieldPropName]['source'] = $key; - - $tabval = explode(',', $value); - foreach ($tabval as $tval) { - if (isset($facetteValue[$fieldPropName][$tval])) { - $facetteValue[$fieldPropName][$tval]++; - } else { - $facetteValue[$fieldPropName][$tval] = 1; - } - } - } elseif (!$onlyLists) { - // texte - $facetteValue[$key]['type'] = 'form'; - $facetteValue[$key]['source'] = $key; - if (isset($facetteValue[$key][$value])) { - $facetteValue[$key][$value]++; - } else { - $facetteValue[$key][$value] = 1; - } - } - } - } - } - } - - // remove `id_typeannonce` if only one form - if (isset($facetteValue['id_typeannonce'])) { - $nbForms = count( - array_filter( - array_keys($facetteValue['id_typeannonce']), - function ($key) { - return !in_array($key, ['type', 'source']); - } - ) - ); - if ($nbForms < 2) { - unset($facetteValue['id_typeannonce']); - } - } - - return $facetteValue; - } - - /* - * Filter an array of fields by their potential entry ID - */ - private function filterFieldsByPropertyName(array $fields, array $id) - { - if (count($id) === 1 && $id[0] === 'all') { - return array_filter($fields, function ($field) { - if ($field instanceof EnumField) { - return true; - } - }); - } else { - return array_filter($fields, function ($field) use ($id) { - if ($field instanceof BazarField) { - return $id[0] === 'all' || in_array($field->getPropertyName(), $id); - } - }); - } - } - /** * put a form form External Wiki in cache. */ diff --git a/tools/bazar/services/ListManager.php b/tools/bazar/services/ListManager.php index da74fc908..091a29597 100644 --- a/tools/bazar/services/ListManager.php +++ b/tools/bazar/services/ListManager.php @@ -57,17 +57,29 @@ public function getOne($id): ?array $page = $this->pageManager->getOne($id); $json = json_decode($page['body'], true); + $json = $this->convertDataStructure($json); + $json['id'] = $id; + $this->cachedLists[$id] = $json; - if (YW_CHARSET !== 'UTF-8') { - $this->cachedLists[$id]['titre_liste'] = mb_convert_encoding($json['titre_liste'], 'ISO-8859-1', 'UTF-8'); - $this->cachedLists[$id]['label'] = array_map(function ($value) { - return mb_convert_encoding($value, 'ISO-8859-1', 'UTF-8'); - }, $json['label']); - } else { - $this->cachedLists[$id] = $json; + return $json; + } + + // The structure of List object has been changed in 2024 + // Convert old List { titre_liste: "My List", label: { id1: "first Key", id2: "second id" } } + // to { title: "My List", values: [{ id: "id1", label: "first id"}, { id: "id2", label: "second id"}]} + // We still convert the strucure on the fly in case the migration went wrong + public function convertDataStructure($json) + { + if (isset($json['titre_liste'])) { + $newJson = ['title' => $json['titre_liste'], 'nodes' => []]; + foreach ($json['label'] as $id => $label) { + $newJson['nodes'][] = ['id' => $id, 'label' => $label]; + } + + return $newJson; } - return $this->cachedLists[$id]; + return $json; } public function getAll(): array @@ -82,25 +94,16 @@ public function getAll(): array return $result; } - public function create($title, $values) + public function create($title, $nodes) { if ($this->securityController->isWikiHibernated()) { throw new \Exception(_t('WIKI_IN_HIBERNATION')); } - $id = genere_nom_wiki('Liste ' . $title); - - $values = $this->sanitizeHMTL($values); - - if (YW_CHARSET !== 'UTF-8') { - $values = array_map(function ($value) { - return mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1'); - }, $values); - $title = mb_convert_encoding($title, 'UTF-8', 'ISO-8859-1'); - } + $id = genere_nom_wiki('List' . $title); $this->pageManager->save($id, json_encode([ - 'titre_liste' => $title, - 'label' => $values, + 'title' => $title, + 'nodes' => $this->sanitizeHMTL($nodes), ])); $this->tripleStore->create($id, TripleStore::TYPE_URI, self::TRIPLES_LIST_ID, '', ''); @@ -108,23 +111,15 @@ public function create($title, $values) return $id; } - public function update($id, $title, $values) + public function update($id, $title, $nodes) { if ($this->securityController->isWikiHibernated()) { throw new \Exception(_t('WIKI_IN_HIBERNATION')); } - $values = $this->sanitizeHMTL($values); - if (YW_CHARSET !== 'UTF-8') { - $values = array_map(function ($value) { - return mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1'); - }, $values); - $title = mb_convert_encoding($title, 'UTF-8', 'ISO-8859-1'); - } - $this->pageManager->save($id, json_encode([ - 'titre_liste' => $title, - 'label' => $values, + 'title' => $title, + 'nodes' => $this->sanitizeHMTL($nodes), ])); } @@ -146,10 +141,13 @@ public function delete($id) $this->tripleStore->delete($id, TripleStore::TYPE_URI, null, '', ''); } - private function sanitizeHMTL(array $values) + private function sanitizeHMTL(array $nodes) { - return array_map(function ($value) { - return $this->htmlPurifierService->cleanHTML($value); - }, $values); + return array_map(function ($node) { + $node['label'] = $this->htmlPurifierService->cleanHTML($node['label']); + $node['children'] = $this->sanitizeHMTL($node['children']); + + return $node; + }, $nodes); } } diff --git a/tools/bazar/templates/bazar-import-export-form.twig b/tools/bazar/templates/bazar-import-export-form.twig index 11cfcd7e2..abe30bced 100644 --- a/tools/bazar/templates/bazar-import-export-form.twig +++ b/tools/bazar/templates/bazar-import-export-form.twig @@ -1,4 +1,4 @@ -{{ include_javascript('tools/bazar/libs/bazar.js') }} +{{ include_javascript('tools/bazar/presentation/javascripts/bazar.js') }}
{% block formContent %} diff --git a/tools/bazar/templates/entries/form.twig b/tools/bazar/templates/entries/form.twig index f9367f9d9..d641b9fdc 100644 --- a/tools/bazar/templates/entries/form.twig +++ b/tools/bazar/templates/entries/form.twig @@ -1,4 +1,4 @@ -{{ include_javascript('tools/bazar/libs/bazar.js') }} +{{ include_javascript('tools/bazar/presentation/javascripts/bazar.js') }} {{ error|raw }}

{{ _t('BAZ_TITRE_SAISIE_FICHE') }} {{ form.bn_label_nature }}

diff --git a/tools/bazar/templates/entries/index-dynamic.twig b/tools/bazar/templates/entries/index-dynamic.twig index 7c4d6e72f..906e89b55 100644 --- a/tools/bazar/templates/entries/index-dynamic.twig +++ b/tools/bazar/templates/entries/index-dynamic.twig @@ -1,15 +1,15 @@ {{ include_javascript('javascripts/vendor/vue/vue.js') }} {% block assets %} -{% if params.showmapinlistview %} - {{ include_css('styles/vendor/leaflet/leaflet.css') }} - {{ include_javascript('javascripts/vendor/leaflet/leaflet.min.js') }} - {{ include_javascript('javascripts/vendor/leaflet-providers/leaflet-providers.js') }} -{% endif %} + {% if params.showmapinlistview %} + {{ include_css('styles/vendor/leaflet/leaflet.css') }} + {{ include_javascript('javascripts/vendor/leaflet/leaflet.min.js') }} + {{ include_javascript('javascripts/vendor/leaflet-providers/leaflet-providers.js') }} + {% endif %} {% endblock %} - -{{ include_javascript('tools/bazar/presentation/javascripts/bazar-list-dynamic.js', false, true) }} -{{ include_css('tools/bazar/presentation/styles/bazar-list-dynamic.css') }} + +{{ include_javascript('tools/bazar/presentation/javascripts/entries-index-dynamic.js', false, true) }} +{{ include_css('tools/bazar/presentation/styles/entries/index-dynamic.css') }} {# Extra Fields to retrieve from API call. By default we retrieve the one specified in the displayfields config #} {% if necessary_fields is defined %} diff --git a/tools/bazar/templates/entries/index-dynamic/_filters.twig b/tools/bazar/templates/entries/index-dynamic/_filters.twig index 1628f0c73..de828bd1a 100644 --- a/tools/bazar/templates/entries/index-dynamic/_filters.twig +++ b/tools/bazar/templates/entries/index-dynamic/_filters.twig @@ -25,17 +25,12 @@
diff --git a/tools/bazar/templates/entries/index/_filters.twig b/tools/bazar/templates/entries/index/_filters.twig index 4b9db7ed3..30f75a0c9 100644 --- a/tools/bazar/templates/entries/index/_filters.twig +++ b/tools/bazar/templates/entries/index/_filters.twig @@ -35,15 +35,18 @@ {{ filter.title }}
- {% for filterValue in filter.list %} -
- -
+ {% for filterNode in filter.nodes %} + {% if filterNode.count > 0 %} +
+ +
+ {% endif %} {% endfor %}
diff --git a/tools/bazar/templates/fields/checkbox-tree.twig b/tools/bazar/templates/fields/checkbox-tree.twig new file mode 100644 index 000000000..a693b702a --- /dev/null +++ b/tools/bazar/templates/fields/checkbox-tree.twig @@ -0,0 +1,32 @@ +{% extends "@bazar/layouts/field.twig" %} + +{% block value_container %} + + {{ _self.renderNodes(treeValues) }} + +{% endblock %} + +{% macro renderNodes(nodes) %} + +{# Add some classes "leaf", "only-leafs", "one-child" to make customization with CSS easy #} +{% set allChildrenEmpty = true %} +{% for node in nodes %} + {% if node.children is not empty %} + {% set allChildrenEmpty = false %} + {% endif %} +{% endfor %} +
    + {% for node in nodes %} +
  • + {{ node.label|raw }} + {% if node.children|length > 0 %} + {{ _self.renderNodes(node.children) }} + {% endif %} +
  • + {% endfor %} +
+{% endmacro %} + + + + diff --git a/tools/bazar/templates/fields/map.twig b/tools/bazar/templates/fields/map.twig index eca5f71b6..eeb26bc97 100644 --- a/tools/bazar/templates/fields/map.twig +++ b/tools/bazar/templates/fields/map.twig @@ -1,7 +1,7 @@ {{ include_css('styles/vendor/leaflet/leaflet.css') }} {{ include_javascript('javascripts/vendor/leaflet/leaflet.min.js') }} {{ include_javascript('javascripts/vendor/leaflet-providers/leaflet-providers.js') }} -{{ include_javascript('tools/bazar/presentation/javascripts/map-field-map-entry.js', false, true) }} +{{ include_javascript('tools/bazar/presentation/javascripts/fields/map-field-map-entry.js', false, true) }}
diff --git a/tools/bazar/templates/inputs/checkbox-tree.twig b/tools/bazar/templates/inputs/checkbox-tree.twig new file mode 100644 index 000000000..0cca72876 --- /dev/null +++ b/tools/bazar/templates/inputs/checkbox-tree.twig @@ -0,0 +1,59 @@ +{% extends "@bazar/layouts/input.twig" %} + +{% block input %} + {% if displaySelectAllLimit and data|length > displaySelectAllLimit %} + {{ _self.checkAll() }} + {% endif %} + {% for node in data %} + {{ _self.node(node, field, values, displaySelectAllLimit) }} + {% endfor %} + + {{ include_css('tools/bazar/presentation/styles/inputs/checkbox-tree.css') }} + {{ include_javascript('tools/bazar/presentation/javascripts/inputs/checkbox-tree.js') }} +{% endblock %} + +{% macro node(node, field, values, displaySelectAllLimit) %} +{% set checked = node.id in values %} +
+ {% set name = field.propertyName ~ '[' ~ node.id ~ ']' %} +
+ +
+
+
+ {% if displaySelectAllLimit and node.children|length > displaySelectAllLimit %} + {{ _self.checkAll() }} + {% endif %} + {% for childNode in node.children %} + {{ _self.node(childNode, field, values, displaySelectAllLimit) }} + {% endfor %} +
+
+{% endmacro %} + +{% macro checkAll() %} +
+ +
+{% endmacro %} + + + + + diff --git a/tools/bazar/templates/inputs/checkbox.twig b/tools/bazar/templates/inputs/checkbox.twig index 9e7cd7804..fdb01fb89 100644 --- a/tools/bazar/templates/inputs/checkbox.twig +++ b/tools/bazar/templates/inputs/checkbox.twig @@ -1,31 +1,39 @@ {% extends "@bazar/layouts/input.twig" %} {% block input %} - {% set display_filter = (displayFilterLimit and options|length > displayFilterLimit) %} - {% if display_filter %} - - {% endif %} - {% if displaySelectAllLimit and options|length > displaySelectAllLimit %} - - {% endif %} - {% if display_filter %} -
+ {% set displayFilter = displayFilterLimit and options|length > displayFilterLimit %} + {% set displayCheckAll = displaySelectAllLimit and options|length > displaySelectAllLimit %} + + {% if displayFilter or displayCheckAll %} +
+ {% if displayCheckAll %} + + {% endif %} + {% if displayFilter %} + + {% endif %} +
{% endif %} + {% block list_options %} -
- {% for key, option in options %} -
- {% block item_option %} - - {% endblock %} -
- {% endfor %} -
+
+ {% for key, option in options %} +
+ {% block item_option %} + + {% endblock %} +
+ {% endfor %} +
{% endblock %} - + {% endblock %} diff --git a/tools/bazar/templates/inputs/checkbox_drag_and_drop.twig b/tools/bazar/templates/inputs/checkbox_drag_and_drop.twig index d987537d2..23cb8b920 100644 --- a/tools/bazar/templates/inputs/checkbox_drag_and_drop.twig +++ b/tools/bazar/templates/inputs/checkbox_drag_and_drop.twig @@ -1,7 +1,7 @@ {% extends "@bazar/layouts/input.twig" %} {% block input %} -{{ include_css('tools/bazar/presentation/styles/checkbox-drag-and-drop.css') }} +{{ include_css('tools/bazar/presentation/styles/inputs/checkbox-drag-and-drop.css') }}

{{ _t('BAZ_DRAG_n_DROP_CHECKBOX_AVAILABLE_ITEM') }}{{ _t('BAZ_DRAG_n_DROP_CHECKBOX_ITEM_INFO')  ~

@@ -77,5 +77,5 @@
{{ include_javascript('javascripts/vendor/jquery-ui-sortable/jquery-ui.min.js') }} {{ include_javascript('tools/bazar/libs/vendor/jquery.fastLiveFilter.js') }} -{{ include_javascript('tools/bazar/presentation/javascripts/checkbox-drag-and-drop.js') }} +{{ include_javascript('tools/bazar/presentation/javascripts/inputs/checkbox-drag-and-drop.js') }} {% endblock %} diff --git a/tools/bazar/templates/inputs/checkbox_list.twig b/tools/bazar/templates/inputs/checkbox_list.twig index 65161e081..3340c641a 100644 --- a/tools/bazar/templates/inputs/checkbox_list.twig +++ b/tools/bazar/templates/inputs/checkbox_list.twig @@ -2,11 +2,10 @@ {% block list_options %}
    - {% for key, option in options %} -
    - {{ block('item_option') }} -
    - {% endfor %} -
+ {% for key, option in options %} +
+ {{ block('item_option') }} +
+ {% endfor %} + {% endblock %} - diff --git a/tools/bazar/templates/inputs/checkbox_tags.twig b/tools/bazar/templates/inputs/checkbox_tags.twig index 1215ff875..ae3c27a62 100644 --- a/tools/bazar/templates/inputs/checkbox_tags.twig +++ b/tools/bazar/templates/inputs/checkbox_tags.twig @@ -2,7 +2,7 @@ {% block input %} {{ include_javascript('tools/tags/libs/vendor/bootstrap-tagsinput.min.js') }} - {{ include_javascript('tools/bazar/presentation/javascripts/bazar-tagsinput.js') }} + {{ include_javascript('tools/bazar/presentation/javascripts/inputs/checkbox-tags.js') }} \ No newline at end of file diff --git a/tools/bazar/templates/inputs/date.twig b/tools/bazar/templates/inputs/date.twig index e9fd994a3..0fd5f1954 100644 --- a/tools/bazar/templates/inputs/date.twig +++ b/tools/bazar/templates/inputs/date.twig @@ -1,6 +1,8 @@ {% embed "@bazar/layouts/input.twig" %} {% block input %} + {{ include_css('tools/bazar/presentation/styles/inputs/date.css') }} {{ include_javascript('tools/bazar/libs/vendor/bootstrap-datepicker.js') }} +
diff --git a/tools/bazar/templates/inputs/map.twig b/tools/bazar/templates/inputs/map.twig index e1543fe8b..6ae0a5558 100644 --- a/tools/bazar/templates/inputs/map.twig +++ b/tools/bazar/templates/inputs/map.twig @@ -5,11 +5,11 @@ town: "{{ field.autocomplete|split(',')[1:1]|first }}" } - {{ include_javascript('tools/bazar/presentation/javascripts/map-field-autocomplete.js') }} + {{ include_javascript('tools/bazar/presentation/javascripts/inputs/map-autocomplete.js') }} {% endif %} {{ include_css('styles/vendor/leaflet/leaflet.css') }} -{{ include_javascript('tools/bazar/presentation/javascripts/geolocationHelper.js') }} +{{ include_javascript('tools/bazar/presentation/javascripts/inputs/map-geolocation-helper.js') }} {{ include_javascript('javascripts/vendor/leaflet/leaflet.min.js') }} {{ include_javascript('javascripts/vendor/leaflet-providers/leaflet-providers.js') }} @@ -17,7 +17,7 @@ var mapFieldData = {{ mapFieldData|json_encode|raw }} -{{ include_javascript('tools/bazar/presentation/javascripts/map-field-leaflet.js') }} +{{ include_javascript('tools/bazar/presentation/javascripts/inputs/map-leaflet.js') }}
diff --git a/tools/bazar/templates/inputs/radio.twig b/tools/bazar/templates/inputs/radio.twig index c184fb881..b8f838ebd 100644 --- a/tools/bazar/templates/inputs/radio.twig +++ b/tools/bazar/templates/inputs/radio.twig @@ -1,19 +1,22 @@ {% extends "@bazar/layouts/input.twig" %} {% block input %} - {% set display_filter = (displayFilterLimit and options|length > displayFilterLimit) %} - {% if display_filter %} - -
+ {% set displayFilter = (displayFilterLimit and options|length > displayFilterLimit) %} + {% if displayFilter %} + +
{% endif %}
+ {% if displayFilter %}style="overflow:auto;max-height:{{ displayFilterLimit * 30 }}px;"{% endif %}> {% for key, option in options %} {% if value == key %} {% block radioInput %}
diff --git a/tools/bazar/templates/inputs/radio_tags.twig b/tools/bazar/templates/inputs/radio_tags.twig index 33d291268..18d75eae1 100644 --- a/tools/bazar/templates/inputs/radio_tags.twig +++ b/tools/bazar/templates/inputs/radio_tags.twig @@ -1,8 +1,8 @@ {% extends "@bazar/layouts/input.twig" %} {% block input %} - {{ include_javascript('tools/tags/libs/vendor/bootstrap-tagsinput.min.js') }} - {{ include_javascript('tools/bazar/presentation/javascripts/bazar-tagsinput.js') }} + {{ include_javascript('tools/tags/libs/vendor/bootstrap-tagsinput.min.js') }} + {{ include_javascript('tools/bazar/presentation/javascripts/inputs/checkbox-tags.js') }} {% if field.isDistantJson %}
{% endif %} diff --git a/tools/bazar/templates/inputs/range.twig b/tools/bazar/templates/inputs/range.twig index b2d960b87..e7b6a09fa 100644 --- a/tools/bazar/templates/inputs/range.twig +++ b/tools/bazar/templates/inputs/range.twig @@ -1,6 +1,8 @@ {% extends "@bazar/layouts/input.twig" %} {% block input %} + {{ include_css('tools/bazar/presentation/styles/inputs/range.css') }} +
{% set defaultValue = ((((field.size != "0" and field.size is empty) ? 0 : field.size)|number_format + ((field.maxChars != "0" and field.maxChars is empty) ? 100 : field.maxChars)|number_format)/2)|round(0,'ceil') %} diff --git a/tools/bazar/templates/inputs/textarea.twig b/tools/bazar/templates/inputs/textarea.twig index b486a408c..bafb62827 100644 --- a/tools/bazar/templates/inputs/textarea.twig +++ b/tools/bazar/templates/inputs/textarea.twig @@ -14,6 +14,8 @@ {% endblock %} {% block input %} + {{ include_css('tools/bazar/presentation/styles/inputs/textarea.css') }} +
{% if field.syntax == 'wiki-textarea' %} {{ renderAction('aceditor', { name: field.name, value: value, rows: field.numRows, tempTag: tempTag })|raw }} diff --git a/tools/bazar/templates/inputs/user.twig b/tools/bazar/templates/inputs/user.twig index 622369964..c1670074b 100644 --- a/tools/bazar/templates/inputs/user.twig +++ b/tools/bazar/templates/inputs/user.twig @@ -63,4 +63,4 @@
{% endif %} -{{ include_javascript('tools/bazar/presentation/javascripts/user-field-update-email.js') }} +{{ include_javascript('tools/bazar/presentation/javascripts/inputs/user-field-update-email.js') }} \ No newline at end of file diff --git a/tools/bazar/templates/lists/list_form.twig b/tools/bazar/templates/lists/list_form.twig index 79190476f..3443349cc 100755 --- a/tools/bazar/templates/lists/list_form.twig +++ b/tools/bazar/templates/lists/list_form.twig @@ -1,138 +1,39 @@ -{{ include_javascript('javascripts/vendor/jquery-ui-sortable/jquery-ui.min.js') }} -{{ include_javascript('tools/bazar/libs/bazar.edit_lists.js') }} +{{ include_javascript('javascripts/vendor/vue/vue.js') }} - -
- -
- -
-
-
- -
- -
{{ _t('BAZ_VALEURS_LISTE_HINT') }}
-
+{{ include_javascript('javascripts/vendor/sortablejs/sortable.js') }} +{{ include_javascript('javascripts/vendor/vuedraggable/vuedraggable.js') }} +{{ include_javascript('tools/bazar/presentation/javascripts/list-form.js', false, true) }} +{{ include_css('tools/bazar/presentation/styles/list-form.css') }} - -
-
-
- - - {{ _t('BAZ_ANNULER') }} - -
-
- +
+
+ +
+ +
+
- -
-
  • - - - - - - - - -
  • + {# List Nodes #} + +
    + +
    + {# Hidden JSON Nodes #} + + + {# Form Actions #} +
    +
    + + + {{ _t('BAZ_ANNULER') }} + +
    +
    diff --git a/tools/bazar/templates/lists/list_table.twig b/tools/bazar/templates/lists/list_table.twig index bffe39626..c04e03d3a 100644 --- a/tools/bazar/templates/lists/list_table.twig +++ b/tools/bazar/templates/lists/list_table.twig @@ -1,144 +1,112 @@ -{{ include_javascript('tools/bazar/libs/bazar.edit_lists.js') }} +{{ include_javascript('javascripts/vendor/datatables-full/jquery.dataTables.min.js') }} +{{ include_css('styles/vendor/datatables-full/dataTables.bootstrap.min.css') }} +{{ include_javascript('tools/bazar/presentation/javascripts/list-import.js') }} {% if lists|length == 0 %} -
    - × - {{ _t('BAZ_INTRO_AJOUT_LISTE')|raw }} -
    +
    + × + {{ _t('BAZ_INTRO_AJOUT_LISTE')|raw }} +
    {% else %} - {{ include_javascript('javascripts/vendor/datatables-full/jquery.dataTables.min.js') }} - {{ include_css('styles/vendor/datatables-full/dataTables.bootstrap.min.css') }} -
    - - - - - - - - - - - {% for key, list in lists %} - - - - - - - {% endfor %} - -
    {{ _t('BAZ_TITLE') }}{{ _t('BAZ_VALEURS_LISTE') }}{{ _t('BAZ_ID') }}{{ _t('BAZ_ACTIONS') }}
    - {{ list.title }} - - {% if list.options %} - - {% endif %} - {{ key }} - {% if list.canEdit %} - - - - {% endif %} - {% if list.canDelete %} - - - - {% endif %} -
    -
    +
    + + + + + + + + + + + {% for list in lists %} + + + + + + + {% endfor %} + +
    {{ _t('BAZ_TITLE') }}{{ _t('BAZ_VALEURS_LISTE') }}{{ _t('BAZ_ID') }}{{ _t('BAZ_ACTIONS') }}
    + {{ list.title }} + + {% if list.nodes %} + + {% endif %} + {{ list.id }} + {% if list.canEdit %} + + + + {% endif %} + {% if list.canDelete %} + + + + {% endif %} +
    +
    {% endif %} - - - {{ _t('BAZ_NOUVELLE_LISTE') }} + + + {{ _t('BAZ_NOUVELLE_LISTE') }} -
    +
    {# LIST IMPORT #} {{ _t('BAZ_IMPORT_LISTS_FROM_URL') }} -
    - - - - - - - -
    +
    + + + + + + + +
    -
    +
    -
    - - - - - - - - - - -
    - - {{ _t('BAZ_ID') }}{{ _t('BAZ_TITLE') }}{{ _t('BAZ_VALEURS_LISTE') }}
    -
    -
    - -
    - +
    + + + + + + + + + + +
    + + {{ _t('BAZ_ID') }}{{ _t('BAZ_TITLE') }}{{ _t('BAZ_VALEURS_LISTE') }}
    +
    + +
    + +
    +
    -
    -
    +
    diff --git a/yarn.lock b/yarn.lock index 188fec72c..f18d01a6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1659,6 +1659,16 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +sortablejs@1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290" + integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A== + +sortablejs@^1.15.2: + version "1.15.2" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.2.tgz#4e9f7bda4718bd1838add9f1866ec77169149809" + integrity sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA== + source-map-js@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" @@ -1865,6 +1875,13 @@ vue@^2.6.14: "@vue/compiler-sfc" "2.7.16" csstype "^3.1.0" +vuedraggable@^2.24.3: + version "2.24.3" + resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19" + integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g== + dependencies: + sortablejs "1.10.2" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"