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,29 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* Get the correct transition keyword of the browser.
* @param {string} type - The property name (transition for example).
* @param {string} lifecycle - Which lifecycle of the property name to catch (end, start...).
* @return {string} The transition keywoard of the browser.
*/
const getAnimationEvent = (type, lifecycle) => {
const el = document.createElement('element');
const typeUpper = type.charAt(0).toUpperCase() + type.substring(1);
const lifecycleUpper = lifecycle.charAt(0).toUpperCase() + lifecycle.substring(1);
const properties = {
transition: `${type}${lifecycle}`,
OTransition: `o${typeUpper}${lifecycleUpper}`,
MozTransition: `${type}${lifecycle}`,
WebkitTransition: `webkit${typeUpper}${lifecycleUpper}`,
};
const key = Object.keys(properties).find((propKey) => el.style[propKey] !== undefined);
return key !== undefined ? properties[key] : false;
};
export default getAnimationEvent;

View File

@@ -0,0 +1,24 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
import 'bootstrap-colorpicker';
const {$} = window;
/**
* Enable all colorpickers.
*/
const init = function initDatePickers() {
$('.colorpicker input[type="text"]').each((i, picker) => {
$(picker).colorpicker();
$(picker).on('colorpickerCreate', () => {
$(picker).css('background-color', $(picker).val());
});
$(picker).on('colorpickerChange', (event) => {
$(picker).css('background-color', event.color.toString());
});
});
};
export default init;

View File

@@ -0,0 +1,77 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
import 'url-polyfill';
const {$} = window;
const replaceDatePicker = () => {
const datepickerWidget = $('body').find(
'.bootstrap-datetimepicker-widget:last',
);
if (datepickerWidget.length <= 0) {
return;
}
const position = datepickerWidget.offset();
const originalHeight = datepickerWidget.outerHeight();
const margin = (datepickerWidget.outerHeight(true) - originalHeight) / 2;
// Move datepicker to the exact same place it was but attached to body
datepickerWidget.appendTo('body');
// Height changed because the css from column-filters is not applied any more
let top = position.top + margin;
// Datepicker is settle to the top position
if (datepickerWidget.hasClass('top')) {
top += originalHeight - datepickerWidget.outerHeight(true) - margin;
}
datepickerWidget.css({
position: 'absolute',
top,
bottom: 'auto',
left: position.left,
right: 'auto',
});
$(window).on('resize', replaceDatePicker);
};
/**
* Enable all datepickers.
*/
const init = function initDatePickers() {
const $datePickers = $('.datepicker input[type="text"]');
$.each($datePickers, (i, picker) => {
$(picker)
.datetimepicker({
locale: window.full_language_code,
format: $(picker).data('format')
? $(picker).data('format')
: 'YYYY-MM-DD',
sideBySide: true,
icons: {
time: 'time',
date: 'date',
up: 'up',
down: 'down',
},
})
.on('dp.show', replaceDatePicker)
.on('dp.hide', () => {
$(window).off('resize', replaceDatePicker);
})
.on('dp.change', (e) => {
// Looks like we can't bind an event to a datepicker selected afterwhile.
// So we emit an event on change to manipulate datas
const event = new CustomEvent('datepickerChange', e);
window.document.dispatchEvent(event);
});
});
};
export default init;

View File

@@ -0,0 +1,23 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
import punycode from 'punycode';
const {$} = window;
const init = function initEmailFields(selector) {
const $emailFields = $(selector);
$.each($emailFields, (i, field) => {
if (!field.checkValidity()) {
const parts = field.value.split('@');
// if local part is not ASCII only, chrome will not auto-convert the domain part to utf8
if (punycode.toASCII(parts[0]) === parts[0]) {
field.value = punycode.toASCII(field.value);
}
}
});
};
export default init;

View File

@@ -0,0 +1,17 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
const {$} = window;
/**
* Enable all datepickers.
*/
const initInvalidFields = () => {
$('input,select,textarea').on('invalid', function scroll() {
this.scrollIntoView(false);
});
};
export default initInvalidFields;

View File

@@ -0,0 +1,25 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
export const showGrowl = (type, message, durationTime) => {
const duration = undefined !== durationTime ? durationTime : 2000;
if (type === 'success') {
window.$.growl({
title: '',
size: 'large',
message,
duration,
});
} else {
window.$.growl[type]({
title: '',
size: 'large',
message,
duration,
});
}
};
export default showGrowl;

View File

@@ -0,0 +1,155 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
// External components
import {EventEmitter} from '@components/event-emitter';
// Core components
import ChoiceTable from '@js/components/choice-table';
import ChoiceTree from '@js/components/form/choice-tree';
import ColorPicker from '@js/app/utils/colorpicker';
import CountryDniRequiredToggler from '@components/country-dni-required-toggler';
import CountryStateSelectionToggler from '@components/country-state-selection-toggler';
import DateRange from '@js/components/form/date-range';
import DeltaQuantityInput from '@components/form/delta-quantity-input';
import DisablingSwitch from '@components/form/disabling-switch';
import GeneratableInput from '@js/components/generatable-input';
import TextWithRecommendedLengthCounter from '@components/form/text-with-recommended-length-counter';
import Grid from '@components/grid/grid';
import ModifyAllShopsCheckbox from '@components/modify-all-shops-checkbox';
import MultipleChoiceTable from '@js/components/multiple-choice-table';
import MultistoreConfigField from '@js/components/form/multistore-config-field';
import PreviewOpener from '@components/form/preview-opener';
import Router from '@components/router';
import ShopSelector from '@components/shop-selector/shop-selector';
import TaggableField from '@js/components/taggable-field';
import TextWithLengthCounter from '@components/form/text-with-length-counter';
import TinyMCEEditor from '@js/components/tinymce-editor';
import TranslatableField from '@js/components/translatable-field';
import TranslatableInput from '@js/components/translatable-input';
import EntitySearchInput from '@js/components/entity-search-input';
import MultipleZoneChoice from '@js/components/form/multiple-zone-choice';
import ToggleChildrenChoice from '@js/components/form/toggle-children-choice';
import FilterLinkGroup from '@components/filter/filter-link-group';
// Grid extensions
import AsyncToggleColumnExtension from '@components/grid/extension/column/common/async-toggle-column-extension';
import BulkActionCheckboxExtension from '@components/grid/extension/bulk-action-checkbox-extension';
import BulkOpenTabsExtension from '@components/grid/extension/bulk-open-tabs-extension';
import ChoiceExtension from '@components/grid/extension/choice-extension';
import ColumnTogglingExtension from '@components/grid/extension/column-toggling-extension';
import ExportToSqlManagerExtension from '@components/grid/extension/export-to-sql-manager-extension';
import FiltersResetExtension from '@components/grid/extension/filters-reset-extension';
import FiltersSubmitButtonEnablerExtension from '@components/grid/extension/filters-submit-button-enabler-extension';
import IframeClient from '@components/modal/iframe-client';
import LinkRowActionExtension from '@components/grid/extension/link-row-action-extension';
import ModalFormSubmitExtension from '@components/grid/extension/modal-form-submit-extension';
import PositionExtension from '@components/grid/extension/position-extension';
import PreviewExtension from '@components/grid/extension/preview-extension';
import ReloadListExtension from '@components/grid/extension/reload-list-extension';
import SortingExtension from '@components/grid/extension/sorting-extension';
import SubmitBulkActionExtension from '@components/grid/extension/submit-bulk-action-extension';
import AjaxBulkActionExtension from '@components/grid/extension/ajax-bulk-action-extension';
import SubmitGridActionExtension from '@components/grid/extension/submit-grid-action-extension';
import SubmitRowActionExtension from '@components/grid/extension/action/row/submit-row-action-extension';
import FormFieldToggler from '@components/form/form-field-toggler';
import EmailInput from '@components/email-input';
const GridExtensions = {
AjaxBulkActionExtension,
AsyncToggleColumnExtension,
BulkActionCheckboxExtension,
BulkOpenTabsExtension,
ChoiceExtension,
ColumnTogglingExtension,
ExportToSqlManagerExtension,
FilterLinkGroup,
FiltersResetExtension,
FiltersSubmitButtonEnablerExtension,
LinkRowActionExtension,
ModalFormSubmitExtension,
PositionExtension,
PreviewExtension,
ReloadListExtension,
SortingExtension,
SubmitBulkActionExtension,
SubmitGridActionExtension,
SubmitRowActionExtension,
};
const initPrestashopComponents = (): void => {
window.prestashop = {...window.prestashop};
if (!window.prestashop.instance) {
window.prestashop.instance = {};
}
window.prestashop.component = {
initComponents(components: string[]) {
components.forEach((component: string): void => {
if (window.prestashop.component[component] === undefined) {
console.error(`Failed to initialize PrestaShop component "${component}". This component doesn't exist.`);
return;
}
const componentInstanceName = component.charAt(0).toLowerCase() + component.slice(1);
if (window.prestashop.instance[componentInstanceName] !== undefined) {
console.warn(
`Failed to initialize PrestaShop component "${component}". This component is already initialized.`,
);
return;
}
// EventEmitter is a special case it has no constructor and could be used via
// window.prestashop.component.EventEmitter straight away
if (component === 'EventEmitter') {
window.prestashop.instance[componentInstanceName] = window.prestashop.component[component];
return;
}
window.prestashop.instance[componentInstanceName] = new window.prestashop.component[component]();
});
// Send an event so external users can initiate their own components
EventEmitter.emit('PSComponentsInitiated');
},
// @todo: add all standard components in this list
ChoiceTable,
ChoiceTree,
ColorPicker,
CountryDniRequiredToggler,
CountryStateSelectionToggler,
DeltaQuantityInput,
DisablingSwitch,
EventEmitter,
FormFieldToggler,
GeneratableInput,
DateRange,
Grid,
GridExtensions,
IframeClient,
ModifyAllShopsCheckbox,
MultipleChoiceTable,
MultistoreConfigField,
PreviewOpener,
Router,
TextWithRecommendedLengthCounter,
ShopSelector,
TaggableField,
TextWithLengthCounter,
TinyMCEEditor,
TranslatableField,
TranslatableInput,
EntitySearchInput,
EmailInput,
MultipleZoneChoice,
ToggleChildrenChoice,
};
};
export default initPrestashopComponents;

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.
*/
const findAllUnwantedCharsExceptTheLatestOne = /(?:(?!^-\d+))[^\d]+(?=.*[^\d])/g;
const findAllUnwantedChars = /(?:(?!^-\d+))([^\d]+)/g;
/**
* If there is a dot in the string
* split the string at the first dot, and
* replace all unwanted characters.
* Otherwise, replace all unwanted characters expect the
* latest one, and replace the latest character
* by a dot.
*/
export const transform = (value) => {
let val = value;
const unwantedChars = val.match(findAllUnwantedChars);
if (unwantedChars === null) {
return val;
}
if (unwantedChars.length > 1) {
const unwantedCharsSet = new Set(unwantedChars);
const unique = Array.from(unwantedCharsSet);
if (unique.length === 1) {
return val.replace(findAllUnwantedChars, '');
}
}
val = val
.replace(findAllUnwantedCharsExceptTheLatestOne, '')
.replace(findAllUnwantedChars, '.');
return val;
};
const clearNumberInputValue = (event, selector) => {
if (!event.target.matches(selector)) {
return;
}
const {value} = event.target;
event.target.value = transform(value);
};
export default (selector) => {
document.addEventListener(
'change',
(event) => {
clearNumberInputValue(event, selector);
},
true,
);
};

View File

@@ -0,0 +1,16 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* Send a Post Request to reset search Action.
*/
const {$} = window;
const init = function resetSearch(url, redirectUrl) {
$.post(url).then(() => window.location.assign(redirectUrl));
};
export default init;

View File

@@ -0,0 +1,177 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
import {createApp} from 'vue';
import {EventEmitter} from '@components/event-emitter';
import serp from './serp.vue';
const {$} = window;
/**
* Vue component displaying a search page result, Google style.
* Requires a tag with the id "#serp-app" to be present in the DOM to run it.
* The component is automatically updated by watching several inputs.
* Set the proper class to link a input to a part of the panel.
*/
class SerpApp {
constructor(selectors, url) {
// If the selector cannot be found, we do not load the Vue app
if ($(selectors.container).length === 0) {
return;
}
this.originalUrl = url;
this.selectors = selectors;
this.useMultiLang = selectors.multiLanguageInput !== undefined || selectors.multiLanguageField !== undefined;
if (this.useMultiLang) {
const possibleSelectors = [];
if (selectors.multiLanguageInput) {
possibleSelectors.push(selectors.multiLanguageInput);
}
if (selectors.multiLanguageField) {
possibleSelectors.push(selectors.multiLanguageField);
}
this.multiLangSelector = possibleSelectors.join(',');
this.attachMultiLangEvents();
}
this.data = {
url,
title: '',
description: '',
};
this.appendTitle = selectors.appendTitle ? $(selectors.appendTitle).val() : '';
this.initializeSelectors(selectors);
this.attachInputEvents();
}
updateComponent() {
if (this.vm) {
this.vm.unmount();
}
this.vm = createApp({
template: '<serp ref="serp" :url="url" :title="title" :description="description" />',
components: {serp},
data: () => this.data,
});
this.vm.mount(this.selectors.container);
}
attachMultiLangEvents(itemSelector) {
$('body').on(
'click',
itemSelector,
() => {
this.checkTitle();
this.checkDesc();
this.checkUrl();
},
);
EventEmitter.on('languageSelected', () => {
this.checkTitle();
this.checkDesc();
this.checkUrl();
});
}
initializeSelectors(selectors) {
this.defaultTitle = $(selectors.defaultTitle);
this.watchedTitle = $(selectors.watchedTitle);
this.defaultDescription = $(selectors.defaultDescription);
this.watchedDescription = $(selectors.watchedDescription);
this.watchedMetaUrl = $(selectors.watchedMetaUrl);
}
attachInputEvents() {
$(this.defaultTitle).on('keyup change', () => this.checkTitle());
$(this.watchedTitle).on('keyup change', () => this.checkTitle());
$(this.defaultDescription).on('keyup change', () => this.checkDesc());
$(this.watchedDescription).on('keyup change', () => this.checkDesc());
this.watchedMetaUrl.on('keyup change', () => this.checkUrl());
this.checkTitle();
this.checkDesc();
this.checkUrl();
}
setTitle(title) {
this.data.title = title;
}
setDescription(description) {
this.data.description = description;
}
setUrl(rewrite) {
// We replace two placeholders because there was a typo in the initial one ('friendy' instead of 'friendly')
this.data.url = this.originalUrl.replace(
'{friendy-url}',
rewrite,
);
this.data.url = this.data.url.replace(
'{friendly-url}',
rewrite,
);
}
checkTitle() {
let {defaultTitle} = this;
let {watchedTitle} = this;
if (this.useMultiLang) {
watchedTitle = watchedTitle.closest(this.multiLangSelector).find('input');
defaultTitle = defaultTitle.closest(this.multiLangSelector).find('input');
}
const title1 = watchedTitle.length ? watchedTitle.val() : '';
const title2 = defaultTitle.length ? defaultTitle.val() : '';
this.setTitle(`${title1 === '' ? title2 : title1}${this.appendTitle ? ` ${this.appendTitle}` : ''}`);
// Always check for url if title change
this.checkUrl();
this.updateComponent();
}
checkDesc() {
let {watchedDescription} = this;
let {defaultDescription} = this;
if (this.useMultiLang) {
const watchedDescriptionTarget = watchedDescription
.closest(this.multiLangSelector)
.find(this.watchedDescription.is('input') ? 'input' : 'textarea');
watchedDescription = watchedDescriptionTarget.length ? watchedDescriptionTarget : watchedDescription;
const defaultDescriptionTarget = defaultDescription
.closest(this.multiLangSelector)
.find(this.defaultDescription.is('input') ? 'input' : 'textarea');
defaultDescription = defaultDescriptionTarget.length ? defaultDescriptionTarget : defaultDescription;
}
const desc1 = watchedDescription.length ? watchedDescription.val().innerText || watchedDescription.val() : '';
const desc2 = defaultDescription.length ? defaultDescription.text() : '';
this.setDescription(desc1 === '' ? desc2 : desc1);
this.updateComponent();
}
checkUrl() {
let {watchedMetaUrl} = this;
if (this.useMultiLang) {
watchedMetaUrl = watchedMetaUrl.closest(this.multiLangSelector).find('input');
}
this.setUrl(watchedMetaUrl.val());
this.updateComponent();
}
}
export default SerpApp;

View File

@@ -0,0 +1,151 @@
<!--*
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*-->
<template>
<div id="serp">
<div class="serp-preview">
<div class="serp-url">
<span class="serp-base-url">{{ displayedBaseURL }}</span>
{{ displayedRelativePath }}
<i class="material-icons serp-url-more">more_vert</i>
</div>
<div class="serp-title">
{{ displayedTitle }}
</div>
<div class="serp-description">
{{ displayedDescription }}
</div>
</div>
</div>
</template>
<script>
import {defineComponent} from 'vue';
export default defineComponent({
name: 'Serp',
props: {
url: {
type: String,
default: 'https://www.example.com/',
},
description: {
type: String,
default: '',
},
title: {
type: String,
default: '',
},
},
computed: {
displayedBaseURL() {
const parseUrl = new URL(this.url);
const baseUrl = `${parseUrl.protocol}//${parseUrl.hostname}`;
return baseUrl;
},
displayedRelativePath() {
const parseUrl = new URL(this.url);
const relativePath = decodeURI(parseUrl.pathname).replaceAll('/', ' \u203a ');
if (relativePath.length > 50) {
return `${relativePath.substring(0, 50)}...`;
}
return relativePath;
},
displayedTitle() {
if (this.title.length > 70) {
return `${this.title.substring(0, 70)}...`;
}
return this.title;
},
displayedDescription() {
const plainTextDescription = this.stripHtml(this.description);
if (plainTextDescription.length > 150) {
return `${plainTextDescription.substring(0, 150)}...`;
}
return plainTextDescription;
},
},
methods: {
stripHtml(html) {
const div = document.createElement('div');
div.innerHTML = html;
return div.textContent || '';
},
},
});
</script>
<style lang="scss" type="text/scss" scoped>
@import "~@scss/config/bootstrap.scss";
@import "~@scss/config/settings.scss";
.serp-preview {
padding: var(--#{$cdk}size-24) var(--#{$cdk}size-30);
margin: var(--#{$cdk}size-16) 0;
background-color: var(--#{$cdk}white);
border: 1px solid var(--#{$cdk}primary-400);
box-shadow: var(--#{$cdk}box-shadow-default);
.serp-url {
font-family: arial, sans-serif;
font-size: var(--#{$cdk}size-12);
font-style: normal;
font-weight: 400;
line-height: var(--#{$cdk}size-18);
color: $serp-url-light-color;
text-align: left;
direction: ltr;
cursor: pointer;
visibility: visible;
display: flex;
align-items: center;
}
.serp-base-url {
color: $serp-url-dark-color;
}
.serp-url-more {
margin-left: var(--#{$cdk}size-12);
font-size: var(--#{$cdk}size-18);
color: $serp-url-light-color;
cursor: pointer;
}
.serp-title {
font-family: arial, sans-serif;
font-size: 1.25rem;
font-weight: 400;
color: $serp-title-color;
text-align: left;
text-decoration: none;
white-space: nowrap;
cursor: pointer;
visibility: visible;
text-overflow: ellipsis;
overflow: hidden;
}
.serp-title:hover {
text-decoration: underline;
}
.serp-description {
font-family: arial, sans-serif;
font-size: 0.875rem;
font-weight: 400;
color: $serp-description-color;
text-align: left;
word-wrap: break-word;
visibility: visible;
}
}
</style>

View File

@@ -0,0 +1,65 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
const {$} = window;
/**
* Allow to display the last SQL query in a modal and redirect to SQL Manager.
*/
class SqlManager {
showLastSqlQuery() {
$('#catalog_sql_query_modal_content textarea[name="sql"]').val($('tbody.sql-manager').data('query'));
$('#catalog_sql_query_modal .btn-sql-submit').on('click', () => {
$('#catalog_sql_query_modal_content').submit();
});
$('#catalog_sql_query_modal').modal('show');
}
sendLastSqlQuery(name) {
$('#catalog_sql_query_modal_content textarea[name="sql"]').val($('tbody.sql-manager').data('query'));
$('#catalog_sql_query_modal_content input[name="name"]').val(name);
$('#catalog_sql_query_modal_content').submit();
}
createSqlQueryName() {
let container = false;
let current = false;
if ($('.breadcrumb')) {
container = $('.breadcrumb li').eq(0).text().replace(/\s+/g, ' ')
.trim();
current = $('.breadcrumb li').eq(-1).text().replace(/\s+/g, ' ')
.trim();
}
let title = false;
if ($('h2.title')) {
title = $('h2.title').first().text().replace(/\s+/g, ' ')
.trim();
}
let name = false;
if (container && current && container !== current) {
name = `${container} > ${current}`;
} else if (container) {
name = container;
} else if (current) {
name = current;
}
if (title && title !== current && title !== container) {
if (name) {
name = `${name} > ${title}`;
} else {
name = title;
}
}
return name.trim();
}
}
export default SqlManager;

View File

@@ -0,0 +1,102 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
const {$} = window;
/**
* Makes a table sortable by columns.
* This forces a page reload with more query parameters.
*/
class TableSorting {
selector: string;
idTable: string;
columns: JQuery;
/**
* @param {jQuery} table
*/
constructor(table: JQuery) {
this.selector = '.ps-sortable-column';
this.idTable = table.attr('id') ?? '';
this.columns = table.find(this.selector);
}
/**
* Attaches the listeners
*/
attach(): void {
this.columns.on('click', (e) => {
const $column = $(e.delegateTarget);
this.sortByColumn($column, this.getToggledSortDirection($column));
});
}
/**
* Sort using a column name
* @param {string} columnName
* @param {string} direction "asc" or "desc"
*/
sortBy(columnName: string, direction: string): void {
const $column = this.columns.is(`[data-sort-col-name="${columnName}"]`);
if (!$column) {
throw new Error(`Cannot sort by "${columnName}": invalid column`);
}
this.sortByColumn(this.columns, direction);
}
/**
* Sort using a column element
* @param {jQuery} column
* @param {string} direction "asc" or "desc"
* @private
*/
private sortByColumn(column: JQuery, direction: string): void {
window.location.href = this.getUrl(
column.data('sortColName'),
direction === 'desc' ? 'desc' : 'asc',
column.data('sortPrefix'),
);
}
/**
* Returns the inverted direction to sort according to the column's current one
* @param {jQuery} column
* @return {string}
* @private
*/
private getToggledSortDirection(column: JQuery): string {
return column.data('sortDirection') === 'asc' ? 'desc' : 'asc';
}
/**
* Returns the url for the sorted table
* @param {string} colName
* @param {string} direction
* @param {string} prefix
* @return {string}
* @private
*/
private getUrl(colName: string, direction: string, prefix: string): string {
const url = new URL(window.location.href);
const params = url.searchParams;
if (prefix) {
params.set(`${prefix}[orderBy]`, colName);
params.set(`${prefix}[sortOrder]`, direction);
} else {
params.set('orderBy', colName);
params.set('sortOrder', direction);
}
url.hash = this.idTable;
return url.toString();
}
}
export default TableSorting;

View File

@@ -0,0 +1,59 @@
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
// Mimic Symfony debug toolbar getPreference function to get the toolbar state
const profilerStorageKey = 'symfony/profiler/';
const getPreference = (name: string): string | null => {
if (!window.localStorage) {
return null;
}
return localStorage.getItem(profilerStorageKey + name);
};
const refreshDelay = 100;
const waitForDebugContent = (debugToken: string): void => {
// Wait until the toolbar content is present on page
const debugBarContentId = `sfToolbarMainContent-${debugToken}`;
const toolbar = document.getElementById(debugBarContentId);
if (toolbar) {
initToggleWatching(debugToken);
} else {
setTimeout(() => waitForDebugContent(debugToken), refreshDelay);
}
};
const initToggleWatching = (debugToken: string): void => {
document.getElementById(`sfToolbarMiniToggler-${debugToken}`)?.addEventListener('click', toggleDebugMode);
document.getElementById(`sfToolbarHideButton-${debugToken}`)?.addEventListener('click', toggleDebugMode);
toggleDebugMode();
};
const toggleDebugMode = (): void => {
if (getPreference('toolbar/displayState') === 'none') {
document.body.classList.add('debug-toolbar-hidden');
document.body.classList.remove('debug-toolbar-shown');
} else {
// Alternative is block (set as shown) or null (default setting is shown)
document.body.classList.add('debug-toolbar-shown');
document.body.classList.remove('debug-toolbar-hidden');
}
};
const watchSymfonyDebugBar = (): void => {
const debugToolbar = document.querySelector<HTMLElement>('[id^=sfwdt]');
if (!debugToolbar) {
// If initial container is not present the debug toolbar will never be displayed, so nothing to do
return;
}
const debugToken = debugToolbar.id.replace(/^sfwdt/, '');
waitForDebugContent(debugToken);
};
export default watchSymfonyDebugBar;