193 lines
5.0 KiB
JavaScript
193 lines
5.0 KiB
JavaScript
export const buildUrl = (appBase, path) => new URL(path, appBase).toString();
|
|
|
|
export const getAppBase = () => {
|
|
const baseEl = document.querySelector('base');
|
|
if (baseEl && baseEl.href) {
|
|
return baseEl.href;
|
|
}
|
|
return `${window.location.origin}/`;
|
|
};
|
|
|
|
export const escapeHtml = (value) => String(value ?? '').replace(/[&<>"']/g, (char) => ({
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": '''
|
|
}[char]));
|
|
|
|
export const buildBadgeList = (items, options = {}) => {
|
|
const {
|
|
variant = 'neutral',
|
|
primaryVariant = 'info',
|
|
primaryTooltip = ''
|
|
} = options;
|
|
|
|
let list = items;
|
|
let primary = '';
|
|
if (items && typeof items === 'object' && !Array.isArray(items)) {
|
|
list = Array.isArray(items.items) ? items.items : [];
|
|
primary = String(items.primary ?? '');
|
|
}
|
|
if (!Array.isArray(list) || list.length === 0) {return '';}
|
|
|
|
const tooltipAttr = primaryTooltip
|
|
? ` data-tooltip="${escapeHtml(primaryTooltip)}" data-tooltip-pos="top"`
|
|
: '';
|
|
|
|
const badges = list.map((label) => {
|
|
const labelText = String(label ?? '');
|
|
const isPrimary = primary !== '' && labelText === primary;
|
|
const badgeVariant = isPrimary ? primaryVariant : variant;
|
|
const badgeTooltip = isPrimary && primaryTooltip ? tooltipAttr : '';
|
|
return `<span class="badge" data-variant="${badgeVariant}"${badgeTooltip}>${escapeHtml(labelText)}</span>`;
|
|
}).join(' ');
|
|
|
|
return `<div class="badge-list">${badges}</div>`;
|
|
};
|
|
|
|
export const postAction = async (url, csrf, data = {}) => {
|
|
const body = new URLSearchParams({ ...data, [csrf.key]: csrf.token });
|
|
return fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'Accept': 'application/json',
|
|
'X-Requested-With': 'fetch'
|
|
},
|
|
body
|
|
});
|
|
};
|
|
|
|
export const badgeHtml = (gridjs, value = '') => {
|
|
const safe = value ?? '';
|
|
return gridjs.html(`<span class="badge" data-variant="neutral">${safe}</span>`);
|
|
};
|
|
|
|
export const normalizeMultiSelectValue = (value) => {
|
|
if (Array.isArray(value)) {
|
|
return value
|
|
.map((item) => String(item ?? '').trim())
|
|
.filter(Boolean);
|
|
}
|
|
if (typeof value === 'string') {
|
|
return value
|
|
.split(',')
|
|
.map((item) => item.trim())
|
|
.filter(Boolean);
|
|
}
|
|
return [];
|
|
};
|
|
|
|
export const gridFilterSelect = (input, param, options = {}) => ({
|
|
input,
|
|
param,
|
|
...options
|
|
});
|
|
|
|
export const gridFilterDate = (input, param, options = {}) => ({
|
|
input,
|
|
param,
|
|
normalize: (value) => {
|
|
const date = String(value ?? '').trim();
|
|
return /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : '';
|
|
},
|
|
...options
|
|
});
|
|
|
|
export const gridFilterText = (input, param, options = {}) => ({
|
|
input,
|
|
param,
|
|
normalize: (value) => String(value ?? '').trim(),
|
|
...options
|
|
});
|
|
|
|
export const gridFilterMultiCsv = (input, param, options = {}) => ({
|
|
input,
|
|
param,
|
|
normalize: normalizeMultiSelectValue,
|
|
...options
|
|
});
|
|
|
|
export const gridFilterNumber = (input, param, options = {}) => ({
|
|
input,
|
|
param,
|
|
normalize: (value) => {
|
|
const raw = String(value ?? '').trim();
|
|
if (raw === '') {
|
|
return '';
|
|
}
|
|
const parsed = Number(raw);
|
|
return Number.isFinite(parsed) ? String(parsed) : '';
|
|
},
|
|
...options
|
|
});
|
|
|
|
export const gridFilterHidden = (input, param, options = {}) => ({
|
|
input,
|
|
param,
|
|
normalize: (value) => String(value ?? '').trim(),
|
|
...options
|
|
});
|
|
|
|
const NORMALIZERS = {
|
|
trim: (value) => String(value ?? '').trim(),
|
|
trim_lower: (value) => String(value ?? '').trim().toLowerCase(),
|
|
all_to_empty: (value) => {
|
|
const normalized = String(value ?? '').trim();
|
|
return normalized === '' || normalized === 'all' ? '' : normalized;
|
|
},
|
|
csv: normalizeMultiSelectValue
|
|
};
|
|
|
|
export const gridFiltersFromSchema = (schema) => {
|
|
if (!Array.isArray(schema)) {
|
|
return [];
|
|
}
|
|
|
|
return schema.map((entry) => {
|
|
if (!entry || typeof entry !== 'object') {
|
|
return null;
|
|
}
|
|
|
|
const type = String(entry.type ?? '').trim().toLowerCase();
|
|
const input = entry.input ?? '';
|
|
const param = String(entry.param ?? '').trim();
|
|
if (!input || !param) {
|
|
return null;
|
|
}
|
|
|
|
const options = {};
|
|
if ('default' in entry) {
|
|
options.default = entry.default;
|
|
}
|
|
if (typeof entry.event === 'string' && entry.event.trim() !== '') {
|
|
options.event = entry.event.trim();
|
|
}
|
|
if (typeof entry.normalize === 'string' && NORMALIZERS[entry.normalize]) {
|
|
options.normalize = NORMALIZERS[entry.normalize];
|
|
}
|
|
|
|
if (type === 'date') {
|
|
return gridFilterDate(input, param, options);
|
|
}
|
|
if (type === 'select') {
|
|
return gridFilterSelect(input, param, options);
|
|
}
|
|
if (type === 'multi_csv') {
|
|
return gridFilterMultiCsv(input, param, options);
|
|
}
|
|
if (type === 'number') {
|
|
return gridFilterNumber(input, param, options);
|
|
}
|
|
if (type === 'hidden') {
|
|
return gridFilterHidden(input, param, options);
|
|
}
|
|
if (type === 'text') {
|
|
return gridFilterText(input, param, options);
|
|
}
|
|
|
|
return null;
|
|
}).filter(Boolean);
|
|
};
|