fix: tooltip issue about customer follower

change: Grid.export optimized (compression with `deflate`)
feature: ui-tab
feature: Popup.closable
This commit is contained in:
Chen Lily 2024-03-12 13:36:54 +08:00
parent f54eb3ac24
commit e07342a257
10 changed files with 160 additions and 43 deletions

View File

@ -360,7 +360,7 @@ export default class CustomerCommunication {
);
this._var.followers.appendChild(item);
if (span.scrollWidth > span.offsetWidth) {
tips.splice(0, 0, r('P_WO_NAME_COLON', 'Name:') + ` ${c.Name}`);
tips.splice(0, 0, r('P_WO_NAME_COLON', 'Name:') + ` ${f.Name}`);
}
setTooltip(span, tips.join('\n'));
}

View File

@ -4,6 +4,7 @@ import { createElement } from "./functions";
import { createIcon, changeIcon, resolveIcon } from "./ui/icon";
import { createCheckbox, createRadiobox, resolveCheckbox } from "./ui/checkbox";
import { setTooltip, resolveTooltip } from "./ui/tooltip";
import { createTab } from "./ui/tab";
import { Dropdown } from "./ui/dropdown";
import { Grid } from "./ui/grid/grid";
import { GridColumn, GridInputColumn, GridDropdownColumn, GridCheckboxColumn, GridIconColumn, GridTextColumn, GridDateColumn } from './ui/grid/column';
@ -25,6 +26,8 @@ export {
// tooltip
setTooltip,
resolveTooltip,
// tab
createTab,
// dropdown
Dropdown,
// grid

View File

@ -70,6 +70,10 @@ $buttonHeight: 28px;
cursor: move;
}
>.ui-popup-header-title.no-move {
cursor: default;
}
>.ui-popup-header-icons {
flex: 0 0 auto;
padding: 10px 12px 6px 0;

47
lib/ui/css/tab.scss Normal file
View File

@ -0,0 +1,47 @@
.ui-tab-container {
display: flex;
flex-direction: column;
--header-line-height: 24px;
--header-padding: 6px 6px 0;
--header-hover-color: #b9b9b9;
--header-selected-color: rgb(173, 84, 12);
>.ui-tab-header {
flex: 0 0 auto;
display: flex;
flex-wrap: wrap;
line-height: var(--header-line-height);
padding: var(--header-padding);
border-bottom: 1px solid var(--border-color);
user-select: none;
>.ui-tab-title {
border: 1px solid transparent;
background-color: var(--bg-color);
cursor: pointer;
padding: 0 12px;
&:hover {
color: var(--header-hover-color);
}
&.selected {
border-color: var(--border-color);
border-bottom-color: var(--bg-color);
color: var(--header-selected-color);
}
}
}
>.ui-tab-page {
flex: 1 1 auto;
padding: 6px;
overflow: auto;
width: 100%;
height: 100%;
box-sizing: border-box;
position: relative;
display: none;
}
}

View File

@ -16,6 +16,8 @@ import { GridColumn, GridInputColumn, GridTextColumn, GridDropdownColumn, GridCh
* @version 1.0.2
*/
const ScriptPath = (self.document == null ? self.location.href : self.document.currentScript.src).replace(/\?.+$/, '');
const ColumnChangedType = {
Reorder: 'reorder',
Resize: 'resize',
@ -2092,7 +2094,7 @@ export class Grid {
* }>
* }
* ```
* @param {boolean} compressed - 是否压缩
* @param {boolean} [compressed=true] - 是否采用 deflate 压缩
* @returns {Promise<Uint8Array>} 返回 `Uint8Array` 数据对象
* @since 1.0.2
*/
@ -2150,15 +2152,14 @@ export class Grid {
sortKey: this.sortKey,
sortArray: this.sortArray
};
const json = JSON.stringify(data);
const uncompressed = json => new TextEncoder('utf-8').encode(json);
if (compressed === false) {
const encoder = new TextEncoder();
const uncompressed = encoder.encode(JSON.stringify(data));
return Promise.resolve(uncompressed);
return Promise.resolve(uncompressed(json));
}
const js = new Blob(['function h(e,t,o){if(null==e)return"";let r;const l={},h={};let s="",n="",p="",a=2,f=3,c=2;const u=[];let i=0,d=0;for(let w=0;w<e.length;w+=1)if(s=e.charAt(w),Object.prototype.hasOwnProperty.call(l,s)||(l[s]=f++,h[s]=!0),n=p+s,Object.prototype.hasOwnProperty.call(l,n))p=n;else{if(Object.prototype.hasOwnProperty.call(h,p)){if(p.charCodeAt(0)<256){for(let e=0;e<c;e++)i<<=1,d==t-1?(d=0,u.push(o(i)),i=0):d++;r=p.charCodeAt(0);for(let e=0;e<8;e++)i=i<<1|1&r,d==t-1?(d=0,u.push(o(i)),i=0):d++,r>>=1}else{r=1;for(let e=0;e<c;e++)i=i<<1|r,d==t-1?(d=0,u.push(o(i)),i=0):d++,r=0;r=p.charCodeAt(0);for(let e=0;e<16;e++)i=i<<1|1&r,d==t-1?(d=0,u.push(o(i)),i=0):d++,r>>=1}0==--a&&(a=Math.pow(2,c),c++),delete h[p]}else{r=l[p];for(let e=0;e<c;e++)i=i<<1|1&r,d==t-1?(d=0,u.push(o(i)),i=0):d++,r>>=1}0==--a&&(a=Math.pow(2,c),c++),l[n]=f++,p=String(s)}if(""!==p){if(Object.prototype.hasOwnProperty.call(h,p)){if(p.charCodeAt(0)<256){for(let e=0;e<c;e++)i<<=1,d==t-1?(d=0,u.push(o(i)),i=0):d++;r=p.charCodeAt(0);for(let e=0;e<8;e++)i=i<<1|1&r,d==t-1?(d=0,u.push(o(i)),i=0):d++,r>>=1}else{r=1;for(let e=0;e<c;e++)i=i<<1|r,d==t-1?(d=0,u.push(o(i)),i=0):d++,r=0;r=p.charCodeAt(0);for(let e=0;e<16;e++)i=i<<1|1&r,d==t-1?(d=0,u.push(o(i)),i=0):d++,r>>=1}0==--a&&(a=Math.pow(2,c),c++),delete h[p]}else{r=l[p];for(let e=0;e<c;e++)i=i<<1|1&r,d==t-1?(d=0,u.push(o(i)),i=0):d++,r>>=1}0==--a&&(a=Math.pow(2,c),c++)}r=2;for(let e=0;e<c;e++)i=i<<1|1&r,d==t-1?(d=0,u.push(o(i)),i=0):d++,r>>=1;let w=!0;do{i<<=1,d==t-1?(u.push(o(i)),w=!1):d++}while(w);return u.join("")}function s(e){return null==e?"":h(e,16,e=>String.fromCharCode(e))}function i(e){const t=s(e),o=new Uint8Array(2*t.length);for(let e=0,r=t.length;e<r;e++){const r=t.charCodeAt(e);o[2*e]=r>>>8,o[2*e+1]=r%256}return o}self.addEventListener("message",function(e){this.self.postMessage(i(e.data))},!1);']);
return new Promise((resolve, reject) => {
return new Promise(resolve => {
let working;
const url = URL.createObjectURL(js);
const url = URL.createObjectURL(new Blob([`let wasm,WASM_VECTOR_LEN=0,encoder=new TextEncoder("utf-8"),cachegetUint8Memory0=null;function getUint8Memory0(){return null!==cachegetUint8Memory0&&cachegetUint8Memory0.buffer===wasm.memory.buffer||(cachegetUint8Memory0=new Uint8Array(wasm.memory.buffer)),cachegetUint8Memory0}let cachegetInt32Memory0=null;function getInt32Memory0(){return null!==cachegetInt32Memory0&&cachegetInt32Memory0.buffer===wasm.memory.buffer||(cachegetInt32Memory0=new Int32Array(wasm.memory.buffer)),cachegetInt32Memory0}function passArray8ToWasm0(e,t){const n=t(1*e.length);return getUint8Memory0().set(e,n/1),WASM_VECTOR_LEN=e.length,n}function getArrayU8FromWasm0(e,t){return getUint8Memory0().subarray(e/1,e/1+t)}function deflate_encode_raw(e){var t=passArray8ToWasm0(e,wasm.__wbindgen_malloc),n=WASM_VECTOR_LEN;wasm.deflate_encode_raw(8,t,n);var r=getInt32Memory0()[2],a=getInt32Memory0()[3],s=getArrayU8FromWasm0(r,a).slice();return wasm.__wbindgen_free(r,1*a),s}self.addEventListener("message",e=>{const t=e.data.type;if("init"===t)if("function"==typeof WebAssembly.instantiateStreaming){const t={},n=fetch(e.data.module);WebAssembly.instantiateStreaming(n,t).then(({instance:e})=>{wasm=e.exports,self.postMessage({type:"init",result:0})}).catch(e=>n.then(t=>{"application/wasm"!==t.headers.get("Content-Type")?self.postMessage({type:"init",error:"\`WebAssembly.instantiateStreaming\` failed because your server does not serve wasm with \`application/wasm\` MIME type. Original error: "+e.message}):self.postMessage({type:"init",error:e.message})}))}else self.postMessage({type:"init",error:"no \`WebAssembly.instantiateStreaming\`"});else if("compress"===t)if(null==wasm)self.postMessage({error:"no \`wasm\` instance"});else{let t=deflate_encode_raw(encoder.encode(e.data.text));self.postMessage(t,[t.buffer])}});`]));
const worker = new Worker(url);
/**
* @private
@ -2171,26 +2172,37 @@ export class Grid {
URL.revokeObjectURL(url);
next(data);
}
// 超过 60 秒则拒绝
// 超过 30 秒则返回无压缩数据
const timer = setTimeout(() => {
if (working) {
terminate(reject, { message: 'timeout' });
// terminate(reject, { message: 'timeout' });
terminate(resolve, { data: uncompressed(json), error: 'timeout' });
}
}, 60000);
}, 30000);
worker.addEventListener('message', e => {
if (working) {
clearTimeout(timer);
terminate(resolve, e.data);
if (e.data.error != null) {
// terminate(reject, { message: e.data.error });
terminate(resolve, { data: uncompressed(json), error: e.data.error });
} else {
if (e.data.type === 'init') {
worker.postMessage({ type: 'compress', text: json });
} else {
clearTimeout(timer);
terminate(resolve, { type: 'compressed', data: e.data });
}
}
}
})
worker.addEventListener('error', e => {
if (working) {
clearTimeout(timer);
terminate(reject, e);
// terminate(reject, e);
terminate(resolve, { data: uncompressed(json), error: e.message });
}
})
working = true;
worker.postMessage(JSON.stringify(data));
worker.postMessage({ type: 'init', module: ScriptPath.replace(/ui\.min\.js$/, 'wasm_flate_bg.wasm') });
});
}

2
lib/ui/popup.d.ts vendored
View File

@ -13,6 +13,8 @@ interface PopupOptions {
zIndex?: number;
/** 是否在获取焦点时修改 z-index */
changeZIndex?: boolean;
/** 是否允许关闭 */
closable?: boolean;
/** 是否允许移动 */
movable?: boolean;
/** 是否允许修改大小 */

View File

@ -172,7 +172,11 @@ export class Popup {
let title = option.title;
if (!(title instanceof HTMLElement)) {
title = createElement('div', t => {
t.className = 'ui-popup-header-title';
if (option.movable === false) {
t.className = 'ui-popup-header-title no-move';
} else {
t.className = 'ui-popup-header-title';
}
t.innerText = title;
});
}
@ -244,15 +248,17 @@ export class Popup {
});
icons.appendChild(collapse);
}
const cancel = createIcon('fa-regular', 'times');
cancel.tabIndex = tabIndex + 3;
cancel.addEventListener('keypress', e => {
if (e.key === ' ' || e.key === 'Enter') {
this.close();
}
});
cancel.addEventListener('click', () => this.close());
icons.appendChild(cancel);
if (option.closable !== false) {
const cancel = createIcon('fa-regular', 'times');
cancel.tabIndex = tabIndex + 3;
cancel.addEventListener('keypress', e => {
if (e.key === ' ' || e.key === 'Enter') {
this.close();
}
});
cancel.addEventListener('click', () => this.close());
icons.appendChild(cancel);
}
});
header.appendChild(icons);
}),

43
lib/ui/tab.js Normal file
View File

@ -0,0 +1,43 @@
import "./css/tab.scss";
/**
* Tab 页创建参数类
* @typedef TabOption
* @property {HTMLElement} container - 父容器
*/
import { createElement } from "../functions";
/**
* 创建 Tab
* @param {TabOption | HTMLElement} options - 创建选项
*/
export function createTab(options) {
if (options instanceof HTMLElement) {
options = { container: options };
}
let container;
if (options?.container instanceof HTMLElement) {
container = options.container;
if (!container.classList.contains('ui-tab-container')) {
container.classList.add('ui-tab-container');
}
} else {
container = createElement('div', 'ui-tab-container');
}
container.replaceChildren(
createElement('div', header => {
header.className = 'ui-tab-header';
header.addEventListener('click', e => {
const title = e.target;
if (title.classList.contains('ui-tab-title')) {
// title
header.querySelectorAll('.ui-tab-title').forEach(t => t === title ? t.classList.add('selected') : t.classList.remove('selected'));
// pages
const page = title.dataset.for;
container.querySelectorAll('[data-page]').forEach(p => p.style.display = p.dataset.page === page ? 'block' : '');
}
});
})
);
}

34
package-lock.json generated
View File

@ -14,7 +14,7 @@
"jsdoc": "^4.0.2",
"postcss-preset-env": "^9.5.0",
"sass": "^1.71.1",
"typedoc": "^0.25.11",
"typedoc": "^0.25.12",
"vite": "^5.1.5",
"vite-plugin-externals": "^0.6.2"
}
@ -1823,9 +1823,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001594",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001594.tgz",
"integrity": "sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==",
"version": "1.0.30001597",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz",
"integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==",
"dev": true,
"funding": [
{
@ -1991,9 +1991,9 @@
}
},
"node_modules/cssdb": {
"version": "7.11.1",
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.1.tgz",
"integrity": "sha512-F0nEoX/Rv8ENTHsjMPGHd9opdjGfXkgRBafSUGnQKPzGZFB7Lm0BbT10x21TMOCrKLbVsJ0NoCDMk6AfKqw8/A==",
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz",
"integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==",
"dev": true,
"funding": [
{
@ -2038,9 +2038,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.694",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.694.tgz",
"integrity": "sha512-kM3SwvGTYpBFJSc8jm4IYVMIOzDmAGd/Ry96O9elRiM6iEwHKNKhtXyFGzpfMMIGZD84W4/hyaULlMmNVvLQlQ==",
"version": "1.4.699",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.699.tgz",
"integrity": "sha512-I7q3BbQi6e4tJJN5CRcyvxhK0iJb34TV8eJQcgh+fR2fQ8miMgZcEInckCo1U9exDHbfz7DLDnFn8oqH/VcRKw==",
"dev": true
},
"node_modules/entities": {
@ -3463,9 +3463,9 @@
"dev": true
},
"node_modules/typedoc": {
"version": "0.25.11",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.11.tgz",
"integrity": "sha512-5MbI1W/FOG6oXsd8bdssQidSTeKh8Kt3xA5uKVzI+K99uzP8EGN45uPnPvQesyaWdD+89s4wCQdtWEd8QUbiRg==",
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.12.tgz",
"integrity": "sha512-F+qhkK2VoTweDXd1c42GS/By2DvI2uDF4/EpG424dTexSHdtCH52C6IcAvMA6jR3DzAWZjHpUOW+E02kyPNUNw==",
"dev": true,
"dependencies": {
"lunr": "^2.3.9",
@ -3480,13 +3480,13 @@
"node": ">= 16"
},
"peerDependencies": {
"typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x"
"typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x"
}
},
"node_modules/typescript": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"dev": true,
"peer": true,
"bin": {

View File

@ -34,7 +34,7 @@
"jsdoc": "^4.0.2",
"postcss-preset-env": "^9.5.0",
"sass": "^1.71.1",
"typedoc": "^0.25.11",
"typedoc": "^0.25.12",
"vite": "^5.1.5",
"vite-plugin-externals": "^0.6.2"
}