mirror of
https://gitee.com/orangeform/orange-admin.git
synced 2026-01-18 02:56:30 +08:00
commit:升级到vue3,更新最近工作流技术栈,支持sa-token
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
/* eslint no-bitwise: "off" */
|
||||
/*
|
||||
v: int value
|
||||
digit: bit len of v
|
||||
flag: true or false
|
||||
*/
|
||||
const bitmap = (v, digit, flag) => {
|
||||
const b = 1 << digit;
|
||||
return flag ? v | b : v ^ b;
|
||||
};
|
||||
export default bitmap;
|
||||
@@ -0,0 +1,39 @@
|
||||
// src: include chars: [0-9], +, -, *, /
|
||||
// // 9+(3-1)*3+10/2 => 9 3 1-3*+ 10 2/+
|
||||
const infix2suffix = src => {
|
||||
const operatorStack = [];
|
||||
const stack = [];
|
||||
for (let i = 0; i < src.length; i += 1) {
|
||||
const c = src.charAt(i);
|
||||
if (c !== ' ') {
|
||||
if (c >= '0' && c <= '9') {
|
||||
stack.push(c);
|
||||
} else if (c === ')') {
|
||||
let c1 = operatorStack.pop();
|
||||
while (c1 !== '(') {
|
||||
stack.push(c1);
|
||||
c1 = operatorStack.pop();
|
||||
}
|
||||
} else {
|
||||
// priority: */ > +-
|
||||
if (operatorStack.length > 0 && (c === '+' || c === '-')) {
|
||||
const last = operatorStack[operatorStack.length - 1];
|
||||
if (last === '*' || last === '/') {
|
||||
while (operatorStack.length > 0) {
|
||||
stack.push(operatorStack.pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
operatorStack.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (operatorStack.length > 0) {
|
||||
stack.push(operatorStack.pop());
|
||||
}
|
||||
return stack;
|
||||
};
|
||||
|
||||
export default {
|
||||
infix2suffix,
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 171 KiB |
@@ -0,0 +1,137 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" width="262px" height="72px" viewBox="0 0 262 72" preserveAspectRatio="none">
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(0,0)"><path fill="#000000" fill-rule="evenodd" d="M11.5656391,4.43436088 L9,7 L16,7 L16,0 L13.0418424,2.95815758 C11.5936787,1.73635959 9.72260775,1 7.67955083,1 C4.22126258,1 1.25575599,3.10984908 0,6 L2,7 C2.93658775,4.60974406 5.12943697,3.08011229 7.67955083,3 C9.14881247,3.0528747 10.4994783,3.57862053 11.5656391,4.43436088 Z" transform="matrix(-1 0 0 1 17 5)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(18,0)"><path fill="#000000" fill-rule="evenodd" d="M11.5656391,4.43436088 L9,7 L16,7 L16,0 L13.0418424,2.95815758 C11.5936787,1.73635959 9.72260775,1 7.67955083,1 C4.22126258,1 1.25575599,3.10984908 0,6 L2,7 C2.93658775,4.60974406 5.12943697,3.08011229 7.67955083,3 C9.14881247,3.0528747 10.4994783,3.57862053 11.5656391,4.43436088 Z" transform="translate(1 5)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(36,0)"><path fill="#000000" fill-rule="evenodd" d="M13,14 L3,14 L3,11 L0,11 L0,6.00591905 C0,4.89808055 0.894513756,4 1.99406028,4 L14.0059397,4 C15.1072288,4 16,4.88655484 16,6.00591905 L16,11 L13,11 L13,14 Z M5,9 L11,9 L11,12 L5,12 L5,9 Z M3,0 L13,0 L13,3 L3,3 L3,0 Z M12,6 L14,6 L14,8 L12,8 L12,6 Z" transform="translate(1 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(54,0)"><path fill="#000000" fill-rule="evenodd" d="M9,0 L1,0 C0.45,0 0,0.45 0,1 L0,4 C0,4.55 0.45,5 1,5 L9,5 C9.55,5 10,4.55 10,4 L10,3 L11,3 L11,6 L4,6 L4,14 L6,14 L6,8 L13,8 L13,2 L10,2 L10,1 C10,0.45 9.55,0 9,0 Z" transform="translate(3 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(72,0)"><path fill="#000000" fill-rule="evenodd" d="M0.27,1.55 L5.43,6.7 L3,12 L5.5,12 L7.14,8.42 L11.73,13 L13,11.73 L1.55,0.27 L0.27,1.55 L0.27,1.55 Z M3.82,0 L5.82,2 L7.58,2 L7.03,3.21 L8.74,4.92 L10.08,2 L14,2 L14,0 L3.82,0 L3.82,0 Z" transform="translate(2 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(90,0)"><path fill="#000000" fill-rule="evenodd" d="M9,3.5 C9,1.57 7.43,0 5.5,0 L1.77635684e-15,0 L1.77635684e-15,12 L6.25,12 C8.04,12 9.5,10.54 9.5,8.75 C9.5,7.45 8.73,6.34 7.63,5.82 C8.46,5.24 9,4.38 9,3.5 Z M5,2 C5.82999992,2 6.5,2.67 6.5,3.5 C6.5,4.33 5.82999992,5 5,5 L3,5 L3,2 L5,2 Z M3,10 L3,7 L5.5,7 C6.32999992,7 7,7.67 7,8.5 C7,9.33 6.32999992,10 5.5,10 L3,10 Z" transform="translate(4 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(108,0)"><polygon fill="#000000" fill-rule="evenodd" points="4 0 4 2 6.58 2 2.92 10 0 10 0 12 8 12 8 10 5.42 10 9.08 2 12 2 12 0" transform="translate(3 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(126,0)"><path fill="#000000" d="M6,12 C8.76,12 11,9.76 11,7 L11,0 L9,0 L9,7 C9,8.75029916 7.49912807,10 6,10 C4.50087193,10 3,8.75837486 3,7 L3,0 L1,0 L1,7 C1,9.76 3.24,12 6,12 Z M0,13 L0,15 L12,15 L12,13 L0,13 Z" transform="translate(3 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(144,0)"><path fill="#010101" fill-rule="evenodd" d="M2.8875,3.06 C2.8875,2.6025 2.985,2.18625 3.18375,1.8075 C3.3825,1.42875 3.66,1.10625 4.02,0.84 C4.38,0.57375 4.80375,0.3675 5.29875,0.22125 C5.79375,0.075 6.33375,0 6.92625,0 C7.53375,0 8.085,0.0825 8.58,0.25125 C9.075,0.42 9.49875,0.6525 9.85125,0.95625 C10.20375,1.25625 10.47375,1.6125 10.665,2.02875 C10.85625,2.44125 10.95,2.895 10.95,3.38625 L8.6925,3.38625 C8.6925,3.1575 8.655,2.94375 8.58375,2.74875 C8.5125,2.55 8.4,2.38125 8.25,2.2425 C8.1,2.10375 7.9125,1.99125 7.6875,1.91625 C7.4625,1.8375 7.19625,1.8 6.88875,1.8 C6.5925,1.8 6.3375,1.83375 6.11625,1.8975 C5.89875,1.96125 5.71875,2.05125 5.57625,2.1675 C5.43375,2.28375 5.325,2.41875 5.25375,2.5725 C5.1825,2.72625 5.145,2.895 5.145,3.0675 C5.145,3.4275 5.32875,3.73125 5.69625,3.975 C5.71780203,3.98908066 5.73942012,4.00311728 5.76118357,4.01733315 C6.02342923,4.18863185 6.5,4.5 7,5 L4,5 C4,5 3.21375,4.37625 3.17625,4.30875 C2.985,3.9525 2.8875,3.53625 2.8875,3.06 Z M14,6 L0,6 L0,8 L7.21875,8 C7.35375,8.0525 7.51875,8.105 7.63125,8.15375 C7.90875,8.2775 8.12625,8.40875 8.28375,8.53625 C8.44125,8.6675 8.54625,8.81 8.6025,8.96 C8.65875,9.11375 8.685,9.28625 8.685,9.47375 C8.685,9.65 8.65125,9.815 8.58375,9.965 C8.51625,10.11875 8.41125,10.25 8.2725,10.35875 C8.13375,10.4675 7.95375,10.55375 7.74,10.6175 C7.5225,10.68125 7.27125,10.71125 6.97875,10.71125 C6.6525,10.71125 6.35625,10.6775 6.09,10.61375 C5.82375,10.55 5.59875,10.445 5.41125,10.3025 C5.22375,10.16 5.0775,9.9725 4.9725,9.74375 C4.8675,9.515 4.78125,9.17 4.78125,9 L2.55,9 C2.55,9.2525 2.61,9.6875 2.72625,10.025 C2.8425,10.3625 3.0075,10.66625 3.21375,10.9325 C3.42,11.19875 3.6675,11.4275 3.94875,11.6225 C4.23,11.8175 4.53375,11.9825 4.86375,12.11 C5.19375,12.24125 5.535,12.33875 5.89875,12.39875 C6.25875,12.4625 6.6225,12.4925 6.9825,12.4925 C7.5825,12.4925 8.13,12.425 8.6175,12.28625 C9.105,12.1475 9.525,11.94875 9.87,11.69375 C10.215,11.435 10.48125,11.12 10.6725,10.74125 C10.86375,10.3625 10.95375,9.935 10.95375,9.455 C10.95375,9.005 10.875,8.6 10.72125,8.24375 C10.68375,8.1575 10.6425,8.075 10.59375,7.9925 L14,8 L14,6 Z" transform="translate(2 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(162,0)"><path fill="#000000" fill-rule="evenodd" d="M7,0 L5,0 L0.5,12 L2.5,12 L3.62,9 L8.37,9 L9.49,12 L11.49,12 L7,0 L7,0 Z M4.38,7 L6,2.67 L7.62,7 L4.38,7 L4.38,7 Z" transform="translate(3 1)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(180,0)"><g fill="none" fill-rule="evenodd">
|
||||
<path fill="#000000" d="M14.5,8.87 C14.5,8.87 13,10.49 13,11.49 C13,12.32 13.67,12.99 14.5,12.99 C15.33,12.99 16,12.32 16,11.49 C16,10.5 14.5,8.87 14.5,8.87 L14.5,8.87 Z M12.71,6.79 L5.91,0 L4.85,1.06 L6.44,2.65 L2.29,6.79 C1.9,7.18 1.9,7.81 2.29,8.2 L6.79,12.7 C6.99,12.9 7.24,13 7.5,13 C7.76,13 8.01,12.9 8.21,12.71 L12.71,8.21 C13.1,7.82 13.1,7.18 12.71,6.79 L12.71,6.79 Z M4.21,7 L7.5,3.71 L10.79,7 L4.21,7 L4.21,7 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(198,0)"><path fill="#000000" fill-rule="evenodd" d="M3,6 L1,6 L1,2 L8,2 L8,4 L3,4 L3,6 Z M10,4 L10,2 L17,2 L17,6 L15,6 L15,4 L10,4 Z M10,14 L15,14 L15,12 L17,12 L17,16 L10,16 L10,14 Z M1,12 L3,12 L3,14 L8,14 L8,16 L1,16 L1,12 Z M1,8 L5,8 L5,6 L8,9 L5,12 L5,10 L1,10 L1,8 Z M10,9 L13,6 L13,8 L17,8 L17,10 L13,10 L13,12 L10,9 Z"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(216,0)"><path fill="#000000" fill-rule="evenodd" d="M0,14 L10,14 L10,12 L0,12 L0,14 Z M10,4 L0,4 L0,6 L10,6 L10,4 Z M0,0 L0,2 L14,2 L14,0 L0,0 Z M0,10 L14,10 L14,8 L0,8 L0,10 Z" transform="translate(2 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(234,0)"><path fill="#000000" fill-rule="evenodd" d="M2,12 L2,14 L12,14 L12,12 L2,12 Z M2,4 L2,6 L12,6 L12,4 L2,4 Z M0,10 L14,10 L14,8 L0,8 L0,10 Z M0,0 L0,2 L14,2 L14,0 L0,0 Z" transform="translate(2 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(0,18)"><path fill="#000000" fill-rule="evenodd" d="M4,14 L14,14 L14,12 L4,12 L4,14 Z M0,10 L14,10 L14,8 L0,8 L0,10 Z M0,0 L0,2 L14,2 L14,0 L0,0 Z M4,6 L14,6 L14,4 L4,4 L4,6 Z" transform="translate(2 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(18,18)"><path fill="#000000" fill-rule="evenodd" d="M0,0 L0,2 L12,2 L12,0 L0,0 L0,0 Z M2.5,7 L5,7 L5,14 L7,14 L7,7 L9.5,7 L6,3.5 L2.5,7 L2.5,7 Z" transform="translate(3 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(36,18)"><path fill="#000000" fill-rule="evenodd" d="M9.5,3 L7,3 L7,0 L5,0 L5,3 L2.5,3 L6,6.5 L9.5,3 L9.5,3 Z M0,8 L0,10 L12,10 L12,8 L0,8 L0,8 Z M2.5,15 L5,15 L5,18 L7,18 L7,15 L9.5,15 L6,11.5 L2.5,15 L2.5,15 Z" transform="translate(3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(54,18)"><path fill="#000000" fill-rule="evenodd" d="M9.5,7 L7,7 L7,0 L5,0 L5,7 L2.5,7 L6,10.5 L9.5,7 L9.5,7 Z M0,12 L0,14 L12,14 L12,12 L0,12 L0,12 Z" transform="translate(3 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(72,18)"><path fill="#000000" fill-rule="evenodd" d="M14,0 L0,0 L0,2 L14,2 L14,0 Z M0,12 L4,12 L4,10 L0,10 L0,12 Z M11.5,5 L0,5 L0,7 L11.75,7 C12.58,7 13.25,7.67 13.25,8.5 C13.25,9.33 12.58,10 11.75,10 L9,10 L9,8 L6,11 L9,14 L9,12 L11.5,12 C13.43,12 15,10.43 15,8.5 C15,6.57 13.43,5 11.5,5 Z" transform="translate(2 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(90,18)"><path fill="#000000" fill-rule="evenodd" d="M0,0 L0,1 L6,7 L6,12 L8,11 L8,7 L14,1 L14,0 L0,0 Z M4,3 L10,3 L7,6 L4,3 Z" transform="translate(2 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(108,18)"><polygon fill="#000000" fill-rule="evenodd" points="10 0 0 0 0 1.8 5.5 7 0 12.2 0 14 10 14 10 12 3.1 12 8 7 3.1 2 10 2" transform="translate(4 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(126,18)"><polygon fill="#000000" fill-rule="evenodd" points="0 0 4 4 8 0" transform="translate(5 7)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(144,18)"><polygon fill="#000000" fill-rule="evenodd" points="-2 2 2 6 6 2" transform="rotate(-90 8 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(162,18)"><path fill="#000000" fill-rule="evenodd" d="M1.9,4 C1.9,2.84 2.84,1.9 4,1.9 L8,1.9 L8,0 L4,0 C1.79,0 0,1.79 0,4 C0,6.21 1.79,8 4,8 L8,8 L8,6.1 L4,6.1 C2.84,6.1 1.9,5.16 1.9,4 L1.9,4 Z M14,0 L10,0 L10,1.9 L14,1.9 C15.16,1.9 16.1,2.84 16.1,4 C16.1,5.16 15.16,6.1 14,6.1 L10,6.1 L10,8 L14,8 C16.21,8 18,6.21 18,4 C18,1.79 16.21,0 14,0 L14,0 Z M6,5 L12,5 L12,3 L6,3 L6,5 L6,5 Z" transform="translate(0 5)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(180,18)"><path fill="#000000" fill-rule="evenodd" d="M15,0 C15.55,0 16,0.45 16,1 L16,15 C16,15.55 15.55,16 15,16 L1,16 C0.45,16 0,15.55 0,15 L0,1 C0,0.45 0.45,0 1,0 L15,0 Z M2,2 L2,14 L14,14 L14,2 L2,2 Z M6,12 L4,12 L4,7 L6,7 L6,12 L6,12 Z M9,12 L7,12 L7,4 L9,4 L9,12 L9,12 Z M12,12 L10,12 L10,8 L12,8 L12,12 L12,12 Z" transform="translate(1 1)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(198,18)"><g fill="none" fill-rule="evenodd">
|
||||
<path stroke="#000000" d="M1.5 3.5H16.5V15.5H1.5z"/>
|
||||
<path fill="#000000" d="M6 8H7V15H6z"/>
|
||||
<path fill="#D8D8D8" d="M2 4H16V7H2z"/>
|
||||
<path fill="#000000" d="M2 7H16V8H2zM2 11H16V12H2z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(216,18)"><path fill="#000000" fill-rule="evenodd" d="M2,0.5 C1.17,0.5 0.5,1.17 0.5,2 C0.5,2.83 1.17,3.5 2,3.5 C2.83,3.5 3.5,2.83 3.5,2 C3.5,1.17 2.83,0.5 2,0.5 L2,0.5 Z M12,0.5 C11.17,0.5 10.5,1.17 10.5,2 C10.5,2.83 11.17,3.5 12,3.5 C12.83,3.5 13.5,2.83 13.5,2 C13.5,1.17 12.83,0.5 12,0.5 L12,0.5 Z M7,0.5 C6.17,0.5 5.5,1.17 5.5,2 C5.5,2.83 6.17,3.5 7,3.5 C7.83,3.5 8.5,2.83 8.5,2 C8.5,1.17 7.83,0.5 7,0.5 L7,0.5 Z" transform="translate(2 7)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(234,18)"><path fill="#000000" fill-rule="evenodd" d="M6,4 L6,0 L4,0 L4,4 L0,4 L0,6 L4,6 L4,10 L6,10 L6,6 L10,6 L10,4 L6,4 Z" transform="translate(4 4)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(0,36)"><path fill="#000000" fill-rule="evenodd" d="M0,0 L0,14 L14,14 L14,0 L0,0 L0,0 Z M6,12 L2,12 L2,8 L6,8 L6,12 L6,12 Z M6,6 L2,6 L2,2 L6,2 L6,6 L6,6 Z M12,12 L8,12 L8,8 L12,8 L12,12 L12,12 Z M12,6 L8,6 L8,2 L12,2 L12,6 L12,6 Z" transform="translate(2 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(18,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M2,3 L0,3 L0,5 L2,5 L2,3 L2,3 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M11,0 L9,0 L9,2 L11,2 L11,0 L11,0 Z M2,0 L0,0 L0,2 L2,2 L2,0 L2,0 Z M5,0 L3,0 L3,2 L5,2 L5,0 L5,0 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z" opacity=".54"/>
|
||||
<polygon points="8 0 6 0 6 6 0 6 0 8 6 8 6 14 8 14 8 8 14 8 14 6 8 6"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(36,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M0,2 L2,2 L2,0 L0,0 L0,2 L0,2 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M6,2 L8,2 L8,0 L6,0 L6,2 L6,2 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z" opacity=".54"/>
|
||||
<polygon points="0 8 14 8 14 6 0 6"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(54,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,2 L2,2 L2,0 L0,0 L0,2 L0,2 Z M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M0,8 L2,8 L2,6 L0,6 L0,8 L0,8 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z" opacity=".54"/>
|
||||
<polygon points="6 14 8 14 8 0 6 0"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(72,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M8,3 L6,3 L6,5 L8,5 L8,3 L8,3 Z M11,6 L9,6 L9,8 L11,8 L11,6 L11,6 Z M8,6 L6,6 L6,8 L8,8 L8,6 L8,6 Z M8,9 L6,9 L6,11 L8,11 L8,9 L8,9 Z M5,6 L3,6 L3,8 L5,8 L5,6 L5,6 Z" opacity=".54"/>
|
||||
<path d="M0,0 L14,0 L14,14 L0,14 L0,0 Z M12,12 L12,2 L2,2 L2,12 L12,12 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(90,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M6,8 L8,8 L8,6 L6,6 L6,8 L6,8 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M6,2 L8,2 L8,0 L6,0 L6,2 L6,2 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z" opacity=".54"/>
|
||||
<polygon points="0 14 2 14 2 0 0 0"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(108,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M6,8 L8,8 L8,6 L6,6 L6,8 L6,8 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,8 L2,8 L2,6 L0,6 L0,8 L0,8 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z" opacity=".54"/>
|
||||
<polygon points="0 0 0 2 14 2 14 0"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(126,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M0,2 L2,2 L2,0 L0,0 L0,2 L0,2 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,8 L2,8 L2,6 L0,6 L0,8 L0,8 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M6,2 L8,2 L8,0 L6,0 L6,2 L6,2 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M6,8 L8,8 L8,6 L6,6 L6,8 L6,8 Z" opacity=".54"/>
|
||||
<polygon points="12 0 12 14 14 14 14 0"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(144,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M5,0 L3,0 L3,2 L5,2 L5,0 L5,0 Z M8,6 L6,6 L6,8 L8,8 L8,6 L8,6 Z M8,9 L6,9 L6,11 L8,11 L8,9 L8,9 Z M11,6 L9,6 L9,8 L11,8 L11,6 L11,6 Z M5,6 L3,6 L3,8 L5,8 L5,6 L5,6 Z M11,0 L9,0 L9,2 L11,2 L11,0 L11,0 Z M8,3 L6,3 L6,5 L8,5 L8,3 L8,3 Z M8,0 L6,0 L6,2 L8,2 L8,0 L8,0 Z M2,9 L0,9 L0,11 L2,11 L2,9 L2,9 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M2,0 L0,0 L0,2 L2,2 L2,0 L2,0 Z M2,3 L0,3 L0,5 L2,5 L2,3 L2,3 Z M2,6 L0,6 L0,8 L2,8 L2,6 L2,6 Z" opacity=".54"/>
|
||||
<polygon points="0 14 14 14 14 12 0 12"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(162,36)"><path fill="#000000" fill-rule="evenodd" d="M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M0,2 L2,2 L2,0 L0,0 L0,2 L0,2 Z M0,8 L2,8 L2,6 L0,6 L0,8 L0,8 Z M6,8 L8,8 L8,6 L6,6 L6,8 L6,8 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M6,2 L8,2 L8,0 L6,0 L6,2 L6,2 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z" transform="translate(2 2)" opacity=".54"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(180,36)"><path fill="#000000" fill-rule="evenodd" d="M6.5,3.62 L0,10.12 L0,13 L2.88,13 L9.38,6.5 L6.5,3.62 Z M11.85,4.02 C12.05,3.82 12.05,3.51 11.85,3.31 L9.68,1.14 C9.48,0.94 9.17,0.94 8.97,1.14 L7.62,2.5 L10.5,5.38 L11.85,4.02 L11.85,4.02 Z" transform="translate(4)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(198,36)"><path fill="#000000" fill-rule="evenodd" d="M0,0 L14,0 L14,2 L0,2 L0,0 Z M0,4 L6,4 L6,6 L0,6 L0,4 Z M0,8 L2,8 L2,10 L0,10 L0,8 Z M8,4 L14,4 L14,6 L8,6 L8,4 Z M4,8 L6,8 L6,10 L4,10 L4,8 Z M8,8 L10,8 L10,10 L8,10 L8,8 Z M12,8 L14,8 L14,10 L12,10 L12,8 Z" transform="translate(2 4)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(216,36)"><polygon fill="#000000" fill-rule="evenodd" points="-2 2 2 6 6 2" transform="rotate(90 3 10)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(234,36)"><polygon fill="#000000" fill-rule="evenodd" points="7.53 1.53 6.47 .47 4 2.94 1.53 .47 .47 1.53 2.94 4 .47 6.47 1.53 7.53 4 5.06 6.47 7.53 7.53 6.47 5.06 4" transform="translate(5 5)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(0,54)"><polygon fill="#000000" fill-rule="evenodd" points="8.44 .44 5 3.88 1.56 .44 .5 1.5 5 6 9.5 1.5" transform="translate(4 6)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(18,54)"><polygon fill="#000000" fill-rule="evenodd" points="5 0 .5 4.5 1.56 5.56 5 2.12 8.44 5.56 9.5 4.5" transform="translate(4 6)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(36,54)"><polygon fill="#000000" fill-rule="evenodd" points="8.44 .44 5 3.88 1.56 .44 .5 1.5 5 6 9.5 1.5" transform="rotate(90 4 8)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(54,54)"><polygon fill="#000000" fill-rule="evenodd" points="5 0 .5 4.5 1.56 5.56 5 2.12 8.44 5.56 9.5 4.5" transform="rotate(90 4 8)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(72,54)"><polygon fill="#000000" fill-rule="evenodd" points="12 5 3.125 5 7.06 1.06 6 0 0 6 6 12 7.06 10.94 3.125 7 12 7" transform="matrix(-1 0 0 1 15 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(90,54)"><polygon fill="#000000" fill-rule="evenodd" points="12 5 3.125 5 7.06 1.06 6 0 0 6 6 12 7.06 10.94 3.125 7 12 7" transform="translate(3 3)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 20 KiB |
507
OrangeFormsOpen-VUE3/src/components/SpreadSheet/canvas/draw.js
Normal file
507
OrangeFormsOpen-VUE3/src/components/SpreadSheet/canvas/draw.js
Normal file
@@ -0,0 +1,507 @@
|
||||
const imageMap = new Map();
|
||||
|
||||
function dpr() {
|
||||
return window.devicePixelRatio || 1;
|
||||
}
|
||||
|
||||
function thinLineWidth() {
|
||||
return dpr() - 0.5;
|
||||
}
|
||||
|
||||
function npx(px) {
|
||||
return parseInt(px * dpr(), 10);
|
||||
}
|
||||
|
||||
function npxLine(px) {
|
||||
const n = npx(px);
|
||||
return n > 0 ? n - 0.5 : 0.5;
|
||||
}
|
||||
|
||||
class DrawBox {
|
||||
constructor(x, y, w, h, padding = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
this.padding = padding;
|
||||
this.bgcolor = '#ffffff';
|
||||
// border: [width, style, color]
|
||||
this.borderTop = null;
|
||||
this.borderRight = null;
|
||||
this.borderBottom = null;
|
||||
this.borderLeft = null;
|
||||
}
|
||||
|
||||
setBorders({ top, bottom, left, right }) {
|
||||
if (top) this.borderTop = top;
|
||||
if (right) this.borderRight = right;
|
||||
if (bottom) this.borderBottom = bottom;
|
||||
if (left) this.borderLeft = left;
|
||||
}
|
||||
|
||||
innerWidth() {
|
||||
return this.width - this.padding * 2 - 2;
|
||||
}
|
||||
|
||||
innerHeight() {
|
||||
return this.height - this.padding * 2 - 2;
|
||||
}
|
||||
|
||||
textx(align) {
|
||||
const { width, padding } = this;
|
||||
let { x } = this;
|
||||
if (align === 'left') {
|
||||
x += padding;
|
||||
} else if (align === 'center') {
|
||||
x += width / 2;
|
||||
} else if (align === 'right') {
|
||||
x += width - padding;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
texty(align, h) {
|
||||
const { height, padding } = this;
|
||||
let { y } = this;
|
||||
if (align === 'top') {
|
||||
y += padding;
|
||||
} else if (align === 'middle') {
|
||||
y += height / 2 - h / 2;
|
||||
} else if (align === 'bottom') {
|
||||
y += height - padding - h;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
topxys() {
|
||||
const { x, y, width } = this;
|
||||
return [
|
||||
[x, y],
|
||||
[x + width, y],
|
||||
];
|
||||
}
|
||||
|
||||
rightxys() {
|
||||
const { x, y, width, height } = this;
|
||||
return [
|
||||
[x + width, y],
|
||||
[x + width, y + height],
|
||||
];
|
||||
}
|
||||
|
||||
bottomxys() {
|
||||
const { x, y, width, height } = this;
|
||||
return [
|
||||
[x, y + height],
|
||||
[x + width, y + height],
|
||||
];
|
||||
}
|
||||
|
||||
leftxys() {
|
||||
const { x, y, height } = this;
|
||||
return [
|
||||
[x, y],
|
||||
[x, y + height],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function drawFontLine(type, tx, ty, align, valign, blheight, blwidth) {
|
||||
const floffset = { x: 0, y: 0 };
|
||||
if (type === 'underline') {
|
||||
if (valign === 'bottom') {
|
||||
floffset.y = 0;
|
||||
} else if (valign === 'top') {
|
||||
floffset.y = -(blheight + 2);
|
||||
} else {
|
||||
floffset.y = -blheight / 2;
|
||||
}
|
||||
} else if (type === 'strike') {
|
||||
if (valign === 'bottom') {
|
||||
floffset.y = blheight / 2;
|
||||
} else if (valign === 'top') {
|
||||
floffset.y = -(blheight / 2 + 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (align === 'center') {
|
||||
floffset.x = blwidth / 2;
|
||||
} else if (align === 'right') {
|
||||
floffset.x = blwidth;
|
||||
}
|
||||
this.line([tx - floffset.x, ty - floffset.y], [tx - floffset.x + blwidth, ty - floffset.y]);
|
||||
}
|
||||
|
||||
class Draw {
|
||||
constructor(el, width, height) {
|
||||
this.el = el;
|
||||
this.ctx = el.getContext('2d');
|
||||
this.resize(width, height);
|
||||
this.ctx.scale(dpr(), dpr());
|
||||
}
|
||||
|
||||
resize(width, height) {
|
||||
// console.log('dpr:', dpr);
|
||||
this.el.style.width = `${width}px`;
|
||||
this.el.style.height = `${height}px`;
|
||||
this.el.width = npx(width);
|
||||
this.el.height = npx(height);
|
||||
}
|
||||
|
||||
clear() {
|
||||
const { width, height } = this.el;
|
||||
this.ctx.clearRect(0, 0, width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
attr(options) {
|
||||
Object.assign(this.ctx, options);
|
||||
return this;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.ctx.save();
|
||||
this.ctx.beginPath();
|
||||
return this;
|
||||
}
|
||||
|
||||
restore() {
|
||||
this.ctx.restore();
|
||||
return this;
|
||||
}
|
||||
|
||||
beginPath() {
|
||||
this.ctx.beginPath();
|
||||
return this;
|
||||
}
|
||||
|
||||
translate(x, y) {
|
||||
this.ctx.translate(npx(x), npx(y));
|
||||
return this;
|
||||
}
|
||||
|
||||
scale(x, y) {
|
||||
this.ctx.scale(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
clearRect(x, y, w, h) {
|
||||
this.ctx.clearRect(x, y, w, h);
|
||||
return this;
|
||||
}
|
||||
|
||||
fillRect(x, y, w, h) {
|
||||
this.ctx.fillRect(npx(x) - 0.5, npx(y) - 0.5, npx(w), npx(h));
|
||||
return this;
|
||||
}
|
||||
|
||||
fillText(text, x, y) {
|
||||
this.ctx.fillText(text, npx(x), npx(y));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><>ͼƬ<CDBC><C6AC>
|
||||
* @param {*} box - һ<><D2BB> DrawBox <20><><EFBFBD><EFBFBD>
|
||||
* @param {string} src - ͼƬ<CDBC><C6AC>·<EFBFBD><C2B7>
|
||||
* @param {Object} fixedIndexWidth - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param {Object} fixedIndexHeight - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
|
||||
*/
|
||||
fillImage(box, { value: src }, { fixedIndexWidth, fixedIndexHeight }, scroll, celldata) {
|
||||
if (!celldata.imagewidth) {
|
||||
return;
|
||||
}
|
||||
if (celldata.value == '' || celldata.value == undefined) {
|
||||
imageMap.forEach((value, key) => {
|
||||
if (value[0] === celldata.scaledWidth && value[1] === celldata.scaledHeight) {
|
||||
imageMap.delete(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
const imageTop = celldata.top;
|
||||
const imageLeft = celldata.left;
|
||||
const { x, y, width, height } = box;
|
||||
// if(!((imageTop ==y && imageLeft == x) || (imageTop ==y-scroll.scroll.y && imageLeft == x-scroll.scroll.x)))
|
||||
// {return}
|
||||
const img = new Image();
|
||||
img.src = src;
|
||||
img.onload = () => {
|
||||
this.ctx.save();
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͻ<EFBFBD>λ<EFBFBD>ã<EFBFBD>Ϊʲôtranslateû<65><C3BB><EFBFBD><EFBFBD>Ч<EFBFBD>أ<EFBFBD><D8A3><EFBFBD>Ϊ<EFBFBD>첽<EFBFBD><ECB2BD><EFBFBD><EFBFBD>
|
||||
let sx = x + fixedIndexWidth;
|
||||
let sy = y + fixedIndexHeight;
|
||||
if (scroll) {
|
||||
sx = sx - scroll.scroll.x + 0;
|
||||
sy = sy - scroll.scroll.y + 0;
|
||||
}
|
||||
//<2F><><EFBFBD>㳤<EFBFBD><E3B3A4><EFBFBD><EFBFBD>
|
||||
const imageWidth = celldata.imagewidth;
|
||||
const imageHeight = celldata.imageheight;
|
||||
const imageH = height - 2; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD>ʿ<EFBFBD><CABF>߱ȣ<DFB1>ֱ<EFBFBD><D6B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԫ<EFBFBD><D4AA>
|
||||
const gridCellWidth = width;
|
||||
const gridCellHeight = height;
|
||||
let widthRatio = gridCellWidth / imageWidth;
|
||||
let heightRatio = gridCellHeight / imageHeight;
|
||||
let scaleRatio = Math.min(widthRatio, heightRatio);
|
||||
let scaledWidth = imageWidth * scaleRatio;
|
||||
let scaledHeight = imageHeight * scaleRatio;
|
||||
if (imageMap.has(img.src)) {
|
||||
// scaledWidth = imageMap.get(img.src)[0]
|
||||
// scaledHeight = imageMap.get(img.src)[1]
|
||||
// celldata.scaledWidth = scaledWidth
|
||||
// celldata.scaledHeight = scaledHeight
|
||||
// celldata.scaleRatio = scaleRatio
|
||||
|
||||
//<2F><><EFBFBD><EFBFBD>ͼƬʱ<C6AC>Ѵ<EFBFBD><D1B4><EFBFBD>ƫ<EFBFBD><C6AB>
|
||||
if (imageMap.get(img.src)[2] != 0 || imageMap.get(img.src)[3] != 0) {
|
||||
//<2F><><EFBFBD>¼<EFBFBD><C2BC><EFBFBD><EFBFBD>ʼλ<CABC><CEBB>
|
||||
// if (scroll.scroll.x > imageMap.get(img.src)[2]) {
|
||||
// sx = sx + (scroll.scroll.x - imageMap.get(img.src)[2])
|
||||
// } else {
|
||||
// sx = sx - (imageMap.get(img.src)[2] - scroll.scroll.x)
|
||||
// }
|
||||
// if (scroll.scroll.y > imageMap.get(img.src)[3]) {
|
||||
// sy = sy + (scroll.scroll.y - imageMap.get(img.src)[3])
|
||||
// } else {
|
||||
// sy = sy - (imageMap.get(img.src)[3] - scroll.scroll.y)
|
||||
// }
|
||||
// sx = sx - (scroll.scroll.x - imageMap.get(img.src)[2]) - 5
|
||||
// sy = sy - (scroll.scroll.y - imageMap.get(img.src)[3]) - 5
|
||||
// <20><>ֹ<EFBFBD>ظ<EFBFBD><D8B8><EFBFBD>Ⱦ
|
||||
if (imageTop == y - scroll.scroll.y && imageLeft == x - scroll.scroll.x) {
|
||||
this.ctx.drawImage(img, npx(sx), npx(sy), npx(imageWidth), npx(imageH));
|
||||
this.ctx.restore();
|
||||
}
|
||||
} else {
|
||||
// if (imageTop == y && imageLeft == x) {
|
||||
this.ctx.drawImage(img, npx(sx), npx(sy), npx(imageWidth), npx(imageH));
|
||||
this.ctx.restore();
|
||||
// }
|
||||
}
|
||||
} else {
|
||||
// imageMap.set(img.src, [scaledWidth, scaledHeight, scroll.scroll.x, scroll.scroll.y])
|
||||
// celldata.scaledWidth = scaledWidth
|
||||
// celldata.scaledHeight = scaledHeight
|
||||
// celldata.scaleRatio = scaleRatio
|
||||
// if (imageTop == y - scroll.scroll.y && imageLeft == x - scroll.scroll.x) {
|
||||
this.ctx.drawImage(img, npx(sx), npx(sy), npx(imageWidth), npx(imageH));
|
||||
this.ctx.restore();
|
||||
// }
|
||||
}
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><><EFBFBD><EFBFBD>ͼ<EFBFBD>Ρ<EFBFBD>
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD>ﱾ<EFBFBD><EFB1BE><EFBFBD><EFBFBD><EFBFBD>ο<EFBFBD>text<78><74><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DFBC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԫ<EFBFBD><D4AA>Ϊ radio, checkbox, date ʱ<><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>ͼ<EFBFBD>Ρ<EFBFBD>
|
||||
* @param {Object} cell - <20><>Ԫ<EFBFBD><D4AA>
|
||||
* @param {Object} box - DrawBox
|
||||
* @param {Object} fixedIndexWidth - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param {Object} fixedIndexHeight - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
|
||||
* @returns {Draw} CanvasRenderingContext2D ʵ<><CAB5>
|
||||
*/
|
||||
async geometry(cell, box, { fixedIndexWidth, fixedIndexHeight }, style, scroll, celldata) {
|
||||
const { type } = cell;
|
||||
switch (type) {
|
||||
case 'image':
|
||||
await this.fillImage(box, cell, { fixedIndexWidth, fixedIndexHeight }, scroll, celldata);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
txt: render text
|
||||
box: DrawBox
|
||||
attr: {
|
||||
align: left | center | right
|
||||
valign: top | middle | bottom
|
||||
color: '#333333',
|
||||
strike: false,
|
||||
font: {
|
||||
name: 'Arial',
|
||||
size: 14,
|
||||
bold: false,
|
||||
italic: false,
|
||||
}
|
||||
}
|
||||
textWrap: text wrapping
|
||||
*/
|
||||
text(mtxt, box, attr = {}, textWrap = true) {
|
||||
const { ctx } = this;
|
||||
const { align, valign, font, color, strike, underline } = attr;
|
||||
const tx = box.textx(align);
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
this.attr({
|
||||
textAlign: align,
|
||||
textBaseline: valign,
|
||||
font: `${font.italic ? 'italic' : ''} ${font.bold ? 'bold' : ''} ${npx(font.size)}px ${
|
||||
font.name
|
||||
}`,
|
||||
fillStyle: color,
|
||||
strokeStyle: color,
|
||||
});
|
||||
const txts = `${mtxt}`.split('\n');
|
||||
const biw = box.innerWidth();
|
||||
const ntxts = [];
|
||||
txts.forEach(it => {
|
||||
const txtWidth = ctx.measureText(it).width;
|
||||
if (textWrap && txtWidth > npx(biw)) {
|
||||
let textLine = { w: 0, len: 0, start: 0 };
|
||||
for (let i = 0; i < it.length; i += 1) {
|
||||
if (textLine.w >= npx(biw)) {
|
||||
ntxts.push(it.substr(textLine.start, textLine.len));
|
||||
textLine = { w: 0, len: 0, start: i };
|
||||
}
|
||||
textLine.len += 1;
|
||||
textLine.w += ctx.measureText(it[i]).width + 1;
|
||||
}
|
||||
if (textLine.len > 0) {
|
||||
ntxts.push(it.substr(textLine.start, textLine.len));
|
||||
}
|
||||
} else {
|
||||
ntxts.push(it);
|
||||
}
|
||||
});
|
||||
const txtHeight = (ntxts.length - 1) * (font.size + 2);
|
||||
let ty = box.texty(valign, txtHeight);
|
||||
ntxts.forEach(txt => {
|
||||
const txtWidth = ctx.measureText(txt).width;
|
||||
this.fillText(txt, tx, ty);
|
||||
if (strike) {
|
||||
drawFontLine.call(this, 'strike', tx, ty, align, valign, font.size, txtWidth);
|
||||
}
|
||||
if (underline) {
|
||||
drawFontLine.call(this, 'underline', tx, ty, align, valign, font.size, txtWidth);
|
||||
}
|
||||
ty += font.size + 2;
|
||||
});
|
||||
ctx.restore();
|
||||
return this;
|
||||
}
|
||||
|
||||
border(style, color) {
|
||||
const { ctx } = this;
|
||||
ctx.lineWidth = thinLineWidth;
|
||||
ctx.strokeStyle = color;
|
||||
// console.log('style:', style);
|
||||
if (style === 'medium') {
|
||||
ctx.lineWidth = npx(2) - 0.5;
|
||||
} else if (style === 'thick') {
|
||||
ctx.lineWidth = npx(3);
|
||||
} else if (style === 'dashed') {
|
||||
ctx.setLineDash([npx(3), npx(2)]);
|
||||
} else if (style === 'dotted') {
|
||||
ctx.setLineDash([npx(1), npx(1)]);
|
||||
} else if (style === 'double') {
|
||||
ctx.setLineDash([npx(2), 0]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
line(...xys) {
|
||||
const { ctx } = this;
|
||||
if (xys.length > 1) {
|
||||
ctx.beginPath();
|
||||
const [x, y] = xys[0];
|
||||
ctx.moveTo(npxLine(x), npxLine(y));
|
||||
for (let i = 1; i < xys.length; i += 1) {
|
||||
const [x1, y1] = xys[i];
|
||||
ctx.lineTo(npxLine(x1), npxLine(y1));
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
strokeBorders(box) {
|
||||
const { ctx } = this;
|
||||
ctx.save();
|
||||
// border
|
||||
const { borderTop, borderRight, borderBottom, borderLeft } = box;
|
||||
if (borderTop) {
|
||||
this.border(...borderTop);
|
||||
// console.log('box.topxys:', box.topxys());
|
||||
this.line(...box.topxys());
|
||||
}
|
||||
if (borderRight) {
|
||||
this.border(...borderRight);
|
||||
this.line(...box.rightxys());
|
||||
}
|
||||
if (borderBottom) {
|
||||
this.border(...borderBottom);
|
||||
this.line(...box.bottomxys());
|
||||
}
|
||||
if (borderLeft) {
|
||||
this.border(...borderLeft);
|
||||
this.line(...box.leftxys());
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
dropdown(box) {
|
||||
const { ctx } = this;
|
||||
const { x, y, width, height } = box;
|
||||
const sx = x + width - 15;
|
||||
const sy = y + height - 15;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(npx(sx), npx(sy));
|
||||
ctx.lineTo(npx(sx + 8), npx(sy));
|
||||
ctx.lineTo(npx(sx + 4), npx(sy + 6));
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, .45)';
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
error(box) {
|
||||
const { ctx } = this;
|
||||
const { x, y, width } = box;
|
||||
const sx = x + width - 1;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(npx(sx - 8), npx(y - 1));
|
||||
ctx.lineTo(npx(sx), npx(y - 1));
|
||||
ctx.lineTo(npx(sx), npx(y + 8));
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(255, 0, 0, .65)';
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
frozen(box) {
|
||||
const { ctx } = this;
|
||||
const { x, y, width } = box;
|
||||
const sx = x + width - 1;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(npx(sx - 8), npx(y - 1));
|
||||
ctx.lineTo(npx(sx), npx(y - 1));
|
||||
ctx.lineTo(npx(sx), npx(y + 8));
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(0, 255, 0, .85)';
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
rect(box, dtextcb) {
|
||||
const { ctx } = this;
|
||||
const { x, y, width, height, bgcolor } = box;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = bgcolor || '#fff';
|
||||
ctx.rect(npxLine(x + 1), npxLine(y + 1), npx(width - 2), npx(height - 2));
|
||||
ctx.clip();
|
||||
ctx.fill();
|
||||
dtextcb();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { Draw, DrawBox, thinLineWidth, npx };
|
||||
@@ -0,0 +1,90 @@
|
||||
class Draw {
|
||||
constructor(el) {
|
||||
this.el = el;
|
||||
this.ctx = el.getContext('2d');
|
||||
}
|
||||
|
||||
clear() {
|
||||
const { width, height } = this.el;
|
||||
this.ctx.clearRect(0, 0, width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
attr(m) {
|
||||
Object.assign(this.ctx, m);
|
||||
return this;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.ctx.save();
|
||||
this.ctx.beginPath();
|
||||
return this;
|
||||
}
|
||||
|
||||
restore() {
|
||||
this.ctx.restore();
|
||||
return this;
|
||||
}
|
||||
|
||||
beginPath() {
|
||||
this.ctx.beginPath();
|
||||
return this;
|
||||
}
|
||||
|
||||
closePath() {
|
||||
this.ctx.closePath();
|
||||
return this;
|
||||
}
|
||||
|
||||
measureText(text) {
|
||||
return this.ctx.measureText(text);
|
||||
}
|
||||
|
||||
rect(x, y, width, height) {
|
||||
this.ctx.rect(x, y, width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
scale(x, y) {
|
||||
this.ctx.scale(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
rotate(angle) {
|
||||
this.ctx.rotate(angle);
|
||||
return this;
|
||||
}
|
||||
|
||||
translate(x, y) {
|
||||
this.ctx.translate(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
transform(a, b, c, d, e) {
|
||||
this.ctx.transform(a, b, c, d, e);
|
||||
return this;
|
||||
}
|
||||
|
||||
fillRect(x, y, w, h) {
|
||||
this.ctx.fillRect(x, y, w, h);
|
||||
return this;
|
||||
}
|
||||
|
||||
strokeRect(x, y, w, h) {
|
||||
this.ctx.strokeRect(x, y, w, h);
|
||||
return this;
|
||||
}
|
||||
|
||||
fillText(text, x, y, maxWidth) {
|
||||
this.ctx.fillText(text, x, y, maxWidth);
|
||||
return this;
|
||||
}
|
||||
|
||||
strokeText(text, x, y, maxWidth) {
|
||||
this.ctx.strokeText(text, x, y, maxWidth);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { Draw };
|
||||
@@ -0,0 +1,62 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
import DropdownColor from './dropdown_color.js';
|
||||
import DropdownLineType from './dropdown_linetype.js';
|
||||
|
||||
function buildTable(...trs) {
|
||||
return h('table', '').child(h('tbody', '').children(...trs));
|
||||
}
|
||||
|
||||
function buildTd(iconName) {
|
||||
return h('td', '').child(
|
||||
h('div', `${cssPrefix}-border-palette-cell`)
|
||||
.child(new Icon(`border-${iconName}`))
|
||||
.on('click', () => {
|
||||
this.mode = iconName;
|
||||
const { mode, style, color } = this;
|
||||
this.change({ mode, style, color });
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export default class BorderPalette {
|
||||
constructor() {
|
||||
this.color = '#000';
|
||||
this.style = 'thin';
|
||||
this.mode = 'all';
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.ddColor = new DropdownColor('line-color', this.color);
|
||||
this.ddColor.change = color => {
|
||||
this.color = color;
|
||||
};
|
||||
this.ddType = new DropdownLineType(this.style);
|
||||
this.ddType.change = ([s]) => {
|
||||
this.style = s;
|
||||
};
|
||||
this.el = h('div', `${cssPrefix}-border-palette`);
|
||||
const table = buildTable(
|
||||
h('tr', '').children(
|
||||
h('td', `${cssPrefix}-border-palette-left`).child(
|
||||
buildTable(
|
||||
h('tr', '').children(
|
||||
...['all', 'inside', 'horizontal', 'vertical', 'outside'].map(it =>
|
||||
buildTd.call(this, it),
|
||||
),
|
||||
),
|
||||
h('tr', '').children(
|
||||
...['left', 'top', 'right', 'bottom', 'none'].map(it => buildTd.call(this, it)),
|
||||
),
|
||||
),
|
||||
),
|
||||
h('td', `${cssPrefix}-border-palette-right`).children(
|
||||
h('div', `${cssPrefix}-toolbar-btn`).child(this.ddColor.el),
|
||||
h('div', `${cssPrefix}-toolbar-btn`).child(this.ddType.el),
|
||||
),
|
||||
),
|
||||
);
|
||||
this.el.child(table);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { tf } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
import Icon from './icon.js';
|
||||
import FormInput from './form_input.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
// Record: temp not used
|
||||
// import { xtoast } from './message.js';
|
||||
|
||||
class DropdownMore extends Dropdown {
|
||||
constructor(click) {
|
||||
const icon = new Icon('ellipsis');
|
||||
super(icon, 'auto', false, 'top-left');
|
||||
this.contentClick = click;
|
||||
}
|
||||
|
||||
reset(items) {
|
||||
const eles = items.map((it, i) =>
|
||||
h('div', `${cssPrefix}-item`)
|
||||
.css('width', '150px')
|
||||
.css('font-weight', 'normal')
|
||||
.on('click', () => {
|
||||
this.contentClick(i);
|
||||
this.hide();
|
||||
})
|
||||
.child(it),
|
||||
);
|
||||
this.setContentChildren(...eles);
|
||||
}
|
||||
|
||||
setTitle() {
|
||||
console.log('empty function');
|
||||
}
|
||||
}
|
||||
|
||||
const menuItems = [{ key: 'delete', title: tf('contextmenu.deleteSheet') }];
|
||||
|
||||
function buildMenuItem(item) {
|
||||
return h('div', `${cssPrefix}-item`)
|
||||
.child(item.title())
|
||||
.on('click', () => {
|
||||
this.itemClick(item.key);
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
|
||||
function buildMenu() {
|
||||
return menuItems.map(it => buildMenuItem.call(this, it));
|
||||
}
|
||||
|
||||
class ContextMenu {
|
||||
constructor() {
|
||||
this.el = h('div', `${cssPrefix}-contextmenu`)
|
||||
.css('width', '160px')
|
||||
.children(...buildMenu.call(this))
|
||||
.hide();
|
||||
this.itemClick = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
hide() {
|
||||
const { el } = this;
|
||||
el.hide();
|
||||
unbindClickoutside(el);
|
||||
}
|
||||
|
||||
setOffset(offset) {
|
||||
const { el } = this;
|
||||
el.offset(offset);
|
||||
el.show();
|
||||
bindClickoutside(el);
|
||||
}
|
||||
}
|
||||
|
||||
export default class Bottombar {
|
||||
constructor(
|
||||
addFunc = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
swapFunc = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
deleteFunc = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
updateFunc = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
) {
|
||||
this.swapFunc = swapFunc;
|
||||
this.updateFunc = updateFunc;
|
||||
this.dataNames = [];
|
||||
this.activeEl = null;
|
||||
this.deleteEl = null;
|
||||
this.items = [];
|
||||
this.moreEl = new DropdownMore(i => {
|
||||
this.clickSwap2(this.items[i]);
|
||||
});
|
||||
this.contextMenu = new ContextMenu();
|
||||
this.contextMenu.itemClick = deleteFunc;
|
||||
this.el = h('div', `${cssPrefix}-bottombar`).children(
|
||||
this.contextMenu.el,
|
||||
(this.menuEl = h('ul', `${cssPrefix}-menu`).child(
|
||||
h('li', '').children(
|
||||
new Icon('add').on('click', () => {
|
||||
addFunc();
|
||||
}),
|
||||
h('span', '').child(this.moreEl),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
addItem(name, active, options) {
|
||||
this.dataNames.push(name);
|
||||
const item = h('li', active ? 'active' : '').child(name);
|
||||
item
|
||||
.on('click', () => {
|
||||
this.clickSwap2(item);
|
||||
})
|
||||
.on('contextmenu', evt => {
|
||||
if (options.mode === 'read') return;
|
||||
const { offsetLeft, offsetHeight } = evt.target;
|
||||
this.contextMenu.setOffset({ left: offsetLeft, bottom: offsetHeight + 1 });
|
||||
this.deleteEl = item;
|
||||
})
|
||||
.on('dblclick', () => {
|
||||
if (options.mode === 'read') return;
|
||||
const v = item.html();
|
||||
const input = new FormInput('auto', '');
|
||||
input.val(v);
|
||||
input.input.on('blur', ({ target }) => {
|
||||
const { value } = target;
|
||||
const nindex = this.dataNames.findIndex(it => it === v);
|
||||
this.renameItem(nindex, value);
|
||||
/*
|
||||
this.dataNames.splice(nindex, 1, value);
|
||||
this.moreEl.reset(this.dataNames);
|
||||
item.html('').child(value);
|
||||
this.updateFunc(nindex, value);
|
||||
*/
|
||||
});
|
||||
item.html('').child(input.el);
|
||||
input.focus();
|
||||
});
|
||||
if (active) {
|
||||
this.clickSwap(item);
|
||||
}
|
||||
this.items.push(item);
|
||||
this.menuEl.child(item);
|
||||
this.moreEl.reset(this.dataNames);
|
||||
}
|
||||
|
||||
renameItem(index, value) {
|
||||
this.dataNames.splice(index, 1, value);
|
||||
this.moreEl.reset(this.dataNames);
|
||||
this.items[index].html('').child(value);
|
||||
this.updateFunc(index, value);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.items.forEach(it => {
|
||||
this.menuEl.removeChild(it.el);
|
||||
});
|
||||
this.items = [];
|
||||
this.dataNames = [];
|
||||
this.moreEl.reset(this.dataNames);
|
||||
}
|
||||
|
||||
deleteItem() {
|
||||
const { activeEl, deleteEl } = this;
|
||||
if (this.items.length > 1) {
|
||||
const index = this.items.findIndex(it => it === deleteEl);
|
||||
this.items.splice(index, 1);
|
||||
this.dataNames.splice(index, 1);
|
||||
this.menuEl.removeChild(deleteEl.el);
|
||||
this.moreEl.reset(this.dataNames);
|
||||
if (activeEl === deleteEl) {
|
||||
const [f] = this.items;
|
||||
this.activeEl = f;
|
||||
this.activeEl.toggle();
|
||||
return [index, 0];
|
||||
}
|
||||
return [index, -1];
|
||||
}
|
||||
return [-1];
|
||||
}
|
||||
|
||||
clickSwap2(item) {
|
||||
const index = this.items.findIndex(it => it === item);
|
||||
this.clickSwap(item);
|
||||
this.activeEl.toggle();
|
||||
this.swapFunc(index);
|
||||
}
|
||||
|
||||
clickSwap(item) {
|
||||
if (this.activeEl !== null) {
|
||||
this.activeEl.toggle();
|
||||
}
|
||||
this.activeEl = item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { Element } from './element.js';
|
||||
|
||||
export default class Button extends Element {
|
||||
// type: primary
|
||||
constructor(title, type = '') {
|
||||
super('div', `${cssPrefix}-button ${type}`);
|
||||
this.child(t(`button.${title}`));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
function addMonth(date, step) {
|
||||
date.setMonth(date.getMonth() + step);
|
||||
}
|
||||
|
||||
function weekday(date, index) {
|
||||
const d = new Date(date);
|
||||
d.setDate(index - date.getDay() + 1);
|
||||
return d;
|
||||
}
|
||||
|
||||
function monthDays(year, month, cdate) {
|
||||
// the first day of month
|
||||
const startDate = new Date(year, month, 1, 23, 59, 59);
|
||||
const datess = [[], [], [], [], [], []];
|
||||
for (let i = 0; i < 6; i += 1) {
|
||||
for (let j = 0; j < 7; j += 1) {
|
||||
const index = i * 7 + j;
|
||||
const d = weekday(startDate, index);
|
||||
const disabled = d.getMonth() !== month;
|
||||
// console.log('d:', d, ', cdate:', cdate);
|
||||
const active = d.getMonth() === cdate.getMonth() && d.getDate() === cdate.getDate();
|
||||
datess[i][j] = { d, disabled, active };
|
||||
}
|
||||
}
|
||||
return datess;
|
||||
}
|
||||
|
||||
export default class Calendar {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
this.cvalue = new Date(value);
|
||||
|
||||
this.headerLeftEl = h('div', 'calendar-header-left');
|
||||
this.bodyEl = h('tbody', '');
|
||||
this.buildAll();
|
||||
this.el = h('div', 'x-spreadsheet-calendar').children(
|
||||
h('div', 'calendar-header').children(
|
||||
this.headerLeftEl,
|
||||
h('div', 'calendar-header-right').children(
|
||||
h('a', 'calendar-prev')
|
||||
.on('click.stop', () => this.prev())
|
||||
.child(new Icon('chevron-left')),
|
||||
h('a', 'calendar-next')
|
||||
.on('click.stop', () => this.next())
|
||||
.child(new Icon('chevron-right')),
|
||||
),
|
||||
),
|
||||
h('table', 'calendar-body').children(
|
||||
h('thead', '').child(
|
||||
h('tr', '').children(...t('calendar.weeks').map(week => h('th', 'cell').child(week))),
|
||||
),
|
||||
this.bodyEl,
|
||||
),
|
||||
);
|
||||
this.selectChange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
setValue(value) {
|
||||
this.value = value;
|
||||
this.cvalue = new Date(value);
|
||||
this.buildAll();
|
||||
}
|
||||
|
||||
prev() {
|
||||
const { value } = this;
|
||||
addMonth(value, -1);
|
||||
this.buildAll();
|
||||
}
|
||||
|
||||
next() {
|
||||
const { value } = this;
|
||||
addMonth(value, 1);
|
||||
this.buildAll();
|
||||
}
|
||||
|
||||
buildAll() {
|
||||
this.buildHeaderLeft();
|
||||
this.buildBody();
|
||||
}
|
||||
|
||||
buildHeaderLeft() {
|
||||
const { value } = this;
|
||||
this.headerLeftEl.html(`${t('calendar.months')[value.getMonth()]} ${value.getFullYear()}`);
|
||||
}
|
||||
|
||||
buildBody() {
|
||||
const { value, cvalue, bodyEl } = this;
|
||||
const mDays = monthDays(value.getFullYear(), value.getMonth(), cvalue);
|
||||
const trs = mDays.map(it => {
|
||||
const tds = it.map(it1 => {
|
||||
let cls = 'cell';
|
||||
if (it1.disabled) cls += ' disabled';
|
||||
if (it1.active) cls += ' active';
|
||||
return h('td', '').child(
|
||||
h('div', cls)
|
||||
.on('click.stop', () => {
|
||||
this.selectChange(it1.d);
|
||||
})
|
||||
.child(it1.d.getDate().toString()),
|
||||
);
|
||||
});
|
||||
return h('tr', '').children(...tds);
|
||||
});
|
||||
bodyEl.html('').children(...trs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
const themeColorPlaceHolders = [
|
||||
'#ffffff',
|
||||
'#000100',
|
||||
'#e7e5e6',
|
||||
'#445569',
|
||||
'#5b9cd6',
|
||||
'#ed7d31',
|
||||
'#a5a5a5',
|
||||
'#ffc001',
|
||||
'#4371c6',
|
||||
'#71ae47',
|
||||
];
|
||||
|
||||
const themeColors = [
|
||||
[
|
||||
'#f2f2f2',
|
||||
'#7f7f7f',
|
||||
'#d0cecf',
|
||||
'#d5dce4',
|
||||
'#deeaf6',
|
||||
'#fce5d5',
|
||||
'#ededed',
|
||||
'#fff2cd',
|
||||
'#d9e2f3',
|
||||
'#e3efd9',
|
||||
],
|
||||
[
|
||||
'#d8d8d8',
|
||||
'#595959',
|
||||
'#afabac',
|
||||
'#adb8ca',
|
||||
'#bdd7ee',
|
||||
'#f7ccac',
|
||||
'#dbdbdb',
|
||||
'#ffe59a',
|
||||
'#b3c6e7',
|
||||
'#c5e0b3',
|
||||
],
|
||||
[
|
||||
'#bfbfbf',
|
||||
'#3f3f3f',
|
||||
'#756f6f',
|
||||
'#8596b0',
|
||||
'#9cc2e6',
|
||||
'#f4b184',
|
||||
'#c9c9c9',
|
||||
'#fed964',
|
||||
'#8eaada',
|
||||
'#a7d08c',
|
||||
],
|
||||
[
|
||||
'#a5a5a5',
|
||||
'#262626',
|
||||
'#3a3839',
|
||||
'#333f4f',
|
||||
'#2e75b5',
|
||||
'#c45a10',
|
||||
'#7b7b7b',
|
||||
'#bf8e01',
|
||||
'#2f5596',
|
||||
'#538136',
|
||||
],
|
||||
[
|
||||
'#7f7f7f',
|
||||
'#0c0c0c',
|
||||
'#171516',
|
||||
'#222a35',
|
||||
'#1f4e7a',
|
||||
'#843c0a',
|
||||
'#525252',
|
||||
'#7e6000',
|
||||
'#203864',
|
||||
'#365624',
|
||||
],
|
||||
];
|
||||
|
||||
const standardColors = [
|
||||
'#c00000',
|
||||
'#fe0000',
|
||||
'#fdc101',
|
||||
'#ffff01',
|
||||
'#93d051',
|
||||
'#00b04e',
|
||||
'#01b0f1',
|
||||
'#0170c1',
|
||||
'#012060',
|
||||
'#7030a0',
|
||||
];
|
||||
|
||||
function buildTd(bgcolor) {
|
||||
return h('td', '').child(
|
||||
h('div', `${cssPrefix}-color-palette-cell`)
|
||||
.on('click.stop', () => this.change(bgcolor))
|
||||
.css('background-color', bgcolor),
|
||||
);
|
||||
}
|
||||
|
||||
export default class ColorPalette {
|
||||
constructor() {
|
||||
this.el = h('div', `${cssPrefix}-color-palette`);
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
const table = h('table', '').children(
|
||||
h('tbody', '').children(
|
||||
h('tr', `${cssPrefix}-theme-color-placeholders`).children(
|
||||
...themeColorPlaceHolders.map(color => buildTd.call(this, color)),
|
||||
),
|
||||
...themeColors.map(it =>
|
||||
h('tr', `${cssPrefix}-theme-colors`).children(
|
||||
...it.map(color => buildTd.call(this, color)),
|
||||
),
|
||||
),
|
||||
h('tr', `${cssPrefix}-standard-colors`).children(
|
||||
...standardColors.map(color => buildTd.call(this, color)),
|
||||
),
|
||||
),
|
||||
);
|
||||
this.el.child(table);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { tf } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
|
||||
const menuItems = [
|
||||
{ key: 'copy', title: tf('contextmenu.copy'), label: 'Ctrl+C' },
|
||||
{ key: 'cut', title: tf('contextmenu.cut'), label: 'Ctrl+X' },
|
||||
{ key: 'paste', title: tf('contextmenu.paste'), label: 'Ctrl+V' },
|
||||
{ key: 'paste-value', title: tf('contextmenu.pasteValue'), label: 'Ctrl+Shift+V' },
|
||||
{ key: 'paste-format', title: tf('contextmenu.pasteFormat'), label: 'Ctrl+Alt+V' },
|
||||
{ key: 'divider' },
|
||||
{ key: 'insert-row', title: tf('contextmenu.insertRow') },
|
||||
{ key: 'insert-column', title: tf('contextmenu.insertColumn') },
|
||||
{ key: 'divider' },
|
||||
{ key: 'delete-row', title: tf('contextmenu.deleteRow') },
|
||||
{ key: 'delete-column', title: tf('contextmenu.deleteColumn') },
|
||||
{ key: 'delete-cell-text', title: tf('contextmenu.deleteCellText') },
|
||||
{ key: 'hide', title: tf('contextmenu.hide') },
|
||||
{ key: 'divider' },
|
||||
{ key: 'validation', title: tf('contextmenu.validation') },
|
||||
{ key: 'divider' },
|
||||
{ key: 'cell-printable', title: tf('contextmenu.cellprintable') },
|
||||
{ key: 'cell-non-printable', title: tf('contextmenu.cellnonprintable') },
|
||||
{ key: 'divider' },
|
||||
{ key: 'cell-editable', title: tf('contextmenu.celleditable') },
|
||||
{ key: 'cell-non-editable', title: tf('contextmenu.cellnoneditable') },
|
||||
];
|
||||
|
||||
function buildMenuItem(item) {
|
||||
if (item.key === 'divider') {
|
||||
return h('div', `${cssPrefix}-item divider`);
|
||||
}
|
||||
return h('div', `${cssPrefix}-item`)
|
||||
.on('click', () => {
|
||||
this.itemClick(item.key);
|
||||
this.hide();
|
||||
})
|
||||
.children(item.title(), h('div', 'label').child(item.label || ''));
|
||||
}
|
||||
|
||||
function buildMenu() {
|
||||
return menuItems.map(it => buildMenuItem.call(this, it));
|
||||
}
|
||||
|
||||
export default class ContextMenu {
|
||||
constructor(viewFn, isHide = false) {
|
||||
this.menuItems = buildMenu.call(this);
|
||||
this.el = h('div', `${cssPrefix}-contextmenu`)
|
||||
.children(...this.menuItems)
|
||||
.hide();
|
||||
this.viewFn = viewFn;
|
||||
this.itemClick = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.isHide = isHide;
|
||||
this.setMode('range');
|
||||
}
|
||||
|
||||
// row-col: the whole rows or the whole cols
|
||||
// range: select range
|
||||
setMode(mode) {
|
||||
const hideEl = this.menuItems[12];
|
||||
if (mode === 'row-col') {
|
||||
hideEl.show();
|
||||
} else {
|
||||
hideEl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
const { el } = this;
|
||||
el.hide();
|
||||
unbindClickoutside(el);
|
||||
}
|
||||
|
||||
setPosition(x, y) {
|
||||
if (this.isHide) return;
|
||||
const { el } = this;
|
||||
const { width } = el.show().offset();
|
||||
const view = this.viewFn();
|
||||
const vhf = view.height / 2;
|
||||
let left = x;
|
||||
if (view.width - x <= width) {
|
||||
left -= width;
|
||||
}
|
||||
el.css('left', `${left}px`);
|
||||
if (y > vhf) {
|
||||
el.css('bottom', `${view.height - y}px`)
|
||||
.css('max-height', `${y}px`)
|
||||
.css('top', 'auto');
|
||||
} else {
|
||||
el.css('top', `${y}px`)
|
||||
.css('max-height', `${view.height - y}px`)
|
||||
.css('bottom', 'auto');
|
||||
}
|
||||
bindClickoutside(el);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Calendar from './calendar.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class Datepicker {
|
||||
constructor() {
|
||||
this.calendar = new Calendar(new Date());
|
||||
this.el = h('div', `${cssPrefix}-datepicker`).child(this.calendar.el).hide();
|
||||
}
|
||||
|
||||
setValue(date) {
|
||||
// console.log(':::::::', date, typeof date, date instanceof string);
|
||||
const { calendar } = this;
|
||||
if (typeof date === 'string') {
|
||||
// console.log(/^\d{4}-\d{1,2}-\d{1,2}$/.test(date));
|
||||
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(date)) {
|
||||
calendar.setValue(new Date(date.replace(new RegExp('-', 'g'), '/')));
|
||||
}
|
||||
} else if (date instanceof Date) {
|
||||
calendar.setValue(date);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
change(cb) {
|
||||
this.calendar.selectChange = d => {
|
||||
cb(d);
|
||||
this.hide();
|
||||
};
|
||||
}
|
||||
|
||||
show() {
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { Element, h } from './element.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
|
||||
export default class Dropdown extends Element {
|
||||
constructor(title, width, showArrow, placement, ...children) {
|
||||
super('div', `${cssPrefix}-dropdown ${placement}`);
|
||||
this.title = title;
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.headerClick = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
if (typeof title === 'string') {
|
||||
this.title = h('div', `${cssPrefix}-dropdown-title`).child(title);
|
||||
} else if (showArrow) {
|
||||
this.title.addClass('arrow-left');
|
||||
}
|
||||
this.contentEl = h('div', `${cssPrefix}-dropdown-content`).css('width', width).hide();
|
||||
|
||||
this.setContentChildren(...children);
|
||||
|
||||
this.headerEl = h('div', `${cssPrefix}-dropdown-header`);
|
||||
this.headerEl
|
||||
.on('click', () => {
|
||||
if (this.contentEl.css('display') !== 'block') {
|
||||
this.show();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
})
|
||||
.children(
|
||||
this.title,
|
||||
showArrow
|
||||
? h('div', `${cssPrefix}-icon arrow-right`).child(
|
||||
h('div', `${cssPrefix}-icon-img arrow-down`),
|
||||
)
|
||||
: '',
|
||||
);
|
||||
this.children(this.headerEl, this.contentEl);
|
||||
}
|
||||
|
||||
setContentChildren(...children) {
|
||||
this.contentEl.html('');
|
||||
if (children.length > 0) {
|
||||
this.contentEl.children(...children);
|
||||
}
|
||||
}
|
||||
|
||||
setTitle(title) {
|
||||
this.title.html(title);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
show() {
|
||||
const { contentEl } = this;
|
||||
contentEl.show();
|
||||
this.parent().active();
|
||||
bindClickoutside(this.parent(), () => {
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.parent().active(false);
|
||||
this.contentEl.hide();
|
||||
unbindClickoutside(this.parent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
function buildItemWithIcon(iconName) {
|
||||
return h('div', `${cssPrefix}-item`).child(new Icon(iconName));
|
||||
}
|
||||
|
||||
export default class DropdownAlign extends Dropdown {
|
||||
constructor(aligns, align) {
|
||||
const icon = new Icon(`align-${align}`);
|
||||
const naligns = aligns.map(it =>
|
||||
buildItemWithIcon(`align-${it}`).on('click', () => {
|
||||
this.setTitle(it);
|
||||
this.change(it);
|
||||
}),
|
||||
);
|
||||
super(icon, 'auto', true, 'bottom-left', ...naligns);
|
||||
}
|
||||
|
||||
setTitle(align) {
|
||||
this.title.setName(`align-${align}`);
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Dropdown from './dropdown.js';
|
||||
import Icon from './icon.js';
|
||||
import BorderPalette from './border_palette.js';
|
||||
|
||||
export default class DropdownBorder extends Dropdown {
|
||||
constructor() {
|
||||
const icon = new Icon('border-all');
|
||||
const borderPalette = new BorderPalette();
|
||||
borderPalette.change = v => {
|
||||
this.change(v);
|
||||
this.hide();
|
||||
};
|
||||
super(icon, 'auto', false, 'bottom-left', borderPalette.el);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import Dropdown from './dropdown.js';
|
||||
import Icon from './icon.js';
|
||||
import ColorPalette from './color_palette.js';
|
||||
|
||||
export default class DropdownColor extends Dropdown {
|
||||
constructor(iconName, color) {
|
||||
const icon = new Icon(iconName)
|
||||
.css('height', '16px')
|
||||
.css('border-bottom', `3px solid ${color}`);
|
||||
const colorPalette = new ColorPalette();
|
||||
colorPalette.change = v => {
|
||||
this.setTitle(v);
|
||||
this.change(v);
|
||||
};
|
||||
super(icon, 'auto', false, 'bottom-left', colorPalette.el);
|
||||
}
|
||||
|
||||
setTitle(color) {
|
||||
this.title.css('border-color', color);
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { baseFonts } from '../core/font.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class DropdownFont extends Dropdown {
|
||||
constructor() {
|
||||
const nfonts = baseFonts.map(it =>
|
||||
h('div', `${cssPrefix}-item`)
|
||||
.on('click', () => {
|
||||
this.setTitle(it.title);
|
||||
this.change(it);
|
||||
})
|
||||
.child(it.title),
|
||||
);
|
||||
super(baseFonts[0].title, '160px', true, 'bottom-left', ...nfonts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { fontSizes } from '../core/font.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class DropdownFontSize extends Dropdown {
|
||||
constructor() {
|
||||
const nfontSizes = fontSizes.map(it =>
|
||||
h('div', `${cssPrefix}-item`)
|
||||
.on('click', () => {
|
||||
this.setTitle(`${it.pt}`);
|
||||
this.change(it);
|
||||
})
|
||||
.child(`${it.pt}`),
|
||||
);
|
||||
super('10', '60px', true, 'bottom-left', ...nfontSizes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { baseFormats } from '../core/format.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class DropdownFormat extends Dropdown {
|
||||
constructor() {
|
||||
let nformats = baseFormats.slice(0);
|
||||
nformats.splice(2, 0, { key: 'divider' });
|
||||
nformats.splice(8, 0, { key: 'divider' });
|
||||
nformats = nformats.map(it => {
|
||||
const item = h('div', `${cssPrefix}-item`);
|
||||
if (it.key === 'divider') {
|
||||
item.addClass('divider');
|
||||
} else {
|
||||
item.child(it.title()).on('click', () => {
|
||||
this.setTitle(it.title());
|
||||
this.change(it);
|
||||
});
|
||||
if (it.label) item.child(h('div', 'label').html(it.label));
|
||||
}
|
||||
return item;
|
||||
});
|
||||
super('Normal', '220px', true, 'bottom-left', ...nformats);
|
||||
}
|
||||
|
||||
setTitle(key) {
|
||||
for (let i = 0; i < baseFormats.length; i += 1) {
|
||||
if (baseFormats[i].key === key) {
|
||||
this.title.html(baseFormats[i].title());
|
||||
}
|
||||
}
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { baseFormulas } from '../core/formula.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import Icon from './icon.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class DropdownFormula extends Dropdown {
|
||||
constructor() {
|
||||
const nformulas = baseFormulas.map(it =>
|
||||
h('div', `${cssPrefix}-item`)
|
||||
.on('click', () => {
|
||||
this.hide();
|
||||
this.change(it);
|
||||
})
|
||||
.child(it.key),
|
||||
);
|
||||
super(new Icon('formula'), '180px', true, 'bottom-left', ...nformulas);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
const lineTypes = [
|
||||
[
|
||||
'thin',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="1" style="user-select: none;"><line x1="0" y1="0.5" x2="50" y2="0.5" stroke-width="1" stroke="black" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
[
|
||||
'medium',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="2" style="user-select: none;"><line x1="0" y1="1.0" x2="50" y2="1.0" stroke-width="2" stroke="black" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
[
|
||||
'thick',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="3" style="user-select: none;"><line x1="0" y1="1.5" x2="50" y2="1.5" stroke-width="3" stroke="black" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
[
|
||||
'dashed',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="1" style="user-select: none;"><line x1="0" y1="0.5" x2="50" y2="0.5" stroke-width="1" stroke="black" stroke-dasharray="2" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
[
|
||||
'dotted',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="1" style="user-select: none;"><line x1="0" y1="0.5" x2="50" y2="0.5" stroke-width="1" stroke="black" stroke-dasharray="1" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
// ['double', '<svg xmlns="http://www.w3.org/2000/svg" width="50" height="3" style="user-select: none;"><line x1="0" y1="0.5" x2="50" y2="0.5" stroke-width="1" stroke="black" style="user-select: none;"></line><line x1="0" y1="2.5" x2="50" y2="2.5" stroke-width="1" stroke="black" style="user-select: none;"></line></svg>'],
|
||||
];
|
||||
|
||||
export default class DropdownLineType extends Dropdown {
|
||||
constructor(type) {
|
||||
const icon = new Icon('line-type');
|
||||
let beforei = 0;
|
||||
const lineTypeEls = lineTypes.map((it, iti) =>
|
||||
h('div', `${cssPrefix}-item state ${type === it[0] ? 'checked' : ''}`)
|
||||
.on('click', () => {
|
||||
lineTypeEls[beforei].toggle('checked');
|
||||
lineTypeEls[iti].toggle('checked');
|
||||
beforei = iti;
|
||||
this.hide();
|
||||
this.change(it);
|
||||
})
|
||||
.child(h('div', `${cssPrefix}-line-type`).html(it[1])),
|
||||
);
|
||||
|
||||
super(icon, 'auto', false, 'bottom-left', ...lineTypeEls);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Suggest from './suggest.js';
|
||||
// TODO datepicker 暂不支持,后续放开datepicker解决报错后使用
|
||||
// import Datepicker from './datepicker';
|
||||
// import { mouseMoveUp } from '../event.js';
|
||||
|
||||
function resetTextareaSize() {
|
||||
const { inputText } = this;
|
||||
if (!/^\s*$/.test(inputText)) {
|
||||
const { textlineEl, textEl, areaOffset } = this;
|
||||
const txts = inputText.split('\n');
|
||||
const maxTxtSize = Math.max(...txts.map(it => it.length));
|
||||
const tlOffset = textlineEl.offset();
|
||||
const fontWidth = tlOffset.width / inputText.length;
|
||||
const tlineWidth = (maxTxtSize + 1) * fontWidth + 5;
|
||||
const maxWidth = this.viewFn().width - areaOffset.left - fontWidth;
|
||||
let h1 = txts.length;
|
||||
if (tlineWidth > areaOffset.width) {
|
||||
let twidth = tlineWidth;
|
||||
if (tlineWidth > maxWidth) {
|
||||
twidth = maxWidth;
|
||||
h1 += parseInt(tlineWidth / maxWidth, 10);
|
||||
h1 += tlineWidth % maxWidth > 0 ? 1 : 0;
|
||||
}
|
||||
textEl.css('width', `${twidth}px`);
|
||||
}
|
||||
h1 *= this.rowHeight;
|
||||
if (h1 > areaOffset.height) {
|
||||
textEl.css('height', `${h1}px`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function insertText({ target }, itxt) {
|
||||
const { value, selectionEnd } = target;
|
||||
const ntxt = `${value.slice(0, selectionEnd)}${itxt}${value.slice(selectionEnd)}`;
|
||||
target.value = ntxt;
|
||||
target.setSelectionRange(selectionEnd + 1, selectionEnd + 1);
|
||||
|
||||
this.inputText = ntxt;
|
||||
this.textlineEl.html(ntxt);
|
||||
resetTextareaSize.call(this);
|
||||
}
|
||||
|
||||
function keydownEventHandler(evt) {
|
||||
const { keyCode, altKey } = evt;
|
||||
if (keyCode !== 13 && keyCode !== 9) evt.stopPropagation();
|
||||
if (keyCode === 13 && altKey) {
|
||||
insertText.call(this, evt, '\n');
|
||||
evt.stopPropagation();
|
||||
}
|
||||
if (keyCode === 13 && !altKey) evt.preventDefault();
|
||||
}
|
||||
|
||||
function inputEventHandler(evt) {
|
||||
const v = evt.target.value;
|
||||
// console.log(evt, 'v:', v);
|
||||
const { suggest, textlineEl, validator } = this;
|
||||
const { cell } = this;
|
||||
if (cell !== null) {
|
||||
if (('editable' in cell && cell.editable === true) || cell.editable === undefined) {
|
||||
this.inputText = v;
|
||||
if (validator) {
|
||||
if (validator.type === 'list') {
|
||||
suggest.search(v);
|
||||
} else {
|
||||
suggest.hide();
|
||||
}
|
||||
} else {
|
||||
const start = v.lastIndexOf('=');
|
||||
if (start !== -1) {
|
||||
suggest.search(v.substring(start + 1));
|
||||
} else {
|
||||
suggest.hide();
|
||||
}
|
||||
}
|
||||
textlineEl.html(v);
|
||||
resetTextareaSize.call(this);
|
||||
this.change('input', v);
|
||||
} else {
|
||||
evt.target.value = cell.text || '';
|
||||
}
|
||||
} else {
|
||||
this.inputText = v;
|
||||
if (validator) {
|
||||
if (validator.type === 'list') {
|
||||
suggest.search(v);
|
||||
} else {
|
||||
suggest.hide();
|
||||
}
|
||||
} else {
|
||||
const start = v.lastIndexOf('=');
|
||||
if (start !== -1) {
|
||||
suggest.search(v.substring(start + 1));
|
||||
} else {
|
||||
suggest.hide();
|
||||
}
|
||||
}
|
||||
textlineEl.html(v);
|
||||
resetTextareaSize.call(this);
|
||||
this.change('input', v);
|
||||
}
|
||||
}
|
||||
|
||||
function setTextareaRange(position) {
|
||||
const { el } = this.textEl;
|
||||
setTimeout(() => {
|
||||
el.focus();
|
||||
el.setSelectionRange(position, position);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function setText(text, position) {
|
||||
const { textEl, textlineEl } = this;
|
||||
// firefox bug
|
||||
textEl.el.blur();
|
||||
|
||||
textEl.val(text);
|
||||
textlineEl.html(text);
|
||||
setTextareaRange.call(this, position);
|
||||
}
|
||||
|
||||
function suggestItemClick(it) {
|
||||
const { inputText, validator } = this;
|
||||
let position = 0;
|
||||
if (validator && validator.type === 'list') {
|
||||
this.inputText = it;
|
||||
position = this.inputText.length;
|
||||
} else {
|
||||
const start = inputText.lastIndexOf('=');
|
||||
const sit = inputText.substring(0, start + 1);
|
||||
let eit = inputText.substring(start + 1);
|
||||
if (eit.indexOf(')') !== -1) {
|
||||
eit = eit.substring(eit.indexOf(')'));
|
||||
} else {
|
||||
eit = '';
|
||||
}
|
||||
this.inputText = `${sit + it.key}(`;
|
||||
// console.log('inputText:', this.inputText);
|
||||
position = this.inputText.length;
|
||||
this.inputText += `)${eit}`;
|
||||
}
|
||||
setText.call(this, this.inputText, position);
|
||||
}
|
||||
|
||||
function resetSuggestItems() {
|
||||
this.suggest.setItems(this.formulas);
|
||||
}
|
||||
|
||||
function dateFormat(d) {
|
||||
let month = d.getMonth() + 1;
|
||||
let date = d.getDate();
|
||||
if (month < 10) month = `0${month}`;
|
||||
if (date < 10) date = `0${date}`;
|
||||
return `${d.getFullYear()}-${month}-${date}`;
|
||||
}
|
||||
|
||||
export default class Editor {
|
||||
constructor(formulas, viewFn, rowHeight) {
|
||||
this.viewFn = viewFn;
|
||||
this.rowHeight = rowHeight;
|
||||
this.formulas = formulas;
|
||||
this.suggest = new Suggest(formulas, it => {
|
||||
suggestItemClick.call(this, it);
|
||||
});
|
||||
// this.datepicker = new Datepicker();
|
||||
// this.datepicker.change((d) => {
|
||||
// // console.log('d:', d);
|
||||
// this.setText(dateFormat(d));
|
||||
// this.clear();
|
||||
// });
|
||||
this.areaEl = h('div', `${cssPrefix}-editor-area`)
|
||||
.children(
|
||||
(this.textEl = h('textarea', '')
|
||||
.on('input', evt => inputEventHandler.call(this, evt))
|
||||
.on('paste.stop', () => {
|
||||
console.log('empty function');
|
||||
})
|
||||
.on('keydown', evt => keydownEventHandler.call(this, evt))),
|
||||
(this.textlineEl = h('div', 'textline')),
|
||||
this.suggest.el,
|
||||
// this.datepicker.el,
|
||||
)
|
||||
.on('mousemove.stop', () => {
|
||||
console.log('empty function');
|
||||
})
|
||||
.on('mousedown.stop', () => {
|
||||
console.log('empty function');
|
||||
});
|
||||
this.el = h('div', `${cssPrefix}-editor`).child(this.areaEl).hide();
|
||||
this.suggest.bindInputEvents(this.textEl);
|
||||
|
||||
this.areaOffset = null;
|
||||
this.freeze = { w: 0, h: 0 };
|
||||
this.cell = null;
|
||||
this.inputText = '';
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
setFreezeLengths(width, height) {
|
||||
this.freeze.w = width;
|
||||
this.freeze.h = height;
|
||||
}
|
||||
|
||||
clear() {
|
||||
// const { cell } = this;
|
||||
// const cellText = (cell && cell.text) || '';
|
||||
if (this.inputText !== '') {
|
||||
this.change('finished', this.inputText);
|
||||
}
|
||||
this.cell = null;
|
||||
this.areaOffset = null;
|
||||
this.inputText = '';
|
||||
this.el.hide();
|
||||
this.textEl.val('');
|
||||
this.textlineEl.html('');
|
||||
resetSuggestItems.call(this);
|
||||
// this.datepicker.hide();
|
||||
}
|
||||
|
||||
setOffset(offset, suggestPosition = 'top') {
|
||||
const { textEl, areaEl, suggest, freeze, el } = this;
|
||||
if (offset) {
|
||||
this.areaOffset = offset;
|
||||
const { left, top, width, height, l, t } = offset;
|
||||
// console.log('left:', left, ',top:', top, ', freeze:', freeze);
|
||||
const elOffset = { left: 0, top: 0 };
|
||||
// top left
|
||||
if (freeze.w > l && freeze.h > t) {
|
||||
//
|
||||
} else if (freeze.w < l && freeze.h < t) {
|
||||
elOffset.left = freeze.w;
|
||||
elOffset.top = freeze.h;
|
||||
} else if (freeze.w > l) {
|
||||
elOffset.top = freeze.h;
|
||||
} else if (freeze.h > t) {
|
||||
elOffset.left = freeze.w;
|
||||
}
|
||||
el.offset(elOffset);
|
||||
areaEl.offset({ left: left - elOffset.left - 0.8, top: top - elOffset.top - 0.8 });
|
||||
textEl.offset({ width: width - 9 + 0.8, height: height - 3 + 0.8 });
|
||||
const sOffset = { left: 0 };
|
||||
sOffset[suggestPosition] = height;
|
||||
suggest.setOffset(sOffset);
|
||||
suggest.hide();
|
||||
}
|
||||
}
|
||||
|
||||
setCell(cell, validator) {
|
||||
if (cell && cell.editable === false) return;
|
||||
|
||||
// console.log('::', validator);
|
||||
const { el, datepicker, suggest } = this;
|
||||
el.show();
|
||||
this.cell = cell;
|
||||
const text = (cell && cell.text) || '';
|
||||
this.setText(text);
|
||||
|
||||
this.validator = validator;
|
||||
if (validator) {
|
||||
const { type } = validator;
|
||||
if (type === 'date') {
|
||||
// datepicker.show();
|
||||
if (!/^\s*$/.test(text)) {
|
||||
datepicker.setValue(text);
|
||||
}
|
||||
}
|
||||
if (type === 'list') {
|
||||
suggest.setItems(validator.values());
|
||||
suggest.search('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setText(text) {
|
||||
this.inputText = text;
|
||||
// console.log('text>>:', text);
|
||||
setText.call(this, text, text.length);
|
||||
resetTextareaSize.call(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
class Element {
|
||||
constructor(tag, className = '') {
|
||||
if (typeof tag === 'string') {
|
||||
this.el = document.createElement(tag);
|
||||
this.el.className = className;
|
||||
} else {
|
||||
this.el = tag;
|
||||
}
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
data(key, value) {
|
||||
if (value !== undefined) {
|
||||
this.data[key] = value;
|
||||
return this;
|
||||
}
|
||||
return this.data[key];
|
||||
}
|
||||
|
||||
on(eventNames, handler) {
|
||||
const [fen, ...oen] = eventNames.split('.');
|
||||
let eventName = fen;
|
||||
if (eventName === 'mousewheel' && /Firefox/i.test(window.navigator.userAgent)) {
|
||||
eventName = 'DOMMouseScroll';
|
||||
}
|
||||
this.el.addEventListener(eventName, evt => {
|
||||
handler(evt);
|
||||
for (let i = 0; i < oen.length; i += 1) {
|
||||
const k = oen[i];
|
||||
if (k === 'left' && evt.button !== 0) {
|
||||
return;
|
||||
}
|
||||
if (k === 'right' && evt.button !== 2) {
|
||||
return;
|
||||
}
|
||||
if (k === 'stop') {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
offset(value) {
|
||||
if (value !== undefined) {
|
||||
Object.keys(value).forEach(k => {
|
||||
this.css(k, `${value[k]}px`);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
const { offsetTop, offsetLeft, offsetHeight, offsetWidth } = this.el;
|
||||
return {
|
||||
top: offsetTop,
|
||||
left: offsetLeft,
|
||||
height: offsetHeight,
|
||||
width: offsetWidth,
|
||||
};
|
||||
}
|
||||
|
||||
scroll(v) {
|
||||
const { el } = this;
|
||||
if (v !== undefined) {
|
||||
if (v.left !== undefined) {
|
||||
el.scrollLeft = v.left;
|
||||
}
|
||||
if (v.top !== undefined) {
|
||||
el.scrollTop = v.top;
|
||||
}
|
||||
}
|
||||
return { left: el.scrollLeft, top: el.scrollTop };
|
||||
}
|
||||
|
||||
box() {
|
||||
return this.el.getBoundingClientRect();
|
||||
}
|
||||
|
||||
parent() {
|
||||
return new Element(this.el.parentNode);
|
||||
}
|
||||
|
||||
children(...eles) {
|
||||
if (arguments.length === 0) {
|
||||
return this.el.childNodes;
|
||||
}
|
||||
eles.forEach(ele => this.child(ele));
|
||||
return this;
|
||||
}
|
||||
|
||||
removeChild(el) {
|
||||
this.el.removeChild(el);
|
||||
}
|
||||
|
||||
/*
|
||||
first() {
|
||||
return this.el.firstChild;
|
||||
}
|
||||
|
||||
last() {
|
||||
return this.el.lastChild;
|
||||
}
|
||||
|
||||
remove(ele) {
|
||||
return this.el.removeChild(ele);
|
||||
}
|
||||
|
||||
prepend(ele) {
|
||||
const { el } = this;
|
||||
if (el.children.length > 0) {
|
||||
el.insertBefore(ele, el.firstChild);
|
||||
} else {
|
||||
el.appendChild(ele);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
prev() {
|
||||
return this.el.previousSibling;
|
||||
}
|
||||
|
||||
next() {
|
||||
return this.el.nextSibling;
|
||||
}
|
||||
*/
|
||||
|
||||
child(arg) {
|
||||
let ele = arg;
|
||||
if (typeof arg === 'string') {
|
||||
ele = document.createTextNode(arg);
|
||||
} else if (arg instanceof Element) {
|
||||
ele = arg.el;
|
||||
}
|
||||
this.el.appendChild(ele);
|
||||
return this;
|
||||
}
|
||||
|
||||
contains(ele) {
|
||||
return this.el.contains(ele);
|
||||
}
|
||||
|
||||
className(v) {
|
||||
if (v !== undefined) {
|
||||
this.el.className = v;
|
||||
return this;
|
||||
}
|
||||
return this.el.className;
|
||||
}
|
||||
|
||||
addClass(name) {
|
||||
this.el.classList.add(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
hasClass(name) {
|
||||
return this.el.classList.contains(name);
|
||||
}
|
||||
|
||||
removeClass(name) {
|
||||
this.el.classList.remove(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
toggle(cls = 'active') {
|
||||
return this.toggleClass(cls);
|
||||
}
|
||||
|
||||
toggleClass(name) {
|
||||
return this.el.classList.toggle(name);
|
||||
}
|
||||
|
||||
active(flag = true, cls = 'active') {
|
||||
if (flag) this.addClass(cls);
|
||||
else this.removeClass(cls);
|
||||
return this;
|
||||
}
|
||||
|
||||
checked(flag = true) {
|
||||
this.active(flag, 'checked');
|
||||
return this;
|
||||
}
|
||||
|
||||
disabled(flag = true) {
|
||||
if (flag) this.addClass('disabled');
|
||||
else this.removeClass('disabled');
|
||||
return this;
|
||||
}
|
||||
|
||||
// key, value
|
||||
// key
|
||||
// {k, v}...
|
||||
attr(key, value) {
|
||||
if (value !== undefined) {
|
||||
this.el.setAttribute(key, value);
|
||||
} else {
|
||||
if (typeof key === 'string') {
|
||||
return this.el.getAttribute(key);
|
||||
}
|
||||
Object.keys(key).forEach(k => {
|
||||
this.el.setAttribute(k, key[k]);
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
removeAttr(key) {
|
||||
this.el.removeAttribute(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
html(content) {
|
||||
if (content !== undefined) {
|
||||
this.el.innerHTML = content;
|
||||
return this;
|
||||
}
|
||||
return this.el.innerHTML;
|
||||
}
|
||||
|
||||
val(v) {
|
||||
if (v !== undefined) {
|
||||
this.el.value = v;
|
||||
return this;
|
||||
}
|
||||
return this.el.value;
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.el.focus();
|
||||
}
|
||||
|
||||
cssRemoveKeys(...keys) {
|
||||
keys.forEach(k => this.el.style.removeProperty(k));
|
||||
return this;
|
||||
}
|
||||
|
||||
// css( propertyName )
|
||||
// css( propertyName, value )
|
||||
// css( properties )
|
||||
css(name, value) {
|
||||
if (value === undefined && typeof name !== 'string') {
|
||||
Object.keys(name).forEach(k => {
|
||||
this.el.style[k] = name[k];
|
||||
});
|
||||
return this;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
this.el.style[name] = value;
|
||||
return this;
|
||||
}
|
||||
return this.el.style[name];
|
||||
}
|
||||
|
||||
computedStyle() {
|
||||
return window.getComputedStyle(this.el, null);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.css('display', 'block');
|
||||
return this;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.css('display', 'none');
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
const h = (tag, className = '') => new Element(tag, className);
|
||||
|
||||
export { Element, h };
|
||||
@@ -0,0 +1,148 @@
|
||||
export function bind(target, name, fn) {
|
||||
target.addEventListener(name, fn);
|
||||
}
|
||||
export function unbind(target, name, fn) {
|
||||
target.removeEventListener(name, fn);
|
||||
}
|
||||
export function unbindClickoutside(el) {
|
||||
if (el.xclickoutside) {
|
||||
unbind(window.document.body, 'click', el.xclickoutside);
|
||||
delete el.xclickoutside;
|
||||
}
|
||||
}
|
||||
|
||||
// the left mouse button: mousedown → mouseup → click
|
||||
// the right mouse button: mousedown → contenxtmenu → mouseup
|
||||
// the right mouse button in firefox(>65.0): mousedown → contenxtmenu → mouseup → click on window
|
||||
export function bindClickoutside(el, cb) {
|
||||
el.xclickoutside = evt => {
|
||||
// ignore double click
|
||||
// console.log('evt:', evt);
|
||||
if (evt.detail === 2 || el.contains(evt.target)) return;
|
||||
if (cb) cb(el);
|
||||
else {
|
||||
el.hide();
|
||||
unbindClickoutside(el);
|
||||
}
|
||||
};
|
||||
bind(window.document.body, 'click', el.xclickoutside);
|
||||
}
|
||||
export function mouseMoveUp(target, movefunc, upfunc) {
|
||||
bind(target, 'mousemove', movefunc);
|
||||
const t = target;
|
||||
t.xEvtUp = evt => {
|
||||
// console.log('mouseup>>>');
|
||||
unbind(target, 'mousemove', movefunc);
|
||||
unbind(target, 'mouseup', target.xEvtUp);
|
||||
upfunc(evt);
|
||||
};
|
||||
bind(target, 'mouseup', target.xEvtUp);
|
||||
}
|
||||
|
||||
function calTouchDirection(spanx, spany, evt, cb) {
|
||||
let direction = '';
|
||||
// console.log('spanx:', spanx, ', spany:', spany);
|
||||
if (Math.abs(spanx) > Math.abs(spany)) {
|
||||
// horizontal
|
||||
direction = spanx > 0 ? 'right' : 'left';
|
||||
cb(direction, spanx, evt);
|
||||
} else {
|
||||
// vertical
|
||||
direction = spany > 0 ? 'down' : 'up';
|
||||
cb(direction, spany, evt);
|
||||
}
|
||||
}
|
||||
// cb = (direction, distance) => {}
|
||||
export function bindTouch(target, { move, end }) {
|
||||
let startx = 0;
|
||||
let starty = 0;
|
||||
bind(target, 'touchstart', evt => {
|
||||
const { pageX, pageY } = evt.touches[0];
|
||||
startx = pageX;
|
||||
starty = pageY;
|
||||
});
|
||||
bind(target, 'touchmove', evt => {
|
||||
if (!move) return;
|
||||
const { pageX, pageY } = evt.changedTouches[0];
|
||||
const spanx = pageX - startx;
|
||||
const spany = pageY - starty;
|
||||
if (Math.abs(spanx) > 10 || Math.abs(spany) > 10) {
|
||||
// console.log('spanx:', spanx, ', spany:', spany);
|
||||
calTouchDirection(spanx, spany, evt, move);
|
||||
startx = pageX;
|
||||
starty = pageY;
|
||||
}
|
||||
evt.preventDefault();
|
||||
});
|
||||
bind(target, 'touchend', evt => {
|
||||
if (!end) return;
|
||||
const { pageX, pageY } = evt.changedTouches[0];
|
||||
const spanx = pageX - startx;
|
||||
const spany = pageY - starty;
|
||||
calTouchDirection(spanx, spany, evt, end);
|
||||
});
|
||||
}
|
||||
|
||||
// eventemiter
|
||||
export function createEventEmitter() {
|
||||
const listeners = new Map();
|
||||
|
||||
function on(eventName, callback) {
|
||||
const push = () => {
|
||||
const currentListener = listeners.get(eventName);
|
||||
return (Array.isArray(currentListener) && currentListener.push(callback)) || false;
|
||||
};
|
||||
|
||||
const create = () => listeners.set(eventName, [].concat(callback));
|
||||
|
||||
return (listeners.has(eventName) && push()) || create();
|
||||
}
|
||||
|
||||
function fire(eventName, args) {
|
||||
const exec = () => {
|
||||
const currentListener = listeners.get(eventName);
|
||||
for (const callback of currentListener) callback.call(null, ...args);
|
||||
};
|
||||
|
||||
return listeners.has(eventName) && exec();
|
||||
}
|
||||
|
||||
function removeListener(eventName, callback) {
|
||||
const remove = () => {
|
||||
const currentListener = listeners.get(eventName);
|
||||
const idx = currentListener.indexOf(callback);
|
||||
return (
|
||||
idx >= 0 &&
|
||||
currentListener.splice(idx, 1) &&
|
||||
listeners.get(eventName).length === 0 &&
|
||||
listeners.delete(eventName)
|
||||
);
|
||||
};
|
||||
|
||||
return listeners.has(eventName) && remove();
|
||||
}
|
||||
|
||||
function once(eventName, callback) {
|
||||
const execCalllback = (...args) => {
|
||||
callback.call(null, ...args);
|
||||
removeListener(eventName, execCalllback);
|
||||
};
|
||||
|
||||
return on(eventName, execCalllback);
|
||||
}
|
||||
|
||||
function removeAllListeners() {
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
return {
|
||||
get current() {
|
||||
return listeners;
|
||||
},
|
||||
on,
|
||||
once,
|
||||
fire,
|
||||
removeListener,
|
||||
removeAllListeners,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
const patterns = {
|
||||
number: /(^\d+$)|(^\d+(\.\d{0,4})?$)/,
|
||||
date: /^\d{4}-\d{1,2}-\d{1,2}$/,
|
||||
};
|
||||
|
||||
// rule: { required: false, type, pattern: // }
|
||||
export default class FormField {
|
||||
constructor(input, rule, label, labelWidth) {
|
||||
this.label = '';
|
||||
this.rule = rule;
|
||||
if (label) {
|
||||
this.label = h('label', 'label').css('width', `${labelWidth}px`).html(label);
|
||||
}
|
||||
this.tip = h('div', 'tip').child('tip').hide();
|
||||
this.input = input;
|
||||
this.input.vchange = () => this.validate();
|
||||
this.el = h('div', `${cssPrefix}-form-field`).children(this.label, input.el, this.tip);
|
||||
}
|
||||
|
||||
isShow() {
|
||||
return this.el.css('display') !== 'none';
|
||||
}
|
||||
|
||||
show() {
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
return this;
|
||||
}
|
||||
|
||||
val(v) {
|
||||
return this.input.val(v);
|
||||
}
|
||||
|
||||
hint(hint) {
|
||||
this.input.hint(hint);
|
||||
}
|
||||
|
||||
validate() {
|
||||
const { input, rule, tip, el } = this;
|
||||
const v = input.val();
|
||||
if (rule.required) {
|
||||
if (/^\s*$/.test(v)) {
|
||||
tip.html(t('validation.required'));
|
||||
el.addClass('error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (rule.type || rule.pattern) {
|
||||
const pattern = rule.pattern || patterns[rule.type];
|
||||
if (!pattern.test(v)) {
|
||||
tip.html(t('validation.notMatch'));
|
||||
el.addClass('error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
el.removeClass('error');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class FormInput {
|
||||
constructor(width, hint) {
|
||||
this.vchange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.el = h('div', `${cssPrefix}-form-input`);
|
||||
this.input = h('input', '')
|
||||
.css('width', width)
|
||||
.on('input', evt => this.vchange(evt))
|
||||
.attr('placeholder', hint);
|
||||
this.el.child(this.input);
|
||||
}
|
||||
|
||||
focus() {
|
||||
setTimeout(() => {
|
||||
this.input.el.focus();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
hint(v) {
|
||||
this.input.attr('placeholder', v);
|
||||
}
|
||||
|
||||
val(v) {
|
||||
return this.input.val(v);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Suggest from './suggest.js';
|
||||
|
||||
export default class FormSelect {
|
||||
constructor(
|
||||
key,
|
||||
items,
|
||||
width,
|
||||
getTitle = it => it,
|
||||
change = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
) {
|
||||
this.key = key;
|
||||
this.getTitle = getTitle;
|
||||
this.vchange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.el = h('div', `${cssPrefix}-form-select`);
|
||||
this.suggest = new Suggest(
|
||||
items.map(it => ({ key: it, title: this.getTitle(it) })),
|
||||
it => {
|
||||
this.itemClick(it.key);
|
||||
change(it.key);
|
||||
this.vchange(it.key);
|
||||
},
|
||||
width,
|
||||
this.el,
|
||||
);
|
||||
this.el
|
||||
.children((this.itemEl = h('div', 'input-text').html(this.getTitle(key))), this.suggest.el)
|
||||
.on('click', () => this.show());
|
||||
}
|
||||
|
||||
show() {
|
||||
this.suggest.search('');
|
||||
}
|
||||
|
||||
itemClick(it) {
|
||||
this.key = it;
|
||||
this.itemEl.html(this.getTitle(it));
|
||||
}
|
||||
|
||||
val(v) {
|
||||
if (v !== undefined) {
|
||||
this.key = v;
|
||||
this.itemEl.html(this.getTitle(v));
|
||||
return this;
|
||||
}
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { Element, h } from './element.js';
|
||||
|
||||
export default class Icon extends Element {
|
||||
constructor(name) {
|
||||
super('div', `${cssPrefix}-icon`);
|
||||
this.iconNameEl = h('div', `${cssPrefix}-icon-img ${name}`);
|
||||
this.child(this.iconNameEl);
|
||||
}
|
||||
|
||||
setName(name) {
|
||||
this.iconNameEl.className(`${cssPrefix}-icon-img ${name}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
export function xtoast(title, content) {
|
||||
const el = h('div', `${cssPrefix}-toast`);
|
||||
const dimmer = h('div', `${cssPrefix}-dimmer active`);
|
||||
const remove = () => {
|
||||
document.body.removeChild(el.el);
|
||||
document.body.removeChild(dimmer.el);
|
||||
};
|
||||
|
||||
el.children(
|
||||
h('div', `${cssPrefix}-toast-header`).children(
|
||||
new Icon('close').on('click.stop', () => remove()),
|
||||
title,
|
||||
),
|
||||
h('div', `${cssPrefix}-toast-content`).html(content),
|
||||
);
|
||||
document.body.appendChild(el.el);
|
||||
document.body.appendChild(dimmer.el);
|
||||
// set offset
|
||||
const { width, height } = el.box();
|
||||
const { clientHeight, clientWidth } = document.documentElement;
|
||||
el.offset({
|
||||
left: (clientWidth - width) / 2,
|
||||
top: (clientHeight - height) / 3,
|
||||
});
|
||||
}
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,45 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
import { bind, unbind } from './event.js';
|
||||
|
||||
export default class Modal {
|
||||
constructor(title, content, width = '600px') {
|
||||
this.title = title;
|
||||
this.el = h('div', `${cssPrefix}-modal`)
|
||||
.css('width', width)
|
||||
.children(
|
||||
h('div', `${cssPrefix}-modal-header`).children(
|
||||
new Icon('close').on('click.stop', () => this.hide()),
|
||||
this.title,
|
||||
),
|
||||
h('div', `${cssPrefix}-modal-content`).children(...content),
|
||||
)
|
||||
.hide();
|
||||
}
|
||||
|
||||
show() {
|
||||
// dimmer
|
||||
this.dimmer = h('div', `${cssPrefix}-dimmer active`);
|
||||
document.body.appendChild(this.dimmer.el);
|
||||
const { width, height } = this.el.show().box();
|
||||
const { clientHeight, clientWidth } = document.documentElement;
|
||||
this.el.offset({
|
||||
left: (clientWidth - width) / 2,
|
||||
top: (clientHeight - height) / 3,
|
||||
});
|
||||
window.xkeydownEsc = evt => {
|
||||
if (evt.keyCode === 27) {
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
bind(window, 'keydown', window.xkeydownEsc);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
document.body.removeChild(this.dimmer.el);
|
||||
unbind(window, 'keydown', window.xkeydownEsc);
|
||||
delete window.xkeydownEsc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
import { t } from '../locale/locale.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Modal from './modal.js';
|
||||
import FormInput from './form_input.js';
|
||||
import FormSelect from './form_select.js';
|
||||
import FormField from './form_field.js';
|
||||
import Button from './button.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
const fieldLabelWidth = 100;
|
||||
|
||||
export default class ModalValidation extends Modal {
|
||||
constructor() {
|
||||
const mf = new FormField(
|
||||
new FormSelect(
|
||||
'cell',
|
||||
['cell'], // cell|row|column
|
||||
'100%',
|
||||
it => t(`dataValidation.modeType.${it}`),
|
||||
),
|
||||
{ required: true },
|
||||
`${t('dataValidation.range')}:`,
|
||||
fieldLabelWidth,
|
||||
);
|
||||
const rf = new FormField(new FormInput('120px', 'E3 or E3:F12'), {
|
||||
required: true,
|
||||
pattern: /^([A-Z]{1,2}[1-9]\d*)(:[A-Z]{1,2}[1-9]\d*)?$/,
|
||||
});
|
||||
const cf = new FormField(
|
||||
new FormSelect(
|
||||
'list',
|
||||
['list', 'number', 'date', 'phone', 'email'],
|
||||
'100%',
|
||||
it => t(`dataValidation.type.${it}`),
|
||||
it => this.criteriaSelected(it),
|
||||
),
|
||||
{ required: true },
|
||||
`${t('dataValidation.criteria')}:`,
|
||||
fieldLabelWidth,
|
||||
);
|
||||
|
||||
// operator
|
||||
const of = new FormField(
|
||||
new FormSelect(
|
||||
'be',
|
||||
['be', 'nbe', 'eq', 'neq', 'lt', 'lte', 'gt', 'gte'],
|
||||
'160px',
|
||||
it => t(`dataValidation.operator.${it}`),
|
||||
it => this.criteriaOperatorSelected(it),
|
||||
),
|
||||
{ required: true },
|
||||
).hide();
|
||||
// min, max
|
||||
const minvf = new FormField(new FormInput('70px', '10'), { required: true }).hide();
|
||||
const maxvf = new FormField(new FormInput('70px', '100'), {
|
||||
required: true,
|
||||
type: 'number',
|
||||
}).hide();
|
||||
// value
|
||||
const svf = new FormField(new FormInput('120px', 'a,b,c'), { required: true });
|
||||
const vf = new FormField(new FormInput('70px', '10'), {
|
||||
required: true,
|
||||
type: 'number',
|
||||
}).hide();
|
||||
|
||||
super(t('contextmenu.validation'), [
|
||||
h('div', `${cssPrefix}-form-fields`).children(mf.el, rf.el),
|
||||
h('div', `${cssPrefix}-form-fields`).children(
|
||||
cf.el,
|
||||
of.el,
|
||||
minvf.el,
|
||||
maxvf.el,
|
||||
vf.el,
|
||||
svf.el,
|
||||
),
|
||||
h('div', `${cssPrefix}-buttons`).children(
|
||||
new Button('cancel').on('click', () => this.btnClick('cancel')),
|
||||
new Button('remove').on('click', () => this.btnClick('remove')),
|
||||
new Button('save', 'primary').on('click', () => this.btnClick('save')),
|
||||
),
|
||||
]);
|
||||
this.mf = mf;
|
||||
this.rf = rf;
|
||||
this.cf = cf;
|
||||
this.of = of;
|
||||
this.minvf = minvf;
|
||||
this.maxvf = maxvf;
|
||||
this.vf = vf;
|
||||
this.svf = svf;
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
showVf(it) {
|
||||
const hint = it === 'date' ? '2018-11-12' : '10';
|
||||
const { vf } = this;
|
||||
vf.input.hint(hint);
|
||||
vf.show();
|
||||
}
|
||||
|
||||
criteriaSelected(it) {
|
||||
const { of, minvf, maxvf, vf, svf } = this;
|
||||
if (it === 'date' || it === 'number') {
|
||||
of.show();
|
||||
minvf.rule.type = it;
|
||||
maxvf.rule.type = it;
|
||||
if (it === 'date') {
|
||||
minvf.hint('2018-11-12');
|
||||
maxvf.hint('2019-11-12');
|
||||
} else {
|
||||
minvf.hint('10');
|
||||
maxvf.hint('100');
|
||||
}
|
||||
minvf.show();
|
||||
maxvf.show();
|
||||
vf.hide();
|
||||
svf.hide();
|
||||
} else {
|
||||
if (it === 'list') {
|
||||
svf.show();
|
||||
} else {
|
||||
svf.hide();
|
||||
}
|
||||
vf.hide();
|
||||
of.hide();
|
||||
minvf.hide();
|
||||
maxvf.hide();
|
||||
}
|
||||
}
|
||||
|
||||
criteriaOperatorSelected(it) {
|
||||
if (!it) return;
|
||||
const { minvf, maxvf, vf } = this;
|
||||
if (it === 'be' || it === 'nbe') {
|
||||
minvf.show();
|
||||
maxvf.show();
|
||||
vf.hide();
|
||||
} else {
|
||||
const type = this.cf.val();
|
||||
vf.rule.type = type;
|
||||
if (type === 'date') {
|
||||
vf.hint('2018-11-12');
|
||||
} else {
|
||||
vf.hint('10');
|
||||
}
|
||||
vf.show();
|
||||
minvf.hide();
|
||||
maxvf.hide();
|
||||
}
|
||||
}
|
||||
|
||||
btnClick(action) {
|
||||
if (action === 'cancel') {
|
||||
this.hide();
|
||||
} else if (action === 'remove') {
|
||||
this.change('remove');
|
||||
this.hide();
|
||||
} else if (action === 'save') {
|
||||
// validate
|
||||
const attrs = ['mf', 'rf', 'cf', 'of', 'svf', 'vf', 'minvf', 'maxvf'];
|
||||
for (let i = 0; i < attrs.length; i += 1) {
|
||||
const field = this[attrs[i]];
|
||||
// console.log('field:', field);
|
||||
if (field.isShow()) {
|
||||
// console.log('it:', it);
|
||||
if (!field.validate()) return;
|
||||
}
|
||||
}
|
||||
|
||||
const mode = this.mf.val();
|
||||
const ref = this.rf.val();
|
||||
const type = this.cf.val();
|
||||
const operator = this.of.val();
|
||||
let value = this.svf.val();
|
||||
if (type === 'number' || type === 'date') {
|
||||
if (operator === 'be' || operator === 'nbe') {
|
||||
value = [this.minvf.val(), this.maxvf.val()];
|
||||
} else {
|
||||
value = this.vf.val();
|
||||
}
|
||||
}
|
||||
// console.log(mode, ref, type, operator, value);
|
||||
this.change('save', mode, ref, type, operator, {
|
||||
required: false,
|
||||
value,
|
||||
});
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
// validation: { mode, ref, validator }
|
||||
setValue(v) {
|
||||
if (v) {
|
||||
const { mf, rf, cf, of, svf, vf, minvf, maxvf } = this;
|
||||
const { mode, ref, validator } = v;
|
||||
const { type, operator, value } = validator || { type: 'list' };
|
||||
mf.val(mode || 'cell');
|
||||
rf.val(ref);
|
||||
cf.val(type);
|
||||
of.val(operator);
|
||||
if (Array.isArray(value)) {
|
||||
minvf.val(value[0]);
|
||||
maxvf.val(value[1]);
|
||||
} else {
|
||||
svf.val(value || '');
|
||||
vf.val(value || '');
|
||||
}
|
||||
this.criteriaSelected(type);
|
||||
this.criteriaOperatorSelected(operator);
|
||||
}
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { Draw } from '../canvas/draw.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import Button from './button.js';
|
||||
import { renderCell } from './table.js';
|
||||
|
||||
// resolution: 72 => 595 x 842
|
||||
// 150 => 1240 x 1754
|
||||
// 200 => 1654 x 2339
|
||||
// 300 => 2479 x 3508
|
||||
// 96 * cm / 2.54 , 96 * cm / 2.54
|
||||
|
||||
const PAGER_SIZES = [
|
||||
['A3', 11.69, 16.54],
|
||||
['A4', 8.27, 11.69],
|
||||
['A5', 5.83, 8.27],
|
||||
['B4', 9.84, 13.9],
|
||||
['B5', 6.93, 9.84],
|
||||
];
|
||||
|
||||
const PAGER_ORIENTATIONS = ['landscape', 'portrait'];
|
||||
|
||||
function inches2px(inc) {
|
||||
return parseInt(96 * inc, 10);
|
||||
}
|
||||
|
||||
function btnClick(type) {
|
||||
if (type === 'cancel') {
|
||||
this.el.hide();
|
||||
} else {
|
||||
this.toPrint();
|
||||
}
|
||||
}
|
||||
|
||||
function pagerSizeChange(evt) {
|
||||
const { paper } = this;
|
||||
const { value } = evt.target;
|
||||
const ps = PAGER_SIZES[value];
|
||||
paper.w = inches2px(ps[1]);
|
||||
paper.h = inches2px(ps[2]);
|
||||
// console.log('paper:', ps, paper);
|
||||
this.preview();
|
||||
}
|
||||
function pagerOrientationChange(evt) {
|
||||
const { paper } = this;
|
||||
const { value } = evt.target;
|
||||
const v = PAGER_ORIENTATIONS[value];
|
||||
paper.orientation = v;
|
||||
this.preview();
|
||||
}
|
||||
|
||||
export default class Print {
|
||||
constructor(data) {
|
||||
this.paper = {
|
||||
w: inches2px(PAGER_SIZES[0][1]),
|
||||
h: inches2px(PAGER_SIZES[0][2]),
|
||||
padding: 50,
|
||||
orientation: PAGER_ORIENTATIONS[0],
|
||||
get width() {
|
||||
return this.orientation === 'landscape' ? this.h : this.w;
|
||||
},
|
||||
get height() {
|
||||
return this.orientation === 'landscape' ? this.w : this.h;
|
||||
},
|
||||
};
|
||||
this.data = data;
|
||||
this.el = h('div', `${cssPrefix}-print`)
|
||||
.children(
|
||||
h('div', `${cssPrefix}-print-bar`).children(
|
||||
h('div', '-title').child('Print settings'),
|
||||
h('div', '-right').children(
|
||||
h('div', `${cssPrefix}-buttons`).children(
|
||||
new Button('cancel').on('click', btnClick.bind(this, 'cancel')),
|
||||
new Button('next', 'primary').on('click', btnClick.bind(this, 'next')),
|
||||
),
|
||||
),
|
||||
),
|
||||
h('div', `${cssPrefix}-print-content`).children(
|
||||
(this.contentEl = h('div', '-content')),
|
||||
h('div', '-sider').child(
|
||||
h('form', '').children(
|
||||
h('fieldset', '').children(
|
||||
h('label', '').child(`${t('print.size')}`),
|
||||
h('select', '')
|
||||
.children(
|
||||
...PAGER_SIZES.map((it, index) =>
|
||||
h('option', '')
|
||||
.attr('value', index)
|
||||
.child(`${it[0]} ( ${it[1]}''x${it[2]}'' )`),
|
||||
),
|
||||
)
|
||||
.on('change', pagerSizeChange.bind(this)),
|
||||
),
|
||||
h('fieldset', '').children(
|
||||
h('label', '').child(`${t('print.orientation')}`),
|
||||
h('select', '')
|
||||
.children(
|
||||
...PAGER_ORIENTATIONS.map((it, index) =>
|
||||
h('option', '')
|
||||
.attr('value', index)
|
||||
.child(`${t('print.orientations')[index]}`),
|
||||
),
|
||||
)
|
||||
.on('change', pagerOrientationChange.bind(this)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.hide();
|
||||
}
|
||||
|
||||
resetData(data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
preview() {
|
||||
const { data, paper } = this;
|
||||
const { width, height, padding } = paper;
|
||||
const iwidth = width - padding * 2;
|
||||
const iheight = height - padding * 2;
|
||||
const cr = data.contentRange();
|
||||
const pages = parseInt(cr.h / iheight, 10) + 1;
|
||||
const scale = iwidth / cr.w;
|
||||
let left = padding;
|
||||
const top = padding;
|
||||
if (scale > 1) {
|
||||
left += (iwidth - cr.w) / 2;
|
||||
}
|
||||
let ri = 0;
|
||||
let yoffset = 0;
|
||||
this.contentEl.html('');
|
||||
this.canvases = [];
|
||||
const mViewRange = {
|
||||
sri: 0,
|
||||
sci: 0,
|
||||
eri: 0,
|
||||
eci: 0,
|
||||
};
|
||||
for (let i = 0; i < pages; i += 1) {
|
||||
let th = 0;
|
||||
let yo = 0;
|
||||
const wrap = h('div', `${cssPrefix}-canvas-card`);
|
||||
const canvas = h('canvas', `${cssPrefix}-canvas`);
|
||||
this.canvases.push(canvas.el);
|
||||
const draw = new Draw(canvas.el, width, height);
|
||||
// cell-content
|
||||
draw.save();
|
||||
draw.translate(left, top);
|
||||
if (scale < 1) draw.scale(scale, scale);
|
||||
// console.log('ri:', ri, cr.eri, yoffset);
|
||||
for (; ri <= cr.eri; ri += 1) {
|
||||
const rh = data.rows.getHeight(ri);
|
||||
th += rh;
|
||||
if (th < iheight) {
|
||||
for (let ci = 0; ci <= cr.eci; ci += 1) {
|
||||
renderCell(draw, data, ri, ci, yoffset);
|
||||
mViewRange.eci = ci;
|
||||
}
|
||||
} else {
|
||||
yo = -(th - rh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mViewRange.eri = ri;
|
||||
draw.restore();
|
||||
// merge-cell
|
||||
draw.save();
|
||||
draw.translate(left, top);
|
||||
if (scale < 1) draw.scale(scale, scale);
|
||||
const yof = yoffset;
|
||||
data.eachMergesInView(mViewRange, ({ sri, sci }) => {
|
||||
renderCell(draw, data, sri, sci, yof);
|
||||
});
|
||||
draw.restore();
|
||||
|
||||
mViewRange.sri = mViewRange.eri;
|
||||
mViewRange.sci = mViewRange.eci;
|
||||
yoffset += yo;
|
||||
this.contentEl.child(h('div', `${cssPrefix}-canvas-card-wraper`).child(wrap.child(canvas)));
|
||||
}
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
toPrint() {
|
||||
this.el.hide();
|
||||
const { paper } = this;
|
||||
const iframe = h('iframe', '').hide();
|
||||
const { el } = iframe;
|
||||
window.document.body.appendChild(el);
|
||||
const { contentWindow } = el;
|
||||
const idoc = contentWindow.document;
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `
|
||||
@page { size: ${paper.width}px ${paper.height}px; };
|
||||
canvas {
|
||||
page-break-before: auto;
|
||||
page-break-after: always;
|
||||
image-rendering: pixelated;
|
||||
};
|
||||
`;
|
||||
idoc.head.appendChild(style);
|
||||
this.canvases.forEach(it => {
|
||||
const cn = it.cloneNode(false);
|
||||
const ctx = cn.getContext('2d');
|
||||
// ctx.imageSmoothingEnabled = true;
|
||||
ctx.drawImage(it, 0, 0);
|
||||
idoc.body.appendChild(cn);
|
||||
});
|
||||
contentWindow.print();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import { mouseMoveUp } from './event.js';
|
||||
|
||||
export default class Resizer {
|
||||
constructor(vertical = false, minDistance) {
|
||||
this.moving = false;
|
||||
this.vertical = vertical;
|
||||
this.el = h('div', `${cssPrefix}-resizer ${vertical ? 'vertical' : 'horizontal'}`)
|
||||
.children(
|
||||
(this.unhideHoverEl = h('div', `${cssPrefix}-resizer-hover`)
|
||||
.on('dblclick.stop', evt => this.mousedblclickHandler(evt))
|
||||
.css('position', 'absolute')
|
||||
.hide()),
|
||||
(this.hoverEl = h('div', `${cssPrefix}-resizer-hover`).on('mousedown.stop', evt =>
|
||||
this.mousedownHandler(evt),
|
||||
)),
|
||||
(this.lineEl = h('div', `${cssPrefix}-resizer-line`).hide()),
|
||||
)
|
||||
.hide();
|
||||
// cell rect
|
||||
this.cRect = null;
|
||||
this.finishedFn = null;
|
||||
this.minDistance = minDistance;
|
||||
this.unhideFn = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
showUnhide(index) {
|
||||
this.unhideIndex = index;
|
||||
this.unhideHoverEl.show();
|
||||
}
|
||||
|
||||
hideUnhide() {
|
||||
this.unhideHoverEl.hide();
|
||||
}
|
||||
|
||||
// rect : {top, left, width, height}
|
||||
// line : {width, height}
|
||||
show(rect, line) {
|
||||
const { moving, vertical, hoverEl, lineEl, el, unhideHoverEl } = this;
|
||||
if (moving) return;
|
||||
this.cRect = rect;
|
||||
const { left, top, width, height } = rect;
|
||||
el.offset({
|
||||
left: vertical ? left + width - 5 : left,
|
||||
top: vertical ? top : top + height - 5,
|
||||
}).show();
|
||||
hoverEl.offset({
|
||||
width: vertical ? 5 : width,
|
||||
height: vertical ? height : 5,
|
||||
});
|
||||
lineEl.offset({
|
||||
width: vertical ? 0 : line.width,
|
||||
height: vertical ? line.height : 0,
|
||||
});
|
||||
unhideHoverEl.offset({
|
||||
left: vertical ? 5 - width : left,
|
||||
top: vertical ? top : 5 - height,
|
||||
width: vertical ? 5 : width,
|
||||
height: vertical ? height : 5,
|
||||
});
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el
|
||||
.offset({
|
||||
left: 0,
|
||||
top: 0,
|
||||
})
|
||||
.hide();
|
||||
this.hideUnhide();
|
||||
}
|
||||
|
||||
mousedblclickHandler() {
|
||||
if (this.unhideIndex) this.unhideFn(this.unhideIndex);
|
||||
}
|
||||
|
||||
mousedownHandler(evt) {
|
||||
let startEvt = evt;
|
||||
const { el, lineEl, cRect, vertical, minDistance } = this;
|
||||
let distance = vertical ? cRect.width : cRect.height;
|
||||
// console.log('distance:', distance);
|
||||
lineEl.show();
|
||||
mouseMoveUp(
|
||||
window,
|
||||
e => {
|
||||
this.moving = true;
|
||||
if (startEvt !== null && e.buttons === 1) {
|
||||
// console.log('top:', top, ', left:', top, ', cRect:', cRect);
|
||||
if (vertical) {
|
||||
distance += e.movementX;
|
||||
if (distance > minDistance) {
|
||||
el.css('left', `${cRect.left + distance}px`);
|
||||
}
|
||||
} else {
|
||||
distance += e.movementY;
|
||||
if (distance > minDistance) {
|
||||
el.css('top', `${cRect.top + distance}px`);
|
||||
}
|
||||
}
|
||||
startEvt = e;
|
||||
}
|
||||
},
|
||||
() => {
|
||||
startEvt = null;
|
||||
lineEl.hide();
|
||||
this.moving = false;
|
||||
this.hide();
|
||||
if (this.finishedFn) {
|
||||
if (distance < minDistance) distance = minDistance;
|
||||
this.finishedFn(cRect, distance);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class Scrollbar {
|
||||
constructor(vertical) {
|
||||
this.vertical = vertical;
|
||||
this.moveFn = null;
|
||||
this.el = h('div', `${cssPrefix}-scrollbar ${vertical ? 'vertical' : 'horizontal'}`)
|
||||
.child((this.contentEl = h('div', '')))
|
||||
.on('mousemove.stop', () => {
|
||||
console.log('mousemove.stop');
|
||||
})
|
||||
.on('scroll.stop', evt => {
|
||||
const { scrollTop, scrollLeft } = evt.target;
|
||||
// console.log('scrollTop:', scrollTop);
|
||||
if (this.moveFn) {
|
||||
this.moveFn(this.vertical ? scrollTop : scrollLeft, evt);
|
||||
}
|
||||
// console.log('evt:::', evt);
|
||||
});
|
||||
}
|
||||
|
||||
move(v) {
|
||||
this.el.scroll(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
scroll() {
|
||||
return this.el.scroll();
|
||||
}
|
||||
|
||||
set(distance, contentDistance) {
|
||||
const d = distance - 1;
|
||||
// console.log('distance:', distance, ', contentDistance:', contentDistance);
|
||||
if (contentDistance > d) {
|
||||
const cssKey = this.vertical ? 'height' : 'width';
|
||||
// console.log('d:', d);
|
||||
this.el.css(cssKey, `${d - 15}px`).show();
|
||||
this.contentEl
|
||||
.css(this.vertical ? 'width' : 'height', '1px')
|
||||
.css(cssKey, `${contentDistance}px`);
|
||||
} else {
|
||||
this.el.hide();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { CellRange } from '../core/cell_range.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
const selectorHeightBorderWidth = 2 * 2 - 1;
|
||||
let startZIndex = 10;
|
||||
|
||||
class SelectorElement {
|
||||
constructor(useHideInput = false, autoFocus = true) {
|
||||
this.useHideInput = useHideInput;
|
||||
this.autoFocus = autoFocus;
|
||||
this.inputChange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.cornerEl = h('div', `${cssPrefix}-selector-corner`);
|
||||
this.areaEl = h('div', `${cssPrefix}-selector-area`).child(this.cornerEl).hide();
|
||||
this.clipboardEl = h('div', `${cssPrefix}-selector-clipboard`).hide();
|
||||
this.autofillEl = h('div', `${cssPrefix}-selector-autofill`).hide();
|
||||
this.el = h('div', `${cssPrefix}-selector`)
|
||||
.css('z-index', `${startZIndex}`)
|
||||
.children(this.areaEl, this.clipboardEl, this.autofillEl)
|
||||
.hide();
|
||||
if (useHideInput) {
|
||||
this.hideInput = h('input', '').on('compositionend', evt => {
|
||||
this.inputChange(evt.target.value);
|
||||
});
|
||||
this.el.child((this.hideInputDiv = h('div', 'hide-input').child(this.hideInput)));
|
||||
this.el.child((this.hideInputDiv = h('div', 'hide-input').child(this.hideInput)));
|
||||
}
|
||||
startZIndex += 1;
|
||||
}
|
||||
|
||||
setOffset(v) {
|
||||
this.el.offset(v).show();
|
||||
return this;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
return this;
|
||||
}
|
||||
|
||||
setAreaOffset(v) {
|
||||
const { left, top, width, height } = v;
|
||||
const of = {
|
||||
width: width - selectorHeightBorderWidth + 0.8,
|
||||
height: height - selectorHeightBorderWidth + 0.8,
|
||||
left: left - 0.8,
|
||||
top: top - 0.8,
|
||||
};
|
||||
this.areaEl.offset(of).show();
|
||||
if (this.useHideInput) {
|
||||
this.hideInputDiv.offset(of);
|
||||
if (this.autoFocus) {
|
||||
this.hideInput.val('').focus();
|
||||
} else {
|
||||
this.hideInput.val('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setClipboardOffset(v) {
|
||||
const { left, top, width, height } = v;
|
||||
this.clipboardEl.offset({
|
||||
left,
|
||||
top,
|
||||
width: width - 5,
|
||||
height: height - 5,
|
||||
});
|
||||
}
|
||||
|
||||
showAutofill(v) {
|
||||
const { left, top, width, height } = v;
|
||||
this.autofillEl
|
||||
.offset({
|
||||
width: width - selectorHeightBorderWidth,
|
||||
height: height - selectorHeightBorderWidth,
|
||||
left,
|
||||
top,
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
hideAutofill() {
|
||||
this.autofillEl.hide();
|
||||
}
|
||||
|
||||
showClipboard() {
|
||||
this.clipboardEl.show();
|
||||
}
|
||||
|
||||
hideClipboard() {
|
||||
this.clipboardEl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function calBRAreaOffset(offset) {
|
||||
const { data } = this;
|
||||
const { left, top, width, height, scroll, l, t } = offset;
|
||||
const ftwidth = data.freezeTotalWidth();
|
||||
const ftheight = data.freezeTotalHeight();
|
||||
let left0 = left - ftwidth;
|
||||
if (ftwidth > l) left0 -= scroll.x;
|
||||
let top0 = top - ftheight;
|
||||
if (ftheight > t) top0 -= scroll.y;
|
||||
return {
|
||||
left: left0,
|
||||
top: top0,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
function calTAreaOffset(offset) {
|
||||
const { data } = this;
|
||||
const { left, width, height, l, t, scroll } = offset;
|
||||
const ftwidth = data.freezeTotalWidth();
|
||||
let left0 = left - ftwidth;
|
||||
if (ftwidth > l) left0 -= scroll.x;
|
||||
return {
|
||||
left: left0,
|
||||
top: t,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
function calLAreaOffset(offset) {
|
||||
const { data } = this;
|
||||
const { top, width, height, l, t, scroll } = offset;
|
||||
const ftheight = data.freezeTotalHeight();
|
||||
let top0 = top - ftheight;
|
||||
// console.log('ftheight:', ftheight, ', t:', t);
|
||||
if (ftheight > t) top0 -= scroll.y;
|
||||
return {
|
||||
left: l,
|
||||
top: top0,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
function setBRAreaOffset(offset) {
|
||||
const { br } = this;
|
||||
br.setAreaOffset(calBRAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setTLAreaOffset(offset) {
|
||||
const { tl } = this;
|
||||
tl.setAreaOffset(offset);
|
||||
}
|
||||
|
||||
function setTAreaOffset(offset) {
|
||||
const { t } = this;
|
||||
t.setAreaOffset(calTAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setLAreaOffset(offset) {
|
||||
const { l } = this;
|
||||
l.setAreaOffset(calLAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setLClipboardOffset(offset) {
|
||||
const { l } = this;
|
||||
l.setClipboardOffset(calLAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setBRClipboardOffset(offset) {
|
||||
const { br } = this;
|
||||
br.setClipboardOffset(calBRAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setTLClipboardOffset(offset) {
|
||||
const { tl } = this;
|
||||
tl.setClipboardOffset(offset);
|
||||
}
|
||||
|
||||
function setTClipboardOffset(offset) {
|
||||
const { t } = this;
|
||||
t.setClipboardOffset(calTAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setAllAreaOffset(offset) {
|
||||
setBRAreaOffset.call(this, offset);
|
||||
setTLAreaOffset.call(this, offset);
|
||||
setTAreaOffset.call(this, offset);
|
||||
setLAreaOffset.call(this, offset);
|
||||
}
|
||||
|
||||
function setAllClipboardOffset(offset) {
|
||||
setBRClipboardOffset.call(this, offset);
|
||||
setTLClipboardOffset.call(this, offset);
|
||||
setTClipboardOffset.call(this, offset);
|
||||
setLClipboardOffset.call(this, offset);
|
||||
}
|
||||
|
||||
export default class Selector {
|
||||
constructor(data) {
|
||||
const { autoFocus } = data.settings;
|
||||
this.inputChange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.data = data;
|
||||
this.br = new SelectorElement(true, autoFocus);
|
||||
this.t = new SelectorElement();
|
||||
this.l = new SelectorElement();
|
||||
this.tl = new SelectorElement();
|
||||
this.br.inputChange = v => {
|
||||
this.inputChange(v);
|
||||
};
|
||||
this.br.el.show();
|
||||
this.offset = null;
|
||||
this.areaOffset = null;
|
||||
this.indexes = null;
|
||||
this.range = null;
|
||||
this.arange = null;
|
||||
this.el = h('div', `${cssPrefix}-selectors`)
|
||||
.children(this.tl.el, this.t.el, this.l.el, this.br.el)
|
||||
.hide();
|
||||
|
||||
// for performance
|
||||
this.lastri = -1;
|
||||
this.lastci = -1;
|
||||
|
||||
startZIndex += 1;
|
||||
}
|
||||
|
||||
resetData(data) {
|
||||
this.data = data;
|
||||
this.range = data.selector.range;
|
||||
this.resetAreaOffset();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
}
|
||||
|
||||
resetOffset() {
|
||||
const { data, tl, t, l, br } = this;
|
||||
const freezeHeight = data.freezeTotalHeight();
|
||||
const freezeWidth = data.freezeTotalWidth();
|
||||
if (freezeHeight > 0 || freezeWidth > 0) {
|
||||
tl.setOffset({ width: freezeWidth, height: freezeHeight });
|
||||
t.setOffset({ left: freezeWidth, height: freezeHeight });
|
||||
l.setOffset({ top: freezeHeight, width: freezeWidth });
|
||||
br.setOffset({ left: freezeWidth, top: freezeHeight });
|
||||
} else {
|
||||
tl.hide();
|
||||
t.hide();
|
||||
l.hide();
|
||||
br.setOffset({ left: 0, top: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
resetAreaOffset() {
|
||||
// console.log('offset:', offset);
|
||||
const offset = this.data.getSelectedRect();
|
||||
const coffset = this.data.getClipboardRect();
|
||||
setAllAreaOffset.call(this, offset);
|
||||
setAllClipboardOffset.call(this, coffset);
|
||||
this.resetOffset();
|
||||
}
|
||||
|
||||
resetBRTAreaOffset() {
|
||||
const offset = this.data.getSelectedRect();
|
||||
const coffset = this.data.getClipboardRect();
|
||||
setBRAreaOffset.call(this, offset);
|
||||
setTAreaOffset.call(this, offset);
|
||||
setBRClipboardOffset.call(this, coffset);
|
||||
setTClipboardOffset.call(this, coffset);
|
||||
this.resetOffset();
|
||||
}
|
||||
|
||||
resetBRLAreaOffset() {
|
||||
const offset = this.data.getSelectedRect();
|
||||
const coffset = this.data.getClipboardRect();
|
||||
setBRAreaOffset.call(this, offset);
|
||||
setLAreaOffset.call(this, offset);
|
||||
setBRClipboardOffset.call(this, coffset);
|
||||
setLClipboardOffset.call(this, coffset);
|
||||
this.resetOffset();
|
||||
}
|
||||
|
||||
set(ri, ci, indexesUpdated = true) {
|
||||
const { data } = this;
|
||||
const cellRange = data.calSelectedRangeByStart(ri, ci);
|
||||
const { sri, sci } = cellRange;
|
||||
if (indexesUpdated) {
|
||||
let [cri, cci] = [ri, ci];
|
||||
if (ri < 0) cri = 0;
|
||||
if (ci < 0) cci = 0;
|
||||
data.selector.setIndexes(cri, cci);
|
||||
this.indexes = [cri, cci];
|
||||
}
|
||||
|
||||
this.moveIndexes = [sri, sci];
|
||||
// this.sIndexes = sIndexes;
|
||||
// this.eIndexes = eIndexes;
|
||||
this.range = cellRange;
|
||||
this.resetAreaOffset();
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
setEnd(ri, ci, moving = true) {
|
||||
const { data, lastri, lastci } = this;
|
||||
if (moving) {
|
||||
if (ri === lastri && ci === lastci) return;
|
||||
this.lastri = ri;
|
||||
this.lastci = ci;
|
||||
}
|
||||
this.range = data.calSelectedRangeByEnd(ri, ci);
|
||||
setAllAreaOffset.call(this, this.data.getSelectedRect());
|
||||
}
|
||||
|
||||
reset() {
|
||||
// console.log('::::', this.data);
|
||||
const { eri, eci } = this.data.selector.range;
|
||||
this.setEnd(eri, eci);
|
||||
}
|
||||
|
||||
showAutofill(ri, ci) {
|
||||
if (ri === -1 && ci === -1) return;
|
||||
// console.log('ri:', ri, ', ci:', ci);
|
||||
// const [sri, sci] = this.sIndexes;
|
||||
// const [eri, eci] = this.eIndexes;
|
||||
const { sri, sci, eri, eci } = this.range;
|
||||
const [nri, nci] = [ri, ci];
|
||||
// const rn = eri - sri;
|
||||
// const cn = eci - sci;
|
||||
const srn = sri - ri;
|
||||
const scn = sci - ci;
|
||||
const ern = eri - ri;
|
||||
const ecn = eci - ci;
|
||||
if (scn > 0) {
|
||||
// left
|
||||
// console.log('left');
|
||||
this.arange = new CellRange(sri, nci, eri, sci - 1);
|
||||
// this.saIndexes = [sri, nci];
|
||||
// this.eaIndexes = [eri, sci - 1];
|
||||
// data.calRangeIndexes2(
|
||||
} else if (srn > 0) {
|
||||
// top
|
||||
// console.log('top');
|
||||
// nri = sri;
|
||||
this.arange = new CellRange(nri, sci, sri - 1, eci);
|
||||
// this.saIndexes = [nri, sci];
|
||||
// this.eaIndexes = [sri - 1, eci];
|
||||
} else if (ecn < 0) {
|
||||
// right
|
||||
// console.log('right');
|
||||
// nci = eci;
|
||||
this.arange = new CellRange(sri, eci + 1, eri, nci);
|
||||
// this.saIndexes = [sri, eci + 1];
|
||||
// this.eaIndexes = [eri, nci];
|
||||
} else if (ern < 0) {
|
||||
// bottom
|
||||
// console.log('bottom');
|
||||
// nri = eri;
|
||||
this.arange = new CellRange(eri + 1, sci, nri, eci);
|
||||
// this.saIndexes = [eri + 1, sci];
|
||||
// this.eaIndexes = [nri, eci];
|
||||
} else {
|
||||
// console.log('else:');
|
||||
this.arange = null;
|
||||
// this.saIndexes = null;
|
||||
// this.eaIndexes = null;
|
||||
return;
|
||||
}
|
||||
if (this.arange !== null) {
|
||||
// console.log(this.saIndexes, ':', this.eaIndexes);
|
||||
const offset = this.data.getRect(this.arange);
|
||||
offset.width += 2;
|
||||
offset.height += 2;
|
||||
const { br, l, t, tl } = this;
|
||||
br.showAutofill(calBRAreaOffset.call(this, offset));
|
||||
l.showAutofill(calLAreaOffset.call(this, offset));
|
||||
t.showAutofill(calTAreaOffset.call(this, offset));
|
||||
tl.showAutofill(offset);
|
||||
}
|
||||
}
|
||||
|
||||
hideAutofill() {
|
||||
['br', 'l', 't', 'tl'].forEach(property => {
|
||||
this[property].hideAutofill();
|
||||
});
|
||||
}
|
||||
|
||||
showClipboard() {
|
||||
const coffset = this.data.getClipboardRect();
|
||||
setAllClipboardOffset.call(this, coffset);
|
||||
['br', 'l', 't', 'tl'].forEach(property => {
|
||||
this[property].showClipboard();
|
||||
});
|
||||
}
|
||||
|
||||
hideClipboard() {
|
||||
['br', 'l', 't', 'tl'].forEach(property => {
|
||||
this[property].hideClipboard();
|
||||
});
|
||||
}
|
||||
}
|
||||
1089
OrangeFormsOpen-VUE3/src/components/SpreadSheet/component/sheet.js
Normal file
1089
OrangeFormsOpen-VUE3/src/components/SpreadSheet/component/sheet.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,148 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import Button from './button.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
|
||||
function buildMenu(clsName) {
|
||||
return h('div', `${cssPrefix}-item ${clsName}`);
|
||||
}
|
||||
|
||||
function buildSortItem(it) {
|
||||
return buildMenu('state')
|
||||
.child(t(`sort.${it}`))
|
||||
.on('click.stop', () => this.itemClick(it));
|
||||
}
|
||||
|
||||
function buildFilterBody(items) {
|
||||
const { filterbEl, filterValues } = this;
|
||||
filterbEl.html('');
|
||||
const itemKeys = Object.keys(items);
|
||||
itemKeys.forEach((it, index) => {
|
||||
const cnt = items[it];
|
||||
const active = filterValues.includes(it) ? 'checked' : '';
|
||||
filterbEl.child(
|
||||
h('div', `${cssPrefix}-item state ${active}`)
|
||||
.on('click.stop', () => this.filterClick(index, it))
|
||||
.children(it === '' ? t('filter.empty') : it, h('div', 'label').html(`(${cnt})`)),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function resetFilterHeader() {
|
||||
const { filterhEl, filterValues, values } = this;
|
||||
filterhEl.html(`${filterValues.length} / ${values.length}`);
|
||||
filterhEl.checked(filterValues.length === values.length);
|
||||
}
|
||||
|
||||
export default class SortFilter {
|
||||
constructor() {
|
||||
this.filterbEl = h('div', `${cssPrefix}-body`);
|
||||
this.filterhEl = h('div', `${cssPrefix}-header state`).on('click.stop', () =>
|
||||
this.filterClick(0, 'all'),
|
||||
);
|
||||
this.el = h('div', `${cssPrefix}-sort-filter`)
|
||||
.children(
|
||||
(this.sortAscEl = buildSortItem.call(this, 'asc')),
|
||||
(this.sortDescEl = buildSortItem.call(this, 'desc')),
|
||||
buildMenu('divider'),
|
||||
h('div', `${cssPrefix}-filter`).children(this.filterhEl, this.filterbEl),
|
||||
h('div', `${cssPrefix}-buttons`).children(
|
||||
new Button('cancel').on('click', () => this.btnClick('cancel')),
|
||||
new Button('ok', 'primary').on('click', () => this.btnClick('ok')),
|
||||
),
|
||||
)
|
||||
.hide();
|
||||
// this.setFilters(['test1', 'test2', 'text3']);
|
||||
this.ci = null;
|
||||
this.sortDesc = null;
|
||||
this.values = null;
|
||||
this.filterValues = [];
|
||||
}
|
||||
|
||||
btnClick(it) {
|
||||
if (it === 'ok') {
|
||||
const { ci, sort, filterValues } = this;
|
||||
if (this.ok) {
|
||||
this.ok(ci, sort, 'in', filterValues);
|
||||
}
|
||||
}
|
||||
this.hide();
|
||||
}
|
||||
|
||||
itemClick(it) {
|
||||
// console.log('it:', it);
|
||||
this.sort = it;
|
||||
const { sortAscEl, sortDescEl } = this;
|
||||
sortAscEl.checked(it === 'asc');
|
||||
sortDescEl.checked(it === 'desc');
|
||||
}
|
||||
|
||||
filterClick(index, it) {
|
||||
// console.log('index:', index, it);
|
||||
const { filterbEl, filterValues, values } = this;
|
||||
const children = filterbEl.children();
|
||||
if (it === 'all') {
|
||||
if (children.length === filterValues.length) {
|
||||
this.filterValues = [];
|
||||
children.forEach(i => h(i).checked(false));
|
||||
} else {
|
||||
this.filterValues = Array.from(values);
|
||||
children.forEach(i => h(i).checked(true));
|
||||
}
|
||||
} else {
|
||||
const checked = h(children[index]).toggle('checked');
|
||||
if (checked) {
|
||||
filterValues.push(it);
|
||||
} else {
|
||||
filterValues.splice(
|
||||
filterValues.findIndex(i => i === it),
|
||||
1,
|
||||
);
|
||||
}
|
||||
}
|
||||
resetFilterHeader.call(this);
|
||||
}
|
||||
|
||||
// v: autoFilter
|
||||
// items: {value: cnt}
|
||||
// sort { ci, order }
|
||||
set(ci, items, filter, sort) {
|
||||
this.ci = ci;
|
||||
const { sortAscEl, sortDescEl } = this;
|
||||
if (sort !== null) {
|
||||
this.sort = sort.order;
|
||||
sortAscEl.checked(sort.asc());
|
||||
sortDescEl.checked(sort.desc());
|
||||
} else {
|
||||
this.sortDesc = null;
|
||||
sortAscEl.checked(false);
|
||||
sortDescEl.checked(false);
|
||||
}
|
||||
// this.setFilters(items, filter);
|
||||
this.values = Object.keys(items);
|
||||
this.filterValues = filter ? Array.from(filter.value) : Object.keys(items);
|
||||
buildFilterBody.call(this, items, filter);
|
||||
resetFilterHeader.call(this);
|
||||
}
|
||||
|
||||
setOffset(v) {
|
||||
this.el.offset(v).show();
|
||||
let tindex = 1;
|
||||
bindClickoutside(this.el, () => {
|
||||
if (tindex <= 0) {
|
||||
this.hide();
|
||||
}
|
||||
tindex -= 1;
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
unbindClickoutside(this.el);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
|
||||
function inputMovePrev(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
const { filterItems } = this;
|
||||
if (filterItems.length <= 0) return;
|
||||
if (this.itemIndex >= 0) filterItems[this.itemIndex].toggle();
|
||||
this.itemIndex -= 1;
|
||||
if (this.itemIndex < 0) {
|
||||
this.itemIndex = filterItems.length - 1;
|
||||
}
|
||||
filterItems[this.itemIndex].toggle();
|
||||
}
|
||||
|
||||
function inputMoveNext(evt) {
|
||||
evt.stopPropagation();
|
||||
const { filterItems } = this;
|
||||
if (filterItems.length <= 0) return;
|
||||
if (this.itemIndex >= 0) filterItems[this.itemIndex].toggle();
|
||||
this.itemIndex += 1;
|
||||
if (this.itemIndex > filterItems.length - 1) {
|
||||
this.itemIndex = 0;
|
||||
}
|
||||
filterItems[this.itemIndex].toggle();
|
||||
}
|
||||
|
||||
function inputEnter(evt) {
|
||||
evt.preventDefault();
|
||||
const { filterItems } = this;
|
||||
if (filterItems.length <= 0) return;
|
||||
evt.stopPropagation();
|
||||
if (this.itemIndex < 0) this.itemIndex = 0;
|
||||
filterItems[this.itemIndex].el.click();
|
||||
this.hide();
|
||||
}
|
||||
|
||||
function inputKeydownHandler(evt) {
|
||||
const { keyCode } = evt;
|
||||
if (evt.ctrlKey) {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
switch (keyCode) {
|
||||
case 37: // left
|
||||
evt.stopPropagation();
|
||||
break;
|
||||
case 38: // up
|
||||
inputMovePrev.call(this, evt);
|
||||
break;
|
||||
case 39: // right
|
||||
evt.stopPropagation();
|
||||
break;
|
||||
case 40: // down
|
||||
inputMoveNext.call(this, evt);
|
||||
break;
|
||||
case 13: // enter
|
||||
inputEnter.call(this, evt);
|
||||
break;
|
||||
case 9:
|
||||
inputEnter.call(this, evt);
|
||||
break;
|
||||
default:
|
||||
evt.stopPropagation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export default class Suggest {
|
||||
constructor(items, itemClick, width = '200px') {
|
||||
this.filterItems = [];
|
||||
this.items = items;
|
||||
this.el = h('div', `${cssPrefix}-suggest`).css('width', width).hide();
|
||||
this.itemClick = itemClick;
|
||||
this.itemIndex = -1;
|
||||
}
|
||||
|
||||
setOffset(v) {
|
||||
this.el.cssRemoveKeys('top', 'bottom').offset(v);
|
||||
}
|
||||
|
||||
hide() {
|
||||
const { el } = this;
|
||||
this.filterItems = [];
|
||||
this.itemIndex = -1;
|
||||
el.hide();
|
||||
unbindClickoutside(this.el.parent());
|
||||
}
|
||||
|
||||
setItems(items) {
|
||||
this.items = items;
|
||||
// this.search('');
|
||||
}
|
||||
|
||||
search(word) {
|
||||
let { items } = this;
|
||||
if (!/^\s*$/.test(word)) {
|
||||
items = items.filter(it => (it.key || it).startsWith(word.toUpperCase()));
|
||||
}
|
||||
items = items.map(it => {
|
||||
let { title } = it;
|
||||
if (title) {
|
||||
if (typeof title === 'function') {
|
||||
title = title();
|
||||
}
|
||||
} else {
|
||||
title = it;
|
||||
}
|
||||
const item = h('div', `${cssPrefix}-item`)
|
||||
.child(title)
|
||||
.on('click.stop', () => {
|
||||
this.itemClick(it);
|
||||
this.hide();
|
||||
});
|
||||
if (it.label) {
|
||||
item.child(h('div', 'label').html(it.label));
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.filterItems = items;
|
||||
if (items.length <= 0) {
|
||||
return;
|
||||
}
|
||||
const { el } = this;
|
||||
// items[0].toggle();
|
||||
el.html('')
|
||||
.children(...items)
|
||||
.show();
|
||||
bindClickoutside(el.parent(), () => {
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
|
||||
bindInputEvents(input) {
|
||||
input.on('keydown', evt => inputKeydownHandler.call(this, evt));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
import { stringAt } from '../core/alphabet.js';
|
||||
import { getFontSizePxByPt } from '../core/font.js';
|
||||
import _cell from '../core/cell.js';
|
||||
import { formulam } from '../core/formula.js';
|
||||
import { formatm } from '../core/format.js';
|
||||
|
||||
import { Draw, DrawBox, thinLineWidth, npx } from '../canvas/draw.js';
|
||||
// gobal var
|
||||
const cellPaddingWidth = 5;
|
||||
const tableFixedHeaderCleanStyle = { fillStyle: '#fff' };
|
||||
const tableGridStyle = {
|
||||
fillStyle: '#fff',
|
||||
lineWidth: thinLineWidth,
|
||||
strokeStyle: '#e6e6e6',
|
||||
};
|
||||
function tableFixedHeaderStyle() {
|
||||
return {
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
font: `500 ${npx(12)}px Source Sans Pro`,
|
||||
fillStyle: '#585757',
|
||||
lineWidth: thinLineWidth(),
|
||||
strokeStyle: '#e6e6e6',
|
||||
};
|
||||
}
|
||||
|
||||
function getDrawBox(data, rindex, cindex, yoffset = 0) {
|
||||
const { left, top, width, height } = data.cellRect(rindex, cindex);
|
||||
return new DrawBox(left, top + yoffset, width, height, cellPaddingWidth);
|
||||
}
|
||||
/*
|
||||
function renderCellBorders(bboxes, translateFunc) {
|
||||
const { draw } = this;
|
||||
if (bboxes) {
|
||||
const rset = new Set();
|
||||
// console.log('bboxes:', bboxes);
|
||||
bboxes.forEach(({ ri, ci, box }) => {
|
||||
if (!rset.has(ri)) {
|
||||
rset.add(ri);
|
||||
translateFunc(ri);
|
||||
}
|
||||
draw.strokeBorders(box);
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
export function renderCell(draw, data, rindex, cindex, yoffset = 0, scroll) {
|
||||
const { sortedRowMap, rows, cols } = data;
|
||||
if (rows.isHide(rindex) || cols.isHide(cindex)) return;
|
||||
let nrindex = rindex;
|
||||
if (sortedRowMap.has(rindex)) {
|
||||
nrindex = sortedRowMap.get(rindex);
|
||||
}
|
||||
|
||||
const cell = data.getCell(nrindex, cindex);
|
||||
if (cell === null) return;
|
||||
let frozen = false;
|
||||
if ('editable' in cell && cell.editable === false) {
|
||||
frozen = true;
|
||||
}
|
||||
|
||||
const style = data.getCellStyleOrDefault(nrindex, cindex);
|
||||
const dbox = getDrawBox(data, rindex, cindex, yoffset);
|
||||
dbox.bgcolor = style.bgcolor;
|
||||
if (style.border !== undefined) {
|
||||
dbox.setBorders(style.border);
|
||||
// bboxes.push({ ri: rindex, ci: cindex, box: dbox });
|
||||
draw.strokeBorders(dbox);
|
||||
}
|
||||
draw.rect(dbox, () => {
|
||||
if (['image'].includes(cell.type) && !cell.hidden) {
|
||||
let celldata = data.rows.getCell(rindex, cindex);
|
||||
// 如果单元格类型是单选框,则添加前缀的圆弧画法
|
||||
// 在这里传递一下行坐标与列坐标的宽度,方便异步加载图片时使用
|
||||
const fixedIndexWidth = cols.indexWidth;
|
||||
const fixedIndexHeight = rows.indexHeight;
|
||||
draw.geometry(cell, dbox, { fixedIndexWidth, fixedIndexHeight }, style, scroll, celldata);
|
||||
}
|
||||
// render text
|
||||
let cellText = '';
|
||||
if (!data.settings.evalPaused) {
|
||||
cellText = _cell.render(cell.text || '', formulam, (y, x) => data.getCellTextOrDefault(x, y));
|
||||
} else {
|
||||
cellText = cell.text || '';
|
||||
}
|
||||
if (style.format) {
|
||||
// console.log(data.formatm, '>>', cell.format);
|
||||
cellText = formatm[style.format].render(cellText);
|
||||
}
|
||||
const font = Object.assign({}, style.font);
|
||||
font.size = getFontSizePxByPt(font.size);
|
||||
// console.log('style:', style);
|
||||
draw.text(
|
||||
cellText,
|
||||
dbox,
|
||||
{
|
||||
align: style.align,
|
||||
valign: style.valign,
|
||||
font,
|
||||
color: style.color,
|
||||
strike: style.strike,
|
||||
underline: style.underline,
|
||||
},
|
||||
style.textwrap,
|
||||
);
|
||||
// error
|
||||
const error = data.validations.getError(rindex, cindex);
|
||||
if (error) {
|
||||
// console.log('error:', rindex, cindex, error);
|
||||
draw.error(dbox);
|
||||
}
|
||||
if (frozen) {
|
||||
draw.frozen(dbox);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderAutofilter(viewRange) {
|
||||
const { data, draw } = this;
|
||||
if (viewRange) {
|
||||
const { autoFilter } = data;
|
||||
if (!autoFilter.active()) return;
|
||||
const afRange = autoFilter.hrange();
|
||||
if (viewRange.intersects(afRange)) {
|
||||
afRange.each((ri, ci) => {
|
||||
const dbox = getDrawBox(data, ri, ci);
|
||||
draw.dropdown(dbox);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderContent(viewRange, fw, fh, tx, ty, scroll) {
|
||||
const { draw, data } = this;
|
||||
draw.save();
|
||||
draw.translate(fw, fh).translate(tx, ty);
|
||||
|
||||
const { exceptRowSet } = data;
|
||||
// const exceptRows = Array.from(exceptRowSet);
|
||||
const filteredTranslateFunc = ri => {
|
||||
const ret = exceptRowSet.has(ri);
|
||||
if (ret) {
|
||||
const height = data.rows.getHeight(ri);
|
||||
draw.translate(0, -height);
|
||||
}
|
||||
return !ret;
|
||||
};
|
||||
|
||||
const exceptRowTotalHeight = data.exceptRowTotalHeight(viewRange.sri, viewRange.eri);
|
||||
// 1 render cell
|
||||
draw.save();
|
||||
draw.translate(0, -exceptRowTotalHeight);
|
||||
viewRange.each(
|
||||
(ri, ci) => {
|
||||
renderCell(draw, data, ri, ci, 0, scroll);
|
||||
},
|
||||
ri => filteredTranslateFunc(ri),
|
||||
);
|
||||
draw.restore();
|
||||
|
||||
// 2 render mergeCell
|
||||
const rset = new Set();
|
||||
draw.save();
|
||||
draw.translate(0, -exceptRowTotalHeight);
|
||||
data.eachMergesInView(viewRange, ({ sri, sci, eri }) => {
|
||||
if (!exceptRowSet.has(sri)) {
|
||||
renderCell(draw, data, sri, sci, 0, scroll);
|
||||
} else if (!rset.has(sri)) {
|
||||
rset.add(sri);
|
||||
const height = data.rows.sumHeight(sri, eri + 1);
|
||||
draw.translate(0, -height);
|
||||
}
|
||||
});
|
||||
draw.restore();
|
||||
|
||||
// 3 render autofilter
|
||||
renderAutofilter.call(this, viewRange);
|
||||
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
function renderSelectedHeaderCell(x, y, w, h) {
|
||||
const { draw } = this;
|
||||
draw.save();
|
||||
draw.attr({ fillStyle: 'rgba(76,76,76,.1)' }).fillRect(x, y, w, h);
|
||||
draw.restore();
|
||||
}
|
||||
function renderLeftHeaderCell(x, y, w, h, op) {
|
||||
const { draw } = this;
|
||||
op = op || {};
|
||||
draw.save();
|
||||
draw.attr({ fillStyle: 'rgba(76,76,76,.1)', ...op }).fillRect(x, y, w, h);
|
||||
draw.restore();
|
||||
}
|
||||
// viewRange
|
||||
// type: all | left | top
|
||||
// w: the fixed width of header
|
||||
// h: the fixed height of header
|
||||
// tx: moving distance on x-axis
|
||||
// ty: moving distance on y-axis
|
||||
function renderFixedHeaders(type, viewRange, w, h, tx, ty) {
|
||||
const { draw, data } = this;
|
||||
const sumHeight = viewRange.h; // rows.sumHeight(viewRange.sri, viewRange.eri + 1);
|
||||
const sumWidth = viewRange.w; // cols.sumWidth(viewRange.sci, viewRange.eci + 1);
|
||||
const nty = ty + h;
|
||||
const ntx = tx + w;
|
||||
|
||||
draw.save();
|
||||
// draw rect background
|
||||
draw.attr(tableFixedHeaderCleanStyle);
|
||||
if (type === 'all' || type === 'left') draw.fillRect(0, nty, w, sumHeight);
|
||||
if (type === 'all' || type === 'top') draw.fillRect(ntx, 0, sumWidth, h);
|
||||
|
||||
const { sri, sci, eri, eci } = data.selector.range;
|
||||
// console.log(data.selectIndexes);
|
||||
// draw text
|
||||
// text font, align...
|
||||
draw.attr(tableFixedHeaderStyle());
|
||||
// y-header-text
|
||||
if (type === 'all' || type === 'left') {
|
||||
data.rowEach(viewRange.sri, viewRange.eri, (i, y1, rowHeight) => {
|
||||
const y = nty + y1;
|
||||
const ii = i;
|
||||
draw.line([0, y], [w, y]);
|
||||
if (
|
||||
data.settings.leftFixHeaderRender &&
|
||||
data.settings.leftFixHeaderRender instanceof Function
|
||||
) {
|
||||
let cfg = data.settings.leftFixHeaderRender(i);
|
||||
if (cfg) {
|
||||
renderLeftHeaderCell.call(this, 0, y, w, rowHeight, cfg);
|
||||
} else {
|
||||
if (sri <= ii && ii < eri + 1) {
|
||||
renderSelectedHeaderCell.call(this, 0, y, w, rowHeight);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sri <= ii && ii < eri + 1) {
|
||||
renderSelectedHeaderCell.call(this, 0, y, w, rowHeight);
|
||||
}
|
||||
}
|
||||
draw.fillText(ii + 1, w / 2, y + rowHeight / 2);
|
||||
if (i > 0 && data.rows.isHide(i - 1)) {
|
||||
draw.save();
|
||||
draw.attr({ strokeStyle: '#c6c6c6' });
|
||||
draw.line([5, y + 5], [w - 5, y + 5]);
|
||||
draw.restore();
|
||||
}
|
||||
});
|
||||
draw.line([0, sumHeight + nty], [w, sumHeight + nty]);
|
||||
draw.line([w, nty], [w, sumHeight + nty]);
|
||||
}
|
||||
// x-header-text
|
||||
if (type === 'all' || type === 'top') {
|
||||
data.colEach(viewRange.sci, viewRange.eci, (i, x1, colWidth) => {
|
||||
const x = ntx + x1;
|
||||
const ii = i;
|
||||
draw.line([x, 0], [x, h]);
|
||||
if (sci <= ii && ii < eci + 1) {
|
||||
renderSelectedHeaderCell.call(this, x, 0, colWidth, h);
|
||||
}
|
||||
draw.fillText(stringAt(ii), x + colWidth / 2, h / 2);
|
||||
if (i > 0 && data.cols.isHide(i - 1)) {
|
||||
draw.save();
|
||||
draw.attr({ strokeStyle: '#c6c6c6' });
|
||||
draw.line([x + 5, 5], [x + 5, h - 5]);
|
||||
draw.restore();
|
||||
}
|
||||
});
|
||||
draw.line([sumWidth + ntx, 0], [sumWidth + ntx, h]);
|
||||
draw.line([0, h], [sumWidth + ntx, h]);
|
||||
}
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
function renderFixedLeftTopCell(fw, fh) {
|
||||
const { draw, data } = this;
|
||||
if (data.settings.mode !== 'edit') return;
|
||||
draw.save();
|
||||
// left-top-cell
|
||||
draw.attr({ fillStyle: '#fff' }).fillRect(0, 0, fw, fh);
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
function renderContentGrid({ sri, sci, eri, eci, w, h }, fw, fh, tx, ty) {
|
||||
const { draw, data } = this;
|
||||
const { settings } = data;
|
||||
|
||||
draw.save();
|
||||
draw.attr(tableGridStyle).translate(fw + tx, fh + ty);
|
||||
// const sumWidth = cols.sumWidth(sci, eci + 1);
|
||||
// const sumHeight = rows.sumHeight(sri, eri + 1);
|
||||
// console.log('sumWidth:', sumWidth);
|
||||
// draw.clearRect(0, 0, w, h);
|
||||
if (!settings.showGrid) {
|
||||
draw.restore();
|
||||
return;
|
||||
}
|
||||
// console.log('rowStart:', rowStart, ', rowLen:', rowLen);
|
||||
data.rowEach(sri, eri, (i, y, ch) => {
|
||||
// console.log('y:', y);
|
||||
if (i !== sri) draw.line([0, y], [w, y]);
|
||||
if (i === eri) draw.line([0, y + ch], [w, y + ch]);
|
||||
});
|
||||
data.colEach(sci, eci, (i, x, cw) => {
|
||||
if (i !== sci) draw.line([x, 0], [x, h]);
|
||||
if (i === eci) draw.line([x + cw, 0], [x + cw, h]);
|
||||
});
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
function renderFreezeHighlightLine(fw, fh, ftw, fth) {
|
||||
const { draw, data } = this;
|
||||
const twidth = data.viewWidth() - fw;
|
||||
const theight = data.viewHeight() - fh;
|
||||
draw.save().translate(fw, fh).attr({ strokeStyle: 'rgba(75, 137, 255, .6)' });
|
||||
draw.line([0, fth], [twidth, fth]);
|
||||
draw.line([ftw, 0], [ftw, theight]);
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
/** end */
|
||||
class Table {
|
||||
constructor(el, data) {
|
||||
this.el = el;
|
||||
this.draw = new Draw(el, data.viewWidth(), data.viewHeight());
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
resetData(data) {
|
||||
this.data = data;
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
// resize canvas
|
||||
const { data } = this;
|
||||
const { rows, cols } = data;
|
||||
// fixed width of header
|
||||
const fw = cols.indexWidth;
|
||||
// fixed height of header
|
||||
const fh = rows.height;
|
||||
|
||||
this.draw.resize(data.viewWidth(), data.viewHeight());
|
||||
this.clear();
|
||||
|
||||
const viewRange = data.viewRange();
|
||||
// renderAll.call(this, viewRange, data.scroll);
|
||||
const tx = data.freezeTotalWidth();
|
||||
const ty = data.freezeTotalHeight();
|
||||
const { x, y } = data.scroll;
|
||||
// 1
|
||||
renderContentGrid.call(this, viewRange, fw, fh, tx, ty);
|
||||
renderContent.call(this, viewRange, fw, fh, -x, -y, data);
|
||||
renderFixedHeaders.call(this, 'all', viewRange, fw, fh, tx, ty);
|
||||
renderFixedLeftTopCell.call(this, fw, fh);
|
||||
const [fri, fci] = data.freeze;
|
||||
if (fri > 0 || fci > 0) {
|
||||
// 2
|
||||
if (fri > 0) {
|
||||
const vr = viewRange.clone();
|
||||
vr.sri = 0;
|
||||
vr.eri = fri - 1;
|
||||
vr.h = ty;
|
||||
renderContentGrid.call(this, vr, fw, fh, tx, 0);
|
||||
renderContent.call(this, vr, fw, fh, -x, 0, data);
|
||||
renderFixedHeaders.call(this, 'top', vr, fw, fh, tx, 0);
|
||||
}
|
||||
// 3
|
||||
if (fci > 0) {
|
||||
const vr = viewRange.clone();
|
||||
vr.sci = 0;
|
||||
vr.eci = fci - 1;
|
||||
vr.w = tx;
|
||||
renderContentGrid.call(this, vr, fw, fh, 0, ty);
|
||||
renderFixedHeaders.call(this, 'left', vr, fw, fh, 0, ty);
|
||||
renderContent.call(this, vr, fw, fh, 0, -y, data);
|
||||
}
|
||||
// 4
|
||||
const freezeViewRange = data.freezeViewRange();
|
||||
renderContentGrid.call(this, freezeViewRange, fw, fh, 0, 0);
|
||||
renderFixedHeaders.call(this, 'all', freezeViewRange, fw, fh, 0, 0);
|
||||
renderContent.call(this, freezeViewRange, fw, fh, 0, 0, data);
|
||||
// 5
|
||||
renderFreezeHighlightLine.call(this, fw, fh, tx, ty);
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.draw.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export default Table;
|
||||
@@ -0,0 +1,258 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import { bind } from './event.js';
|
||||
import tooltip from './tooltip.js';
|
||||
import DropdownFont from './dropdown_font.js';
|
||||
import DropdownFontSize from './dropdown_fontsize.js';
|
||||
import DropdownFormat from './dropdown_format.js';
|
||||
import DropdownFormula from './dropdown_formula.js';
|
||||
import DropdownColor from './dropdown_color.js';
|
||||
import DropdownAlign from './dropdown_align.js';
|
||||
import DropdownBorder from './dropdown_border.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
function buildIcon(name) {
|
||||
return new Icon(name);
|
||||
}
|
||||
|
||||
function buildButton(tooltipdata) {
|
||||
return h('div', `${cssPrefix}-toolbar-btn`)
|
||||
.on('mouseenter', evt => {
|
||||
tooltip(tooltipdata, evt.target);
|
||||
})
|
||||
.attr('data-tooltip', tooltipdata);
|
||||
}
|
||||
|
||||
function buildDivider() {
|
||||
return h('div', `${cssPrefix}-toolbar-divider`);
|
||||
}
|
||||
|
||||
function buildButtonWithIcon(
|
||||
tooltipdata,
|
||||
iconName,
|
||||
change = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
) {
|
||||
return buildButton(tooltipdata)
|
||||
.child(buildIcon(iconName))
|
||||
.on('click', () => change());
|
||||
}
|
||||
|
||||
function bindDropdownChange() {
|
||||
this.ddFormat.change = it => this.change('format', it.key);
|
||||
this.ddFont.change = it => this.change('font-name', it.key);
|
||||
this.ddFormula.change = it => this.change('formula', it.key);
|
||||
this.ddFontSize.change = it => this.change('font-size', it.pt);
|
||||
this.ddTextColor.change = it => this.change('color', it);
|
||||
this.ddFillColor.change = it => this.change('bgcolor', it);
|
||||
this.ddAlign.change = it => this.change('align', it);
|
||||
this.ddVAlign.change = it => this.change('valign', it);
|
||||
this.ddBorder.change = it => this.change('border', it);
|
||||
}
|
||||
|
||||
function toggleChange(type) {
|
||||
let elName = type;
|
||||
const types = type.split('-');
|
||||
if (types.length > 1) {
|
||||
types.forEach((it, i) => {
|
||||
if (i === 0) elName = it;
|
||||
else elName += it[0].toUpperCase() + it.substring(1);
|
||||
});
|
||||
}
|
||||
const el = this[`${elName}El`];
|
||||
el.toggle();
|
||||
this.change(type, el.hasClass('active'));
|
||||
}
|
||||
|
||||
class DropdownMore extends Dropdown {
|
||||
constructor() {
|
||||
const icon = new Icon('ellipsis');
|
||||
const moreBtns = h('div', `${cssPrefix}-toolbar-more`);
|
||||
super(icon, 'auto', false, 'bottom-right', moreBtns);
|
||||
this.moreBtns = moreBtns;
|
||||
this.contentEl.css('max-width', '420px');
|
||||
}
|
||||
}
|
||||
|
||||
function initBtns2() {
|
||||
this.btns2 = this.btnChildren.map(it => {
|
||||
const rect = it.box();
|
||||
const { marginLeft, marginRight } = it.computedStyle();
|
||||
return [it, rect.width + parseInt(marginLeft, 10) + parseInt(marginRight, 10)];
|
||||
});
|
||||
}
|
||||
|
||||
function moreResize() {
|
||||
const { el, btns, moreEl, ddMore, btns2 } = this;
|
||||
const { moreBtns, contentEl } = ddMore;
|
||||
el.css('width', `${this.widthFn() - 60}px`);
|
||||
const elBox = el.box();
|
||||
|
||||
let sumWidth = 160;
|
||||
let sumWidth2 = 12;
|
||||
const list1 = [];
|
||||
const list2 = [];
|
||||
btns2.forEach(([it, w], index) => {
|
||||
sumWidth += w;
|
||||
if (index === btns2.length - 1 || sumWidth < elBox.width) {
|
||||
list1.push(it);
|
||||
} else {
|
||||
sumWidth2 += w;
|
||||
list2.push(it);
|
||||
}
|
||||
});
|
||||
btns.html('').children(...list1);
|
||||
moreBtns.html('').children(...list2);
|
||||
contentEl.css('width', `${sumWidth2}px`);
|
||||
if (list2.length > 0) {
|
||||
moreEl.show();
|
||||
} else {
|
||||
moreEl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
export default class Toolbar {
|
||||
constructor(data, widthFn, isHide = false) {
|
||||
this.data = data;
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.widthFn = widthFn;
|
||||
const style = data.defaultStyle();
|
||||
// console.log('data:', data);
|
||||
this.ddFormat = new DropdownFormat();
|
||||
this.ddFont = new DropdownFont();
|
||||
this.ddFormula = new DropdownFormula();
|
||||
this.ddFontSize = new DropdownFontSize();
|
||||
this.ddTextColor = new DropdownColor('text-color', style.color);
|
||||
this.ddFillColor = new DropdownColor('fill-color', style.bgcolor);
|
||||
this.ddAlign = new DropdownAlign(['left', 'center', 'right'], style.align);
|
||||
this.ddVAlign = new DropdownAlign(['top', 'middle', 'bottom'], style.valign);
|
||||
this.ddBorder = new DropdownBorder();
|
||||
this.ddMore = new DropdownMore();
|
||||
this.btnChildren = [
|
||||
(this.undoEl = buildButtonWithIcon(`${t('toolbar.undo')} (Ctrl+Z)`, 'undo', () =>
|
||||
this.change('undo'),
|
||||
)),
|
||||
(this.redoEl = buildButtonWithIcon(`${t('toolbar.undo')} (Ctrl+Y)`, 'redo', () =>
|
||||
this.change('redo'),
|
||||
)),
|
||||
// this.printEl = buildButtonWithIcon('Print (Ctrl+P)', 'print', () => this.change('print')),
|
||||
(this.paintformatEl = buildButtonWithIcon(`${t('toolbar.paintformat')}`, 'paintformat', () =>
|
||||
toggleChange.call(this, 'paintformat'),
|
||||
)),
|
||||
(this.clearformatEl = buildButtonWithIcon(`${t('toolbar.clearformat')}`, 'clearformat', () =>
|
||||
this.change('clearformat'),
|
||||
)),
|
||||
buildDivider(),
|
||||
buildButton(`${t('toolbar.format')}`).child(this.ddFormat.el),
|
||||
buildDivider(),
|
||||
buildButton(`${t('toolbar.font')}`).child(this.ddFont.el),
|
||||
buildButton(`${t('toolbar.fontSize')}`).child(this.ddFontSize.el),
|
||||
buildDivider(),
|
||||
(this.fontBoldEl = buildButtonWithIcon(`${t('toolbar.fontBold')} (Ctrl+B)`, 'bold', () =>
|
||||
toggleChange.call(this, 'font-bold'),
|
||||
)),
|
||||
(this.fontItalicEl = buildButtonWithIcon(
|
||||
`${t('toolbar.fontItalic')} (Ctrl+I)`,
|
||||
'italic',
|
||||
() => toggleChange.call(this, 'font-italic'),
|
||||
)),
|
||||
(this.underlineEl = buildButtonWithIcon(
|
||||
`${t('toolbar.underline')} (Ctrl+U)`,
|
||||
'underline',
|
||||
() => toggleChange.call(this, 'underline'),
|
||||
)),
|
||||
(this.strikeEl = buildButtonWithIcon(`${t('toolbar.strike')}`, 'strike', () =>
|
||||
toggleChange.call(this, 'strike'),
|
||||
)),
|
||||
buildButton(`${t('toolbar.textColor')}`).child(this.ddTextColor.el),
|
||||
buildDivider(),
|
||||
buildButton(`${t('toolbar.fillColor')}`).child(this.ddFillColor.el),
|
||||
buildButton(`${t('toolbar.border')}`).child(this.ddBorder.el),
|
||||
(this.mergeEl = buildButtonWithIcon(`${t('toolbar.merge')}`, 'merge', () =>
|
||||
toggleChange.call(this, 'merge'),
|
||||
)),
|
||||
buildDivider(),
|
||||
buildButton(`${t('toolbar.align')}`).child(this.ddAlign.el),
|
||||
buildButton(`${t('toolbar.valign')}`).child(this.ddVAlign.el),
|
||||
(this.textwrapEl = buildButtonWithIcon(`${t('toolbar.textwrap')}`, 'textwrap', () =>
|
||||
toggleChange.call(this, 'textwrap'),
|
||||
)),
|
||||
buildDivider(),
|
||||
// this.linkEl = buildButtonWithIcon('Insert link', 'link'),
|
||||
// this.chartEl = buildButtonWithIcon('Insert chart', 'chart'),
|
||||
(this.freezeEl = buildButtonWithIcon(`${t('toolbar.freeze')}`, 'freeze', () =>
|
||||
toggleChange.call(this, 'freeze'),
|
||||
)),
|
||||
(this.autofilterEl = buildButtonWithIcon(`${t('toolbar.autofilter')}`, 'autofilter', () =>
|
||||
toggleChange.call(this, 'autofilter'),
|
||||
)),
|
||||
buildButton(`${t('toolbar.formula')}`).child(this.ddFormula.el),
|
||||
// buildDivider(),
|
||||
(this.moreEl = buildButton(`${t('toolbar.more')}`)
|
||||
.child(this.ddMore.el)
|
||||
.hide()),
|
||||
];
|
||||
this.el = h('div', `${cssPrefix}-toolbar`);
|
||||
this.btns = h('div', `${cssPrefix}-toolbar-btns`).children(...this.btnChildren);
|
||||
this.el.child(this.btns);
|
||||
if (isHide) this.el.hide();
|
||||
bindDropdownChange.call(this);
|
||||
this.reset();
|
||||
setTimeout(() => {
|
||||
initBtns2.call(this);
|
||||
moreResize.call(this);
|
||||
}, 0);
|
||||
bind(window, 'resize', () => {
|
||||
moreResize.call(this);
|
||||
});
|
||||
}
|
||||
|
||||
paintformatActive() {
|
||||
return this.paintformatEl.hasClass('active');
|
||||
}
|
||||
|
||||
paintformatToggle() {
|
||||
this.paintformatEl.toggle();
|
||||
}
|
||||
|
||||
trigger(type) {
|
||||
toggleChange.call(this, type);
|
||||
}
|
||||
|
||||
reset() {
|
||||
const { data } = this;
|
||||
const style = data.getSelectedCellStyle();
|
||||
const cell = data.getSelectedCell();
|
||||
// console.log('canUndo:', data.canUndo());
|
||||
this.undoEl.disabled(!data.canUndo());
|
||||
this.redoEl.disabled(!data.canRedo());
|
||||
this.mergeEl.active(data.canUnmerge()).disabled(!data.selector.multiple());
|
||||
this.autofilterEl.active(!data.canAutofilter());
|
||||
// this.mergeEl.disabled();
|
||||
// console.log('selectedCell:', style, cell);
|
||||
const { font } = style;
|
||||
this.ddFont.setTitle(font.name);
|
||||
this.ddFontSize.setTitle(font.size);
|
||||
this.fontBoldEl.active(font.bold);
|
||||
this.fontItalicEl.active(font.italic);
|
||||
this.underlineEl.active(style.underline);
|
||||
this.strikeEl.active(style.strike);
|
||||
this.ddTextColor.setTitle(style.color);
|
||||
this.ddFillColor.setTitle(style.bgcolor);
|
||||
this.ddAlign.setTitle(style.align);
|
||||
this.ddVAlign.setTitle(style.valign);
|
||||
this.textwrapEl.active(style.textwrap);
|
||||
// console.log('freeze is Active:', data.freezeIsActive());
|
||||
this.freezeEl.active(data.freezeIsActive());
|
||||
if (cell) {
|
||||
if (cell.format) {
|
||||
this.ddFormat.setTitle(cell.format);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import DropdownAlign from '../dropdown_align.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Align extends DropdownItem {
|
||||
constructor(value) {
|
||||
super('align', '', value);
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
const { value } = this;
|
||||
return new DropdownAlign(['left', 'center', 'right'], value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Autofilter extends ToggleItem {
|
||||
constructor() {
|
||||
super('autofilter');
|
||||
}
|
||||
|
||||
setState() {
|
||||
console.log('empty function');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Bold extends ToggleItem {
|
||||
constructor() {
|
||||
super('font-bold', 'Ctrl+B');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import DropdownBorder from '../dropdown_border.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Border extends DropdownItem {
|
||||
constructor() {
|
||||
super('border');
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownBorder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import IconItem from './icon_item.js';
|
||||
|
||||
export default class Clearformat extends IconItem {
|
||||
constructor() {
|
||||
super('clearformat');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import Item from './item.js';
|
||||
|
||||
export default class DropdownItem extends Item {
|
||||
dropdown() {
|
||||
console.log('empty function');
|
||||
}
|
||||
|
||||
getValue(v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
element() {
|
||||
const { tag } = this;
|
||||
this.dd = this.dropdown();
|
||||
this.dd.change = it => this.change(tag, this.getValue(it));
|
||||
return super.element().child(this.dd);
|
||||
}
|
||||
|
||||
setState(v) {
|
||||
if (v) {
|
||||
this.value = v;
|
||||
this.dd.setTitle(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import DropdownColor from '../dropdown_color.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class FillColor extends DropdownItem {
|
||||
constructor(color) {
|
||||
super('bgcolor', undefined, color);
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
const { tag, value } = this;
|
||||
return new DropdownColor(tag, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import DropdownFont from '../dropdown_font.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Font extends DropdownItem {
|
||||
constructor() {
|
||||
super('font-name');
|
||||
}
|
||||
|
||||
getValue(it) {
|
||||
return it.key;
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownFont();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import DropdownFontsize from '../dropdown_fontsize.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Format extends DropdownItem {
|
||||
constructor() {
|
||||
super('font-size');
|
||||
}
|
||||
|
||||
getValue(it) {
|
||||
return it.pt;
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownFontsize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import DropdownFormat from '../dropdown_format.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Format extends DropdownItem {
|
||||
constructor() {
|
||||
super('format');
|
||||
}
|
||||
|
||||
getValue(it) {
|
||||
return it.key;
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownFormat();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import DropdownFormula from '../dropdown_formula.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Format extends DropdownItem {
|
||||
constructor() {
|
||||
super('formula');
|
||||
}
|
||||
|
||||
getValue(it) {
|
||||
return it.key;
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownFormula();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Freeze extends ToggleItem {
|
||||
constructor() {
|
||||
super('freeze');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Icon from '../icon.js';
|
||||
import Item from './item.js';
|
||||
|
||||
export default class IconItem extends Item {
|
||||
element() {
|
||||
return super
|
||||
.element()
|
||||
.child(new Icon(this.tag))
|
||||
.on('click', () => this.change(this.tag));
|
||||
}
|
||||
|
||||
setState(disabled) {
|
||||
this.el.disabled(disabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
import { h } from '../element.js';
|
||||
import { cssPrefix } from '../../config.js';
|
||||
import { bind } from '../event.js';
|
||||
import Align from './align.js';
|
||||
import Valign from './valign.js';
|
||||
// import Autofilter from './autofilter.js';
|
||||
import Bold from './bold.js';
|
||||
import Italic from './italic.js';
|
||||
import Strike from './strike.js';
|
||||
import Underline from './underline.js';
|
||||
import Border from './border.js';
|
||||
import Clearformat from './clearformat.js';
|
||||
import Paintformat from './paintformat.js';
|
||||
import TextColor from './text_color.js';
|
||||
import FillColor from './fill_color.js';
|
||||
import FontSize from './font_size.js';
|
||||
import Font from './font.js';
|
||||
import Format from './format.js';
|
||||
// import Formula from './formula.js';
|
||||
// import Freeze from './freeze';
|
||||
import Merge from './merge.js';
|
||||
import Redo from './redo.js';
|
||||
import Undo from './undo.js';
|
||||
// import Print from './print.js';
|
||||
// import Textwrap from './textwrap';
|
||||
import More from './more.js';
|
||||
import Item from './item.js';
|
||||
|
||||
function buildDivider() {
|
||||
return h('div', `${cssPrefix}-toolbar-divider`);
|
||||
}
|
||||
|
||||
function initBtns2() {
|
||||
this.btns2 = [];
|
||||
this.items.forEach(it => {
|
||||
if (Array.isArray(it)) {
|
||||
it.forEach(({ el }) => {
|
||||
const rect = el.box();
|
||||
const { marginLeft, marginRight } = el.computedStyle();
|
||||
this.btns2.push([el, rect.width + parseInt(marginLeft, 10) + parseInt(marginRight, 10)]);
|
||||
});
|
||||
} else {
|
||||
const rect = it.box();
|
||||
const { marginLeft, marginRight } = it.computedStyle();
|
||||
this.btns2.push([it, rect.width + parseInt(marginLeft, 10) + parseInt(marginRight, 10)]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function moreResize() {
|
||||
const { el, btns, moreEl, btns2 } = this;
|
||||
const { moreBtns, contentEl } = moreEl.dd;
|
||||
el.css('width', `${this.widthFn()}px`);
|
||||
const elBox = el.box();
|
||||
|
||||
let sumWidth = 160;
|
||||
let sumWidth2 = 12;
|
||||
const list1 = [];
|
||||
const list2 = [];
|
||||
btns2.forEach(([it, w], index) => {
|
||||
sumWidth += w;
|
||||
if (index === btns2.length - 1 || sumWidth < elBox.width) {
|
||||
list1.push(it);
|
||||
} else {
|
||||
sumWidth2 += w;
|
||||
list2.push(it);
|
||||
}
|
||||
});
|
||||
btns.html('').children(...list1);
|
||||
moreBtns.html('').children(...list2);
|
||||
contentEl.css('width', `${sumWidth2}px`);
|
||||
if (list2.length > 0) {
|
||||
moreEl.show();
|
||||
} else {
|
||||
moreEl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function genBtn(it) {
|
||||
const btn = new Item();
|
||||
btn.el.on('click', () => {
|
||||
if (it.onClick) it.onClick(this.data.getData(), this.data);
|
||||
});
|
||||
btn.tip = it.tip || '';
|
||||
|
||||
let { el } = it;
|
||||
|
||||
if (it.icon) {
|
||||
el = h('img').attr('src', it.icon);
|
||||
}
|
||||
|
||||
if (el) {
|
||||
const icon = h('div', `${cssPrefix}-icon`);
|
||||
icon.child(el);
|
||||
btn.el.child(icon);
|
||||
}
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
export default class Toolbar {
|
||||
constructor(data, widthFn, isHide = false) {
|
||||
this.data = data;
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.widthFn = widthFn;
|
||||
this.isHide = isHide;
|
||||
const style = data.defaultStyle();
|
||||
this.items = [
|
||||
[
|
||||
(this.undoEl = new Undo()),
|
||||
(this.redoEl = new Redo()),
|
||||
// new Print(),
|
||||
(this.paintformatEl = new Paintformat()),
|
||||
(this.clearformatEl = new Clearformat()),
|
||||
],
|
||||
buildDivider(),
|
||||
[(this.formatEl = new Format())],
|
||||
buildDivider(),
|
||||
[(this.fontEl = new Font()), (this.fontSizeEl = new FontSize())],
|
||||
buildDivider(),
|
||||
[
|
||||
(this.boldEl = new Bold()),
|
||||
(this.italicEl = new Italic()),
|
||||
(this.underlineEl = new Underline()),
|
||||
(this.strikeEl = new Strike()),
|
||||
(this.textColorEl = new TextColor(style.color)),
|
||||
],
|
||||
buildDivider(),
|
||||
[
|
||||
(this.fillColorEl = new FillColor(style.bgcolor)),
|
||||
(this.borderEl = new Border()),
|
||||
(this.mergeEl = new Merge()),
|
||||
],
|
||||
buildDivider(),
|
||||
[
|
||||
(this.alignEl = new Align(style.align)),
|
||||
(this.valignEl = new Valign(style.valign)),
|
||||
// this.textwrapEl = new Textwrap()
|
||||
],
|
||||
// buildDivider(),
|
||||
// [
|
||||
// this.freezeEl = new Freeze(),
|
||||
// this.autofilterEl = new Autofilter(),
|
||||
// this.formulaEl = new Formula()
|
||||
// ]
|
||||
];
|
||||
|
||||
const { extendToolbar = {} } = data.settings;
|
||||
|
||||
if (extendToolbar.left && extendToolbar.left.length > 0) {
|
||||
this.items.unshift(buildDivider());
|
||||
const btns = extendToolbar.left.map(genBtn.bind(this));
|
||||
|
||||
this.items.unshift(btns);
|
||||
}
|
||||
if (extendToolbar.right && extendToolbar.right.length > 0) {
|
||||
this.items.push(buildDivider());
|
||||
const btns = extendToolbar.right.map(genBtn.bind(this));
|
||||
this.items.push(btns);
|
||||
}
|
||||
|
||||
this.items.push([(this.moreEl = new More())]);
|
||||
|
||||
this.el = h('div', `${cssPrefix}-toolbar`);
|
||||
this.btns = h('div', `${cssPrefix}-toolbar-btns`);
|
||||
|
||||
this.items.forEach(it => {
|
||||
if (Array.isArray(it)) {
|
||||
it.forEach(i => {
|
||||
this.btns.child(i.el);
|
||||
i.change = (...args) => {
|
||||
this.change(...args);
|
||||
};
|
||||
});
|
||||
} else {
|
||||
this.btns.child(it.el);
|
||||
}
|
||||
});
|
||||
|
||||
this.el.child(this.btns);
|
||||
if (isHide) {
|
||||
this.el.hide();
|
||||
} else {
|
||||
this.reset();
|
||||
setTimeout(() => {
|
||||
initBtns2.call(this);
|
||||
moreResize.call(this);
|
||||
}, 0);
|
||||
bind(window, 'resize', () => {
|
||||
moreResize.call(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
paintformatActive() {
|
||||
return this.paintformatEl.active();
|
||||
}
|
||||
|
||||
paintformatToggle() {
|
||||
this.paintformatEl.toggle();
|
||||
}
|
||||
|
||||
trigger(type) {
|
||||
this[`${type}El`].click();
|
||||
}
|
||||
|
||||
resetData(data) {
|
||||
this.data = data;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (this.isHide) return;
|
||||
const { data } = this;
|
||||
const style = data.getSelectedCellStyle();
|
||||
// console.log('canUndo:', data.canUndo());
|
||||
this.undoEl.setState(!data.canUndo());
|
||||
this.redoEl.setState(!data.canRedo());
|
||||
this.mergeEl.setState(data.canUnmerge(), !data.selector.multiple());
|
||||
// this.autofilterEl.setState(!data.canAutofilter());
|
||||
// this.mergeEl.disabled();
|
||||
// console.log('selectedCell:', style, cell);
|
||||
const { font, format } = style;
|
||||
this.formatEl.setState(format);
|
||||
this.fontEl.setState(font.name);
|
||||
this.fontSizeEl.setState(font.size);
|
||||
this.boldEl.setState(font.bold);
|
||||
this.italicEl.setState(font.italic);
|
||||
this.underlineEl.setState(style.underline);
|
||||
this.strikeEl.setState(style.strike);
|
||||
this.textColorEl.setState(style.color);
|
||||
this.fillColorEl.setState(style.bgcolor);
|
||||
this.alignEl.setState(style.align);
|
||||
this.valignEl.setState(style.valign);
|
||||
// this.textwrapEl.setState(style.textwrap);
|
||||
// console.log('freeze is Active:', data.freezeIsActive());
|
||||
// this.freezeEl.setState(data.freezeIsActive());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Italic extends ToggleItem {
|
||||
constructor() {
|
||||
super('font-italic', 'Ctrl+I');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { cssPrefix } from '../../config.js';
|
||||
import tooltip from '../tooltip.js';
|
||||
import { h } from '../element.js';
|
||||
import { t } from '../../locale/locale.js';
|
||||
|
||||
export default class Item {
|
||||
// tooltip
|
||||
// tag: the subclass type
|
||||
// shortcut: shortcut key
|
||||
constructor(tag, shortcut, value) {
|
||||
this.tip = '';
|
||||
if (tag) this.tip = t(`toolbar.${tag.replace(/-[a-z]/g, c => c[1].toUpperCase())}`);
|
||||
if (shortcut) this.tip += ` (${shortcut})`;
|
||||
this.tag = tag;
|
||||
this.shortcut = shortcut;
|
||||
this.value = value;
|
||||
this.el = this.element();
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
element() {
|
||||
const { tip } = this;
|
||||
return h('div', `${cssPrefix}-toolbar-btn`)
|
||||
.on('mouseenter', evt => {
|
||||
if (this.tip) tooltip(this.tip, evt.target);
|
||||
})
|
||||
.attr('data-tooltip', tip);
|
||||
}
|
||||
|
||||
setState() {
|
||||
console.log('empty function');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Merge extends ToggleItem {
|
||||
constructor() {
|
||||
super('merge');
|
||||
}
|
||||
|
||||
setState(active, disabled) {
|
||||
this.el.active(active).disabled(disabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Dropdown from '../dropdown.js';
|
||||
|
||||
import { cssPrefix } from '../../config.js';
|
||||
import { h } from '../element.js';
|
||||
import Icon from '../icon.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
class DropdownMore extends Dropdown {
|
||||
constructor() {
|
||||
const icon = new Icon('ellipsis');
|
||||
const moreBtns = h('div', `${cssPrefix}-toolbar-more`);
|
||||
super(icon, 'auto', false, 'bottom-right', moreBtns);
|
||||
this.moreBtns = moreBtns;
|
||||
this.contentEl.css('max-width', '420px');
|
||||
}
|
||||
}
|
||||
|
||||
export default class More extends DropdownItem {
|
||||
constructor() {
|
||||
super('more');
|
||||
this.el.hide();
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownMore();
|
||||
}
|
||||
|
||||
show() {
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Paintformat extends ToggleItem {
|
||||
constructor() {
|
||||
super('paintformat');
|
||||
}
|
||||
|
||||
setState() {
|
||||
console.log('empty function');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import IconItem from './icon_item.js';
|
||||
|
||||
export default class Print extends IconItem {
|
||||
constructor() {
|
||||
super('print', 'Ctrl+P');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import IconItem from './icon_item.js';
|
||||
|
||||
export default class Redo extends IconItem {
|
||||
constructor() {
|
||||
super('redo', 'Ctrl+Y');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Strike extends ToggleItem {
|
||||
constructor() {
|
||||
super('strike', 'Ctrl+U');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import DropdownColor from '../dropdown_color.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class TextColor extends DropdownItem {
|
||||
constructor(color) {
|
||||
super('color', undefined, color);
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
const { tag, value } = this;
|
||||
return new DropdownColor(tag, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Textwrap extends ToggleItem {
|
||||
constructor() {
|
||||
super('textwrap');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import Icon from '../icon.js';
|
||||
import Item from './item.js';
|
||||
|
||||
export default class ToggleItem extends Item {
|
||||
element() {
|
||||
const { tag } = this;
|
||||
return super
|
||||
.element()
|
||||
.child(new Icon(tag))
|
||||
.on('click', () => this.click());
|
||||
}
|
||||
|
||||
click() {
|
||||
this.change(this.tag, this.toggle());
|
||||
}
|
||||
|
||||
setState(active) {
|
||||
this.el.active(active);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
return this.el.toggle();
|
||||
}
|
||||
|
||||
active() {
|
||||
return this.el.hasClass('active');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Underline extends ToggleItem {
|
||||
constructor() {
|
||||
super('underline', 'Ctrl+U');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import IconItem from './icon_item.js';
|
||||
|
||||
export default class Undo extends IconItem {
|
||||
constructor() {
|
||||
super('undo', 'Ctrl+Z');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import DropdownAlign from '../dropdown_align.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Valign extends DropdownItem {
|
||||
constructor(value) {
|
||||
super('valign', '', value);
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
const { value } = this;
|
||||
return new DropdownAlign(['top', 'middle', 'bottom'], value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import { bind } from './event.js';
|
||||
|
||||
export default function tooltip(html, target) {
|
||||
if (target.classList.contains('active')) {
|
||||
return;
|
||||
}
|
||||
const { left, top, width, height } = target.getBoundingClientRect();
|
||||
const el = h('div', `${cssPrefix}-tooltip`).html(html).show();
|
||||
document.body.appendChild(el.el);
|
||||
const elBox = el.box();
|
||||
// console.log('elBox:', elBox);
|
||||
el.css('left', `${left + width / 2 - elBox.width / 2}px`).css('top', `${top + height + 2}px`);
|
||||
|
||||
bind(target, 'mouseleave', () => {
|
||||
if (document.body.contains(el.el)) {
|
||||
document.body.removeChild(el.el);
|
||||
}
|
||||
});
|
||||
|
||||
bind(target, 'click', () => {
|
||||
if (document.body.contains(el.el)) {
|
||||
document.body.removeChild(el.el);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export const cssPrefix = 'x-spreadsheet';
|
||||
export const dpr = window.devicePixelRatio || 1;
|
||||
export default {
|
||||
cssPrefix,
|
||||
dpr,
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// font.js
|
||||
/**
|
||||
* @typedef {number} fontsizePX px for fontSize
|
||||
*/
|
||||
/**
|
||||
* @typedef {number} fontsizePT pt for fontSize
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} BaseFont
|
||||
* @property {string} key inner key
|
||||
* @property {string} title title for display
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} FontSize
|
||||
* @property {fontsizePT} pt
|
||||
* @property {fontsizePX} px
|
||||
*/
|
||||
|
||||
// alphabet.js
|
||||
/**
|
||||
* @typedef {string} tagA1 A1 tag for XY-tag (0, 0)
|
||||
* @example "A1"
|
||||
*/
|
||||
/**
|
||||
* @typedef {[number, number]} tagXY
|
||||
* @example [0, 0]
|
||||
*/
|
||||
117
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/alphabet.js
Normal file
117
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/alphabet.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import './_.prototypes.js';
|
||||
|
||||
const alphabets = [
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
];
|
||||
|
||||
/** index number 2 letters
|
||||
* @example stringAt(26) ==> 'AA'
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {number} index
|
||||
* @returns {string}
|
||||
*/
|
||||
export function stringAt(index) {
|
||||
let str = '';
|
||||
let cindex = index;
|
||||
while (cindex >= alphabets.length) {
|
||||
cindex /= alphabets.length;
|
||||
cindex -= 1;
|
||||
str += alphabets[parseInt(cindex, 10) % alphabets.length];
|
||||
}
|
||||
const last = index % alphabets.length;
|
||||
str += alphabets[last];
|
||||
return str;
|
||||
}
|
||||
|
||||
/** translate letter in A1-tag to number
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {string} str "AA" in A1-tag "AA1"
|
||||
* @returns {number}
|
||||
*/
|
||||
export function indexAt(str) {
|
||||
let ret = 0;
|
||||
for (let i = 0; i !== str.length; ++i) ret = 26 * ret + str.charCodeAt(i) - 64;
|
||||
return ret - 1;
|
||||
}
|
||||
|
||||
// B10 => x,y
|
||||
/** translate A1-tag to XY-tag
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {tagA1} src
|
||||
* @returns {tagXY}
|
||||
*/
|
||||
export function expr2xy(src) {
|
||||
let x = '';
|
||||
let y = '';
|
||||
for (let i = 0; i < src.length; i += 1) {
|
||||
if (src.charAt(i) >= '0' && src.charAt(i) <= '9') {
|
||||
y += src.charAt(i);
|
||||
} else {
|
||||
x += src.charAt(i);
|
||||
}
|
||||
}
|
||||
return [indexAt(x), parseInt(y, 10) - 1];
|
||||
}
|
||||
|
||||
/** translate XY-tag to A1-tag
|
||||
* @example x,y => B10
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @returns {tagA1}
|
||||
*/
|
||||
export function xy2expr(x, y) {
|
||||
return `${stringAt(x)}${y + 1}`;
|
||||
}
|
||||
|
||||
/** translate A1-tag src by (xn, yn)
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {tagA1} src
|
||||
* @param {number} xn
|
||||
* @param {number} yn
|
||||
* @returns {tagA1}
|
||||
*/
|
||||
export function expr2expr(src, xn, yn, condition = () => true) {
|
||||
if (xn === 0 && yn === 0) return src;
|
||||
const [x, y] = expr2xy(src);
|
||||
if (!condition(x, y)) return src;
|
||||
return xy2expr(x + xn, y + yn);
|
||||
}
|
||||
|
||||
export default {
|
||||
stringAt,
|
||||
indexAt,
|
||||
expr2xy,
|
||||
xy2expr,
|
||||
expr2expr,
|
||||
};
|
||||
@@ -0,0 +1,183 @@
|
||||
import { CellRange } from './cell_range.js';
|
||||
// operator: all|eq|neq|gt|gte|lt|lte|in|be
|
||||
// value:
|
||||
// in => []
|
||||
// be => [min, max]
|
||||
class Filter {
|
||||
constructor(ci, operator, value) {
|
||||
this.ci = ci;
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
set(operator, value) {
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
includes(v) {
|
||||
const { operator, value } = this;
|
||||
if (operator === 'all') {
|
||||
return true;
|
||||
}
|
||||
if (operator === 'in') {
|
||||
return value.includes(v);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vlength() {
|
||||
const { operator, value } = this;
|
||||
if (operator === 'in') {
|
||||
return value.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
getData() {
|
||||
const { ci, operator, value } = this;
|
||||
return { ci, operator, value };
|
||||
}
|
||||
}
|
||||
|
||||
class Sort {
|
||||
constructor(ci, order) {
|
||||
this.ci = ci;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
asc() {
|
||||
return this.order === 'asc';
|
||||
}
|
||||
|
||||
desc() {
|
||||
return this.order === 'desc';
|
||||
}
|
||||
}
|
||||
|
||||
export default class AutoFilter {
|
||||
constructor() {
|
||||
this.ref = null;
|
||||
this.filters = [];
|
||||
this.sort = null;
|
||||
}
|
||||
|
||||
setData({ ref, filters, sort }) {
|
||||
if (ref != null) {
|
||||
this.ref = ref;
|
||||
this.filters = filters.map(it => new Filter(it.ci, it.operator, it.value));
|
||||
if (sort) {
|
||||
this.sort = new Sort(sort.ci, sort.order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getData() {
|
||||
if (this.active()) {
|
||||
const { ref, filters, sort } = this;
|
||||
return { ref, filters: filters.map(it => it.getData()), sort };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
addFilter(ci, operator, value) {
|
||||
const filter = this.getFilter(ci);
|
||||
if (filter == null) {
|
||||
this.filters.push(new Filter(ci, operator, value));
|
||||
} else {
|
||||
filter.set(operator, value);
|
||||
}
|
||||
}
|
||||
|
||||
setSort(ci, order) {
|
||||
this.sort = order ? new Sort(ci, order) : null;
|
||||
}
|
||||
|
||||
includes(ri, ci) {
|
||||
if (this.active()) {
|
||||
return this.hrange().includes(ri, ci);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getSort(ci) {
|
||||
const { sort } = this;
|
||||
if (sort && sort.ci === ci) {
|
||||
return sort;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getFilter(ci) {
|
||||
const { filters } = this;
|
||||
for (let i = 0; i < filters.length; i += 1) {
|
||||
if (filters[i].ci === ci) {
|
||||
return filters[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
filteredRows(getCell) {
|
||||
// const ary = [];
|
||||
// let lastri = 0;
|
||||
const rset = new Set();
|
||||
const fset = new Set();
|
||||
if (this.active()) {
|
||||
const { sri, eri } = this.range();
|
||||
const { filters } = this;
|
||||
for (let ri = sri + 1; ri <= eri; ri += 1) {
|
||||
for (let i = 0; i < filters.length; i += 1) {
|
||||
const filter = filters[i];
|
||||
const cell = getCell(ri, filter.ci);
|
||||
const ctext = cell ? cell.text : '';
|
||||
if (!filter.includes(ctext)) {
|
||||
rset.add(ri);
|
||||
break;
|
||||
} else {
|
||||
fset.add(ri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return { rset, fset };
|
||||
}
|
||||
|
||||
items(ci, getCell) {
|
||||
const m = {};
|
||||
if (this.active()) {
|
||||
const { sri, eri } = this.range();
|
||||
for (let ri = sri + 1; ri <= eri; ri += 1) {
|
||||
const cell = getCell(ri, ci);
|
||||
if (cell !== null && !/^\s*$/.test(cell.text)) {
|
||||
const key = cell.text;
|
||||
const cnt = (m[key] || 0) + 1;
|
||||
m[key] = cnt;
|
||||
} else {
|
||||
m[''] = (m[''] || 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
range() {
|
||||
return CellRange.valueOf(this.ref);
|
||||
}
|
||||
|
||||
hrange() {
|
||||
const r = this.range();
|
||||
r.eri = r.sri;
|
||||
return r;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.ref = null;
|
||||
this.filters = [];
|
||||
this.sort = null;
|
||||
}
|
||||
|
||||
active() {
|
||||
return this.ref !== null;
|
||||
}
|
||||
}
|
||||
224
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/cell.js
Normal file
224
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/cell.js
Normal file
@@ -0,0 +1,224 @@
|
||||
import { expr2xy, xy2expr } from './alphabet.js';
|
||||
import { numberCalc } from './helper.js';
|
||||
|
||||
// Converting infix expression to a suffix expression
|
||||
// src: AVERAGE(SUM(A1,A2), B1) + 50 + B20
|
||||
// return: [A1, A2], SUM[, B1],AVERAGE,50,+,B20,+
|
||||
const infixExprToSuffixExpr = src => {
|
||||
const operatorStack = [];
|
||||
const stack = [];
|
||||
let subStrs = []; // SUM, A1, B2, 50 ...
|
||||
let fnArgType = 0; // 1 => , 2 => :
|
||||
let fnArgOperator = '';
|
||||
let fnArgsLen = 1; // A1,A2,A3...
|
||||
let oldc = '';
|
||||
for (let i = 0; i < src.length; i += 1) {
|
||||
const c = src.charAt(i);
|
||||
if (c !== ' ') {
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
subStrs.push(c.toUpperCase());
|
||||
} else if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c === '.') {
|
||||
subStrs.push(c);
|
||||
} else if (c === '"') {
|
||||
i += 1;
|
||||
while (src.charAt(i) !== '"') {
|
||||
subStrs.push(src.charAt(i));
|
||||
i += 1;
|
||||
}
|
||||
stack.push(`"${subStrs.join('')}`);
|
||||
subStrs = [];
|
||||
} else if (c === '-' && /[+\-*/,(]/.test(oldc)) {
|
||||
subStrs.push(c);
|
||||
} else {
|
||||
// console.log('subStrs:', subStrs.join(''), stack);
|
||||
if (c !== '(' && subStrs.length > 0) {
|
||||
stack.push(subStrs.join(''));
|
||||
}
|
||||
if (c === ')') {
|
||||
let c1 = operatorStack.pop();
|
||||
if (fnArgType === 2) {
|
||||
// fn argument range => A1:B5
|
||||
try {
|
||||
const [ex, ey] = expr2xy(stack.pop());
|
||||
const [sx, sy] = expr2xy(stack.pop());
|
||||
// console.log('::', sx, sy, ex, ey);
|
||||
let rangelen = 0;
|
||||
for (let x = sx; x <= ex; x += 1) {
|
||||
for (let y = sy; y <= ey; y += 1) {
|
||||
stack.push(xy2expr(x, y));
|
||||
rangelen += 1;
|
||||
}
|
||||
}
|
||||
stack.push([c1, rangelen]);
|
||||
} catch (e) {
|
||||
// console.log(e);
|
||||
}
|
||||
} else if (fnArgType === 1 || fnArgType === 3) {
|
||||
if (fnArgType === 3) stack.push(fnArgOperator);
|
||||
// fn argument => A1,A2,B5
|
||||
stack.push([c1, fnArgsLen]);
|
||||
fnArgsLen = 1;
|
||||
} else {
|
||||
// console.log('c1:', c1, fnArgType, stack, operatorStack);
|
||||
while (c1 !== '(') {
|
||||
stack.push(c1);
|
||||
if (operatorStack.length <= 0) break;
|
||||
c1 = operatorStack.pop();
|
||||
}
|
||||
}
|
||||
fnArgType = 0;
|
||||
} else if (c === '=' || c === '>' || c === '<') {
|
||||
const nc = src.charAt(i + 1);
|
||||
fnArgOperator = c;
|
||||
if (nc === '=' || nc === '-') {
|
||||
fnArgOperator += nc;
|
||||
i += 1;
|
||||
}
|
||||
fnArgType = 3;
|
||||
} else if (c === ':') {
|
||||
fnArgType = 2;
|
||||
} else if (c === ',') {
|
||||
if (fnArgType === 3) {
|
||||
stack.push(fnArgOperator);
|
||||
}
|
||||
fnArgType = 1;
|
||||
fnArgsLen += 1;
|
||||
} else if (c === '(' && subStrs.length > 0) {
|
||||
// function
|
||||
operatorStack.push(subStrs.join(''));
|
||||
} else {
|
||||
// priority: */ > +-
|
||||
// console.log('xxxx:', operatorStack, c, stack);
|
||||
if (operatorStack.length > 0 && (c === '+' || c === '-')) {
|
||||
let top = operatorStack[operatorStack.length - 1];
|
||||
if (top !== '(') stack.push(operatorStack.pop());
|
||||
if (top === '*' || top === '/') {
|
||||
while (operatorStack.length > 0) {
|
||||
top = operatorStack[operatorStack.length - 1];
|
||||
if (top !== '(') stack.push(operatorStack.pop());
|
||||
else break;
|
||||
}
|
||||
}
|
||||
} else if (operatorStack.length > 0) {
|
||||
const top = operatorStack[operatorStack.length - 1];
|
||||
if (top === '*' || top === '/') stack.push(operatorStack.pop());
|
||||
}
|
||||
operatorStack.push(c);
|
||||
}
|
||||
subStrs = [];
|
||||
}
|
||||
oldc = c;
|
||||
}
|
||||
}
|
||||
if (subStrs.length > 0) {
|
||||
stack.push(subStrs.join(''));
|
||||
}
|
||||
while (operatorStack.length > 0) {
|
||||
stack.push(operatorStack.pop());
|
||||
}
|
||||
return stack;
|
||||
};
|
||||
|
||||
const evalSubExpr = (subExpr, cellRender) => {
|
||||
const [fl] = subExpr;
|
||||
let expr = subExpr;
|
||||
if (fl === '"') {
|
||||
return subExpr.substring(1);
|
||||
}
|
||||
let ret = 1;
|
||||
if (fl === '-') {
|
||||
expr = subExpr.substring(1);
|
||||
ret = -1;
|
||||
}
|
||||
if (expr[0] >= '0' && expr[0] <= '9') {
|
||||
return ret * Number(expr);
|
||||
}
|
||||
const [x, y] = expr2xy(expr);
|
||||
return ret * cellRender(x, y);
|
||||
};
|
||||
|
||||
// evaluate the suffix expression
|
||||
// srcStack: <= infixExprToSufixExpr
|
||||
// formulaMap: {'SUM': {}, ...}
|
||||
// cellRender: (x, y) => {}
|
||||
const evalSuffixExpr = (srcStack, formulaMap, cellRender, cellList) => {
|
||||
const stack = [];
|
||||
// console.log(':::::formulaMap:', formulaMap);
|
||||
for (let i = 0; i < srcStack.length; i += 1) {
|
||||
// console.log(':::>>>', srcStack[i]);
|
||||
const expr = srcStack[i];
|
||||
const fc = expr[0];
|
||||
if (expr === '+') {
|
||||
const top = stack.pop();
|
||||
stack.push(numberCalc('+', stack.pop(), top));
|
||||
} else if (expr === '-') {
|
||||
if (stack.length === 1) {
|
||||
const top = stack.pop();
|
||||
stack.push(numberCalc('*', top, -1));
|
||||
} else {
|
||||
const top = stack.pop();
|
||||
stack.push(numberCalc('-', stack.pop(), top));
|
||||
}
|
||||
} else if (expr === '*') {
|
||||
stack.push(numberCalc('*', stack.pop(), stack.pop()));
|
||||
} else if (expr === '/') {
|
||||
const top = stack.pop();
|
||||
stack.push(numberCalc('/', stack.pop(), top));
|
||||
} else if (fc === '=' || fc === '>' || fc === '<') {
|
||||
let top = stack.pop();
|
||||
if (!Number.isNaN(top)) top = Number(top);
|
||||
let left = stack.pop();
|
||||
if (!Number.isNaN(left)) left = Number(left);
|
||||
let ret = false;
|
||||
if (fc === '=') {
|
||||
ret = left === top;
|
||||
} else if (expr === '>') {
|
||||
ret = left > top;
|
||||
} else if (expr === '>=') {
|
||||
ret = left >= top;
|
||||
} else if (expr === '<') {
|
||||
ret = left < top;
|
||||
} else if (expr === '<=') {
|
||||
ret = left <= top;
|
||||
}
|
||||
stack.push(ret);
|
||||
} else if (Array.isArray(expr)) {
|
||||
const [formula, len] = expr;
|
||||
const params = [];
|
||||
for (let j = 0; j < len; j += 1) {
|
||||
params.push(stack.pop());
|
||||
}
|
||||
stack.push(formulaMap[formula].render(params.reverse()));
|
||||
} else {
|
||||
if (cellList.includes(expr)) {
|
||||
return 0;
|
||||
}
|
||||
if ((fc >= 'a' && fc <= 'z') || (fc >= 'A' && fc <= 'Z')) {
|
||||
cellList.push(expr);
|
||||
}
|
||||
stack.push(evalSubExpr(expr, cellRender));
|
||||
cellList.pop();
|
||||
}
|
||||
// console.log('stack:', stack);
|
||||
}
|
||||
return stack[0];
|
||||
};
|
||||
|
||||
const cellRender = (src, formulaMap, getCellText, cellList = []) => {
|
||||
if (src[0] === '=') {
|
||||
const stack = infixExprToSuffixExpr(src.substring(1));
|
||||
if (stack.length <= 0) return src;
|
||||
return evalSuffixExpr(
|
||||
stack,
|
||||
formulaMap,
|
||||
(x, y) => cellRender(getCellText(x, y), formulaMap, getCellText, cellList),
|
||||
cellList,
|
||||
);
|
||||
}
|
||||
return src;
|
||||
};
|
||||
|
||||
export default {
|
||||
render: cellRender,
|
||||
};
|
||||
export { infixExprToSuffixExpr };
|
||||
@@ -0,0 +1,214 @@
|
||||
import { xy2expr, expr2xy } from './alphabet.js';
|
||||
|
||||
class CellRange {
|
||||
constructor(sri, sci, eri, eci, w = 0, h = 0) {
|
||||
this.sri = sri;
|
||||
this.sci = sci;
|
||||
this.eri = eri;
|
||||
this.eci = eci;
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
}
|
||||
|
||||
set(sri, sci, eri, eci) {
|
||||
this.sri = sri;
|
||||
this.sci = sci;
|
||||
this.eri = eri;
|
||||
this.eci = eci;
|
||||
}
|
||||
|
||||
multiple() {
|
||||
return this.eri - this.sri > 0 || this.eci - this.sci > 0;
|
||||
}
|
||||
|
||||
// cell-index: ri, ci
|
||||
// cell-ref: A10
|
||||
includes(...args) {
|
||||
let [ri, ci] = [0, 0];
|
||||
if (args.length === 1) {
|
||||
[ci, ri] = expr2xy(args[0]);
|
||||
} else if (args.length === 2) {
|
||||
[ri, ci] = args;
|
||||
}
|
||||
const { sri, sci, eri, eci } = this;
|
||||
return sri <= ri && ri <= eri && sci <= ci && ci <= eci;
|
||||
}
|
||||
|
||||
each(cb, rowFilter = () => true) {
|
||||
const { sri, sci, eri, eci } = this;
|
||||
for (let i = sri; i <= eri; i += 1) {
|
||||
if (rowFilter(i)) {
|
||||
for (let j = sci; j <= eci; j += 1) {
|
||||
cb(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contains(other) {
|
||||
return (
|
||||
this.sri <= other.sri &&
|
||||
this.sci <= other.sci &&
|
||||
this.eri >= other.eri &&
|
||||
this.eci >= other.eci
|
||||
);
|
||||
}
|
||||
|
||||
// within
|
||||
within(other) {
|
||||
return (
|
||||
this.sri >= other.sri &&
|
||||
this.sci >= other.sci &&
|
||||
this.eri <= other.eri &&
|
||||
this.eci <= other.eci
|
||||
);
|
||||
}
|
||||
|
||||
// disjoint
|
||||
disjoint(other) {
|
||||
return (
|
||||
this.sri > other.eri || this.sci > other.eci || other.sri > this.eri || other.sci > this.eci
|
||||
);
|
||||
}
|
||||
|
||||
// intersects
|
||||
intersects(other) {
|
||||
return (
|
||||
this.sri <= other.eri &&
|
||||
this.sci <= other.eci &&
|
||||
other.sri <= this.eri &&
|
||||
other.sci <= this.eci
|
||||
);
|
||||
}
|
||||
|
||||
// union
|
||||
union(other) {
|
||||
const { sri, sci, eri, eci } = this;
|
||||
return new CellRange(
|
||||
other.sri < sri ? other.sri : sri,
|
||||
other.sci < sci ? other.sci : sci,
|
||||
other.eri > eri ? other.eri : eri,
|
||||
other.eci > eci ? other.eci : eci,
|
||||
);
|
||||
}
|
||||
|
||||
// intersection
|
||||
// intersection(other) {}
|
||||
|
||||
// Returns Array<CellRange> that represents that part of this that does not intersect with other
|
||||
// difference
|
||||
difference(other) {
|
||||
const ret = [];
|
||||
const addRet = (sri, sci, eri, eci) => {
|
||||
ret.push(new CellRange(sri, sci, eri, eci));
|
||||
};
|
||||
const { sri, sci, eri, eci } = this;
|
||||
const dsr = other.sri - sri;
|
||||
const dsc = other.sci - sci;
|
||||
const der = eri - other.eri;
|
||||
const dec = eci - other.eci;
|
||||
if (dsr > 0) {
|
||||
addRet(sri, sci, other.sri - 1, eci);
|
||||
if (der > 0) {
|
||||
addRet(other.eri + 1, sci, eri, eci);
|
||||
if (dsc > 0) {
|
||||
addRet(other.sri, sci, other.eri, other.sci - 1);
|
||||
}
|
||||
if (dec > 0) {
|
||||
addRet(other.sri, other.eci + 1, other.eri, eci);
|
||||
}
|
||||
} else {
|
||||
if (dsc > 0) {
|
||||
addRet(other.sri, sci, eri, other.sci - 1);
|
||||
}
|
||||
if (dec > 0) {
|
||||
addRet(other.sri, other.eci + 1, eri, eci);
|
||||
}
|
||||
}
|
||||
} else if (der > 0) {
|
||||
addRet(other.eri + 1, sci, eri, eci);
|
||||
if (dsc > 0) {
|
||||
addRet(sri, sci, other.eri, other.sci - 1);
|
||||
}
|
||||
if (dec > 0) {
|
||||
addRet(sri, other.eci + 1, other.eri, eci);
|
||||
}
|
||||
}
|
||||
if (dsc > 0) {
|
||||
addRet(sri, sci, eri, other.sci - 1);
|
||||
if (dec > 0) {
|
||||
addRet(sri, other.eri + 1, eri, eci);
|
||||
if (dsr > 0) {
|
||||
addRet(sri, other.sci, other.sri - 1, other.eci);
|
||||
}
|
||||
if (der > 0) {
|
||||
addRet(other.sri + 1, other.sci, eri, other.eci);
|
||||
}
|
||||
} else {
|
||||
if (dsr > 0) {
|
||||
addRet(sri, other.sci, other.sri - 1, eci);
|
||||
}
|
||||
if (der > 0) {
|
||||
addRet(other.sri + 1, other.sci, eri, eci);
|
||||
}
|
||||
}
|
||||
} else if (dec > 0) {
|
||||
addRet(eri, other.eci + 1, eri, eci);
|
||||
if (dsr > 0) {
|
||||
addRet(sri, sci, other.sri - 1, other.eci);
|
||||
}
|
||||
if (der > 0) {
|
||||
addRet(other.eri + 1, sci, eri, other.eci);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size() {
|
||||
return [this.eri - this.sri + 1, this.eci - this.sci + 1];
|
||||
}
|
||||
|
||||
toString() {
|
||||
const { sri, sci, eri, eci } = this;
|
||||
let ref = xy2expr(sci, sri);
|
||||
if (this.multiple()) {
|
||||
ref = `${ref}:${xy2expr(eci, eri)}`;
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
clone() {
|
||||
const { sri, sci, eri, eci, w, h } = this;
|
||||
return new CellRange(sri, sci, eri, eci, w, h);
|
||||
}
|
||||
|
||||
/*
|
||||
toJSON() {
|
||||
return this.toString();
|
||||
}
|
||||
*/
|
||||
|
||||
equals(other) {
|
||||
return (
|
||||
this.eri === other.eri &&
|
||||
this.eci === other.eci &&
|
||||
this.sri === other.sri &&
|
||||
this.sci === other.sci
|
||||
);
|
||||
}
|
||||
|
||||
static valueOf(ref) {
|
||||
// B1:B8, B1 => 1 x 1 cell range
|
||||
const refs = ref.split(':');
|
||||
const [sci, sri] = expr2xy(refs[0]);
|
||||
let [eri, eci] = [sri, sci];
|
||||
if (refs.length > 1) {
|
||||
[eci, eri] = expr2xy(refs[1]);
|
||||
}
|
||||
return new CellRange(sri, sci, eri, eci);
|
||||
}
|
||||
}
|
||||
|
||||
export default CellRange;
|
||||
|
||||
export { CellRange };
|
||||
@@ -0,0 +1,35 @@
|
||||
export default class Clipboard {
|
||||
constructor() {
|
||||
this.range = null; // CellRange
|
||||
this.state = 'clear';
|
||||
}
|
||||
|
||||
copy(cellRange) {
|
||||
this.range = cellRange;
|
||||
this.state = 'copy';
|
||||
return this;
|
||||
}
|
||||
|
||||
cut(cellRange) {
|
||||
this.range = cellRange;
|
||||
this.state = 'cut';
|
||||
return this;
|
||||
}
|
||||
|
||||
isCopy() {
|
||||
return this.state === 'copy';
|
||||
}
|
||||
|
||||
isCut() {
|
||||
return this.state === 'cut';
|
||||
}
|
||||
|
||||
isClear() {
|
||||
return this.state === 'clear';
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.range = null;
|
||||
this.state = 'clear';
|
||||
}
|
||||
}
|
||||
80
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/col.js
Normal file
80
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/col.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import helper from './helper.js';
|
||||
|
||||
class Cols {
|
||||
constructor({ len, width, indexWidth, minWidth }) {
|
||||
this._ = {};
|
||||
this.len = len;
|
||||
this.width = width;
|
||||
this.indexWidth = indexWidth;
|
||||
this.minWidth = minWidth;
|
||||
}
|
||||
|
||||
setData(d) {
|
||||
if (d.len) {
|
||||
this.len = d.len;
|
||||
delete d.len;
|
||||
}
|
||||
this._ = d;
|
||||
}
|
||||
|
||||
getData() {
|
||||
const { len } = this;
|
||||
return Object.assign({ len }, this._);
|
||||
}
|
||||
|
||||
getWidth(i) {
|
||||
if (this.isHide(i)) return 0;
|
||||
const col = this._[i];
|
||||
if (col && col.width) {
|
||||
return col.width;
|
||||
}
|
||||
return this.width;
|
||||
}
|
||||
|
||||
getOrNew(ci) {
|
||||
this._[ci] = this._[ci] || {};
|
||||
return this._[ci];
|
||||
}
|
||||
|
||||
setWidth(ci, width) {
|
||||
const col = this.getOrNew(ci);
|
||||
col.width = width;
|
||||
}
|
||||
|
||||
unhide(idx) {
|
||||
let index = idx;
|
||||
while (index > 0) {
|
||||
index -= 1;
|
||||
if (this.isHide(index)) {
|
||||
this.setHide(index, false);
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
isHide(ci) {
|
||||
const col = this._[ci];
|
||||
return col && col.hide;
|
||||
}
|
||||
|
||||
setHide(ci, v) {
|
||||
const col = this.getOrNew(ci);
|
||||
if (v === true) col.hide = true;
|
||||
else delete col.hide;
|
||||
}
|
||||
|
||||
setStyle(ci, style) {
|
||||
const col = this.getOrNew(ci);
|
||||
col.style = style;
|
||||
}
|
||||
|
||||
sumWidth(min, max) {
|
||||
return helper.rangeSum(min, max, i => this.getWidth(i));
|
||||
}
|
||||
|
||||
totalWidth() {
|
||||
return this.sumWidth(0, this.len);
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { Cols };
|
||||
1252
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/data_proxy.js
Normal file
1252
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/data_proxy.js
Normal file
File diff suppressed because it is too large
Load Diff
71
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/font.js
Normal file
71
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/font.js
Normal file
@@ -0,0 +1,71 @@
|
||||
// docs
|
||||
import './_.prototypes.js';
|
||||
|
||||
/** default font list
|
||||
* @type {BaseFont[]}
|
||||
*/
|
||||
const baseFonts = [
|
||||
{ key: 'Arial', title: 'Arial' },
|
||||
{ key: 'Helvetica', title: 'Helvetica' },
|
||||
{ key: 'Source Sans Pro', title: 'Source Sans Pro' },
|
||||
{ key: 'Comic Sans MS', title: 'Comic Sans MS' },
|
||||
{ key: 'Courier New', title: 'Courier New' },
|
||||
{ key: 'Verdana', title: 'Verdana' },
|
||||
{ key: 'Lato', title: 'Lato' },
|
||||
];
|
||||
|
||||
/** default fontSize list
|
||||
* @type {FontSize[]}
|
||||
*/
|
||||
const fontSizes = [
|
||||
{ pt: 7.5, px: 10 },
|
||||
{ pt: 8, px: 11 },
|
||||
{ pt: 9, px: 12 },
|
||||
{ pt: 10, px: 13 },
|
||||
{ pt: 10.5, px: 14 },
|
||||
{ pt: 11, px: 15 },
|
||||
{ pt: 12, px: 16 },
|
||||
{ pt: 14, px: 18.7 },
|
||||
{ pt: 15, px: 20 },
|
||||
{ pt: 16, px: 21.3 },
|
||||
{ pt: 18, px: 24 },
|
||||
{ pt: 22, px: 29.3 },
|
||||
{ pt: 24, px: 32 },
|
||||
{ pt: 26, px: 34.7 },
|
||||
{ pt: 36, px: 48 },
|
||||
{ pt: 42, px: 56 },
|
||||
// { pt: 54, px: 71.7 },
|
||||
// { pt: 63, px: 83.7 },
|
||||
// { pt: 72, px: 95.6 },
|
||||
];
|
||||
|
||||
/** map pt to px
|
||||
* @date 2019-10-10
|
||||
* @param {fontsizePT} pt
|
||||
* @returns {fontsizePX}
|
||||
*/
|
||||
function getFontSizePxByPt(pt) {
|
||||
for (let i = 0; i < fontSizes.length; i += 1) {
|
||||
const fontSize = fontSizes[i];
|
||||
if (fontSize.pt === pt) {
|
||||
return fontSize.px;
|
||||
}
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
||||
/** transform baseFonts to map
|
||||
* @date 2019-10-10
|
||||
* @param {BaseFont[]} [ary=[]]
|
||||
* @returns {object}
|
||||
*/
|
||||
function fonts(ary = []) {
|
||||
const map = {};
|
||||
baseFonts.concat(ary).forEach(f => {
|
||||
map[f.key] = f;
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { fontSizes, fonts, baseFonts, getFontSizePxByPt };
|
||||
106
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/format.js
Normal file
106
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/format.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import { tf } from '../locale/locale.js';
|
||||
|
||||
const formatStringRender = v => v;
|
||||
|
||||
const formatNumberRender = v => {
|
||||
// match "-12.1" or "12" or "12.1"
|
||||
if (/^(-?\d*.?\d*)$/.test(v)) {
|
||||
const v1 = Number(v).toFixed(2).toString();
|
||||
const [first, ...parts] = v1.split('\\.');
|
||||
return [first.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'), ...parts];
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
const baseFormats = [
|
||||
{
|
||||
key: 'normal',
|
||||
title: tf('format.normal'),
|
||||
type: 'string',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'text',
|
||||
title: tf('format.text'),
|
||||
type: 'string',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'number',
|
||||
title: tf('format.number'),
|
||||
type: 'number',
|
||||
label: '1,000.12',
|
||||
render: formatNumberRender,
|
||||
},
|
||||
{
|
||||
key: 'percent',
|
||||
title: tf('format.percent'),
|
||||
type: 'number',
|
||||
label: '10.12%',
|
||||
render: v => `${v}%`,
|
||||
},
|
||||
{
|
||||
key: 'rmb',
|
||||
title: tf('format.rmb'),
|
||||
type: 'number',
|
||||
label: '¥10.00',
|
||||
render: v => `¥${formatNumberRender(v)}`,
|
||||
},
|
||||
{
|
||||
key: 'usd',
|
||||
title: tf('format.usd'),
|
||||
type: 'number',
|
||||
label: '$10.00',
|
||||
render: v => `$${formatNumberRender(v)}`,
|
||||
},
|
||||
{
|
||||
key: 'eur',
|
||||
title: tf('format.eur'),
|
||||
type: 'number',
|
||||
label: '€10.00',
|
||||
render: v => `€${formatNumberRender(v)}`,
|
||||
},
|
||||
{
|
||||
key: 'date',
|
||||
title: tf('format.date'),
|
||||
type: 'date',
|
||||
label: '26/09/2008',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'time',
|
||||
title: tf('format.time'),
|
||||
type: 'date',
|
||||
label: '15:59:00',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'datetime',
|
||||
title: tf('format.datetime'),
|
||||
type: 'date',
|
||||
label: '26/09/2008 15:59:00',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'duration',
|
||||
title: tf('format.duration'),
|
||||
type: 'date',
|
||||
label: '24:01:00',
|
||||
render: formatStringRender,
|
||||
},
|
||||
];
|
||||
|
||||
// const formats = (ary = []) => {
|
||||
// const map = {};
|
||||
// baseFormats.concat(ary).forEach((f) => {
|
||||
// map[f.key] = f;
|
||||
// });
|
||||
// return map;
|
||||
// };
|
||||
const formatm = {};
|
||||
baseFormats.forEach(f => {
|
||||
formatm[f.key] = f;
|
||||
});
|
||||
|
||||
export default {};
|
||||
export { formatm, baseFormats };
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
formula:
|
||||
key
|
||||
title
|
||||
render
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} Formula
|
||||
* @property {string} key
|
||||
* @property {function} title
|
||||
* @property {function} render
|
||||
*/
|
||||
import { tf } from '../locale/locale.js';
|
||||
import { numberCalc } from './helper.js';
|
||||
|
||||
/** @type {Formula[]} */
|
||||
const baseFormulas = [
|
||||
{
|
||||
key: 'SUM',
|
||||
title: tf('formula.sum'),
|
||||
render: ary => ary.reduce((a, b) => numberCalc('+', a, b), 0),
|
||||
},
|
||||
{
|
||||
key: 'AVERAGE',
|
||||
title: tf('formula.average'),
|
||||
render: ary => ary.reduce((a, b) => Number(a) + Number(b), 0) / ary.length,
|
||||
},
|
||||
{
|
||||
key: 'MAX',
|
||||
title: tf('formula.max'),
|
||||
render: ary => Math.max(...ary.map(v => Number(v))),
|
||||
},
|
||||
{
|
||||
key: 'MIN',
|
||||
title: tf('formula.min'),
|
||||
render: ary => Math.min(...ary.map(v => Number(v))),
|
||||
},
|
||||
{
|
||||
key: 'IF',
|
||||
title: tf('formula._if'),
|
||||
render: ([b, t, f]) => (b ? t : f),
|
||||
},
|
||||
{
|
||||
key: 'AND',
|
||||
title: tf('formula.and'),
|
||||
render: ary => ary.every(it => it),
|
||||
},
|
||||
{
|
||||
key: 'OR',
|
||||
title: tf('formula.or'),
|
||||
render: ary => ary.some(it => it),
|
||||
},
|
||||
{
|
||||
key: 'CONCAT',
|
||||
title: tf('formula.concat'),
|
||||
render: ary => ary.join(''),
|
||||
},
|
||||
/* support: 1 + A1 + B2 * 3
|
||||
{
|
||||
key: 'DIVIDE',
|
||||
title: tf('formula.divide'),
|
||||
render: ary => ary.reduce((a, b) => Number(a) / Number(b)),
|
||||
},
|
||||
{
|
||||
key: 'PRODUCT',
|
||||
title: tf('formula.product'),
|
||||
render: ary => ary.reduce((a, b) => Number(a) * Number(b),1),
|
||||
},
|
||||
{
|
||||
key: 'SUBTRACT',
|
||||
title: tf('formula.subtract'),
|
||||
render: ary => ary.reduce((a, b) => Number(a) - Number(b)),
|
||||
},
|
||||
*/
|
||||
];
|
||||
|
||||
const formulas = baseFormulas;
|
||||
|
||||
// const formulas = (formulaAry = []) => {
|
||||
// const formulaMap = {};
|
||||
// baseFormulas.concat(formulaAry).forEach((f) => {
|
||||
// formulaMap[f.key] = f;
|
||||
// });
|
||||
// return formulaMap;
|
||||
// };
|
||||
const formulam = {};
|
||||
baseFormulas.forEach(f => {
|
||||
formulam[f.key] = f;
|
||||
});
|
||||
|
||||
export default {};
|
||||
|
||||
export { formulam, formulas, baseFormulas };
|
||||
147
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/helper.js
Normal file
147
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/helper.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
function cloneDeep(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
const mergeDeep = (object = {}, ...sources) => {
|
||||
sources.forEach(source => {
|
||||
Object.keys(source).forEach(key => {
|
||||
const v = source[key];
|
||||
// console.log('k:', key, ', v:', source[key], typeof v, v instanceof Object);
|
||||
if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
|
||||
object[key] = v;
|
||||
} else if (typeof v !== 'function' && !Array.isArray(v) && v instanceof Object) {
|
||||
object[key] = object[key] || {};
|
||||
mergeDeep(object[key], v);
|
||||
} else {
|
||||
object[key] = v;
|
||||
}
|
||||
});
|
||||
});
|
||||
// console.log('::', object);
|
||||
return object;
|
||||
};
|
||||
|
||||
function equals(obj1, obj2) {
|
||||
const keys = Object.keys(obj1);
|
||||
if (keys.length !== Object.keys(obj2).length) return false;
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
const k = keys[i];
|
||||
const v1 = obj1[k];
|
||||
const v2 = obj2[k];
|
||||
if (v2 === undefined) return false;
|
||||
if (typeof v1 === 'string' || typeof v1 === 'number' || typeof v1 === 'boolean') {
|
||||
if (v1 !== v2) return false;
|
||||
} else if (Array.isArray(v1)) {
|
||||
if (v1.length !== v2.length) return false;
|
||||
for (let ai = 0; ai < v1.length; ai += 1) {
|
||||
if (!equals(v1[ai], v2[ai])) return false;
|
||||
}
|
||||
} else if (typeof v1 !== 'function' && !Array.isArray(v1) && v1 instanceof Object) {
|
||||
if (!equals(v1, v2)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
objOrAry: obejct or Array
|
||||
cb: (value, index | key) => { return value }
|
||||
*/
|
||||
const sum = (objOrAry, cb = value => value) => {
|
||||
let total = 0;
|
||||
let size = 0;
|
||||
Object.keys(objOrAry).forEach(key => {
|
||||
total += cb(objOrAry[key], key);
|
||||
size += 1;
|
||||
});
|
||||
return [total, size];
|
||||
};
|
||||
|
||||
function deleteProperty(obj, property) {
|
||||
const oldv = obj[`${property}`];
|
||||
delete obj[`${property}`];
|
||||
return oldv;
|
||||
}
|
||||
|
||||
function rangeReduceIf(min, max, inits, initv, ifv, getv) {
|
||||
let s = inits;
|
||||
let v = initv;
|
||||
let i = min;
|
||||
for (; i < max; i += 1) {
|
||||
if (s > ifv) break;
|
||||
v = getv(i);
|
||||
s += v;
|
||||
}
|
||||
return [i, s - v, v];
|
||||
}
|
||||
|
||||
function rangeSum(min, max, getv) {
|
||||
let s = 0;
|
||||
for (let i = min; i < max; i += 1) {
|
||||
s += getv(i);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function rangeEach(min, max, cb) {
|
||||
for (let i = min; i < max; i += 1) {
|
||||
cb(i);
|
||||
}
|
||||
}
|
||||
|
||||
function arrayEquals(a1, a2) {
|
||||
if (a1.length === a2.length) {
|
||||
for (let i = 0; i < a1.length; i += 1) {
|
||||
if (a1[i] !== a2[i]) return false;
|
||||
}
|
||||
} else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function digits(a) {
|
||||
const v = `${a}`;
|
||||
let ret = 0;
|
||||
let flag = false;
|
||||
for (let i = 0; i < v.length; i += 1) {
|
||||
if (flag === true) ret += 1;
|
||||
if (v.charAt(i) === '.') flag = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function numberCalc(type, a1, a2) {
|
||||
if (Number.isNaN(a1) || Number.isNaN(a2)) {
|
||||
return a1 + type + a2;
|
||||
}
|
||||
const al1 = digits(a1);
|
||||
const al2 = digits(a2);
|
||||
const num1 = Number(a1);
|
||||
const num2 = Number(a2);
|
||||
let ret = 0;
|
||||
if (type === '-') {
|
||||
ret = num1 - num2;
|
||||
} else if (type === '+') {
|
||||
ret = num1 + num2;
|
||||
} else if (type === '*') {
|
||||
ret = num1 * num2;
|
||||
} else if (type === '/') {
|
||||
ret = num1 / num2;
|
||||
if (digits(ret) > 5) return ret.toFixed(2);
|
||||
return ret;
|
||||
}
|
||||
return ret.toFixed(Math.max(al1, al2));
|
||||
}
|
||||
|
||||
export default {
|
||||
cloneDeep,
|
||||
merge: (...sources) => mergeDeep({}, ...sources),
|
||||
equals,
|
||||
arrayEquals,
|
||||
sum,
|
||||
rangeEach,
|
||||
rangeSum,
|
||||
rangeReduceIf,
|
||||
deleteProperty,
|
||||
numberCalc,
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
// import helper from '../helper.js';
|
||||
|
||||
export default class History {
|
||||
constructor() {
|
||||
this.undoItems = [];
|
||||
this.redoItems = [];
|
||||
}
|
||||
|
||||
add(data) {
|
||||
this.undoItems.push(JSON.stringify(data));
|
||||
this.redoItems = [];
|
||||
}
|
||||
|
||||
canUndo() {
|
||||
return this.undoItems.length > 0;
|
||||
}
|
||||
|
||||
canRedo() {
|
||||
return this.redoItems.length > 0;
|
||||
}
|
||||
|
||||
undo(currentd, cb) {
|
||||
const { undoItems, redoItems } = this;
|
||||
if (this.canUndo()) {
|
||||
redoItems.push(JSON.stringify(currentd));
|
||||
cb(JSON.parse(undoItems.pop()));
|
||||
}
|
||||
}
|
||||
|
||||
redo(currentd, cb) {
|
||||
const { undoItems, redoItems } = this;
|
||||
if (this.canRedo()) {
|
||||
undoItems.push(JSON.stringify(currentd));
|
||||
cb(JSON.parse(redoItems.pop()));
|
||||
}
|
||||
}
|
||||
}
|
||||
104
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/merge.js
Normal file
104
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/merge.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import { CellRange } from './cell_range.js';
|
||||
|
||||
class Merges {
|
||||
constructor(d = []) {
|
||||
this._ = d;
|
||||
}
|
||||
|
||||
forEach(cb) {
|
||||
this._.forEach(cb);
|
||||
}
|
||||
|
||||
deleteWithin(cr) {
|
||||
this._ = this._.filter(it => !it.within(cr));
|
||||
}
|
||||
|
||||
getFirstIncludes(ri, ci) {
|
||||
for (let i = 0; i < this._.length; i += 1) {
|
||||
const it = this._[i];
|
||||
if (it.includes(ri, ci)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
filterIntersects(cellRange) {
|
||||
return new Merges(this._.filter(it => it.intersects(cellRange)));
|
||||
}
|
||||
|
||||
intersects(cellRange) {
|
||||
for (let i = 0; i < this._.length; i += 1) {
|
||||
const it = this._[i];
|
||||
if (it.intersects(cellRange)) {
|
||||
// console.log('intersects');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
union(cellRange) {
|
||||
let cr = cellRange;
|
||||
this._.forEach(it => {
|
||||
if (it.intersects(cr)) {
|
||||
cr = it.union(cr);
|
||||
}
|
||||
});
|
||||
return cr;
|
||||
}
|
||||
|
||||
add(cr) {
|
||||
this.deleteWithin(cr);
|
||||
this._.push(cr);
|
||||
}
|
||||
|
||||
// type: row | column
|
||||
shift(type, index, n, cbWithin) {
|
||||
this._.forEach(cellRange => {
|
||||
const { sri, sci, eri, eci } = cellRange;
|
||||
const range = cellRange;
|
||||
if (type === 'row') {
|
||||
if (sri >= index) {
|
||||
range.sri += n;
|
||||
range.eri += n;
|
||||
} else if (sri < index && index <= eri) {
|
||||
range.eri += n;
|
||||
cbWithin(sri, sci, n, 0);
|
||||
}
|
||||
} else if (type === 'column') {
|
||||
if (sci >= index) {
|
||||
range.sci += n;
|
||||
range.eci += n;
|
||||
} else if (sci < index && index <= eci) {
|
||||
range.eci += n;
|
||||
cbWithin(sri, sci, 0, n);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
move(cellRange, rn, cn) {
|
||||
this._.forEach(it1 => {
|
||||
const it = it1;
|
||||
if (it.within(cellRange)) {
|
||||
it.eri += rn;
|
||||
it.sri += rn;
|
||||
it.sci += cn;
|
||||
it.eci += cn;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setData(merges) {
|
||||
this._ = merges.map(merge => CellRange.valueOf(merge));
|
||||
return this;
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this._.map(merge => merge.toString());
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { Merges };
|
||||
371
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/row.js
Normal file
371
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/row.js
Normal file
@@ -0,0 +1,371 @@
|
||||
import helper from './helper.js';
|
||||
import { expr2expr } from './alphabet.js';
|
||||
|
||||
class Rows {
|
||||
constructor({ len, height, indexHeight }) {
|
||||
this._ = {};
|
||||
this.len = len;
|
||||
// default row height
|
||||
this.height = height;
|
||||
this.indexHeight = indexHeight;
|
||||
}
|
||||
|
||||
getHeight(ri) {
|
||||
if (this.isHide(ri)) return 0;
|
||||
const row = this.get(ri);
|
||||
if (row && row.height) {
|
||||
return row.height;
|
||||
}
|
||||
return this.height;
|
||||
}
|
||||
|
||||
setHeight(ri, v) {
|
||||
const row = this.getOrNew(ri);
|
||||
row.height = v;
|
||||
}
|
||||
|
||||
unhide(idx) {
|
||||
let index = idx;
|
||||
while (index > 0) {
|
||||
index -= 1;
|
||||
if (this.isHide(index)) {
|
||||
this.setHide(index, false);
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
isHide(ri) {
|
||||
const row = this.get(ri);
|
||||
return row && row.hide;
|
||||
}
|
||||
|
||||
setHide(ri, v) {
|
||||
const row = this.getOrNew(ri);
|
||||
if (v === true) row.hide = true;
|
||||
else delete row.hide;
|
||||
}
|
||||
|
||||
setStyle(ri, style) {
|
||||
const row = this.getOrNew(ri);
|
||||
row.style = style;
|
||||
}
|
||||
|
||||
sumHeight(min, max, exceptSet) {
|
||||
return helper.rangeSum(min, max, i => {
|
||||
if (exceptSet && exceptSet.has(i)) return 0;
|
||||
return this.getHeight(i);
|
||||
});
|
||||
}
|
||||
|
||||
totalHeight() {
|
||||
return this.sumHeight(0, this.len);
|
||||
}
|
||||
|
||||
get(ri) {
|
||||
return this._[ri];
|
||||
}
|
||||
|
||||
getOrNew(ri) {
|
||||
this._[ri] = this._[ri] || { cells: {} };
|
||||
return this._[ri];
|
||||
}
|
||||
|
||||
getCell(ri, ci) {
|
||||
const row = this.get(ri);
|
||||
if (row !== undefined && row.cells !== undefined && row.cells[ci] !== undefined) {
|
||||
return row.cells[ci];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getCellMerge(ri, ci) {
|
||||
const cell = this.getCell(ri, ci);
|
||||
if (cell && cell.merge) return cell.merge;
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
getCellOrNew(ri, ci) {
|
||||
const row = this.getOrNew(ri);
|
||||
row.cells[ci] = row.cells[ci] || {};
|
||||
return row.cells[ci];
|
||||
}
|
||||
|
||||
// what: all | text | format
|
||||
setCell(ri, ci, cell, what = 'all') {
|
||||
const row = this.getOrNew(ri);
|
||||
if (what === 'all') {
|
||||
row.cells[ci] = cell;
|
||||
} else if (what === 'text') {
|
||||
row.cells[ci] = row.cells[ci] || {};
|
||||
row.cells[ci].text = cell.text;
|
||||
} else if (what === 'format') {
|
||||
row.cells[ci] = row.cells[ci] || {};
|
||||
row.cells[ci].style = cell.style;
|
||||
if (cell.merge) row.cells[ci].merge = cell.merge;
|
||||
}
|
||||
}
|
||||
|
||||
setCellText(ri, ci, text) {
|
||||
const cell = this.getCellOrNew(ri, ci);
|
||||
if (cell.editable !== false) cell.text = text;
|
||||
}
|
||||
|
||||
// what: all | format | text
|
||||
copyPaste(
|
||||
srcCellRange,
|
||||
dstCellRange,
|
||||
what,
|
||||
autofill = false,
|
||||
cb = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
) {
|
||||
const { sri, sci, eri, eci } = srcCellRange;
|
||||
const dsri = dstCellRange.sri;
|
||||
const dsci = dstCellRange.sci;
|
||||
const deri = dstCellRange.eri;
|
||||
const deci = dstCellRange.eci;
|
||||
const [rn, cn] = srcCellRange.size();
|
||||
const [drn, dcn] = dstCellRange.size();
|
||||
// console.log(srcIndexes, dstIndexes);
|
||||
let isAdd = true;
|
||||
let dn = 0;
|
||||
if (deri < sri || deci < sci) {
|
||||
isAdd = false;
|
||||
if (deri < sri) dn = drn;
|
||||
else dn = dcn;
|
||||
}
|
||||
for (let i = sri; i <= eri; i += 1) {
|
||||
if (this._[i]) {
|
||||
for (let j = sci; j <= eci; j += 1) {
|
||||
if (this._[i].cells && this._[i].cells[j]) {
|
||||
for (let ii = dsri; ii <= deri; ii += rn) {
|
||||
for (let jj = dsci; jj <= deci; jj += cn) {
|
||||
const nri = ii + (i - sri);
|
||||
const nci = jj + (j - sci);
|
||||
const ncell = helper.cloneDeep(this._[i].cells[j]);
|
||||
// ncell.text
|
||||
if (autofill && ncell && ncell.text && ncell.text.length > 0) {
|
||||
const { text } = ncell;
|
||||
let n = jj - dsci + (ii - dsri) + 2;
|
||||
if (!isAdd) {
|
||||
n -= dn + 1;
|
||||
}
|
||||
if (text[0] === '=') {
|
||||
ncell.text = text.replace(/[a-zA-Z]{1,3}\d+/g, word => {
|
||||
let [xn, yn] = [0, 0];
|
||||
if (sri === dsri) {
|
||||
xn = n - 1;
|
||||
// if (isAdd) xn -= 1;
|
||||
} else {
|
||||
yn = n - 1;
|
||||
}
|
||||
if (/^\d+$/.test(word)) return word;
|
||||
return expr2expr(word, xn, yn);
|
||||
});
|
||||
} else if (
|
||||
(rn <= 1 && cn > 1 && (dsri > eri || deri < sri)) ||
|
||||
(cn <= 1 && rn > 1 && (dsci > eci || deci < sci)) ||
|
||||
(rn <= 1 && cn <= 1)
|
||||
) {
|
||||
const result = /[\\.\d]+$/.exec(text);
|
||||
// console.log('result:', result);
|
||||
if (result !== null) {
|
||||
const index = Number(result[0]) + n - 1;
|
||||
ncell.text = text.substring(0, result.index) + index;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setCell(nri, nci, ncell, what);
|
||||
cb(nri, nci, ncell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cutPaste(srcCellRange, dstCellRange) {
|
||||
const ncellmm = {};
|
||||
this.each(ri => {
|
||||
this.eachCells(ri, ci => {
|
||||
let nri = parseInt(ri, 10);
|
||||
let nci = parseInt(ci, 10);
|
||||
if (srcCellRange.includes(ri, ci)) {
|
||||
nri = dstCellRange.sri + (nri - srcCellRange.sri);
|
||||
nci = dstCellRange.sci + (nci - srcCellRange.sci);
|
||||
}
|
||||
ncellmm[nri] = ncellmm[nri] || { cells: {} };
|
||||
ncellmm[nri].cells[nci] = this._[ri].cells[ci];
|
||||
});
|
||||
});
|
||||
this._ = ncellmm;
|
||||
}
|
||||
|
||||
// src: Array<Array<String>>
|
||||
paste(src, dstCellRange) {
|
||||
if (src.length <= 0) return;
|
||||
const { sri, sci } = dstCellRange;
|
||||
src.forEach((row, i) => {
|
||||
const ri = sri + i;
|
||||
row.forEach((cell, j) => {
|
||||
const ci = sci + j;
|
||||
this.setCellText(ri, ci, cell);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
insert(sri, n = 1) {
|
||||
const ndata = {};
|
||||
this.each((ri, row) => {
|
||||
let nri = parseInt(ri, 10);
|
||||
if (nri >= sri) {
|
||||
nri += n;
|
||||
this.eachCells(ri, (ci, cell) => {
|
||||
if (cell.text && cell.text[0] === '=') {
|
||||
cell.text = cell.text.replace(/[a-zA-Z]{1,3}\d+/g, word =>
|
||||
expr2expr(word, 0, n, (x, y) => y >= sri),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
ndata[nri] = row;
|
||||
});
|
||||
this._ = ndata;
|
||||
this.len += n;
|
||||
}
|
||||
|
||||
delete(sri, eri) {
|
||||
const n = eri - sri + 1;
|
||||
const ndata = {};
|
||||
this.each((ri, row) => {
|
||||
const nri = parseInt(ri, 10);
|
||||
if (nri < sri) {
|
||||
ndata[nri] = row;
|
||||
} else if (ri > eri) {
|
||||
ndata[nri - n] = row;
|
||||
this.eachCells(ri, (ci, cell) => {
|
||||
if (cell.text && cell.text[0] === '=') {
|
||||
cell.text = cell.text.replace(/[a-zA-Z]{1,3}\d+/g, word =>
|
||||
expr2expr(word, 0, -n, (x, y) => y > eri),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
this._ = ndata;
|
||||
this.len -= n;
|
||||
}
|
||||
|
||||
insertColumn(sci, n = 1) {
|
||||
this.each((ri, row) => {
|
||||
const rndata = {};
|
||||
this.eachCells(ri, (ci, cell) => {
|
||||
let nci = parseInt(ci, 10);
|
||||
if (nci >= sci) {
|
||||
nci += n;
|
||||
if (cell.text && cell.text[0] === '=') {
|
||||
cell.text = cell.text.replace(/[a-zA-Z]{1,3}\d+/g, word =>
|
||||
expr2expr(word, n, 0, x => x >= sci),
|
||||
);
|
||||
}
|
||||
}
|
||||
rndata[nci] = cell;
|
||||
});
|
||||
row.cells = rndata;
|
||||
});
|
||||
}
|
||||
|
||||
deleteColumn(sci, eci) {
|
||||
const n = eci - sci + 1;
|
||||
this.each((ri, row) => {
|
||||
const rndata = {};
|
||||
this.eachCells(ri, (ci, cell) => {
|
||||
const nci = parseInt(ci, 10);
|
||||
if (nci < sci) {
|
||||
rndata[nci] = cell;
|
||||
} else if (nci > eci) {
|
||||
rndata[nci - n] = cell;
|
||||
if (cell.text && cell.text[0] === '=') {
|
||||
cell.text = cell.text.replace(/[a-zA-Z]{1,3}\d+/g, word =>
|
||||
expr2expr(word, -n, 0, x => x > eci),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
row.cells = rndata;
|
||||
});
|
||||
}
|
||||
|
||||
// what: all | text | format | merge
|
||||
deleteCells(cellRange, what = 'all') {
|
||||
cellRange.each((i, j) => {
|
||||
this.deleteCell(i, j, what);
|
||||
});
|
||||
}
|
||||
|
||||
// what: all | text | format | merge
|
||||
deleteCell(ri, ci, what = 'all') {
|
||||
const row = this.get(ri);
|
||||
if (row !== null) {
|
||||
const cell = this.getCell(ri, ci);
|
||||
if (cell !== null && cell.editable !== false) {
|
||||
if (what === 'all') {
|
||||
delete row.cells[ci];
|
||||
} else if (what === 'text') {
|
||||
if (cell.text) delete cell.text;
|
||||
if (cell.value) delete cell.value;
|
||||
} else if (what === 'format') {
|
||||
if (cell.style !== undefined) delete cell.style;
|
||||
if (cell.merge) delete cell.merge;
|
||||
} else if (what === 'merge') {
|
||||
if (cell.merge) delete cell.merge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maxCell() {
|
||||
const keys = Object.keys(this._);
|
||||
const ri = keys[keys.length - 1];
|
||||
const col = this._[ri];
|
||||
if (col) {
|
||||
const { cells } = col;
|
||||
const ks = Object.keys(cells);
|
||||
const ci = ks[ks.length - 1];
|
||||
return [parseInt(ri, 10), parseInt(ci, 10)];
|
||||
}
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
each(cb) {
|
||||
Object.entries(this._).forEach(([ri, row]) => {
|
||||
cb(ri, row);
|
||||
});
|
||||
}
|
||||
|
||||
eachCells(ri, cb) {
|
||||
if (this._[ri] && this._[ri].cells) {
|
||||
Object.entries(this._[ri].cells).forEach(([ci, cell]) => {
|
||||
cb(ci, cell);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setData(d) {
|
||||
if (d.len) {
|
||||
this.len = d.len;
|
||||
delete d.len;
|
||||
}
|
||||
this._ = d;
|
||||
}
|
||||
|
||||
getData() {
|
||||
const { len } = this;
|
||||
return Object.assign({ len }, this._);
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { Rows };
|
||||
@@ -0,0 +1,8 @@
|
||||
export default class Scroll {
|
||||
constructor() {
|
||||
this.x = 0; // left
|
||||
this.y = 0; // top
|
||||
this.ri = 0; // cell row-index
|
||||
this.ci = 0; // cell col-index
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { CellRange } from './cell_range.js';
|
||||
|
||||
export default class Selector {
|
||||
constructor() {
|
||||
this.range = new CellRange(0, 0, 0, 0);
|
||||
this.ri = 0;
|
||||
this.ci = 0;
|
||||
}
|
||||
|
||||
multiple() {
|
||||
return this.range.multiple();
|
||||
}
|
||||
|
||||
setIndexes(ri, ci) {
|
||||
this.ri = ri;
|
||||
this.ci = ci;
|
||||
}
|
||||
|
||||
size() {
|
||||
return this.range.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import Validator from './validator.js';
|
||||
import { CellRange } from './cell_range.js';
|
||||
|
||||
class Validation {
|
||||
constructor(mode, refs, validator) {
|
||||
this.refs = refs;
|
||||
this.mode = mode; // cell
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
includes(ri, ci) {
|
||||
const { refs } = this;
|
||||
for (let i = 0; i < refs.length; i += 1) {
|
||||
const cr = CellRange.valueOf(refs[i]);
|
||||
if (cr.includes(ri, ci)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
addRef(ref) {
|
||||
this.remove(CellRange.valueOf(ref));
|
||||
this.refs.push(ref);
|
||||
}
|
||||
|
||||
remove(cellRange) {
|
||||
const nrefs = [];
|
||||
this.refs.forEach(it => {
|
||||
const cr = CellRange.valueOf(it);
|
||||
if (cr.intersects(cellRange)) {
|
||||
const crs = cr.difference(cellRange);
|
||||
crs.forEach(it1 => nrefs.push(it1.toString()));
|
||||
} else {
|
||||
nrefs.push(it);
|
||||
}
|
||||
});
|
||||
this.refs = nrefs;
|
||||
}
|
||||
|
||||
getData() {
|
||||
const { refs, mode, validator } = this;
|
||||
const { type, required, operator, value } = validator;
|
||||
return {
|
||||
refs,
|
||||
mode,
|
||||
type,
|
||||
required,
|
||||
operator,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
static valueOf({ refs, mode, type, required, operator, value }) {
|
||||
return new Validation(mode, refs, new Validator(type, required, value, operator));
|
||||
}
|
||||
}
|
||||
class Validations {
|
||||
constructor() {
|
||||
this._ = [];
|
||||
// ri_ci: errMessage
|
||||
this.errors = new Map();
|
||||
}
|
||||
|
||||
getError(ri, ci) {
|
||||
return this.errors.get(`${ri}_${ci}`);
|
||||
}
|
||||
|
||||
validate(ri, ci, text) {
|
||||
const v = this.get(ri, ci);
|
||||
const key = `${ri}_${ci}`;
|
||||
const { errors } = this;
|
||||
if (v !== null) {
|
||||
const [flag, message] = v.validator.validate(text);
|
||||
if (!flag) {
|
||||
errors.set(key, message);
|
||||
} else {
|
||||
errors.delete(key);
|
||||
}
|
||||
} else {
|
||||
errors.delete(key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// type: date|number|phone|email|list
|
||||
// validator: { required, value, operator }
|
||||
add(mode, ref, { type, required, value, operator }) {
|
||||
const validator = new Validator(type, required, value, operator);
|
||||
const v = this.getByValidator(validator);
|
||||
if (v !== null) {
|
||||
v.addRef(ref);
|
||||
} else {
|
||||
this._.push(new Validation(mode, [ref], validator));
|
||||
}
|
||||
}
|
||||
|
||||
getByValidator(validator) {
|
||||
for (let i = 0; i < this._.length; i += 1) {
|
||||
const v = this._[i];
|
||||
if (v.validator.equals(validator)) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
get(ri, ci) {
|
||||
for (let i = 0; i < this._.length; i += 1) {
|
||||
const v = this._[i];
|
||||
if (v.includes(ri, ci)) return v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
remove(cellRange) {
|
||||
this.each(it => {
|
||||
it.remove(cellRange);
|
||||
});
|
||||
}
|
||||
|
||||
each(cb) {
|
||||
this._.forEach(it => cb(it));
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this._.filter(it => it.refs.length > 0).map(it => it.getData());
|
||||
}
|
||||
|
||||
setData(d) {
|
||||
this._ = d.map(it => Validation.valueOf(it));
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { Validations };
|
||||
@@ -0,0 +1,111 @@
|
||||
import { t } from '../locale/locale.js';
|
||||
import helper from './helper.js';
|
||||
|
||||
const rules = {
|
||||
phone: /^[1-9]\d{10}$/,
|
||||
email: /w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*/,
|
||||
};
|
||||
|
||||
function returnMessage(flag, key, ...arg) {
|
||||
let message = '';
|
||||
if (!flag) {
|
||||
message = t(`validation.${key}`, ...arg);
|
||||
}
|
||||
return [flag, message];
|
||||
}
|
||||
|
||||
export default class Validator {
|
||||
// operator: b|nb|eq|neq|lt|lte|gt|gte
|
||||
// type: date|number|list|phone|email
|
||||
constructor(type, required, value, operator) {
|
||||
this.required = required;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.operator = operator;
|
||||
this.message = '';
|
||||
}
|
||||
|
||||
parseValue(v) {
|
||||
const { type } = this;
|
||||
if (type === 'date') {
|
||||
return new Date(v);
|
||||
}
|
||||
if (type === 'number') {
|
||||
return Number(v);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
let flag =
|
||||
this.type === other.type &&
|
||||
this.required === other.required &&
|
||||
this.operator === other.operator;
|
||||
if (flag) {
|
||||
if (Array.isArray(this.value)) {
|
||||
flag = helper.arrayEquals(this.value, other.value);
|
||||
} else {
|
||||
flag = this.value === other.value;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
values() {
|
||||
return this.value.split(',');
|
||||
}
|
||||
|
||||
validate(v) {
|
||||
const { required, operator, value, type } = this;
|
||||
if (required && /^\s*$/.test(v)) {
|
||||
return returnMessage(false, 'required');
|
||||
}
|
||||
if (/^\s*$/.test(v)) return [true];
|
||||
if (rules[type] && !rules[type].test(v)) {
|
||||
return returnMessage(false, 'notMatch');
|
||||
}
|
||||
if (type === 'list') {
|
||||
return returnMessage(this.values().includes(v), 'notIn');
|
||||
}
|
||||
if (operator) {
|
||||
const v1 = this.parseValue(v);
|
||||
if (operator === 'be') {
|
||||
const [min, max] = value;
|
||||
return returnMessage(
|
||||
v1 >= this.parseValue(min) && v1 <= this.parseValue(max),
|
||||
'between',
|
||||
min,
|
||||
max,
|
||||
);
|
||||
}
|
||||
if (operator === 'nbe') {
|
||||
const [min, max] = value;
|
||||
return returnMessage(
|
||||
v1 < this.parseValue(min) || v1 > this.parseValue(max),
|
||||
'notBetween',
|
||||
min,
|
||||
max,
|
||||
);
|
||||
}
|
||||
if (operator === 'eq') {
|
||||
return returnMessage(v1 === this.parseValue(value), 'equal', value);
|
||||
}
|
||||
if (operator === 'neq') {
|
||||
return returnMessage(v1 !== this.parseValue(value), 'notEqual', value);
|
||||
}
|
||||
if (operator === 'lt') {
|
||||
return returnMessage(v1 < this.parseValue(value), 'lessThan', value);
|
||||
}
|
||||
if (operator === 'lte') {
|
||||
return returnMessage(v1 <= this.parseValue(value), 'lessThanEqual', value);
|
||||
}
|
||||
if (operator === 'gt') {
|
||||
return returnMessage(v1 > this.parseValue(value), 'greaterThan', value);
|
||||
}
|
||||
if (operator === 'gte') {
|
||||
return returnMessage(v1 >= this.parseValue(value), 'greaterThanEqual', value);
|
||||
}
|
||||
}
|
||||
return [true];
|
||||
}
|
||||
}
|
||||
193
OrangeFormsOpen-VUE3/src/components/SpreadSheet/index.d.ts
vendored
Normal file
193
OrangeFormsOpen-VUE3/src/components/SpreadSheet/index.d.ts
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
declare module 'x-data-spreadsheet' {
|
||||
export interface ExtendToolbarOption {
|
||||
tip?: string;
|
||||
el?: HTMLElement;
|
||||
icon?: string;
|
||||
onClick?: (data: object, sheet: object) => void;
|
||||
}
|
||||
export interface Options {
|
||||
mode?: 'edit' | 'read';
|
||||
showToolbar?: boolean;
|
||||
showGrid?: boolean;
|
||||
showContextmenu?: boolean;
|
||||
showBottomBar?: boolean;
|
||||
extendToolbar?: {
|
||||
left?: ExtendToolbarOption[];
|
||||
right?: ExtendToolbarOption[];
|
||||
};
|
||||
autoFocus?: boolean;
|
||||
view?: {
|
||||
height: () => number;
|
||||
width: () => number;
|
||||
};
|
||||
row?: {
|
||||
len: number;
|
||||
height: number;
|
||||
};
|
||||
col?: {
|
||||
len: number;
|
||||
width: number;
|
||||
indexWidth: number;
|
||||
minWidth: number;
|
||||
};
|
||||
style?: {
|
||||
bgcolor: string;
|
||||
align: 'left' | 'center' | 'right';
|
||||
valign: 'top' | 'middle' | 'bottom';
|
||||
textwrap: boolean;
|
||||
strike: boolean;
|
||||
underline: boolean;
|
||||
color: string;
|
||||
font: {
|
||||
name: 'Helvetica';
|
||||
size: number;
|
||||
bold: boolean;
|
||||
italic: false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type CELL_SELECTED = 'cell-selected';
|
||||
export type CELLS_SELECTED = 'cells-selected';
|
||||
export type CELL_EDITED = 'cell-edited';
|
||||
|
||||
export type CellMerge = [number, number];
|
||||
|
||||
export interface SpreadsheetEventHandler {
|
||||
(envt: CELL_SELECTED, callback: (cell: Cell, rowIndex: number, colIndex: number) => void): void;
|
||||
(
|
||||
envt: CELLS_SELECTED,
|
||||
callback: (
|
||||
cell: Cell,
|
||||
parameters: { sri: number; sci: number; eri: number; eci: number },
|
||||
) => void,
|
||||
): void;
|
||||
(evnt: CELL_EDITED, callback: (text: string, rowIndex: number, colIndex: number) => void): void;
|
||||
}
|
||||
|
||||
export interface ColProperties {
|
||||
width?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for representing a cell
|
||||
*/
|
||||
export interface CellData {
|
||||
text: string;
|
||||
style?: number;
|
||||
merge?: CellMerge;
|
||||
}
|
||||
/**
|
||||
* Data for representing a row
|
||||
*/
|
||||
export interface RowData {
|
||||
cells: {
|
||||
[key: number]: CellData;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for representing a sheet
|
||||
*/
|
||||
export interface SheetData {
|
||||
name?: string;
|
||||
freeze?: string;
|
||||
styles?: CellStyle[];
|
||||
merges?: string[];
|
||||
cols?: {
|
||||
len?: number;
|
||||
[key: number]: ColProperties;
|
||||
};
|
||||
rows?: {
|
||||
[key: number]: RowData;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for representing a spreadsheet
|
||||
*/
|
||||
export interface SpreadsheetData {
|
||||
[index: number]: SheetData;
|
||||
}
|
||||
|
||||
export interface CellStyle {
|
||||
align?: 'left' | 'center' | 'right';
|
||||
valign?: 'top' | 'middle' | 'bottom';
|
||||
font?: {
|
||||
bold?: boolean;
|
||||
};
|
||||
bgcolor?: string;
|
||||
textwrap?: boolean;
|
||||
color?: string;
|
||||
border?: {
|
||||
top?: string[];
|
||||
right?: string[];
|
||||
bottom?: string[];
|
||||
left?: string[];
|
||||
};
|
||||
}
|
||||
export interface Editor {}
|
||||
export interface Element {}
|
||||
|
||||
export interface Row {}
|
||||
export interface Table {}
|
||||
export interface Cell {}
|
||||
export interface Sheet {}
|
||||
|
||||
export default class Spreadsheet {
|
||||
constructor(container: string | HTMLElement, opts?: Options);
|
||||
on: SpreadsheetEventHandler;
|
||||
/**
|
||||
* retrieve cell
|
||||
* @param rowIndex {number} row index
|
||||
* @param colIndex {number} column index
|
||||
* @param sheetIndex {number} sheet iindex
|
||||
*/
|
||||
cell(rowIndex: number, colIndex: number, sheetIndex: number): Cell;
|
||||
/**
|
||||
* retrieve cell style
|
||||
* @param rowIndex
|
||||
* @param colIndex
|
||||
* @param sheetIndex
|
||||
*/
|
||||
cellStyle(rowIndex: number, colIndex: number, sheetIndex: number): CellStyle;
|
||||
/**
|
||||
* get/set cell text
|
||||
* @param rowIndex
|
||||
* @param colIndex
|
||||
* @param text
|
||||
* @param sheetIndex
|
||||
*/
|
||||
cellText(rowIndex: number, colIndex: number, text: string, sheetIndex?: number): this;
|
||||
/**
|
||||
* remove current sheet
|
||||
*/
|
||||
deleteSheet(): void;
|
||||
|
||||
/**s
|
||||
* load data
|
||||
* @param json
|
||||
*/
|
||||
loadData(json: Record<string, any>): this;
|
||||
/**
|
||||
* get data
|
||||
*/
|
||||
getData(): Record<string, any>;
|
||||
/**
|
||||
* bind handler to change event, including data change and user actions
|
||||
* @param callback
|
||||
*/
|
||||
change(callback: (json: Record<string, any>) => void): this;
|
||||
/**
|
||||
* set locale
|
||||
* @param lang
|
||||
* @param message
|
||||
*/
|
||||
static locale(lang: string, message: object): void;
|
||||
}
|
||||
global {
|
||||
interface Window {
|
||||
x_spreadsheet(container: string | HTMLElement, opts?: Options): Spreadsheet;
|
||||
}
|
||||
}
|
||||
}
|
||||
143
OrangeFormsOpen-VUE3/src/components/SpreadSheet/index.js
Normal file
143
OrangeFormsOpen-VUE3/src/components/SpreadSheet/index.js
Normal file
@@ -0,0 +1,143 @@
|
||||
import { h } from './component/element.js';
|
||||
import DataProxy from './core/data_proxy.js';
|
||||
import Sheet from './component/sheet.js';
|
||||
import Bottombar from './component/bottombar.js';
|
||||
import { cssPrefix } from './config.js';
|
||||
import { locale } from './locale/locale.js';
|
||||
import './index.scss';
|
||||
|
||||
class Spreadsheet {
|
||||
constructor(selectors, options = {}) {
|
||||
let targetEl = selectors;
|
||||
this.options = { showBottomBar: true, ...options };
|
||||
this.sheetIndex = 1;
|
||||
this.datas = [];
|
||||
if (typeof selectors === 'string') {
|
||||
targetEl = document.querySelector(selectors);
|
||||
}
|
||||
this.bottombar = this.options.showBottomBar
|
||||
? new Bottombar(
|
||||
() => {
|
||||
if (this.options.mode === 'read') return;
|
||||
const d = this.addSheet();
|
||||
this.sheet.resetData(d);
|
||||
},
|
||||
index => {
|
||||
const d = this.datas[index];
|
||||
this.sheet.resetData(d);
|
||||
},
|
||||
() => {
|
||||
this.deleteSheet();
|
||||
},
|
||||
(index, value) => {
|
||||
this.datas[index].name = value;
|
||||
this.sheet.trigger('change');
|
||||
},
|
||||
)
|
||||
: null;
|
||||
this.data = this.addSheet();
|
||||
const rootEl = h('div', `${cssPrefix}`).on('contextmenu', evt => evt.preventDefault());
|
||||
// create canvas element
|
||||
targetEl.appendChild(rootEl.el);
|
||||
this.sheet = new Sheet(rootEl, this.data);
|
||||
if (this.bottombar !== null) {
|
||||
rootEl.child(this.bottombar.el);
|
||||
}
|
||||
}
|
||||
|
||||
addSheet(name, active = true) {
|
||||
const n = name || `sheet${this.sheetIndex}`;
|
||||
const d = new DataProxy(n, this.options);
|
||||
d.change = (...args) => {
|
||||
this.sheet.trigger('change', ...args);
|
||||
};
|
||||
this.datas.push(d);
|
||||
// console.log('d:', n, d, this.datas);
|
||||
if (this.bottombar !== null) {
|
||||
this.bottombar.addItem(n, active, this.options);
|
||||
}
|
||||
this.sheetIndex += 1;
|
||||
return d;
|
||||
}
|
||||
|
||||
deleteSheet() {
|
||||
if (this.bottombar === null) return;
|
||||
|
||||
const [oldIndex, nindex] = this.bottombar.deleteItem();
|
||||
if (oldIndex >= 0) {
|
||||
this.datas.splice(oldIndex, 1);
|
||||
if (nindex >= 0) this.sheet.resetData(this.datas[nindex]);
|
||||
this.sheet.trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
loadData(data) {
|
||||
const ds = Array.isArray(data) ? data : [data];
|
||||
if (this.bottombar !== null) {
|
||||
this.bottombar.clear();
|
||||
}
|
||||
this.datas = [];
|
||||
if (ds.length > 0) {
|
||||
for (let i = 0; i < ds.length; i += 1) {
|
||||
const it = ds[i];
|
||||
const nd = this.addSheet(it.name, i === 0);
|
||||
nd.setData(it);
|
||||
if (i === 0) {
|
||||
this.sheet.resetData(nd);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.datas.map(it => it.getData());
|
||||
}
|
||||
|
||||
cellText(ri, ci, text, sheetIndex = 0) {
|
||||
this.datas[sheetIndex].setCellText(ri, ci, text, 'finished');
|
||||
return this;
|
||||
}
|
||||
|
||||
cell(ri, ci, sheetIndex = 0) {
|
||||
return this.datas[sheetIndex].getCell(ri, ci);
|
||||
}
|
||||
|
||||
cellStyle(ri, ci, sheetIndex = 0) {
|
||||
return this.datas[sheetIndex].getCellStyle(ri, ci);
|
||||
}
|
||||
|
||||
reRender() {
|
||||
this.sheet.table.render();
|
||||
return this;
|
||||
}
|
||||
|
||||
on(eventName, func) {
|
||||
this.sheet.on(eventName, func);
|
||||
return this;
|
||||
}
|
||||
|
||||
validate() {
|
||||
const { validations } = this.data;
|
||||
return validations.errors.size <= 0;
|
||||
}
|
||||
|
||||
change(cb) {
|
||||
this.sheet.on('change', cb);
|
||||
return this;
|
||||
}
|
||||
|
||||
static locale(lang, message) {
|
||||
locale(lang, message);
|
||||
}
|
||||
}
|
||||
|
||||
const spreadsheet = (el, options = {}) => new Spreadsheet(el, options);
|
||||
|
||||
if (window) {
|
||||
window.x_spreadsheet = spreadsheet;
|
||||
window.x_spreadsheet.locale = (lang, message) => locale(lang, message);
|
||||
}
|
||||
|
||||
export default Spreadsheet;
|
||||
export { spreadsheet };
|
||||
1234
OrangeFormsOpen-VUE3/src/components/SpreadSheet/index.scss
Normal file
1234
OrangeFormsOpen-VUE3/src/components/SpreadSheet/index.scss
Normal file
File diff suppressed because it is too large
Load Diff
57
OrangeFormsOpen-VUE3/src/components/SpreadSheet/locale/de.js
Normal file
57
OrangeFormsOpen-VUE3/src/components/SpreadSheet/locale/de.js
Normal file
@@ -0,0 +1,57 @@
|
||||
export default {
|
||||
toolbar: {
|
||||
undo: 'Rückgängig machen',
|
||||
redo: 'Wiederherstellen',
|
||||
paintformat: 'Format kopieren/einfügen',
|
||||
clearformat: 'Format löschen',
|
||||
format: 'Format',
|
||||
font: 'Schriftart',
|
||||
fontSize: 'Schriftgrad',
|
||||
fontBold: 'Fett',
|
||||
fontItalic: 'Kursiv',
|
||||
underline: 'Betonen',
|
||||
strike: 'Streichen',
|
||||
textColor: 'Text Farbe',
|
||||
fillColor: 'Füllung Farbe',
|
||||
border: 'Umrandung',
|
||||
merge: 'Zellen verbinden',
|
||||
align: 'Waagrechte Ausrichtung',
|
||||
valign: 'Vertikale uitlijning',
|
||||
textwrap: 'Textumbruch',
|
||||
freeze: 'Zelle sperren',
|
||||
formula: 'Funktionen',
|
||||
more: 'Mehr',
|
||||
},
|
||||
contextmenu: {
|
||||
copy: 'Kopieren',
|
||||
cut: 'Ausschneiden',
|
||||
paste: 'Einfügen',
|
||||
pasteValue: 'Nur Werte einfügen',
|
||||
pasteFormat: 'Nur Format einfügen',
|
||||
insertRow: 'Zeile einfügen',
|
||||
insertColumn: 'Spalte einfügen',
|
||||
deleteRow: 'Zeile löschen',
|
||||
deleteColumn: 'Spalte löschen',
|
||||
deleteCell: 'Zelle löschen',
|
||||
deleteCellText: 'Zellentext löschen',
|
||||
},
|
||||
format: {
|
||||
normal: 'Regulär',
|
||||
text: 'Text',
|
||||
number: 'Nummer',
|
||||
percent: 'Prozent',
|
||||
rmb: 'RMB',
|
||||
usd: 'USD',
|
||||
date: 'Datum',
|
||||
time: 'Termin',
|
||||
datetime: 'Datum Termin',
|
||||
duration: 'Dauer',
|
||||
},
|
||||
formula: {
|
||||
sum: 'Summe',
|
||||
average: 'Durchschnittliche',
|
||||
max: 'Max',
|
||||
min: 'Min',
|
||||
concat: 'Concat',
|
||||
},
|
||||
};
|
||||
149
OrangeFormsOpen-VUE3/src/components/SpreadSheet/locale/en.js
Normal file
149
OrangeFormsOpen-VUE3/src/components/SpreadSheet/locale/en.js
Normal file
@@ -0,0 +1,149 @@
|
||||
export default {
|
||||
toolbar: {
|
||||
undo: 'Undo',
|
||||
redo: 'Redo',
|
||||
print: 'Print',
|
||||
paintformat: 'Paint format',
|
||||
clearformat: 'Clear format',
|
||||
format: 'Format',
|
||||
fontName: 'Font',
|
||||
fontSize: 'Font size',
|
||||
fontBold: 'Font bold',
|
||||
fontItalic: 'Font italic',
|
||||
underline: 'Underline',
|
||||
strike: 'Strike',
|
||||
color: 'Text color',
|
||||
bgcolor: 'Fill color',
|
||||
border: 'Borders',
|
||||
merge: 'Merge cells',
|
||||
align: 'Horizontal align',
|
||||
valign: 'Vertical align',
|
||||
textwrap: 'Text wrapping',
|
||||
freeze: 'Freeze cell',
|
||||
autofilter: 'Filter',
|
||||
formula: 'Functions',
|
||||
more: 'More',
|
||||
},
|
||||
contextmenu: {
|
||||
copy: 'Copy',
|
||||
cut: 'Cut',
|
||||
paste: 'Paste',
|
||||
pasteValue: 'Paste values only',
|
||||
pasteFormat: 'Paste format only',
|
||||
hide: 'Hide',
|
||||
insertRow: 'Insert row',
|
||||
insertColumn: 'Insert column',
|
||||
deleteSheet: 'Delete',
|
||||
deleteRow: 'Delete row',
|
||||
deleteColumn: 'Delete column',
|
||||
deleteCell: 'Delete cell',
|
||||
deleteCellText: 'Delete cell text',
|
||||
validation: 'Data validations',
|
||||
cellprintable: 'Enable export',
|
||||
cellnonprintable: 'Disable export',
|
||||
celleditable: 'Enable editing',
|
||||
cellnoneditable: 'Disable editing',
|
||||
},
|
||||
print: {
|
||||
size: 'Paper size',
|
||||
orientation: 'Page orientation',
|
||||
orientations: ['Landscape', 'Portrait'],
|
||||
},
|
||||
format: {
|
||||
normal: 'Normal',
|
||||
text: 'Plain Text',
|
||||
number: 'Number',
|
||||
percent: 'Percent',
|
||||
rmb: 'RMB',
|
||||
usd: 'USD',
|
||||
eur: 'EUR',
|
||||
date: 'Date',
|
||||
time: 'Time',
|
||||
datetime: 'Date time',
|
||||
duration: 'Duration',
|
||||
},
|
||||
formula: {
|
||||
sum: 'Sum',
|
||||
average: 'Average',
|
||||
max: 'Max',
|
||||
min: 'Min',
|
||||
_if: 'IF',
|
||||
and: 'AND',
|
||||
or: 'OR',
|
||||
concat: 'Concat',
|
||||
},
|
||||
validation: {
|
||||
required: 'it must be required',
|
||||
notMatch: 'it not match its validation rule',
|
||||
between: 'it is between {} and {}',
|
||||
notBetween: 'it is not between {} and {}',
|
||||
notIn: 'it is not in list',
|
||||
equal: 'it equal to {}',
|
||||
notEqual: 'it not equal to {}',
|
||||
lessThan: 'it less than {}',
|
||||
lessThanEqual: 'it less than or equal to {}',
|
||||
greaterThan: 'it greater than {}',
|
||||
greaterThanEqual: 'it greater than or equal to {}',
|
||||
},
|
||||
error: {
|
||||
pasteForMergedCell: 'Unable to do this for merged cells',
|
||||
},
|
||||
calendar: {
|
||||
weeks: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
months: [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
],
|
||||
},
|
||||
button: {
|
||||
next: 'Next',
|
||||
cancel: 'Cancel',
|
||||
remove: 'Remove',
|
||||
save: 'Save',
|
||||
ok: 'OK',
|
||||
},
|
||||
sort: {
|
||||
desc: 'Sort Z -> A',
|
||||
asc: 'Sort A -> Z',
|
||||
},
|
||||
filter: {
|
||||
empty: 'empty',
|
||||
},
|
||||
dataValidation: {
|
||||
mode: 'Mode',
|
||||
range: 'Cell Range',
|
||||
criteria: 'Criteria',
|
||||
modeType: {
|
||||
cell: 'Cell',
|
||||
column: 'Colun',
|
||||
row: 'Row',
|
||||
},
|
||||
type: {
|
||||
list: 'List',
|
||||
number: 'Number',
|
||||
date: 'Date',
|
||||
phone: 'Phone',
|
||||
email: 'Email',
|
||||
},
|
||||
operator: {
|
||||
be: 'between',
|
||||
nbe: 'not betwwen',
|
||||
lt: 'less than',
|
||||
lte: 'less than or equal to',
|
||||
gt: 'greater than',
|
||||
gte: 'greater than or equal to',
|
||||
eq: 'equal to',
|
||||
neq: 'not equal to',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
import en from './en.js';
|
||||
|
||||
// Defines the fallback language as English
|
||||
let $languages = ['en'];
|
||||
const $messages = {
|
||||
en,
|
||||
};
|
||||
|
||||
function translate(key, messages) {
|
||||
if (messages) {
|
||||
// Return the translation from the first language in the languages array
|
||||
// that has a value for the provided key.
|
||||
for (const lang of $languages) {
|
||||
if (!messages[lang]) break;
|
||||
|
||||
let message = messages[lang];
|
||||
|
||||
// Splits the key at '.' except where escaped as '\.'
|
||||
const keys = key.match(/(?:\\.|[^.])+/g);
|
||||
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
const property = keys[i];
|
||||
const value = message[property];
|
||||
|
||||
// If value doesn't exist, try next language
|
||||
if (!value) break;
|
||||
|
||||
if (i === keys.length - 1) return value;
|
||||
|
||||
// Move down to the next level of the messages object
|
||||
message = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function t(key) {
|
||||
let v = translate(key, $messages);
|
||||
if (!v && window && window.x_spreadsheet && window.x_spreadsheet.$messages) {
|
||||
v = translate(key, window.x_spreadsheet.$messages);
|
||||
}
|
||||
return v || '';
|
||||
}
|
||||
|
||||
function tf(key) {
|
||||
return () => t(key);
|
||||
}
|
||||
|
||||
// If clearLangList is set to false, lang will be added to the front of the
|
||||
// languages array. The languages in the language array are searched in order
|
||||
// to find a translation. This allows the use of other languages as a fallback
|
||||
// if lang is missing some keys. The language array is preloaded with English.
|
||||
// To set the languages array to only include lang, set clearLangList to true.
|
||||
function locale(lang, message, clearLangList = false) {
|
||||
if (clearLangList) {
|
||||
$languages = [lang];
|
||||
} else {
|
||||
// Append to front of array.
|
||||
// Translation method will use the first language in the list that has a
|
||||
// matching key.
|
||||
$languages.unshift(lang);
|
||||
}
|
||||
|
||||
if (message) {
|
||||
$messages[lang] = message;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
t,
|
||||
};
|
||||
|
||||
export { locale, t, tf };
|
||||
57
OrangeFormsOpen-VUE3/src/components/SpreadSheet/locale/nl.js
Normal file
57
OrangeFormsOpen-VUE3/src/components/SpreadSheet/locale/nl.js
Normal file
@@ -0,0 +1,57 @@
|
||||
export default {
|
||||
toolbar: {
|
||||
undo: 'Ongedaan maken',
|
||||
redo: 'Opnieuw uitvoeren',
|
||||
paintformat: 'Opmaak kopiëren/plakken',
|
||||
clearformat: 'Opmaak wissen',
|
||||
format: 'Opmaak',
|
||||
font: 'Lettertype',
|
||||
fontSize: 'Tekengrootte',
|
||||
fontBold: 'Vet',
|
||||
fontItalic: 'Cursief',
|
||||
underline: 'Onderstrepen',
|
||||
strike: 'Doorstrepen',
|
||||
textColor: 'Tekstkleur',
|
||||
fillColor: 'Opvulkleur',
|
||||
border: 'Randen',
|
||||
merge: 'Cellen samenvoegen',
|
||||
align: 'Horizontale uitlijning',
|
||||
valign: 'Verticale uitlijning',
|
||||
textwrap: 'Terugloop',
|
||||
freeze: 'Cel bevriezen',
|
||||
formula: 'Functies',
|
||||
more: 'Meer',
|
||||
},
|
||||
contextmenu: {
|
||||
copy: 'Kopiëren',
|
||||
cut: 'Knippen',
|
||||
paste: 'Plakken',
|
||||
pasteValue: 'Alleen waarden plakken',
|
||||
pasteFormat: 'Alleen opmaak plakken',
|
||||
insertRow: 'Rij invoegen',
|
||||
insertColumn: 'Kolom invoegen',
|
||||
deleteRow: 'Rij verwijderen',
|
||||
deleteColumn: 'Kolom verwijderen',
|
||||
deleteCell: 'Cel verwijderen',
|
||||
deleteCellText: 'Celtekst verwijderen',
|
||||
},
|
||||
format: {
|
||||
normal: 'Standaard',
|
||||
text: 'Tekst',
|
||||
number: 'Nummer',
|
||||
percent: 'Percentage',
|
||||
rmb: 'RMB',
|
||||
usd: 'USD',
|
||||
date: 'Datum',
|
||||
time: 'Tijdstip',
|
||||
datetime: 'Datum tijd',
|
||||
duration: 'Duratie',
|
||||
},
|
||||
formula: {
|
||||
sum: 'Som',
|
||||
average: 'Gemiddelde',
|
||||
max: 'Max',
|
||||
min: 'Min',
|
||||
concat: 'Concat',
|
||||
},
|
||||
};
|
||||
149
OrangeFormsOpen-VUE3/src/components/SpreadSheet/locale/zh-cn.js
Normal file
149
OrangeFormsOpen-VUE3/src/components/SpreadSheet/locale/zh-cn.js
Normal file
@@ -0,0 +1,149 @@
|
||||
export default {
|
||||
toolbar: {
|
||||
undo: '撤销',
|
||||
redo: '恢复',
|
||||
print: '打印',
|
||||
paintformat: '格式刷',
|
||||
clearformat: '清除格式',
|
||||
format: '数据格式',
|
||||
fontName: '字体',
|
||||
fontSize: '字号',
|
||||
fontBold: '加粗',
|
||||
fontItalic: '倾斜',
|
||||
underline: '下划线',
|
||||
strike: '删除线',
|
||||
color: '字体颜色',
|
||||
bgcolor: '填充颜色',
|
||||
border: '边框',
|
||||
merge: '合并单元格',
|
||||
align: '水平对齐',
|
||||
valign: '垂直对齐',
|
||||
textwrap: '自动换行',
|
||||
freeze: '冻结',
|
||||
autofilter: '自动筛选',
|
||||
formula: '函数',
|
||||
more: '更多',
|
||||
},
|
||||
contextmenu: {
|
||||
copy: '复制',
|
||||
cut: '剪切',
|
||||
paste: '粘贴',
|
||||
pasteValue: '粘贴数据',
|
||||
pasteFormat: '粘贴格式',
|
||||
hide: '隐藏',
|
||||
insertRow: '插入行',
|
||||
insertColumn: '插入列',
|
||||
deleteSheet: '删除',
|
||||
deleteRow: '删除行',
|
||||
deleteColumn: '删除列',
|
||||
deleteCell: '删除',
|
||||
deleteCellText: '删除数据',
|
||||
validation: '数据验证',
|
||||
cellprintable: '可打印',
|
||||
cellnonprintable: '不可打印',
|
||||
celleditable: '可编辑',
|
||||
cellnoneditable: '不可编辑',
|
||||
},
|
||||
print: {
|
||||
size: '纸张大小',
|
||||
orientation: '方向',
|
||||
orientations: ['横向', '纵向'],
|
||||
},
|
||||
format: {
|
||||
normal: '正常',
|
||||
text: '文本',
|
||||
number: '数值',
|
||||
percent: '百分比',
|
||||
rmb: '人民币',
|
||||
usd: '美元',
|
||||
eur: '欧元',
|
||||
date: '短日期',
|
||||
time: '时间',
|
||||
datetime: '长日期',
|
||||
duration: '持续时间',
|
||||
},
|
||||
formula: {
|
||||
sum: '求和',
|
||||
average: '求平均值',
|
||||
max: '求最大值',
|
||||
min: '求最小值',
|
||||
concat: '字符拼接',
|
||||
_if: '条件判断',
|
||||
and: '和',
|
||||
or: '或',
|
||||
},
|
||||
validation: {
|
||||
required: '此值必填',
|
||||
notMatch: '此值不匹配验证规则',
|
||||
between: '此值应在 {} 和 {} 之间',
|
||||
notBetween: '此值不应在 {} 和 {} 之间',
|
||||
notIn: '此值不在列表中',
|
||||
equal: '此值应该等于 {}',
|
||||
notEqual: '此值不应该等于 {}',
|
||||
lessThan: '此值应该小于 {}',
|
||||
lessThanEqual: '此值应该小于等于 {}',
|
||||
greaterThan: '此值应该大于 {}',
|
||||
greaterThanEqual: '此值应该大于等于 {}',
|
||||
},
|
||||
error: {
|
||||
pasteForMergedCell: '无法对合并的单元格执行此操作',
|
||||
},
|
||||
calendar: {
|
||||
weeks: ['日', '一', '二', '三', '四', '五', '六'],
|
||||
months: [
|
||||
'一月',
|
||||
'二月',
|
||||
'三月',
|
||||
'四月',
|
||||
'五月',
|
||||
'六月',
|
||||
'七月',
|
||||
'八月',
|
||||
'九月',
|
||||
'十月',
|
||||
'十一月',
|
||||
'十二月',
|
||||
],
|
||||
},
|
||||
button: {
|
||||
next: '下一步',
|
||||
cancel: '取消',
|
||||
remove: '删除',
|
||||
save: '保存',
|
||||
ok: '确认',
|
||||
},
|
||||
sort: {
|
||||
desc: '降序',
|
||||
asc: '升序',
|
||||
},
|
||||
filter: {
|
||||
empty: '空白',
|
||||
},
|
||||
dataValidation: {
|
||||
mode: '模式',
|
||||
range: '单元区间',
|
||||
criteria: '条件',
|
||||
modeType: {
|
||||
cell: '单元格',
|
||||
column: '列模式',
|
||||
row: '行模式',
|
||||
},
|
||||
type: {
|
||||
list: '列表',
|
||||
number: '数字',
|
||||
date: '日期',
|
||||
phone: '手机号',
|
||||
email: '电子邮件',
|
||||
},
|
||||
operator: {
|
||||
be: '在区间',
|
||||
nbe: '不在区间',
|
||||
lt: '小于',
|
||||
lte: '小于等于',
|
||||
gt: '大于',
|
||||
gte: '大于等于',
|
||||
eq: '等于',
|
||||
neq: '不等于',
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user