feature: tiered sort
This commit is contained in:
@ -11,7 +11,8 @@ import { GridColumn, GridInputColumn, GridDropdownColumn, GridCheckboxColumn, Gr
|
||||
import { Popup, createPopup, showAlert, showConfirm } from "./ui/popup";
|
||||
import { createPicture, createAudio, createVideo, createFile, createVideoList } from './ui/media';
|
||||
import { validation, convertCssStyle } from './ui/extension';
|
||||
import { createDateInput, toDateValue, formatDate, setDateValue, getDateValue, DateSelector } from './ui/date';
|
||||
import { createDateInput, toDateValue, getFormatter, formatDate, setDateValue, getDateValue, DateSelector } from './ui/date';
|
||||
import * as utility from './utility';
|
||||
|
||||
export {
|
||||
createElement,
|
||||
@ -47,6 +48,7 @@ export {
|
||||
// dateSelector
|
||||
createDateInput,
|
||||
toDateValue,
|
||||
getFormatter,
|
||||
formatDate,
|
||||
setDateValue,
|
||||
getDateValue,
|
||||
@ -59,5 +61,7 @@ export {
|
||||
createVideoList,
|
||||
// extension
|
||||
validation,
|
||||
convertCssStyle
|
||||
convertCssStyle,
|
||||
// utility
|
||||
utility
|
||||
}
|
||||
|
@ -50,12 +50,12 @@ export function toDateValue(dt, local) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Date} date
|
||||
* @param {boolean} [utc=true]
|
||||
* @returns {string}
|
||||
* 获取日期格式器
|
||||
* @param {Date} date - 待格式的日期
|
||||
* @param {boolean} [utc=true] - 是否按 UTC 时间格式化
|
||||
* @returns {any} 返回格式化工具对象
|
||||
*/
|
||||
function getFormatter(date, utc) {
|
||||
export function getFormatter(date, utc) {
|
||||
const prefix = utc !== false ? 'getUTC' : 'get';
|
||||
const r = {
|
||||
/**
|
||||
|
@ -1,4 +1,3 @@
|
||||
// import { r, global, contains, isPositive, nullOrEmpty } from "../utility";
|
||||
import './css/dropdown.scss';
|
||||
import { r } from "../utility/lgres";
|
||||
import { contains, nullOrEmpty } from "../utility/strings";
|
||||
@ -205,6 +204,7 @@ export class Dropdown {
|
||||
if (options.input) {
|
||||
label = createElement('input', 'ui-drop-text');
|
||||
label.type = 'text';
|
||||
label.autocomplete = 'off';
|
||||
label.draggable = false;
|
||||
options.placeholder && label.setAttribute('placeholder', options.placeholder);
|
||||
isPositive(options.maxLength) && label.setAttribute('maxlength', options.maxLength);
|
||||
|
@ -1,19 +1,19 @@
|
||||
import '../css/grid.scss';
|
||||
import { global, isPositive, isMobile, throttle, truncate, debounce } from "../../utility";
|
||||
import { global, isPositive, isMobile, throttle, debounce, truncate } from "../../utility";
|
||||
import { r as lang } from "../../utility/lgres";
|
||||
import { nullOrEmpty } from "../../utility/strings";
|
||||
import { createElement } from "../../functions";
|
||||
import { createIcon } from "../icon";
|
||||
import { createCheckbox } from "../checkbox";
|
||||
import { setTooltip } from "../tooltip";
|
||||
import { Popup, showAlert } from "../popup";
|
||||
import { Popup, showAlert, showConfirm } from "../popup";
|
||||
import { convertCssStyle } from "../extension";
|
||||
import { GridColumn, GridInputColumn, GridTextColumn, GridDropdownColumn, GridCheckboxColumn, GridRadioboxColumn, GridIconColumn, GridDateColumn } from "./column";
|
||||
|
||||
/**
|
||||
* @author Tsanie Lily <tsorgy@gmail.com>
|
||||
* @license MIT
|
||||
* @version 1.0.2
|
||||
* @version 1.0.3
|
||||
*/
|
||||
|
||||
const ScriptPath = (self.document == null ? self.location.href : self.document.currentScript?.src ?? '').replace(/ui\.min\.js\?.+$/, '');
|
||||
@ -258,6 +258,7 @@ let r = lang;
|
||||
* @property {(ValueItem[] | GridColumnFilterSourceCallback)} [filterSource] - 自定义列过滤器的数据源,支持调用函数返回数据源
|
||||
* @property {boolean} [filterAsValue=false] - 列头过滤强制使用 `Value` 字段
|
||||
* @property {GridItemSortCallback} [sortFilter] - 自定义列排序函数
|
||||
* @property {boolean} [sortAsText=false] - 按照 `DisplayValue` 排序
|
||||
* @property {DropdownOptions} [dropOptions] - 列为下拉列表类型时以该值设置下拉框的参数
|
||||
* @property {boolean} [dropRestrictCase=false] - 下拉列表是否区分大小写
|
||||
* @property {(GridSourceItem[] | Promise<GridSourceItem[]> | GridDropdownSourceCallback)} [source] - 列为下拉列表类型时以该值设置下拉列表数据源,支持返回异步对象,也支持调用函数返回
|
||||
@ -450,6 +451,7 @@ const GridColumnDirection = {
|
||||
* @typedef GridLanguages
|
||||
* @property {string} [all] - ( All )
|
||||
* @property {string} [ok] - OK
|
||||
* @property {string} [yes] - Yes
|
||||
* @property {string} [reset] - Reset
|
||||
* @property {string} [cancel] - Cancel
|
||||
* @property {string} [null] - ( Null )
|
||||
@ -464,6 +466,7 @@ const GridColumnDirection = {
|
||||
* @property {string} [column] - Column
|
||||
* @property {string} [order] - Order
|
||||
* @property {string} [sort] - Sort
|
||||
* @property {string} [sortArrayExists] - This will remove the current tiered sort. Do you wish to continue?
|
||||
* @property {string} [requirePrompt] - All sort criteria must have a column specified. Check the selected sort criteria and try again.
|
||||
* @property {string} [duplicatePrompt] - {column} is being sorted more than once. Delete the duplicate sort criteria and try again.
|
||||
* @interface
|
||||
@ -1348,6 +1351,7 @@ export class Grid {
|
||||
this.langs = {
|
||||
all: r('allItem', '( All )'),
|
||||
ok: r('ok', 'OK'),
|
||||
yes: r('yes', 'Yes'),
|
||||
reset: r('reset', 'Reset'),
|
||||
cancel: r('cancel', 'Cancel'),
|
||||
null: r('null', '( Null )'),
|
||||
@ -1362,6 +1366,7 @@ export class Grid {
|
||||
column: r('column', 'Column'),
|
||||
order: r('order', 'Order'),
|
||||
sort: r('sort', 'Sort'),
|
||||
sortArrayExists: r('sortArrayExists', 'This will remove the current tiered sort. Do you wish to continue?'),
|
||||
requirePrompt: r('requirePrompt', 'All sort criteria must have a column specified. Check the selected sort criteria and try again.'),
|
||||
duplicatePrompt: r('duplicatePrompt', '{column} is being sorted more than once. Delete the duplicate sort criteria and try again.')
|
||||
};
|
||||
@ -1405,10 +1410,9 @@ export class Grid {
|
||||
if (e.target === this._var.el) {
|
||||
// cancel selections
|
||||
const selectedIndexes = this._var.selectedIndexes;
|
||||
if (selectedIndexes == null || selectedIndexes.length === 0) {
|
||||
return;
|
||||
if (selectedIndexes?.length > 0) {
|
||||
selectedIndexes.splice(0);
|
||||
}
|
||||
selectedIndexes.splice(0);
|
||||
if (this.readonly) {
|
||||
this._tableRows.forEach(row => {
|
||||
row.classList.remove('selected');
|
||||
@ -1426,6 +1430,10 @@ export class Grid {
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
if (this._getParentElement(parent) !== this._var.el) {
|
||||
// sub ui-grid
|
||||
return;
|
||||
}
|
||||
const rowIndex = parent.classList.contains('ui-grid-total-row') ? -1 : this._tableRows.indexOf(parent);
|
||||
let colIndex = indexOfParent(target) - (this.expandable ? 1 : 0);
|
||||
if (colIndex >= this.columns.length) {
|
||||
@ -1799,58 +1807,64 @@ export class Grid {
|
||||
grid.onRowChanged();
|
||||
}
|
||||
buttonWrapper.append(
|
||||
createElement('span', 'button',
|
||||
createElement('span', button => {
|
||||
button.className = 'button';
|
||||
button.addEventListener('click', () => {
|
||||
let index = grid.selectedIndex;
|
||||
const n = { column: '', order: 'asc' };
|
||||
if (index >= 0) {
|
||||
index += 1;
|
||||
grid.addItem(n, index);
|
||||
} else {
|
||||
grid.addItem(n);
|
||||
index = grid.source.length - 1;
|
||||
}
|
||||
reload(index);
|
||||
});
|
||||
},
|
||||
createIcon('fa-light', 'plus'),
|
||||
createElement('span', span => {
|
||||
span.innerText = this.langs.addLevel;
|
||||
span.addEventListener('click', () => {
|
||||
let index = grid.selectedIndex;
|
||||
const n = { column: '', order: 'asc' };
|
||||
if (index >= 0) {
|
||||
index += 1;
|
||||
grid.addItem(n, index);
|
||||
} else {
|
||||
grid.addItem(n);
|
||||
index = grid.source.length - 1;
|
||||
}
|
||||
reload(index);
|
||||
});
|
||||
})
|
||||
),
|
||||
createElement('span', 'button ui-button-delete',
|
||||
createElement('span', button => {
|
||||
button.className = 'button ui-button-delete';
|
||||
button.addEventListener('click', () => {
|
||||
let index = grid.selectedIndex;
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
grid.removeItem(index);
|
||||
const length = grid.source.length;
|
||||
if (index >= length) {
|
||||
index = length - 1;
|
||||
}
|
||||
reload(index);
|
||||
});
|
||||
},
|
||||
createIcon('fa-light', 'times'),
|
||||
createElement('span', span => {
|
||||
span.innerText = this.langs.deleteLevel;
|
||||
span.addEventListener('click', () => {
|
||||
let index = grid.selectedIndex;
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
grid.removeItem(index);
|
||||
const length = grid.source.length;
|
||||
if (index >= length) {
|
||||
index = length - 1;
|
||||
}
|
||||
reload(index);
|
||||
});
|
||||
})
|
||||
),
|
||||
createElement('span', 'button ui-button-copy',
|
||||
createElement('span', button => {
|
||||
button.className = 'button ui-button-copy';
|
||||
button.addEventListener('click', () => {
|
||||
const index = grid.selectedIndex;
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
const item = grid.source[index];
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
grid.addItem(Object.assign({}, item), index + 1);
|
||||
reload(index + 1);
|
||||
});
|
||||
},
|
||||
createIcon('fa-light', 'copy'),
|
||||
createElement('span', span => {
|
||||
span.innerText = this.langs.copyLevel;
|
||||
span.addEventListener('click', () => {
|
||||
const index = grid.selectedIndex;
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
const item = grid.source[index];
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
grid.addItem(Object.assign({}, item), index + 1);
|
||||
reload(index + 1);
|
||||
});
|
||||
})
|
||||
),
|
||||
/*
|
||||
@ -2444,22 +2458,39 @@ export class Grid {
|
||||
if (isNaN(direction)) {
|
||||
direction = 1;
|
||||
}
|
||||
const editing = col.sortAsText !== true;
|
||||
return (a, b) => {
|
||||
a = this._getItemProp(a.values, true, col);
|
||||
b = this._getItemProp(b.values, true, col);
|
||||
if (typeof a === 'boolean') {
|
||||
a = a ? 2 : 1;
|
||||
}
|
||||
if (typeof b === 'boolean') {
|
||||
b = b ? 2 : 1;
|
||||
}
|
||||
if (a == null && typeof b === 'number') {
|
||||
a = 0;
|
||||
} else if (typeof a === 'number' && b == null) {
|
||||
b = 0;
|
||||
} else if (a != null && b == null) {
|
||||
return direction;
|
||||
a = this._getItemSortProp(a.values, editing, col);
|
||||
b = this._getItemSortProp(b.values, editing, col);
|
||||
if (editing) {
|
||||
if (typeof a === 'boolean') {
|
||||
a = a ? 2 : 1;
|
||||
}
|
||||
if (typeof b === 'boolean') {
|
||||
b = b ? 2 : 1;
|
||||
}
|
||||
if (a == null && typeof b === 'number') {
|
||||
a = 0;
|
||||
} else if (typeof a === 'number' && b == null) {
|
||||
b = 0;
|
||||
} else if (a != null && b == null) {
|
||||
return direction;
|
||||
} else {
|
||||
if (Array.isArray(a)) {
|
||||
a = a.join(', ');
|
||||
}
|
||||
if (Array.isArray(b)) {
|
||||
b = b.join(', ');
|
||||
}
|
||||
if (typeof a === 'string' && typeof b === 'string') {
|
||||
a = a.toLowerCase();
|
||||
b = b.toLowerCase();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (a != null && b == null) {
|
||||
return direction;
|
||||
}
|
||||
if (Array.isArray(a)) {
|
||||
a = a.join(', ');
|
||||
}
|
||||
@ -3546,6 +3577,21 @@ export class Grid {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {GridRowItem} item
|
||||
* @param {boolean} editing
|
||||
* @param {GridColumnDefinition} col
|
||||
* @returns {any}
|
||||
*/
|
||||
_getItemSortProp(item, editing, col) {
|
||||
const value = item[col.key];
|
||||
if (value != null && Object.prototype.hasOwnProperty.call(value, 'SortValue')) {
|
||||
return value.SortValue;
|
||||
}
|
||||
return this._getItemProp(item, editing, col);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {HTMLElement} target
|
||||
@ -3591,22 +3637,46 @@ export class Grid {
|
||||
return;
|
||||
}
|
||||
if (!this._notHeader(e.target.tagName)) {
|
||||
const index = this.columns.indexOf(col);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
if (this.sortIndex === index) {
|
||||
this.sortDirection = this.sortDirection === 1 ? -1 : 1;
|
||||
if (Array.isArray(this.sortArray) && this.sortArray.length > 0) {
|
||||
showConfirm(this.langs.sort, this.langs.sortArrayExists, [
|
||||
{
|
||||
key: 'yes',
|
||||
text: this.langs.yes
|
||||
},
|
||||
{
|
||||
text: this.langs.cancel
|
||||
}
|
||||
]).then(result => {
|
||||
if (result?.key === 'yes') {
|
||||
this._onDoHeaderSort(col);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.sortIndex = index;
|
||||
}
|
||||
this.sortColumn();
|
||||
if (typeof this.onColumnChanged === 'function') {
|
||||
this.onColumnChanged(ColumnChangedType.Sort, index, this.sortDirection);
|
||||
this._onDoHeaderSort(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {GridColumnDefinition} col
|
||||
*/
|
||||
_onDoHeaderSort(col) {
|
||||
const index = this.columns.indexOf(col);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
if (this.sortIndex === index) {
|
||||
this.sortDirection = this.sortDirection === 1 ? -1 : 1;
|
||||
} else {
|
||||
this.sortIndex = index;
|
||||
}
|
||||
this.sortColumn();
|
||||
if (typeof this.onColumnChanged === 'function') {
|
||||
this.onColumnChanged(ColumnChangedType.Sort, index, this.sortDirection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} [e]
|
||||
|
@ -1,7 +1,7 @@
|
||||
import "./css/media.scss";
|
||||
import { createElement } from "../functions";
|
||||
import { createIcon } from "./icon";
|
||||
import { get } from "../utility";
|
||||
import { get } from "../utility/request";
|
||||
|
||||
export function createPicture(url) {
|
||||
return createElement('a', a => {
|
||||
|
3
lib/ui/popup.d.ts
vendored
3
lib/ui/popup.d.ts
vendored
@ -69,7 +69,8 @@ export class Popup {
|
||||
}
|
||||
|
||||
interface PopupButton {
|
||||
tabIndex: number;
|
||||
className?: string;
|
||||
tabIndex?: number;
|
||||
key: string;
|
||||
text: string;
|
||||
trigger: (this: Popup) => boolean | Promise<boolean>;
|
||||
|
@ -271,6 +271,9 @@ export class Popup {
|
||||
container.appendChild(
|
||||
createElement('div', 'ui-popup-footer', ...option.buttons.map((b, i) => {
|
||||
const button = createElement('button', 'ui-popup-button');
|
||||
if (b.className != null) {
|
||||
button.classList.add(b.className);
|
||||
}
|
||||
if (b.tabIndex > 0) {
|
||||
button.tabIndex = b.tabIndex;
|
||||
} else {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import './css/tooltip.scss';
|
||||
import { createElement } from "../functions";
|
||||
// import { global } from "../utility";
|
||||
|
||||
const pointerHeight = 12;
|
||||
|
||||
|
@ -53,6 +53,22 @@ function isPhone(text) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function verifyPassword(password, min) {
|
||||
if (password == null || typeof password !== 'string') {
|
||||
return false;
|
||||
}
|
||||
if (password.length < 8) {
|
||||
return false;
|
||||
}
|
||||
min ??= 3;
|
||||
let secure = 0;
|
||||
if (/[0-9]/.test(password)) { secure++ }
|
||||
if (/[a-z]/.test(password)) { secure++ }
|
||||
if (/[A-Z]/.test(password)) { secure++ }
|
||||
if (/[^0-9a-zA-Z]/.test(password)) { secure++ }
|
||||
return secure >= min;
|
||||
}
|
||||
|
||||
export {
|
||||
// cookie
|
||||
getCookie,
|
||||
@ -83,5 +99,6 @@ export {
|
||||
debounce,
|
||||
truncate,
|
||||
isEmail,
|
||||
isPhone
|
||||
isPhone,
|
||||
verifyPassword
|
||||
}
|
2
lib/utility/cookie.d.ts
vendored
2
lib/utility/cookie.d.ts
vendored
@ -1,3 +1,3 @@
|
||||
export function getCookie(name: string): string
|
||||
export function setCookie(name: string, value: string, expireDays?: Number): void
|
||||
export function setCookie(name: string, value: string, expireDays?: Number, host?: string, encode?: boolean): void
|
||||
export function deleteCookie(name: string): void
|
@ -1,8 +1,8 @@
|
||||
export function setCookie(name, value, expireDays) {
|
||||
export function setCookie(name, value, expireDays, host, encode) {
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
let extra = `; domain=${location.host}; path=/`;
|
||||
let extra = `; domain=${host ?? location.hostname}; path=/`;
|
||||
if (expireDays != null) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (expireDays * 24 * 60 * 60 * 1000));
|
||||
@ -11,7 +11,10 @@ export function setCookie(name, value, expireDays) {
|
||||
if (/^(https|wss):$/.test(location.protocol)) {
|
||||
extra += '; secure';
|
||||
}
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}${extra}`;
|
||||
if (encode !== false) {
|
||||
value = encodeURIComponent(value);
|
||||
}
|
||||
document.cookie = `${name}=${value}${extra}`;
|
||||
}
|
||||
|
||||
export function getCookie(name) {
|
||||
|
@ -23,17 +23,21 @@ function getCurrentLgId() {
|
||||
lgid = 'en';
|
||||
}
|
||||
switch (lgid) {
|
||||
case 'en':
|
||||
case 'en_au':
|
||||
case 'fr':
|
||||
case 'en_ca':
|
||||
case 'fr_ca':
|
||||
case 'zh_cn':
|
||||
return lgid;
|
||||
}
|
||||
const lang = lgid.split('_')[0];
|
||||
switch (lang) {
|
||||
case 'en':
|
||||
case 'es':
|
||||
case 'fr':
|
||||
case 'pt':
|
||||
return lang;
|
||||
case 'zh':
|
||||
return 'zh_cn';
|
||||
}
|
||||
return 'en';
|
||||
}
|
||||
@ -88,14 +92,20 @@ function applyLanguage(dom, result) {
|
||||
} else {
|
||||
text.innerText = getLanguage(result, key, text.innerText);
|
||||
}
|
||||
// delete text.dataset.lgid;
|
||||
text.dataset.lgid = '';
|
||||
}
|
||||
for (let title of dom.querySelectorAll('[data-title-lgid]')) {
|
||||
const key = title.dataset.titleLgid;
|
||||
title.setAttribute('title', getLanguage(result, key, title.getAttribute('title')));
|
||||
// delete title.dataset.titleLgid;
|
||||
title.dataset.titleLgid = '';
|
||||
}
|
||||
for (let holder of dom.querySelectorAll('[data-placeholder-lgid]')) {
|
||||
const key = holder.dataset.placeholderLgid;
|
||||
holder.setAttribute('placeholder', getLanguage(result, key, holder.getAttribute('placeholder')));
|
||||
// delete holder.dataset.placeholderLgid;
|
||||
holder.dataset.placeholderLgid = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user