parent
db104c20ce
commit
5df5fb5be6
@ -0,0 +1,53 @@
|
|||||||
|
import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
|
||||||
|
import Spreadsheet from './x-sheet';
|
||||||
|
import { css } from '@nocobase/client';
|
||||||
|
import { demoData } from './x-sheet/demo';
|
||||||
|
|
||||||
|
export type SheetRef = {
|
||||||
|
getData: () => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SheetProps = {
|
||||||
|
data?: any;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Sheet = forwardRef<SheetRef, SheetProps>(({ data }, ref) => {
|
||||||
|
const workbookRef = useRef<Spreadsheet>(null);
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
getData,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
if (!workbookRef.current) {
|
||||||
|
throw new Error('Workbook is not initialized');
|
||||||
|
}
|
||||||
|
return workbookRef.current.getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const workbook = new Spreadsheet('#sheet', {
|
||||||
|
view: {
|
||||||
|
height: () => containerRef.current.offsetHeight,
|
||||||
|
width: () => containerRef.current.offsetWidth,
|
||||||
|
},
|
||||||
|
}).loadData(demoData);
|
||||||
|
workbookRef.current = workbook;
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
id="sheet"
|
||||||
|
className={css`
|
||||||
|
height: 800px;
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Sheet.displayName = 'Sheet';
|
||||||
|
|
||||||
|
export default Sheet;
|
@ -1,3 +1,8 @@
|
|||||||
|
export * from './GroupBlockConfigure/GroupBlockConfigure';
|
||||||
|
export * from './PDFViewer';
|
||||||
|
export * from './Sheet';
|
||||||
|
export * from './SignatureInput';
|
||||||
|
export * from './SignaturePad';
|
||||||
export * from './custom-components/CustomAssociatedField';
|
export * from './custom-components/CustomAssociatedField';
|
||||||
export * from './custom-components/CustomComponentDispatcher';
|
export * from './custom-components/CustomComponentDispatcher';
|
||||||
export * from './custom-components/CustomField';
|
export * from './custom-components/CustomField';
|
||||||
@ -7,7 +12,3 @@ export * from './fields/Expression';
|
|||||||
export * from './system/MobileLink';
|
export * from './system/MobileLink';
|
||||||
export * from './system/Notifications';
|
export * from './system/Notifications';
|
||||||
export * from './system/OnlineUserProvider';
|
export * from './system/OnlineUserProvider';
|
||||||
export * from './PDFViewer';
|
|
||||||
export * from './SignatureInput';
|
|
||||||
export * from './SignaturePad';
|
|
||||||
export * from './GroupBlockConfigure/GroupBlockConfigure';
|
|
||||||
|
@ -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: 170 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 |
@ -0,0 +1,403 @@
|
|||||||
|
/* global window */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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,92 @@
|
|||||||
|
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,60 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import Icon from './icon';
|
||||||
|
import DropdownColor from './dropdown_color';
|
||||||
|
import DropdownLineType from './dropdown_linetype';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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 = () => {};
|
||||||
|
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,187 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import { bindClickoutside, unbindClickoutside } from './event';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
import Icon from './icon';
|
||||||
|
import FormInput from './form_input';
|
||||||
|
import Dropdown from './dropdown';
|
||||||
|
// Record: temp not used
|
||||||
|
// import { xtoast } from './message';
|
||||||
|
import { tf } from '../locale/locale';
|
||||||
|
|
||||||
|
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() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = () => {},
|
||||||
|
swapFunc = () => {},
|
||||||
|
deleteFunc = () => {},
|
||||||
|
updateFunc = () => {}) {
|
||||||
|
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 { Element } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
import { t } from '../locale/locale';
|
||||||
|
|
||||||
|
export default class Button extends Element {
|
||||||
|
// type: primary
|
||||||
|
constructor(title, type = '') {
|
||||||
|
super('div', `${cssPrefix}-button ${type}`);
|
||||||
|
this.child(t(`button.${title}`));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import Icon from './icon';
|
||||||
|
import { t } from '../locale/locale';
|
||||||
|
|
||||||
|
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 = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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,43 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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 = () => {};
|
||||||
|
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,100 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import { bindClickoutside, unbindClickoutside } from './event';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
import { tf } from '../locale/locale';
|
||||||
|
|
||||||
|
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 = () => {};
|
||||||
|
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,41 @@
|
|||||||
|
import Calendar from './calendar';
|
||||||
|
import { h } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,64 @@
|
|||||||
|
import { Element, h } from './element';
|
||||||
|
import { bindClickoutside, unbindClickoutside } from './event';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
export default class Dropdown extends Element {
|
||||||
|
constructor(title, width, showArrow, placement, ...children) {
|
||||||
|
super('div', `${cssPrefix}-dropdown ${placement}`);
|
||||||
|
this.title = title;
|
||||||
|
this.change = () => {};
|
||||||
|
this.headerClick = () => {};
|
||||||
|
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,25 @@
|
|||||||
|
import Dropdown from './dropdown';
|
||||||
|
import { h } from './element';
|
||||||
|
import Icon from './icon';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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';
|
||||||
|
import Icon from './icon';
|
||||||
|
import BorderPalette from './border_palette';
|
||||||
|
|
||||||
|
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';
|
||||||
|
import Icon from './icon';
|
||||||
|
import ColorPalette from './color_palette';
|
||||||
|
|
||||||
|
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,16 @@
|
|||||||
|
import Dropdown from './dropdown';
|
||||||
|
import { h } from './element';
|
||||||
|
import { baseFonts } from '../core/font';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,16 @@
|
|||||||
|
import Dropdown from './dropdown';
|
||||||
|
import { h } from './element';
|
||||||
|
import { fontSizes } from '../core/font';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,36 @@
|
|||||||
|
import Dropdown from './dropdown';
|
||||||
|
import { h } from './element';
|
||||||
|
import { baseFormats } from '../core/format';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,17 @@
|
|||||||
|
import Dropdown from './dropdown';
|
||||||
|
import Icon from './icon';
|
||||||
|
import { h } from './element';
|
||||||
|
import { baseFormulas } from '../core/formula';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,33 @@
|
|||||||
|
import Dropdown from './dropdown';
|
||||||
|
import { h } from './element';
|
||||||
|
import Icon from './icon';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,283 @@
|
|||||||
|
//* global window */
|
||||||
|
import { h } from './element';
|
||||||
|
import Suggest from './suggest';
|
||||||
|
import Datepicker from './datepicker';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
// import { mouseMoveUp } from '../event';
|
||||||
|
|
||||||
|
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', () => {})
|
||||||
|
.on('keydown', evt => keydownEventHandler.call(this, evt)),
|
||||||
|
this.textlineEl = h('div', 'textline'),
|
||||||
|
this.suggest.el,
|
||||||
|
this.datepicker.el,
|
||||||
|
)
|
||||||
|
.on('mousemove.stop', () => {})
|
||||||
|
.on('mousedown.stop', () => {});
|
||||||
|
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 = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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,275 @@
|
|||||||
|
/* global document */
|
||||||
|
/* global window */
|
||||||
|
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,153 @@
|
|||||||
|
/* global window */
|
||||||
|
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,69 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
import { t } from '../locale/locale';
|
||||||
|
|
||||||
|
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,27 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
export default class FormInput {
|
||||||
|
constructor(width, hint) {
|
||||||
|
this.vchange = () => {};
|
||||||
|
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,39 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import Suggest from './suggest';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
export default class FormSelect {
|
||||||
|
constructor(key, items, width, getTitle = it => it, change = () => {}) {
|
||||||
|
this.key = key;
|
||||||
|
this.getTitle = getTitle;
|
||||||
|
this.vchange = () => {};
|
||||||
|
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 { Element, h } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,32 @@
|
|||||||
|
/* global document */
|
||||||
|
import { h } from './element';
|
||||||
|
import Icon from './icon';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,44 @@
|
|||||||
|
/* global document */
|
||||||
|
/* global window */
|
||||||
|
import { h } from './element';
|
||||||
|
import Icon from './icon';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
import { bind, unbind } from './event';
|
||||||
|
|
||||||
|
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,227 @@
|
|||||||
|
import Modal from './modal';
|
||||||
|
import FormInput from './form_input';
|
||||||
|
import FormSelect from './form_select';
|
||||||
|
import FormField from './form_field';
|
||||||
|
import Button from './button';
|
||||||
|
import { t } from '../locale/locale';
|
||||||
|
import { h } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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 = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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,203 @@
|
|||||||
|
/* global window document */
|
||||||
|
import { h } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
import Button from './button';
|
||||||
|
import { Draw } from '../canvas/draw';
|
||||||
|
import { renderCell } from './table';
|
||||||
|
import { t } from '../locale/locale';
|
||||||
|
|
||||||
|
// 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.90],
|
||||||
|
['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,114 @@
|
|||||||
|
/* global window */
|
||||||
|
import { h } from './element';
|
||||||
|
import { mouseMoveUp } from './event';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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 = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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,45 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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', () => {})
|
||||||
|
.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,413 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
import { CellRange } from '../core/cell_range';
|
||||||
|
|
||||||
|
const selectorHeightBorderWidth = 2 * 2 - 1;
|
||||||
|
let startZIndex = 10;
|
||||||
|
|
||||||
|
class SelectorElement {
|
||||||
|
constructor(useHideInput = false, autoFocus = true) {
|
||||||
|
this.useHideInput = useHideInput;
|
||||||
|
this.autoFocus = autoFocus;
|
||||||
|
this.inputChange = () => {};
|
||||||
|
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 = () => {};
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,141 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import Button from './button';
|
||||||
|
import { bindClickoutside, unbindClickoutside } from './event';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
import { t } from '../locale/locale';
|
||||||
|
|
||||||
|
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,135 @@
|
|||||||
|
import { h } from './element';
|
||||||
|
import { bindClickoutside, unbindClickoutside } from './event';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,373 @@
|
|||||||
|
import { stringAt } from '../core/alphabet';
|
||||||
|
import { getFontSizePxByPt } from '../core/font';
|
||||||
|
import _cell from '../core/cell';
|
||||||
|
import { formulam } from '../core/formula';
|
||||||
|
import { formatm } from '../core/format';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Draw, DrawBox, thinLineWidth, npx,
|
||||||
|
} from '../canvas/draw';
|
||||||
|
// gobal var
|
||||||
|
const cellPaddingWidth = 5;
|
||||||
|
const tableFixedHeaderCleanStyle = { fillStyle: '#f4f5f8' };
|
||||||
|
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) {
|
||||||
|
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, () => {
|
||||||
|
// 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) {
|
||||||
|
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);
|
||||||
|
}, 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);
|
||||||
|
} 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(75, 137, 255, 0.08)' })
|
||||||
|
.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 (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 } = this;
|
||||||
|
draw.save();
|
||||||
|
// left-top-cell
|
||||||
|
draw.attr({ fillStyle: '#f4f5f8' })
|
||||||
|
.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);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
// 5
|
||||||
|
renderFreezeHighlightLine.call(this, fw, fh, tx, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.draw.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Table;
|
@ -0,0 +1,225 @@
|
|||||||
|
/* global window */
|
||||||
|
import { h } from './element';
|
||||||
|
import { bind } from './event';
|
||||||
|
import tooltip from './tooltip';
|
||||||
|
import DropdownFont from './dropdown_font';
|
||||||
|
import DropdownFontSize from './dropdown_fontsize';
|
||||||
|
import DropdownFormat from './dropdown_format';
|
||||||
|
import DropdownFormula from './dropdown_formula';
|
||||||
|
import DropdownColor from './dropdown_color';
|
||||||
|
import DropdownAlign from './dropdown_align';
|
||||||
|
import DropdownBorder from './dropdown_border';
|
||||||
|
import Dropdown from './dropdown';
|
||||||
|
import Icon from './icon';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
import { t } from '../locale/locale';
|
||||||
|
|
||||||
|
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 = () => {}) {
|
||||||
|
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 = () => {};
|
||||||
|
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 DropdownItem from './dropdown_item';
|
||||||
|
import DropdownAlign from '../dropdown_align';
|
||||||
|
|
||||||
|
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,9 @@
|
|||||||
|
import ToggleItem from './toggle_item';
|
||||||
|
|
||||||
|
export default class Autofilter extends ToggleItem {
|
||||||
|
constructor() {
|
||||||
|
super('autofilter');
|
||||||
|
}
|
||||||
|
|
||||||
|
setState() {}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import ToggleItem from './toggle_item';
|
||||||
|
|
||||||
|
export default class Bold extends ToggleItem {
|
||||||
|
constructor() {
|
||||||
|
super('font-bold', 'Ctrl+B');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import DropdownItem from './dropdown_item';
|
||||||
|
import DropdownBorder from '../dropdown_border';
|
||||||
|
|
||||||
|
export default class Border extends DropdownItem {
|
||||||
|
constructor() {
|
||||||
|
super('border');
|
||||||
|
}
|
||||||
|
|
||||||
|
dropdown() {
|
||||||
|
return new DropdownBorder();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import IconItem from './icon_item';
|
||||||
|
|
||||||
|
export default class Clearformat extends IconItem {
|
||||||
|
constructor() {
|
||||||
|
super('clearformat');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import Item from './item';
|
||||||
|
|
||||||
|
export default class DropdownItem extends Item {
|
||||||
|
dropdown() {}
|
||||||
|
|
||||||
|
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 DropdownItem from './dropdown_item';
|
||||||
|
import DropdownColor from '../dropdown_color';
|
||||||
|
|
||||||
|
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 DropdownItem from './dropdown_item';
|
||||||
|
import DropdownFont from '../dropdown_font';
|
||||||
|
|
||||||
|
export default class Font extends DropdownItem {
|
||||||
|
constructor() {
|
||||||
|
super('font-name');
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue(it) {
|
||||||
|
return it.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
dropdown() {
|
||||||
|
return new DropdownFont();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import DropdownItem from './dropdown_item';
|
||||||
|
import DropdownFontsize from '../dropdown_fontsize';
|
||||||
|
|
||||||
|
export default class Format extends DropdownItem {
|
||||||
|
constructor() {
|
||||||
|
super('font-size');
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue(it) {
|
||||||
|
return it.pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
dropdown() {
|
||||||
|
return new DropdownFontsize();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import DropdownItem from './dropdown_item';
|
||||||
|
import DropdownFormat from '../dropdown_format';
|
||||||
|
|
||||||
|
export default class Format extends DropdownItem {
|
||||||
|
constructor() {
|
||||||
|
super('format');
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue(it) {
|
||||||
|
return it.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
dropdown() {
|
||||||
|
return new DropdownFormat();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import DropdownItem from './dropdown_item';
|
||||||
|
import DropdownFormula from '../dropdown_formula';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
export default class Freeze extends ToggleItem {
|
||||||
|
constructor() {
|
||||||
|
super('freeze');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import Item from './item';
|
||||||
|
import Icon from '../icon';
|
||||||
|
|
||||||
|
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,249 @@
|
|||||||
|
/* global window */
|
||||||
|
|
||||||
|
import Align from './align';
|
||||||
|
import Valign from './valign';
|
||||||
|
import Autofilter from './autofilter';
|
||||||
|
import Bold from './bold';
|
||||||
|
import Italic from './italic';
|
||||||
|
import Strike from './strike';
|
||||||
|
import Underline from './underline';
|
||||||
|
import Border from './border';
|
||||||
|
import Clearformat from './clearformat';
|
||||||
|
import Paintformat from './paintformat';
|
||||||
|
import TextColor from './text_color';
|
||||||
|
import FillColor from './fill_color';
|
||||||
|
import FontSize from './font_size';
|
||||||
|
import Font from './font';
|
||||||
|
import Format from './format';
|
||||||
|
import Formula from './formula';
|
||||||
|
import Freeze from './freeze';
|
||||||
|
import Merge from './merge';
|
||||||
|
import Redo from './redo';
|
||||||
|
import Undo from './undo';
|
||||||
|
import Print from './print';
|
||||||
|
import Textwrap from './textwrap';
|
||||||
|
import More from './more';
|
||||||
|
import Item from './item';
|
||||||
|
|
||||||
|
import { h } from '../element';
|
||||||
|
import { cssPrefix } from '../../config';
|
||||||
|
import { bind } from '../event';
|
||||||
|
|
||||||
|
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 = () => {};
|
||||||
|
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';
|
||||||
|
|
||||||
|
export default class Italic extends ToggleItem {
|
||||||
|
constructor() {
|
||||||
|
super('font-italic', 'Ctrl+I');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
import { cssPrefix } from '../../config';
|
||||||
|
import tooltip from '../tooltip';
|
||||||
|
import { h } from '../element';
|
||||||
|
import { t } from '../../locale/locale';
|
||||||
|
|
||||||
|
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 = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import ToggleItem from './toggle_item';
|
||||||
|
|
||||||
|
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';
|
||||||
|
import DropdownItem from './dropdown_item';
|
||||||
|
|
||||||
|
import { cssPrefix } from '../../config';
|
||||||
|
import { h } from '../element';
|
||||||
|
import Icon from '../icon';
|
||||||
|
|
||||||
|
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,9 @@
|
|||||||
|
import ToggleItem from './toggle_item';
|
||||||
|
|
||||||
|
export default class Paintformat extends ToggleItem {
|
||||||
|
constructor() {
|
||||||
|
super('paintformat');
|
||||||
|
}
|
||||||
|
|
||||||
|
setState() {}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import IconItem from './icon_item';
|
||||||
|
|
||||||
|
export default class Print extends IconItem {
|
||||||
|
constructor() {
|
||||||
|
super('print', 'Ctrl+P');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import IconItem from './icon_item';
|
||||||
|
|
||||||
|
export default class Redo extends IconItem {
|
||||||
|
constructor() {
|
||||||
|
super('redo', 'Ctrl+Y');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import ToggleItem from './toggle_item';
|
||||||
|
|
||||||
|
export default class Strike extends ToggleItem {
|
||||||
|
constructor() {
|
||||||
|
super('strike', 'Ctrl+U');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import DropdownItem from './dropdown_item';
|
||||||
|
import DropdownColor from '../dropdown_color';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
export default class Textwrap extends ToggleItem {
|
||||||
|
constructor() {
|
||||||
|
super('textwrap');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import Item from './item';
|
||||||
|
import Icon from '../icon';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
export default class Underline extends ToggleItem {
|
||||||
|
constructor() {
|
||||||
|
super('underline', 'Ctrl+U');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import IconItem from './icon_item';
|
||||||
|
|
||||||
|
export default class Undo extends IconItem {
|
||||||
|
constructor() {
|
||||||
|
super('undo', 'Ctrl+Z');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import DropdownItem from './dropdown_item';
|
||||||
|
import DropdownAlign from '../dropdown_align';
|
||||||
|
|
||||||
|
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,31 @@
|
|||||||
|
/* global document */
|
||||||
|
import { h } from './element';
|
||||||
|
import { bind } from './event';
|
||||||
|
import { cssPrefix } from '../config';
|
||||||
|
|
||||||
|
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,7 @@
|
|||||||
|
/* global window */
|
||||||
|
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]
|
||||||
|
*/
|
@ -0,0 +1,91 @@
|
|||||||
|
import './_.prototypes';
|
||||||
|
|
||||||
|
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';
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,226 @@
|
|||||||
|
import { expr2xy, xy2expr } from './alphabet';
|
||||||
|
import { numberCalc } from './helper';
|
||||||
|
|
||||||
|
// 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,224 @@
|
|||||||
|
import { xy2expr, expr2xy } from './alphabet';
|
||||||
|
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
import helper from './helper';
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,76 @@
|
|||||||
|
// docs
|
||||||
|
import './_.prototypes';
|
||||||
|
|
||||||
|
/** 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,
|
||||||
|
};
|
@ -0,0 +1,110 @@
|
|||||||
|
import { tf } from '../locale/locale';
|
||||||
|
|
||||||
|
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,98 @@
|
|||||||
|
/**
|
||||||
|
formula:
|
||||||
|
key
|
||||||
|
title
|
||||||
|
render
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @typedef {object} Formula
|
||||||
|
* @property {string} key
|
||||||
|
* @property {function} title
|
||||||
|
* @property {function} render
|
||||||
|
*/
|
||||||
|
import { tf } from '../locale/locale';
|
||||||
|
import { numberCalc } from './helper';
|
||||||
|
|
||||||
|
/** @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,
|
||||||
|
};
|
@ -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';
|
||||||
|
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
import { CellRange } from './cell_range';
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
@ -0,0 +1,356 @@
|
|||||||
|
import helper from './helper';
|
||||||
|
import { expr2expr } from './alphabet';
|
||||||
|
|
||||||
|
class Rows {
|
||||||
|
constructor({ len, height }) {
|
||||||
|
this._ = {};
|
||||||
|
this.len = len;
|
||||||
|
// default row height
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = () => {}) {
|
||||||
|
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';
|
||||||
|
|
||||||
|
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,139 @@
|
|||||||
|
import Validator from './validator';
|
||||||
|
import { CellRange } from './cell_range';
|
||||||
|
|
||||||
|
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,136 @@
|
|||||||
|
import { t } from '../locale/locale';
|
||||||
|
import helper from './helper';
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,597 @@
|
|||||||
|
export const demoData = [
|
||||||
|
{
|
||||||
|
name: 'sheet2',
|
||||||
|
freeze: 'A1',
|
||||||
|
styles: [
|
||||||
|
{
|
||||||
|
border: {
|
||||||
|
bottom: ['thin', '#000'],
|
||||||
|
top: ['thin', '#000'],
|
||||||
|
left: ['thin', '#000'],
|
||||||
|
right: ['thin', '#000'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
merges: ['B23:B34', 'B18:B22', 'B7:B17', 'B1:B6', 'A1:A34'],
|
||||||
|
rows: {
|
||||||
|
'0': {
|
||||||
|
cells: {
|
||||||
|
'0': {
|
||||||
|
style: 0,
|
||||||
|
merge: [33, 0],
|
||||||
|
text: '临平厂房',
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
text: '一、1#厂房内支撑脚手架',
|
||||||
|
style: 0,
|
||||||
|
merge: [5, 0],
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
text: '1层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '8 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '6687 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '2层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '5 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '6848 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '3层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '5 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '6848 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '4层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '5 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '6338 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '5层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '5 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '6338 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'5': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '6层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '5 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '6338 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'6': {
|
||||||
|
cells: {
|
||||||
|
'1': {
|
||||||
|
text: '二、2#厂房内支撑脚手架',
|
||||||
|
style: 0,
|
||||||
|
merge: [10, 0],
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
text: '1层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '7.90 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4149.04 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'7': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '2层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4317.28 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'8': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '3层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4317.28 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'9': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '4层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4317.28 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'10': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '5层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4317.28 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'11': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '6层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4317.28 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'12': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '7层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4317.28 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'13': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '8层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4317.28 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'14': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '9层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4317.28 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'15': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '10层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4317.28 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'16': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '屋顶层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '2.85 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4260.88 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'17': {
|
||||||
|
cells: {
|
||||||
|
'1': {
|
||||||
|
text: '三、3#厂房内支撑脚手架',
|
||||||
|
style: 0,
|
||||||
|
merge: [4, 0],
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
text: '1层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '6.00 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '3964.34 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'18': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '2层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '5.40 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4908.63 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'19': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '3层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '6.00 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4722.95 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'20': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '4层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '6.00 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '4729.63 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'21': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '屋顶层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '3.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '250.00 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'22': {
|
||||||
|
cells: {
|
||||||
|
'1': {
|
||||||
|
text: '四、中建技智选酒店内支撑脚手架',
|
||||||
|
style: 0,
|
||||||
|
merge: [11, 0],
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
text: '1层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '7.90 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1626.50 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'23': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '2层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1598.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'24': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '3层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1562.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'25': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '4层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1562.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'26': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '5层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1562.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'27': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '6层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1562.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'28': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '7层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1562.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'29': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '8层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1562.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'30': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '9层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1562.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'31': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '10层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1562.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'32': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '11层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '4.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '1252.40 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'33': {
|
||||||
|
cells: {
|
||||||
|
'2': {
|
||||||
|
text: '屋顶层',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '3.50 ',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '250.00 \r',
|
||||||
|
style: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
len: 40,
|
||||||
|
},
|
||||||
|
cols: {
|
||||||
|
'1': {
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
len: 10,
|
||||||
|
},
|
||||||
|
validations: [],
|
||||||
|
autofilter: {},
|
||||||
|
},
|
||||||
|
];
|
201
packages/plugins/@hera/plugin-core/src/client/components/x-sheet/index.d.ts
vendored
Normal file
201
packages/plugins/@hera/plugin-core/src/client/components/x-sheet/index.d.ts
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
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;
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
/* global window, document */
|
||||||
|
import { h } from './component/element';
|
||||||
|
import DataProxy from './core/data_proxy';
|
||||||
|
import Sheet from './component/sheet';
|
||||||
|
import Bottombar from './component/bottombar';
|
||||||
|
import { cssPrefix } from './config';
|
||||||
|
import { locale } from './locale/locale';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Spreadsheet;
|
File diff suppressed because it is too large
Load Diff
@ -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',
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,136 @@
|
|||||||
|
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,80 @@
|
|||||||
|
/* global window */
|
||||||
|
import en from './en';
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
};
|
@ -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',
|
||||||
|
},
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user