Lint project files

This commit is contained in:
Bartłomiej Sęk 2024-12-16 13:41:15 +01:00
parent 1b9d3adaf8
commit 23fcad77c8
24 changed files with 638 additions and 644 deletions

View file

@ -2,20 +2,20 @@ import js from '@eslint/js';
import globals from 'globals';
export default [
js.configs.recommended,
{
ignores: ['vendor/', 'node_modules/', 'public/'],
},
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
}
js.configs.recommended,
{
ignores: ['vendor/', 'node_modules/', 'public/'],
},
rules: {
indent: ['error', 4],
semi: ['error', 'always'],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
}
},
rules: {
indent: ['error', 4],
semi: ['error', 'always'],
}
}
}
];

View file

@ -22,9 +22,9 @@ import TabsErrorsController from './controllers/TabsErrorsController';
// Registers Stimulus controllers from controllers.json and in the controllers/ directory
export const app = startStimulusApp(require.context(
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
true,
/\.[jt]sx?$/
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
true,
/\.[jt]sx?$/
));
app.register('live', LiveController);

View file

@ -11,22 +11,22 @@ import { Controller } from '@hotwired/stimulus';
import { Modal } from 'bootstrap';
export default class extends Controller {
static targets = ['modal', 'parent', 'csrfToken'];
static targets = ['modal', 'parent', 'csrfToken'];
connect() {
this.element.addEventListener('sylius_admin:taxon:open_delete_modal', (event) => {
this.csrfTokenTarget.value = event.detail.csrfToken;
this.modalElement = this.modalTarget;
connect() {
this.element.addEventListener('sylius_admin:taxon:open_delete_modal', (event) => {
this.csrfTokenTarget.value = event.detail.csrfToken;
this.modalElement = this.modalTarget;
this.modalElement.closest(".dropdown").appendChild(this.modalElement);
this.modal = new Modal(this.modalElement);
this.modal.show();
this.modalElement.closest(".dropdown").appendChild(this.modalElement);
this.modal = new Modal(this.modalElement);
this.modal.show();
this.modalElement.addEventListener(
'hidden.bs.modal',
() => {this.parentTarget.appendChild(this.modalElement);},
{once: true}
);
});
}
this.modalElement.addEventListener(
'hidden.bs.modal',
() => {this.parentTarget.appendChild(this.modalElement);},
{once: true}
);
});
}
}

View file

@ -10,48 +10,48 @@
import AutocompleteController from '@symfony/ux-autocomplete';
export default class extends AutocompleteController {
observer;
connected = false;
observer;
connected = false;
initialize() {
super.initialize();
initialize() {
super.initialize();
this.element.addEventListener('change', () => {
this.tomSelect.sync();
});
this.observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
this.tomSelect.sync();
});
});
window.addEventListener('sylius_admin.product_attribute_autocomplete.clear_requested', () => {
this.tomSelect.clear();
});
}
connect() {
super.connect();
this.observer.observe(this.element, { attributes: true });
this.connected = true;
}
disconnect() {
super.disconnect();
this.observer.disconnect();
this.connected = false;
}
urlValueChanged() {
if (!this.connected) {
return;
this.element.addEventListener('change', () => {
this.tomSelect.sync();
});
this.observer = new MutationObserver((mutations) => {
mutations.forEach(() => {
this.tomSelect.sync();
});
});
window.addEventListener('sylius_admin.product_attribute_autocomplete.clear_requested', () => {
this.tomSelect.clear();
});
}
this.disconnect();
this.connect();
this.tomSelect.refreshItems();
}
connect() {
super.connect();
this.observer.observe(this.element, { attributes: true });
this.connected = true;
}
disconnect() {
super.disconnect();
this.observer.disconnect();
this.connected = false;
}
urlValueChanged() {
if (!this.connected) {
return;
}
this.disconnect();
this.connect();
this.tomSelect.refreshItems();
}
}

View file

@ -11,52 +11,52 @@ import { Controller } from '@hotwired/stimulus';
import InfiniteTree from "infinite-tree";
export default class extends Controller {
static values = {
treeData: Array,
autoOpen: Boolean,
};
static targets = ['tree', 'filter', 'productTaxons'];
static values = {
treeData: Array,
autoOpen: Boolean,
};
static targets = ['tree', 'filter', 'productTaxons'];
connect() {
this.tree = this.createInfiniteTree();
connect() {
this.tree = this.createInfiniteTree();
this.tree.on('contentDidUpdate', () => this.updateIndeterminateState());
this.tree.on('clusterDidChange', () => this.updateIndeterminateState());
this.tree.on('checkNode', (node) => this.updateProductTaxons(node));
this.tree.on('contentDidUpdate', () => this.updateIndeterminateState());
this.tree.on('clusterDidChange', () => this.updateIndeterminateState());
this.tree.on('checkNode', (node) => this.updateProductTaxons(node));
this.checkInitialNodes();
}
createInfiniteTree() {
return new InfiniteTree({
el: this.treeTarget,
data: this.treeDataValue,
autoOpen: this.autoOpenValue,
selectable: false,
rowRenderer: this.rowRenderer,
});
}
rowRenderer(node, treeOptions) {
const { id, name, children, state } = node;
const { depth, open, path, total, filtered, checked, indeterminate } = state;
const more = node.hasChildren();
const nodeMargin = 20;
if (filtered === false) {
return '';
this.checkInitialNodes();
}
let togglerClass = '';
if (more) {
togglerClass = open ? 'infinite-tree-open' : 'infinite-tree-closed';
createInfiniteTree() {
return new InfiniteTree({
el: this.treeTarget,
data: this.treeDataValue,
autoOpen: this.autoOpenValue,
selectable: false,
rowRenderer: this.rowRenderer,
});
}
const toggler = `<span class="${treeOptions.togglerClass} ${togglerClass}" style="width: ${nodeMargin}px"></span>`;
const checkbox = `<span class="infinite-tree-check" style="width: ${nodeMargin}px;"><input class="form-check-input" type="checkbox" data-action="product-taxon-tree#clickNode" ${indeterminate && !checked ? 'data-indeterminate' : ''} ${checked ? 'checked' : ''}></span>`;
const treeNode = `<div class="infinite-tree-node" style="margin-left: ${(depth * nodeMargin)}px">${toggler}${checkbox}<span class="infinite-tree-title">${name}</span></div>`;
rowRenderer(node, treeOptions) {
const { id, name, children, state } = node;
const { depth, open, path, total, filtered, checked, indeterminate } = state;
const more = node.hasChildren();
const nodeMargin = 20;
return `<div
if (filtered === false) {
return '';
}
let togglerClass = '';
if (more) {
togglerClass = open ? 'infinite-tree-open' : 'infinite-tree-closed';
}
const toggler = `<span class="${treeOptions.togglerClass} ${togglerClass}" style="width: ${nodeMargin}px"></span>`;
const checkbox = `<span class="infinite-tree-check" style="width: ${nodeMargin}px;"><input class="form-check-input" type="checkbox" data-action="product-taxon-tree#clickNode" ${indeterminate && !checked ? 'data-indeterminate' : ''} ${checked ? 'checked' : ''}></span>`;
const treeNode = `<div class="infinite-tree-node" style="margin-left: ${(depth * nodeMargin)}px">${toggler}${checkbox}<span class="infinite-tree-title">${name}</span></div>`;
return `<div
data-id="${id}"
data-expanded="${more && open}"
data-depth="${depth}"
@ -67,106 +67,106 @@ export default class extends Controller {
>
${treeNode}
</div>`;
}
clickNode(event) {
const id = event.target.closest('.infinite-tree-item').dataset.id;
this.checkNode(this.tree.getNodeById(id));
}
checkNode(node, checked) {
if (true === checked) {
node.state.checked = true;
} else if (false === checked) {
node.state.checked = false;
} else {
node.state.checked = node.state.checked === undefined ? true : !node.state.checked;
}
const topParentNode = this.updateParentNodes(node);
this.tree.updateNode(topParentNode);
this.tree.emit('checkNode', node);
}
clickNode(event) {
const id = event.target.closest('.infinite-tree-item').dataset.id;
this.checkNode(this.tree.getNodeById(id));
}
updateParentNodes(childNode) {
let parentNode = childNode;
while (parentNode.parent && parentNode.parent.state.depth >= 0) {
parentNode = parentNode.parent;
let checkedCount = 0;
let indeterminate = false;
parentNode.children.forEach((childNode) => {
indeterminate = indeterminate || !!childNode.state.indeterminate;
if (childNode.state.checked) {
checkedCount++;
checkNode(node, checked) {
if (true === checked) {
node.state.checked = true;
} else if (false === checked) {
node.state.checked = false;
} else {
node.state.checked = node.state.checked === undefined ? true : !node.state.checked;
}
});
if (checkedCount > 0 || indeterminate) {
parentNode.state.indeterminate = true;
} else if (checkedCount === 0) {
parentNode.state.indeterminate = false;
}
const topParentNode = this.updateParentNodes(node);
this.tree.updateNode(topParentNode);
this.tree.emit('checkNode', node);
}
return parentNode;
}
updateParentNodes(childNode) {
let parentNode = childNode;
updateIndeterminateState() {
const checkboxes = this.tree.contentElement.querySelectorAll('input[type="checkbox"]');
for (const checkbox of checkboxes) {
checkbox.indeterminate = checkbox.hasAttribute('data-indeterminate');
}
}
while (parentNode.parent && parentNode.parent.state.depth >= 0) {
parentNode = parentNode.parent;
let checkedCount = 0;
let indeterminate = false;
updateProductTaxons(node) {
let values = this.productTaxonsTarget.value.split(',').filter(Boolean);
parentNode.children.forEach((childNode) => {
indeterminate = indeterminate || !!childNode.state.indeterminate;
if (childNode.state.checked) {
checkedCount++;
}
});
if (false === node.state.checked) {
values.splice(values.indexOf(node.id), 1);
} else if (!values.includes(node.id)) {
values.push(node.id);
if (checkedCount > 0 || indeterminate) {
parentNode.state.indeterminate = true;
} else if (checkedCount === 0) {
parentNode.state.indeterminate = false;
}
}
return parentNode;
}
this.productTaxonsTarget.value = values.join();
}
updateIndeterminateState() {
const checkboxes = this.tree.contentElement.querySelectorAll('input[type="checkbox"]');
for (const checkbox of checkboxes) {
checkbox.indeterminate = checkbox.hasAttribute('data-indeterminate');
}
}
checkInitialNodes() {
this.productTaxonsTarget.value.split(',').filter(Boolean).forEach((productTaxonCode) => {
this.checkNode(this.tree.getNodeById(productTaxonCode));
});
}
updateProductTaxons(node) {
let values = this.productTaxonsTarget.value.split(',').filter(Boolean);
filter(event) {
this.tree.filter(event.target.value, {
caseSensitive: false,
exactMatch: false,
includeAncestors: true,
includeDescendants: true,
});
}
if (false === node.state.checked) {
values.splice(values.indexOf(node.id), 1);
} else if (!values.includes(node.id)) {
values.push(node.id);
}
clearFilter() {
this.filterTarget.value = '';
this.tree.unfilter();
}
this.productTaxonsTarget.value = values.join();
}
checkAll(event) {
event.preventDefault();
this.toggleAllNodes(true);
}
checkInitialNodes() {
this.productTaxonsTarget.value.split(',').filter(Boolean).forEach((productTaxonCode) => {
this.checkNode(this.tree.getNodeById(productTaxonCode));
});
}
uncheckAll(event) {
event.preventDefault();
this.toggleAllNodes(false);
}
filter(event) {
this.tree.filter(event.target.value, {
caseSensitive: false,
exactMatch: false,
includeAncestors: true,
includeDescendants: true,
});
}
toggleAllNodes(isChecked) {
this.tree.nodes.forEach((node) => {
if (!this.tree.filtered || node.state.filtered) {
this.checkNode(node, isChecked);
}
});
}
clearFilter() {
this.filterTarget.value = '';
this.tree.unfilter();
}
checkAll(event) {
event.preventDefault();
this.toggleAllNodes(true);
}
uncheckAll(event) {
event.preventDefault();
this.toggleAllNodes(false);
}
toggleAllNodes(isChecked) {
this.tree.nodes.forEach((node) => {
if (!this.tree.filtered || node.state.filtered) {
this.checkNode(node, isChecked);
}
});
}
}

View file

@ -10,47 +10,47 @@
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static values = {
url: String,
csrfToken: String,
inputSelector: String,
dataKey: String
};
changedPositions = [];
connect() {
document.querySelectorAll(this.inputSelectorValue).forEach(input => {
input.addEventListener('change', (event) => this.handlePositionChange(event));
});
}
handlePositionChange(event) {
const input = event.target;
const elementId = input.getAttribute('data-id');
const changedPosition = this.changedPositions.find(({ id }) => id === elementId);
if (!changedPosition) {
this.changedPositions.push({ id: elementId, position: input.value });
} else {
changedPosition.position = input.value;
}
}
submit() {
const requestOptions = {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ [this.dataKeyValue]: this.changedPositions, _csrf_token: this.csrfTokenValue}),
static values = {
url: String,
csrfToken: String,
inputSelector: String,
dataKey: String
};
changedPositions = [];
fetch(this.urlValue, requestOptions)
.then(response => {
if (!response.ok) {
throw new Error('Failed to move positions.');
connect() {
document.querySelectorAll(this.inputSelectorValue).forEach(input => {
input.addEventListener('change', (event) => this.handlePositionChange(event));
});
}
handlePositionChange(event) {
const input = event.target;
const elementId = input.getAttribute('data-id');
const changedPosition = this.changedPositions.find(({ id }) => id === elementId);
if (!changedPosition) {
this.changedPositions.push({ id: elementId, position: input.value });
} else {
changedPosition.position = input.value;
}
}
window.location.reload();
})
.catch(error => console.error(error.message));
}
submit() {
const requestOptions = {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ [this.dataKeyValue]: this.changedPositions, _csrf_token: this.csrfTokenValue}),
};
fetch(this.urlValue, requestOptions)
.then(response => {
if (!response.ok) {
throw new Error('Failed to move positions.');
}
window.location.reload();
})
.catch(error => console.error(error.message));
}
}

View file

@ -7,21 +7,21 @@
* file that was distributed with this source code.
*/
import {ApplicationController, useDebounce} from 'stimulus-use'
import {ApplicationController, useDebounce} from 'stimulus-use';
import slugify from 'slugify';
export default class extends ApplicationController {
static debounces = ['generateSlug']
static targets = [ 'sluggable', 'slug' ]
static values = { locale: String }
static debounces = ['generateSlug'];
static targets = [ 'sluggable', 'slug' ];
static values = { locale: String };
connect() {
useDebounce(this);
}
connect() {
useDebounce(this);
}
generateSlug() {
this.element.setAttribute('busy', '');
this.slugTarget.value = slugify(this.sluggableTarget.value, { locale: this.localeValue, lower: true });
this.element.removeAttribute('busy');
}
generateSlug() {
this.element.setAttribute('busy', '');
this.slugTarget.value = slugify(this.sluggableTarget.value, { locale: this.localeValue, lower: true });
this.element.removeAttribute('busy');
}
}

View file

@ -10,58 +10,58 @@
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['tab'];
observer;
static targets = ['tab'];
observer;
initialize() {
this.observer = new MutationObserver(() => {
this.findErrors();
});
}
connect() {
this.findErrors();
this.observe();
}
findErrors() {
this.tabTargets.forEach((tab) => {
const tabContentId = tab.attributes['data-bs-target'].value;
const errorsCount = this.countTabErrors(this.element.querySelector(tabContentId));
if (errorsCount > 0) {
this.appendIndicator(tab, errorsCount)
} else {
this.removeIndicator(tab)
}
});
}
countTabErrors(target) {
return target.querySelectorAll('.is-invalid').length;
}
appendIndicator(target, errorsCount) {
const errorElement = document.createElement('div');
const errorContainer = target.querySelector('.tab-icons');
errorElement.classList.add('tab-error');
errorElement.innerText = errorsCount;
if (!errorContainer.querySelector('.tab-error')) {
errorContainer.insertBefore(errorElement, errorContainer.firstChild);
initialize() {
this.observer = new MutationObserver(() => {
this.findErrors();
});
}
}
removeIndicator(tab) {
const errorBadge = tab.querySelector('.tab-error');
if (errorBadge) {
errorBadge.remove();
connect() {
this.findErrors();
this.observe();
}
}
observe() {
this.observer.observe(this.element.querySelector('#product_attribute_tabs'), { attributes: false, childList: true, subtree: false });
}
findErrors() {
this.tabTargets.forEach((tab) => {
const tabContentId = tab.attributes['data-bs-target'].value;
const errorsCount = this.countTabErrors(this.element.querySelector(tabContentId));
if (errorsCount > 0) {
this.appendIndicator(tab, errorsCount);
} else {
this.removeIndicator(tab);
}
});
}
countTabErrors(target) {
return target.querySelectorAll('.is-invalid').length;
}
appendIndicator(target, errorsCount) {
const errorElement = document.createElement('div');
const errorContainer = target.querySelector('.tab-icons');
errorElement.classList.add('tab-error');
errorElement.innerText = errorsCount;
if (!errorContainer.querySelector('.tab-error')) {
errorContainer.insertBefore(errorElement, errorContainer.firstChild);
}
}
removeIndicator(tab) {
const errorBadge = tab.querySelector('.tab-error');
if (errorBadge) {
errorBadge.remove();
}
}
observe() {
this.observer.observe(this.element.querySelector('#product_attribute_tabs'), { attributes: false, childList: true, subtree: false });
}
}

View file

@ -10,30 +10,30 @@
import {default as SlugController} from './SlugController';
export default class extends SlugController {
static debounces = [ 'generateSlug' ]
static targets = [ 'sluggable', 'slug' ]
static values = { url: String, locale: String, parentTaxonCode: String }
parent = document.querySelector('[data-input-id="sylius_taxon_parent"]');
static debounces = [ 'generateSlug' ];
static targets = [ 'sluggable', 'slug' ];
static values = { url: String, locale: String, parentTaxonCode: String };
parent = document.querySelector('[data-input-id="sylius_taxon_parent"]');
connect() {
parent.addEventListener('change', this.onParentTaxonChange.bind(this));
}
connect() {
parent.addEventListener('change', this.onParentTaxonChange.bind(this));
}
disconnect() {
parent.removeEventListener('change', this.onParentTaxonChange.bind(this));
}
disconnect() {
parent.removeEventListener('change', this.onParentTaxonChange.bind(this));
}
onParentTaxonChange(event) {
this.parentTaxonCodeValue = event.detail.value;
this.generateSlug();
}
onParentTaxonChange(event) {
this.parentTaxonCodeValue = event.detail.value;
this.generateSlug();
}
generateSlug() {
let url = this.urlValue + '?' + new URLSearchParams({locale: this.localeValue, name: this.sluggableTarget.value, parentCode: this.parentTaxonCodeValue})
generateSlug() {
let url = this.urlValue + '?' + new URLSearchParams({locale: this.localeValue, name: this.sluggableTarget.value, parentCode: this.parentTaxonCodeValue});
fetch(url)
.then(response => response.json())
.then(data => { this.slugTarget.value = data.slug; })
;
}
fetch(url)
.then(response => response.json())
.then(data => { this.slugTarget.value = data.slug; })
;
}
}

View file

@ -12,55 +12,55 @@ import { getComponent } from '@symfony/ux-live-component';
import InfiniteTree from "infinite-tree";
export default class extends Controller {
static values = {
treeData: Array,
autoOpen: Boolean,
};
static targets = ['tree', 'itemPrototyp'];
static values = {
treeData: Array,
autoOpen: Boolean,
};
static targets = ['tree', 'itemPrototyp'];
async initialize() {
this.component = await getComponent(this.element);
this.tree = this.createInfiniteTree();
async initialize() {
this.component = await getComponent(this.element);
this.tree = this.createInfiniteTree();
this.component.on('render:finished', () => {
this.tree.loadData(this.treeDataValue);
});
}
createInfiniteTree() {
return new InfiniteTree({
el: this.treeTarget,
data: this.treeDataValue,
autoOpen: this.autoOpenValue,
selectable: false,
rowRenderer: (node, treeOptions) => this.rowRenderer(node, treeOptions),
});
}
rowRenderer(node, treeOptions) {
const { id, name, children, state } = node;
const { depth, open, path, total } = state;
const more = node.hasChildren();
const nodeMargin = 20;
let itemPrototyp = this.itemPrototypTarget.firstElementChild.cloneNode(true);
let togglerPrototyp = itemPrototyp.querySelector('[data-infinite-tree-toggler]');
itemPrototyp.style.marginLeft = `${(depth * nodeMargin)}px`;
itemPrototyp.setAttribute('data-id', id);
itemPrototyp.setAttribute('data-expanded', more && open);
itemPrototyp.setAttribute('data-depth', depth);
itemPrototyp.setAttribute('data-path', path);
itemPrototyp.setAttribute('data-children', children.length);
itemPrototyp.setAttribute('data-total', total);
togglerPrototyp.style.width = `${nodeMargin}px`;
if (more) {
togglerPrototyp.classList.add(open ? 'infinite-tree-open' : 'infinite-tree-closed');
} else {
togglerPrototyp.classList.add('infinite-tree-leaf');
this.component.on('render:finished', () => {
this.tree.loadData(this.treeDataValue);
});
}
return itemPrototyp.outerHTML.replaceAll(`__TAXON_ID__`, id).replaceAll(`__TAXON_NAME__`, name);
}
createInfiniteTree() {
return new InfiniteTree({
el: this.treeTarget,
data: this.treeDataValue,
autoOpen: this.autoOpenValue,
selectable: false,
rowRenderer: (node, treeOptions) => this.rowRenderer(node, treeOptions),
});
}
rowRenderer(node) {
const { id, name, children, state } = node;
const { depth, open, path, total } = state;
const more = node.hasChildren();
const nodeMargin = 20;
let itemPrototyp = this.itemPrototypTarget.firstElementChild.cloneNode(true);
let togglerPrototyp = itemPrototyp.querySelector('[data-infinite-tree-toggler]');
itemPrototyp.style.marginLeft = `${(depth * nodeMargin)}px`;
itemPrototyp.setAttribute('data-id', id);
itemPrototyp.setAttribute('data-expanded', more && open);
itemPrototyp.setAttribute('data-depth', depth);
itemPrototyp.setAttribute('data-path', path);
itemPrototyp.setAttribute('data-children', children.length);
itemPrototyp.setAttribute('data-total', total);
togglerPrototyp.style.width = `${nodeMargin}px`;
if (more) {
togglerPrototyp.classList.add(open ? 'infinite-tree-open' : 'infinite-tree-closed');
} else {
togglerPrototyp.classList.add('infinite-tree-leaf');
}
return itemPrototyp.outerHTML.replaceAll(`__TAXON_ID__`, id).replaceAll(`__TAXON_NAME__`, name);
}
}

View file

@ -3,23 +3,23 @@ import * as bootstrap from 'bootstrap';
// Fix dropdowns
(() => {
document.querySelectorAll('.dropdown-static').forEach((dropdownToggleEl) => {
const parent = dropdownToggleEl.closest('[data-bs-toggle="dropdown"]');
if (parent) {
let dropdown = new bootstrap.Dropdown(parent, {
popperConfig(defaultBsPopperConfig) {
return { ...defaultBsPopperConfig, strategy: 'fixed' };
},
});
}
});
document.querySelectorAll('.dropdown-static').forEach((dropdownToggleEl) => {
const parent = dropdownToggleEl.closest('[data-bs-toggle="dropdown"]');
if (parent) {
new bootstrap.Dropdown(parent, {
popperConfig(defaultBsPopperConfig) {
return { ...defaultBsPopperConfig, strategy: 'fixed' };
},
});
}
});
})();
// Initialize tooltips
(() => {
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((tooltipTriggerEl) => {
let tooltip = new bootstrap.Tooltip(tooltipTriggerEl);
});
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((tooltipTriggerEl) => {
new bootstrap.Tooltip(tooltipTriggerEl);
});
})();
window.bootstrap = bootstrap;

View file

@ -7,8 +7,6 @@
* file that was distributed with this source code.
*/
/* global document */
function syliusBulkDelete(form) {
const groupName = form.getAttribute('data-bulk-delete');
const groupItems = Array.from(document.querySelectorAll(`input[data-check-all-group="${groupName}"]`));

View file

@ -7,8 +7,6 @@
* file that was distributed with this source code.
*/
/* global document */
function syliusCheckAll(trigger) {
const groupName = trigger.getAttribute('data-check-all');
const groupItems = Array.from(document.querySelectorAll(`[data-check-all-group="${groupName}"]`));
@ -24,18 +22,18 @@ function syliusCheckAll(trigger) {
groupItems.forEach((item) => {
item.addEventListener('change', () => {
switch (groupItems.filter((filteredItem) => filteredItem.checked).length) {
case groupItems.length:
trigger.indeterminate = false;
trigger.checked = true;
break;
case 0:
trigger.indeterminate = false;
trigger.checked = false;
break;
default:
trigger.indeterminate = true;
trigger.checked = false;
break;
case groupItems.length:
trigger.indeterminate = false;
trigger.checked = true;
break;
case 0:
trigger.indeterminate = false;
trigger.checked = false;
break;
default:
trigger.indeterminate = true;
trigger.checked = false;
break;
}
actionButtonsRefresh();
});

View file

@ -7,64 +7,62 @@
* file that was distributed with this source code.
*/
/* global document */
(function() {
const menuSearchInput = document.querySelector('[data-menu-search]');
const menuClearButton = document.querySelector('[data-menu-search-clear]');
const menuSearchInput = document.querySelector('[data-menu-search]');
const menuClearButton = document.querySelector('[data-menu-search-clear]');
const clearInput = () => {
menuSearchInput.value = '';
menuSearchInput.dispatchEvent(new Event('input'));
};
const clearInput = () => {
menuSearchInput.value = '';
menuSearchInput.dispatchEvent(new Event('input'));
};
if (menuSearchInput) {
menuSearchInput.addEventListener('input', function(e) {
const query = e.target.value.toLowerCase();
const navItems = document.querySelectorAll('.sidebar .nav-item');
if (menuSearchInput) {
menuSearchInput.addEventListener('input', function(e) {
const query = e.target.value.toLowerCase();
const navItems = document.querySelectorAll('.sidebar .nav-item');
navItems.forEach(navItem => {
const navLink = navItem.querySelector('.nav-link');
const dropdownMenu = navItem.querySelector('.dropdown-menu');
const dropdownItems = navItem.querySelectorAll('.dropdown-item');
let matchFound = false;
navItems.forEach(navItem => {
const navLink = navItem.querySelector('.nav-link');
const dropdownMenu = navItem.querySelector('.dropdown-menu');
const dropdownItems = navItem.querySelectorAll('.dropdown-item');
let matchFound = false;
dropdownItems.forEach(item => {
const text = item.textContent.toLowerCase();
if (query === '' || text.includes(query)) {
item.style.display = '';
matchFound = true;
} else {
item.style.display = 'none';
}
dropdownItems.forEach(item => {
const text = item.textContent.toLowerCase();
if (query === '' || text.includes(query)) {
item.style.display = '';
matchFound = true;
} else {
item.style.display = 'none';
}
});
if (matchFound || query === '') {
navItem.style.display = '';
} else {
navItem.style.display = 'none';
}
if (query !== '') {
if (navLink) navLink.classList.add('d-flex');
if (dropdownMenu) dropdownMenu.classList.add('d-flex');
} else {
if (navLink) navLink.classList.remove('d-flex');
if (dropdownMenu) dropdownMenu.classList.remove('d-flex');
}
});
});
if (matchFound || query === '') {
navItem.style.display = '';
} else {
navItem.style.display = 'none';
}
menuSearchInput.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
clearInput();
}
});
}
if (query !== '') {
if (navLink) navLink.classList.add('d-flex');
if (dropdownMenu) dropdownMenu.classList.add('d-flex');
} else {
if (navLink) navLink.classList.remove('d-flex');
if (dropdownMenu) dropdownMenu.classList.remove('d-flex');
}
});
});
menuSearchInput.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
clearInput();
}
});
}
if (menuClearButton) {
menuClearButton.addEventListener('click', function() {
clearInput();
});
}
if (menuClearButton) {
menuClearButton.addEventListener('click', function() {
clearInput();
});
}
})();

View file

@ -8,8 +8,8 @@
*/
window.addEventListener('sylius_admin:product:form:attributed_deleted', () => {
let tabs = document.getElementById('product_attribute_tabs');
let firstTab = tabs.querySelector('.list-group-item');
let tabs = document.getElementById('product_attribute_tabs');
let firstTab = tabs.querySelector('.list-group-item');
window.bootstrap.Tab.getOrCreateInstance(firstTab).show();
window.bootstrap.Tab.getOrCreateInstance(firstTab).show();
});

View file

@ -11,102 +11,102 @@ import ApexCharts from 'apexcharts';
let chart = null;
function renderChart() {
// eslint-disable-next-line no-undef
const statisticsChart = document.querySelector('#statistics-chart');
// eslint-disable-next-line no-undef
const statisticsChart = document.querySelector('#statistics-chart');
if (!statisticsChart) {
return;
}
if (!statisticsChart) {
return;
}
const options = {
colors: ['#32be9f'],
fill: {
colors: ['#32be9f'],
},
series: [{
name: 'Sales',
data: JSON.parse(statisticsChart.dataset.sales),
}],
chart: {
toolbar: {
show: false,
},
height: 350,
type: 'bar',
},
plotOptions: {
bar: {
borderRadius: 4,
dataLabels: {
position: 'top', // top, center, bottom
},
},
},
dataLabels: {
enabled: true,
formatter(val) {
const { currency } = statisticsChart.dataset;
return `${currency}${val}`;
},
offsetY: -20,
style: {
fontSize: '12px',
colors: ['#304758'],
},
},
xaxis: {
categories: JSON.parse(statisticsChart.dataset.intervals),
position: 'top',
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
crosshairs: {
const options = {
colors: ['#32be9f'],
fill: {
type: 'gradient',
gradient: {
colorFrom: '#32be9f',
colorTo: '#2a9f83',
stops: [0, 100],
opacityFrom: 0.4,
opacityTo: 0.5,
},
colors: ['#32be9f'],
},
},
tooltip: {
enabled: true,
},
},
yaxis: {
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
labels: {
show: false,
formatter(val) {
const { currency } = statisticsChart.dataset;
return `${currency}${val}`;
series: [{
name: 'Sales',
data: JSON.parse(statisticsChart.dataset.sales),
}],
chart: {
toolbar: {
show: false,
},
height: 350,
type: 'bar',
},
},
plotOptions: {
bar: {
borderRadius: 4,
dataLabels: {
position: 'top', // top, center, bottom
},
},
},
dataLabels: {
enabled: true,
formatter(val) {
const { currency } = statisticsChart.dataset;
return `${currency}${val}`;
},
offsetY: -20,
style: {
fontSize: '12px',
colors: ['#304758'],
},
},
xaxis: {
categories: JSON.parse(statisticsChart.dataset.intervals),
position: 'top',
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
crosshairs: {
fill: {
type: 'gradient',
gradient: {
colorFrom: '#32be9f',
colorTo: '#2a9f83',
stops: [0, 100],
opacityFrom: 0.4,
opacityTo: 0.5,
},
},
},
tooltip: {
enabled: true,
},
},
yaxis: {
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
labels: {
show: false,
formatter(val) {
const { currency } = statisticsChart.dataset;
return `${currency}${val}`;
},
},
},
title: {
floating: true,
offsetY: 330,
align: 'center',
style: {
color: '#444',
},
},
};
},
title: {
floating: true,
offsetY: 330,
align: 'center',
style: {
color: '#444',
},
},
};
chart = new ApexCharts(statisticsChart, options);
chart.render();
chart = new ApexCharts(statisticsChart, options);
chart.render();
}
renderChart();
@ -114,16 +114,16 @@ renderChart();
const element = document.querySelector('#statistics-chart');
if (element) {
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'data-sales' || mutation.attributeName === 'data-intervals') {
chart.destroy();
renderChart();
}
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'data-sales' || mutation.attributeName === 'data-intervals') {
chart.destroy();
renderChart();
}
});
});
});
observer.observe(element, {
attributes: true,
});
observer.observe(element, {
attributes: true,
});
}

View file

@ -1,12 +1,12 @@
(function() {
const el = document.querySelector('[data-sticky-header]');
const el = document.querySelector('[data-sticky-header]');
const observer = new IntersectionObserver(
([e]) => e.target.classList.toggle('is-sticky', e.intersectionRatio < 1),
{ threshold: [1] }
);
const observer = new IntersectionObserver(
([e]) => e.target.classList.toggle('is-sticky', e.intersectionRatio < 1),
{ threshold: [1] }
);
if (el) {
observer.observe(el)
}
if (el) {
observer.observe(el);
}
})();

View file

@ -11,31 +11,31 @@ const path = require('path');
const Encore = require('@symfony/webpack-encore');
class SyliusAdmin {
static getWebpackConfig(rootDir) {
Encore
.setOutputPath('public/build/admin/')
.setPublicPath('/build/admin')
.addEntry('admin-entry', path.resolve(__dirname, 'Resources/assets/entrypoint.js'))
.addEntry('admin-product-entry', path.resolve(__dirname, 'Resources/assets/product-entrypoint.js'))
.disableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
.enableSassLoader((options) => {
// eslint-disable-next-line no-param-reassign
options.additionalData = `$rootDir: '${rootDir}';`;
})
.enableStimulusBridge(path.resolve(__dirname, 'Resources/assets/controllers.json'));
static getWebpackConfig(rootDir) {
Encore
.setOutputPath('public/build/admin/')
.setPublicPath('/build/admin')
.addEntry('admin-entry', path.resolve(__dirname, 'Resources/assets/entrypoint.js'))
.addEntry('admin-product-entry', path.resolve(__dirname, 'Resources/assets/product-entrypoint.js'))
.disableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
.enableSassLoader((options) => {
// eslint-disable-next-line no-param-reassign
options.additionalData = `$rootDir: '${rootDir}';`;
})
.enableStimulusBridge(path.resolve(__dirname, 'Resources/assets/controllers.json'));
const adminConfig = Encore.getWebpackConfig();
const adminConfig = Encore.getWebpackConfig();
adminConfig.externals = { ...adminConfig.externals, window: 'window', document: 'document' };
adminConfig.name = 'admin';
adminConfig.externals = { ...adminConfig.externals, window: 'window', document: 'document' };
adminConfig.name = 'admin';
Encore.reset();
Encore.reset();
return adminConfig;
}
return adminConfig;
}
}
module.exports = SyliusAdmin;

View file

@ -13,9 +13,9 @@ import ApiLoginController from './controllers/ApiLoginController';
// Registers Stimulus controllers from controllers.json and in the controllers/ directory
export const app = startStimulusApp(require.context(
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
true,
/\.[jt]sx?$/
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
true,
/\.[jt]sx?$/
));
app.register('live', LiveController);

View file

@ -10,34 +10,34 @@
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static values = { url: String };
static targets = ['email', 'password', 'csrfToken', 'error', 'errorPrototype'];
static values = { url: String };
static targets = ['email', 'password', 'csrfToken', 'error', 'errorPrototype'];
login() {
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
body: JSON.stringify({
_username: this.emailTarget.value,
_password: this.passwordTarget.value,
[this.csrfTokenTarget.name]: this.csrfTokenTarget.value,
}),
};
login() {
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
body: JSON.stringify({
_username: this.emailTarget.value,
_password: this.passwordTarget.value,
[this.csrfTokenTarget.name]: this.csrfTokenTarget.value,
}),
};
fetch(this.urlValue, requestOptions)
.then(response => response.json())
.then(response => {
if (response.success) {
window.location.reload();
} else {
const errorElement = this.errorPrototypeTarget.cloneNode(true);
errorElement.innerHTML = response.message;
this.errorTarget.innerHTML = errorElement.outerHTML;
}
})
;
}
fetch(this.urlValue, requestOptions)
.then(response => response.json())
.then(response => {
if (response.success) {
window.location.reload();
} else {
const errorElement = this.errorPrototypeTarget.cloneNode(true);
errorElement.innerHTML = response.message;
this.errorTarget.innerHTML = errorElement.outerHTML;
}
})
;
}
}

View file

@ -7,4 +7,5 @@
* file that was distributed with this source code.
*/
// eslint-disable-next-line no-unused-vars
import * as bootstrap from 'bootstrap';

View file

@ -11,30 +11,30 @@ const path = require('path');
const Encore = require('@symfony/webpack-encore');
class SyliusShop {
static getWebpackConfig(rootDir) {
Encore
.setOutputPath('public/build/shop/')
.setPublicPath('/build/shop')
.addEntry('shop-entry', path.resolve(__dirname, 'Resources/assets/entrypoint.js'))
.disableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
.enableSassLoader((options) => {
// eslint-disable-next-line no-param-reassign
options.additionalData = `$rootDir: '${rootDir}';`;
})
.enableStimulusBridge(path.resolve(__dirname, 'Resources/assets/controllers.json'));
static getWebpackConfig(rootDir) {
Encore
.setOutputPath('public/build/shop/')
.setPublicPath('/build/shop')
.addEntry('shop-entry', path.resolve(__dirname, 'Resources/assets/entrypoint.js'))
.disableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
.enableSassLoader((options) => {
// eslint-disable-next-line no-param-reassign
options.additionalData = `$rootDir: '${rootDir}';`;
})
.enableStimulusBridge(path.resolve(__dirname, 'Resources/assets/controllers.json'));
const shopConfig = Encore.getWebpackConfig();
const shopConfig = Encore.getWebpackConfig();
shopConfig.externals = { ...shopConfig.externals, window: 'window', document: 'document' };
shopConfig.name = 'shop';
shopConfig.externals = { ...shopConfig.externals, window: 'window', document: 'document' };
shopConfig.name = 'shop';
Encore.reset();
Encore.reset();
return shopConfig;
}
return shopConfig;
}
}
module.exports = SyliusShop;

View file

@ -1,20 +1,20 @@
module.exports = {
extends: 'airbnb-base',
env: {
browser: true,
},
rules: {
'object-shorthand': ['error', 'always', {
avoidQuotes: true,
avoidExplicitReturnArrows: true,
}],
'function-paren-newline': ['error', 'consistent'],
'max-len': ['warn', 120, 2, {
ignoreUrls: true,
ignoreComments: false,
ignoreRegExpLiterals: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
}],
},
extends: 'airbnb-base',
env: {
browser: true,
},
rules: {
'object-shorthand': ['error', 'always', {
avoidQuotes: true,
avoidExplicitReturnArrows: true,
}],
'function-paren-newline': ['error', 'consistent'],
'max-len': ['warn', 120, 2, {
ignoreUrls: true,
ignoreComments: false,
ignoreRegExpLiterals: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
}],
},
};

View file

@ -1,5 +1,4 @@
const path = require('path');
const Encore = require('@symfony/webpack-encore');
const SyliusAdmin = require('@sylius-ui/admin');
const SyliusShop = require('@sylius-ui/shop');