Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div
class="ps-alert alert"
:class="classObject"
role="alert"
>
<button
v-if="hasClose"
type="button"
class="close"
data-dismiss="alert"
aria-label="Close"
@click.stop="onClick"
>
<span class="material-icons">close</span>
</button>
<p class="alert-text">
<slot />
</p>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
const ALERT_TYPE_INFO = 'ALERT_TYPE_INFO';
const ALERT_TYPE_WARNING = 'ALERT_TYPE_WARNING';
const ALERT_TYPE_DANGER = 'ALERT_TYPE_DANGER';
const ALERT_TYPE_SUCCESS = 'ALERT_TYPE_SUCCESS';
export default defineComponent({
props: {
duration: {
type: Boolean,
required: false,
default: false,
},
alertType: {
type: String,
required: true,
},
hasClose: {
type: Boolean,
required: true,
},
},
computed: {
classObject(): {
'alert-info': boolean,
'alert-warning': boolean,
'alert-danger': boolean,
'alert-success': boolean
} {
return {
'alert-info': this.alertType === ALERT_TYPE_INFO,
'alert-warning': this.alertType === ALERT_TYPE_WARNING,
'alert-danger': this.alertType === ALERT_TYPE_DANGER,
'alert-success': this.alertType === ALERT_TYPE_SUCCESS,
};
},
isInfo(): boolean {
return this.alertType === ALERT_TYPE_INFO;
},
},
methods: {
onClick(): void {
this.$emit('closeAlert');
},
},
});
</script>

View File

@@ -0,0 +1,44 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<button
type="button"
class="btn"
:class="classObject"
>
<slot />
</button>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
props: {
primary: {type: Boolean},
ghost: {type: Boolean},
},
computed: {
classObject(): {
'btn-outline-primary'?: boolean,
'btn-outline-secondary'?: boolean,
'btn-primary'?: boolean,
'btn-secondary'?: boolean
} {
if (this.ghost) {
return {
'btn-outline-primary': this.primary,
'btn-outline-secondary': !this.primary,
};
}
return {
'btn-primary': this.primary,
'btn-secondary': !this.primary,
};
},
},
});
</script>

View File

@@ -0,0 +1,58 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div class="md-checkbox">
<label>
<input
type="checkbox"
:id="id"
v-model="checked"
:class="{'indeterminate' : isIndeterminate }"
>
<i class="md-checkbox-control" />
<slot name="label" />
</label>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
/**
* @deprecated since 8.0, use app/components/checkbox.vue instead
*/
export default defineComponent({
props: {
id: {
type: String,
required: false,
default: '',
},
model: {
type: Object,
required: false,
default: () => ({}),
},
isIndeterminate: {
type: Boolean,
required: false,
default: false,
},
},
watch: {
checked(val: boolean): void {
this.$emit('checked', {
checked: val,
item: this.model,
});
},
},
data() {
return {
checked: false,
};
},
});
</script>

View File

@@ -0,0 +1,71 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div class="input-group date">
<input
ref="datepicker"
type="text"
:class="['form-control', `datepicker-${type}`]"
>
<div class="input-group-append">
<span class="input-group-text">
<i class="material-icons">event</i>
</span>
</div>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
props: {
locale: {
type: String,
required: true,
default: 'en',
},
type: {
type: String,
required: true,
},
},
mounted() {
$(<HTMLInputElement> this.$refs.datepicker).datetimepicker({
format: 'YYYY-MM-DD',
showClear: true,
useCurrent: false,
}).on('dp.change', (infos: Record<string, any>) => {
infos.dateType = this.type;
this.$emit(
infos.date ? 'dpChange' : 'reset',
infos,
);
});
},
});
</script>
<style lang="scss">
@import '~@scss/config/_settings.scss';
.date {
a[data-action='clear']::before {
font-family: var(--#{$cdk}font-family-material-icons);
content: "\E14C";
font-size: var(--#{$cdk}size-20);
position: absolute;
bottom: var(--#{$cdk}size-16);
left: 50%;
margin-left: calc(-1 * var(--#{$cdk}size-10));
color: var(--#{$cdk}primary-800);
cursor: pointer;
}
.bootstrap-datetimepicker-widget tr td span:hover {
background-color: var(--#{$cdk}white);
}
}
</style>

View File

@@ -0,0 +1,56 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div class="ps-loader">
<div class="timeline-item">
<div class="animated-background">
<slot />
</div>
</div>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
});
</script>
<style lang="scss" scoped>
@import '~@scss/config/_settings.scss';
.ps-loader {
width: 100%;
.animated-background {
animation-duration: 1s;
animation-iteration-count: infinite;
animation-name: loading;
animation-timing-function: linear;
background: var(--#{$cdk}primary-200);
background:
linear-gradient(to right, var(--#{$cdk}primary-200) 10%,
var(--#{$cdk}primary-500) 20%,
var(--#{$cdk}primary-200) 30%);
background-size: 100%;
height: var(--#{$cdk}size-40);
position: relative;
}
.background-masker {
background: var(--#{$cdk}white);
position: absolute;
}
}
@keyframes loading{
0%{
background-position: -500px 0
}
100%{
background-position: 500px 0
}
}
</style>

View File

@@ -0,0 +1,63 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div class="media">
<img
v-if="displayThumb"
:src="thumbnail"
class="thumbnail d-flex"
>
<div
v-else
class="no-img"
/>
<div class="ml-2 desc media-body">
<slot />
</div>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
props: {
thumbnail: {
type: String,
required: false,
default: '',
},
},
computed: {
displayThumb(): boolean {
return this.thumbnail === '' ? false : !!this.thumbnail;
},
},
});
</script>
<style lang="scss" scoped>
@import '~@scss/config/_settings.scss';
.product-title {
.has-combination & {
font-weight: 600;
}
}
.thumbnail, .no-img {
border: 1px solid var(--#{$cdk}primary-400);
max-width: var(--#{$cdk}size-44);
}
.no-img {
background: var(--#{$cdk}white);
width: var(--#{$cdk}size-44);
height: var(--#{$cdk}size-44);
display: inline-block;
vertical-align: middle;
}
.desc {
white-space: normal;
}
</style>

View File

@@ -0,0 +1,106 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div
class="modal fade"
id="ps-modal"
tabindex="-1"
role="dialog"
>
<div
class="modal-dialog"
role="document"
>
<div class="modal-content">
<div class="modal-header">
<button
type="button"
class="close"
data-dismiss="modal"
>
<i class="material-icons">close</i>
</button>
<h4 class="modal-title">
{{ translations.modal_title }}
</h4>
</div>
<div class="modal-body">
{{ translations.modal_content }}
</div>
<div class="modal-footer">
<PSButton
@click="onSave"
class="btn-lg"
primary
data-dismiss="modal"
>
{{ translations.button_save }}
</PSButton>
<PSButton
@click="onLeave"
class="btn-lg"
ghost
data-dismiss="modal"
>
{{ translations.button_leave }}
</PSButton>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import PSButton from '@app/widgets/ps-button.vue';
import {EventEmitter} from '@components/event-emitter';
import {defineComponent} from 'vue';
export default defineComponent({
props: {
translations: {
type: Object,
required: false,
default: () => ({}),
},
},
mounted() {
EventEmitter.on('showModal', () => {
this.showModal();
});
EventEmitter.on('hideModal', () => {
this.hideModal();
});
},
methods: {
showModal(): void {
$(this.$el).modal('show');
},
hideModal(): void {
$(this.$el).modal('hide');
},
onSave(): void {
this.$emit('save');
},
onLeave(): void {
this.$emit('leave');
},
},
components: {
PSButton,
},
});
</script>
<style lang="scss" scoped>
@import '~@scss/config/_settings.scss';
.modal-header .close {
font-size: var(--#{$cdk}size-20);
opacity: 1;
}
.modal-content {
border-radius: 0
}
</style>

View File

@@ -0,0 +1,88 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div
class="ps-number"
:class="{ 'hover-buttons': hoverButtons }"
>
<input
type="number"
class="form-control"
:class="{ danger }"
:value="value"
placeholder="0"
@keyup="onKeyup($event)"
@keydown="onKeydown($event)"
@focus="focusIn($event)"
@blur="focusOut($event)"
>
<div
class="ps-number-spinner d-flex"
v-if="buttons"
>
<span
class="ps-number-up"
@click="increment($event)"
/>
<span
class="ps-number-down"
@click="decrement($event)"
/>
</div>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
props: {
value: {
type: [Number, String],
default: 0,
},
danger: {
type: Boolean,
default: false,
},
buttons: {
type: Boolean,
default: false,
},
hoverButtons: {
type: Boolean,
default: false,
},
},
methods: {
getValue(): number {
const value = Number.isNaN(this.value) ? 0 : Number.parseInt(<string> this.value, 10);
return Number.isNaN(value) ? 0 : value;
},
onKeyup($event: Event): void {
this.$emit('keyup', $event);
},
onKeydown($event: Event): void {
this.$emit('keydown', $event);
},
focusIn($event: Event): void {
this.$emit('focus', $event);
},
focusOut($event: Event): void {
this.$emit('blur', $event);
},
increment($event: Event) {
(<HTMLInputElement>$event.target).value = `${this.getValue() + 1}`;
this.$emit('change', $event);
},
decrement($event: Event): void {
(<HTMLInputElement>$event.target).value = `${this.getValue() - 1}`;
this.$emit('change', $event);
},
},
});
</script>

View File

@@ -0,0 +1,153 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<nav
class="mt-1 mx-auto"
v-if="displayPagination"
>
<ul
class="pagination"
:class="{'multi':isMultiPagination}"
>
<li
v-if="isMultiPagination"
class="page-item previous"
>
<a
v-show="activeLeftArrow"
class="float-left page-link"
@click="prev()"
href="#"
>
<span class="sr-only">Previous</span>
</a>
</li>
<li
class="page-item"
:class="{'active' : checkCurrentIndex(index)}"
v-for="index in pagesCount"
:key="index"
>
<a
v-if="showIndex(index)"
class="page-link"
:class="{
'pl-0' : showFirstDots(index),
'pr-0' : showLastDots(index)
}"
@click.prevent="changePage(index)"
href="#"
>
<span
v-if="isMultiPagination"
v-show="showFirstDots(index)"
>...</span>
{{ index }}
<span
v-if="isMultiPagination"
v-show="showLastDots(index)"
>...</span>
</a>
</li>
<li
v-if="isMultiPagination"
class="page-item next"
>
<a
v-show="activeRightArrow"
class="float-left page-link"
@click="next()"
href="#"
>
<span class="sr-only">Next</span>
</a>
</li>
</ul>
</nav>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
props: {
pagesCount: {
type: Number,
required: true,
},
currentIndex: {
type: Number,
required: true,
},
},
computed: {
isMultiPagination(): boolean {
return this.pagesCount > this.multiPagesActivationLimit;
},
activeLeftArrow(): boolean {
return this.currentIndex !== 1;
},
activeRightArrow(): boolean {
return this.currentIndex !== this.pagesCount;
},
pagesToDisplay(): number {
return this.multiPagesToDisplay;
},
displayPagination(): boolean {
return this.pagesCount > 1;
},
},
methods: {
checkCurrentIndex(index: number): boolean {
return this.currentIndex === index;
},
showIndex(index: number): boolean {
const startPaginationIndex = index < this.currentIndex + this.multiPagesToDisplay;
const lastPaginationIndex = index > this.currentIndex - this.multiPagesToDisplay;
const indexToDisplay = startPaginationIndex && lastPaginationIndex;
const lastIndex = index === this.pagesCount;
const firstIndex = index === 1;
if (!this.isMultiPagination) {
return !this.isMultiPagination;
}
return indexToDisplay || firstIndex || lastIndex;
},
changePage(pageIndex: number): void {
this.$emit('pageChanged', pageIndex);
},
showFirstDots(index: number): boolean {
const pagesToDisplay = this.pagesCount - this.multiPagesToDisplay;
if (!this.isMultiPagination) {
return this.isMultiPagination;
}
return index === this.pagesCount && this.currentIndex <= pagesToDisplay;
},
showLastDots(index: number): boolean {
if (!this.isMultiPagination) {
return this.isMultiPagination;
}
return index === 1 && this.currentIndex > this.multiPagesToDisplay;
},
prev(): void {
if (this.currentIndex > 1) {
this.changePage(this.currentIndex - 1);
}
},
next(): void {
if (this.currentIndex < this.pagesCount) {
this.changePage(this.currentIndex + 1);
}
},
},
data() {
return {
multiPagesToDisplay: 2,
multiPagesActivationLimit: 5,
};
},
});
</script>

View File

@@ -0,0 +1,49 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div class="ps-radio">
<input
type="radio"
:id="id"
name="radio-group"
:checked="checked"
@change="onChange"
>
<label :for="id">{{ label }}</label>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
props: {
id: {
type: String,
required: true,
},
label: {
type: String,
required: false,
default: '',
},
checked: {
type: Boolean,
required: false,
},
value: {
type: String,
required: false,
default: '',
},
},
methods: {
onChange(): void {
this.$emit('change', this.value);
},
},
});
</script>

View File

@@ -0,0 +1,87 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div
class="ps-select"
:id="itemId"
>
<select
class="form-control"
v-model="selected"
@change="onChange"
>
<option
value="default"
selected
>
<slot />
</option>
<option
v-for="(item, index) in items"
:key="index"
:value="item[itemId]"
>
{{ item[itemName] }}
</option>
</select>
</div>
</template>
<script lang="ts">
import {defineComponent, PropType} from 'vue';
export default defineComponent({
props: {
items: {
type: Array as PropType<Array<Record<string, any>>>,
required: true,
},
itemId: {
type: String,
required: false,
default: '',
},
itemName: {
type: String,
required: false,
default: '',
},
},
methods: {
onChange(): void {
this.$emit('change', {
value: this.selected,
itemId: this.itemId,
});
},
},
data() {
return {
selected: 'default',
};
},
});
</script>
<style lang="scss" scoped>
@import '~@scss/config/_settings.scss';
.ps-select {
position: relative;
select {
appearance: none;
border-radius: 0;
}
&::after {
content: "\E313";
font-family: var(--#{$cdk}font-family-material-icons);
color: var(--#{$cdk}primary-400);
font-size: var(--#{$cdk}size-20);
position: absolute;
right: var(--#{$cdk}size-5);
top: var(--#{$cdk}size-5);
}
}
</style>

View File

@@ -0,0 +1,14 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div class="ps-spinner" />
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
});
</script>

View File

@@ -0,0 +1,56 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div
class="ps-sortable-column"
:data-sort-col-name="this.order"
:data-sort-is-current="isCurrent"
:data-sort-direction="sortDirection"
@click="sortToggle"
>
<span role="columnheader"><slot /></span>
<span
role="button"
class="ps-sort"
aria-label="Tri"
/>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
props: {
// column name
order: {
type: String,
required: true,
},
// indicates the currently sorted column in the table
currentSort: {
type: String,
required: true,
},
},
methods: {
sortToggle(): void {
// toggle direction
this.sortDirection = (this.sortDirection === 'asc') ? 'desc' : 'asc';
this.$emit('sort', this.order, this.sortDirection);
},
},
data() {
return {
sortDirection: 'desc',
};
},
computed: {
isCurrent(): boolean {
return this.currentSort === this.order;
},
},
});
</script>

View File

@@ -0,0 +1,18 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div class="table-responsive">
<table class="table">
<slot />
</table>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
});
</script>

View File

@@ -0,0 +1,93 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div
class="tags-input search-input search d-flex flex-wrap"
:class="{ 'search-with-icon': hasIcon }"
@click="focus()"
>
<div class="tags-wrapper">
<span
v-for="(tag, index) in tags"
:key="index"
class="tag"
>{{ tag }}<i
class="material-icons"
@click="close(index)"
>close</i></span>
</div>
<input
ref="tags"
:placeholder="placeholderToDisplay"
type="text"
v-model="tag"
class="form-control input"
@keyup="onKeyUp"
@keydown.enter="add(tag)"
@keydown.delete.stop="remove()"
:size="inputSize"
>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
props: {
tags: {
type: Array,
required: false,
default: () => ([]),
},
placeholder: {
type: String,
required: false,
default: '',
},
hasIcon: {
type: Boolean,
required: false,
},
},
computed: {
inputSize(): number {
return !this.tags.length && this.placeholder ? this.placeholder.length : 0;
},
placeholderToDisplay(): string {
return this.tags.length ? '' : this.placeholder;
},
},
methods: {
onKeyUp() {
this.$emit('typing', (<VTagsInput> this.$refs.tags).value);
},
add(tag: string): void {
if (tag) {
this.tags.push(tag.trim());
this.tag = '';
this.focus();
this.$emit('tagChange', this.tag);
}
},
close(index: number): void {
const tagName = this.tags[index];
this.tags.splice(index, 1);
this.$emit('tagChange', tagName);
},
remove(): void {
if (this.tags && this.tags.length && !this.tag.length) {
const tagName = this.tags[this.tags.length - 1];
this.tags.pop();
this.$emit('tagChange', tagName);
}
},
focus(): void {
(<HTMLInputElement> this.$refs.tags).focus();
},
},
data: () => ({tag: ''}),
});
</script>

View File

@@ -0,0 +1,197 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div
class="ps-tree-items"
:class="{className}"
>
<div
class="d-flex tree-name"
:class="{active: active, disable: model.disable}"
@click="clickElement"
>
<button
class="btn btn-text"
:class="[{hidden: isHidden}, chevronStatus]"
>
<span
v-if="translations"
class="sr-only"
>{{ model.open ? translations.reduce : translations.expand }}</span>
</button>
<PSCheckbox
:ref="model.name"
:id="id.toString()"
:model="model"
@checked="onCheck"
v-if="hasCheckbox"
/>
<span
class="tree-label"
:class="{warning: isWarning}"
>{{ model.name }}</span>
<span
class="tree-extra-label d-sm-none d-xl-inline-block"
v-if="displayExtraLabel"
>{{ getExtraLabel }}</span>
<span
class="tree-extra-label-mini d-xl-none"
v-if="displayExtraLabel"
>{{ model.extraLabel }}</span>
</div>
<ul
v-show="open"
v-if="isFolder"
class="tree"
>
<li
v-for="(element, index) in model.children"
:key="index"
class="tree-item"
:class="{disable: model.disable}"
>
<PSTreeItem
:ref="element.id"
:class="className"
:has-checkbox="hasCheckbox"
:model="element"
:label="element.name"
:translations="translations"
:current-item="currentItem"
@checked="onCheck"
@setCurrentElement="setCurrentElement"
/>
</li>
</ul>
</div>
</template>
<script lang="ts">
import PSCheckbox from '@app/widgets/ps-checkbox.vue';
import {EventEmitter} from '@components/event-emitter';
import {defineComponent} from 'vue';
export default defineComponent({
name: 'PSTreeItem',
props: {
model: {
type: Object,
required: true,
},
className: {
type: String,
required: false,
default: '',
},
hasCheckbox: {
type: Boolean,
required: false,
},
translations: {
type: Object,
required: false,
default: () => ({}),
},
currentItem: {
type: String,
required: false,
default: '',
},
},
computed: {
id(): number {
return this.model.id.toString();
},
isFolder(): boolean {
return this.model.children && this.model.children.length;
},
displayExtraLabel(): boolean {
return this.isFolder && this.model.extraLabel;
},
getExtraLabel(): string {
let extraLabel = '';
if (this.model.extraLabel && this.model.extraLabel === 1) {
extraLabel = this.translations.extra_singular;
} else if (this.model.extraLabel) {
extraLabel = this.translations.extra.replace('%d', this.model.extraLabel);
}
return extraLabel;
},
isHidden(): boolean {
return !this.isFolder;
},
chevronStatus(): string {
return this.open ? 'open' : 'closed';
},
isWarning(): boolean {
return !this.isFolder && this.model.warning;
},
active(): boolean {
return this.model.full_name === this.currentItem;
},
},
methods: {
setCurrentElement(el: any): void {
if (this.$refs[el]) {
this.openTreeItemAction();
this.current = true;
this.parentElement(this.$parent);
} else {
this.current = false;
}
},
parentElement(parent: any): void {
if (parent.clickElement) {
parent.clickElement();
this.parentElement(parent.$parent);
}
},
clickElement(): boolean | void {
return !this.model.disable ? this.openTreeItemAction() : false;
},
openTreeItemAction(): void {
this.setCurrentElement(this.model.full_name);
if (this.isFolder) {
this.open = !this.open;
} else {
EventEmitter.emit('lastTreeItemClick', {
item: this.model,
});
}
},
onCheck(obj: any): void {
this.$emit('checked', obj);
},
},
mounted() {
EventEmitter.on('toggleCheckbox', (tag: any) => {
const checkbox = this.$refs[tag];
if (checkbox) {
(<VCheckbox>checkbox).$data.checked = !(<VCheckbox>checkbox).$data.checked;
}
});
EventEmitter.on('expand', () => {
this.open = true;
});
EventEmitter.on('reduce', () => {
this.open = false;
});
EventEmitter.on('setCurrentElement', (el: HTMLElement) => {
this.setCurrentElement(el);
});
this.setCurrentElement(this.currentItem);
},
components: {
PSCheckbox,
},
data: () => ({
open: false,
current: false,
}),
});
</script>

View File

@@ -0,0 +1,96 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div class="ps-tree">
<div class="mb-3 tree-header">
<button
class="btn btn-text text-uppercase pointer"
@click="expand"
data-action="expand"
>
<i class="material-icons">keyboard_arrow_down</i>
<span v-if="translations">{{ translations.expand }}</span>
</button>
<button
class="btn btn-text float-right text-uppercase pointer"
@click="reduce"
data-action="reduce"
>
<i class="material-icons">keyboard_arrow_up</i>
<span v-if="translations">{{ translations.reduce }}</span>
</button>
</div>
<ul
class="tree"
:class="className"
>
<li
v-for="(element, index) in model"
:key="index"
>
<PSTreeItem
ref="item"
:has-checkbox="hasCheckbox"
:model="element"
:label="element.name"
:translations="translations"
:current-item="currentItem"
@checked="onCheck"
@setCurrentElement="setCurrentElement"
/>
</li>
</ul>
</div>
</template>
<script lang="ts">
import {defineComponent, PropType} from 'vue';
import {EventEmitter} from '@components/event-emitter';
import PSTreeItem from './ps-tree-item.vue';
export default defineComponent({
name: 'PSTree',
props: {
model: {
type: Array as PropType<Array<Record<string, any>>>,
default: () => ([]),
},
className: {
type: String,
default: '',
},
currentItem: {
type: String,
default: '',
},
hasCheckbox: {
type: Boolean,
default: false,
},
translations: {
type: Object,
required: false,
default: () => ({}),
},
},
methods: {
onCheck(obj: any): void {
this.$emit('checked', obj);
},
expand(): void {
EventEmitter.emit('expand');
},
reduce(): void {
EventEmitter.emit('reduce');
},
setCurrentElement(id: string | number): void {
EventEmitter.emit('setCurrentElement', id);
},
},
components: {
PSTreeItem,
},
});
</script>