mirror of
https://github.com/jacobtread/obsidian-timekeep.git
synced 2026-07-22 10:10:27 +00:00
Merge pull request #72 from jacobtread/refactor-pdf-make-renderer
Refactor pdf make renderer
This commit is contained in:
commit
f6f5ebb342
17 changed files with 624 additions and 1051 deletions
|
|
@ -3,30 +3,30 @@
|
|||
const process = require("process");
|
||||
const builtins = require("builtin-modules");
|
||||
|
||||
const prod = (process.argv[2] === "production");
|
||||
|
||||
const prod = process.argv[2] === "production";
|
||||
|
||||
module.exports = {
|
||||
platform: "browser",
|
||||
bundle: true,
|
||||
external: [
|
||||
"obsidian",
|
||||
"electron",
|
||||
"@codemirror/autocomplete",
|
||||
"@codemirror/collab",
|
||||
"@codemirror/commands",
|
||||
"@codemirror/language",
|
||||
"@codemirror/lint",
|
||||
"@codemirror/search",
|
||||
"@codemirror/state",
|
||||
"@codemirror/view",
|
||||
"@lezer/common",
|
||||
"@lezer/highlight",
|
||||
"@lezer/lr",
|
||||
...builtins],
|
||||
format: "cjs",
|
||||
target: "es2018",
|
||||
sourcemap: prod ? false : "inline",
|
||||
treeShaking: true,
|
||||
loader: { '.ttf': 'dataurl' },
|
||||
}
|
||||
platform: "browser",
|
||||
bundle: true,
|
||||
external: [
|
||||
"obsidian",
|
||||
"electron",
|
||||
"@codemirror/autocomplete",
|
||||
"@codemirror/collab",
|
||||
"@codemirror/commands",
|
||||
"@codemirror/language",
|
||||
"@codemirror/lint",
|
||||
"@codemirror/search",
|
||||
"@codemirror/state",
|
||||
"@codemirror/view",
|
||||
"@lezer/common",
|
||||
"@lezer/highlight",
|
||||
"@lezer/lr",
|
||||
...builtins,
|
||||
],
|
||||
format: "cjs",
|
||||
target: "es2018",
|
||||
sourcemap: prod ? false : "inline",
|
||||
treeShaking: true,
|
||||
loader: { ".ttf": "dataurl" },
|
||||
};
|
||||
|
|
|
|||
546
package-lock.json
generated
546
package-lock.json
generated
|
|
@ -9,8 +9,8 @@
|
|||
"version": "1.16.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-pdf/renderer": "^4.3.2",
|
||||
"moment": "^2.30.1",
|
||||
"pdfmake": "^0.3.7",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"uuid": "^11.0.5",
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
"devDependencies": {
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/pdfmake": "^0.3.2",
|
||||
"@types/react": "^19.2.4",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "8.57.2",
|
||||
|
|
@ -496,15 +497,6 @@
|
|||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
|
||||
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||
|
|
@ -1728,6 +1720,30 @@
|
|||
"@tybys/wasm-util": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/ciphers": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
|
||||
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
|
|
@ -1752,174 +1768,6 @@
|
|||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/fns": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.2.tgz",
|
||||
"integrity": "sha512-qTKGUf0iAMGg2+OsUcp9ffKnKi41RukM/zYIWMDJ4hRVYSr89Q7e3wSDW/Koqx3ea3Uy/z3h2y3wPX6Bdfxk6g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-pdf/font": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-4.0.4.tgz",
|
||||
"integrity": "sha512-8YtgGtL511txIEc9AjiilpZ7yjid8uCd8OGUl6jaL3LIHnrToUupSN4IzsMQpVTCMYiDLFnDNQzpZsOYtRS/Pg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-pdf/pdfkit": "^4.1.0",
|
||||
"@react-pdf/types": "^2.9.2",
|
||||
"fontkit": "^2.0.2",
|
||||
"is-url": "^1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/image": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-3.0.4.tgz",
|
||||
"integrity": "sha512-z0ogVQE0bKqgXQ5smgzIU857rLV7bMgVdrYsu3UfXDDLSzI7QPvzf6MFTFllX6Dx2rcsF13E01dqKPtJEM799g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-pdf/png-js": "^3.0.0",
|
||||
"jay-peg": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/layout": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-4.4.2.tgz",
|
||||
"integrity": "sha512-gNu2oh8MiGR+NJZYTJ4c4q0nWCESBI6rKFiodVhE7OeVAjtzZzd6l65wsN7HXdWJqOZD3ttD97iE+tf5SOd/Yg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-pdf/fns": "3.1.2",
|
||||
"@react-pdf/image": "^3.0.4",
|
||||
"@react-pdf/primitives": "^4.1.1",
|
||||
"@react-pdf/stylesheet": "^6.1.2",
|
||||
"@react-pdf/textkit": "^6.1.0",
|
||||
"@react-pdf/types": "^2.9.2",
|
||||
"emoji-regex-xs": "^1.0.0",
|
||||
"queue": "^6.0.1",
|
||||
"yoga-layout": "^3.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/pdfkit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-4.1.0.tgz",
|
||||
"integrity": "sha512-Wm/IOAv0h/U5Ra94c/PltFJGcpTUd/fwVMVeFD6X9tTTPCttIwg0teRG1Lqq617J8K4W7jpL/B0HTH0mjp3QpQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/png-js": "^3.0.0",
|
||||
"browserify-zlib": "^0.2.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"fontkit": "^2.0.2",
|
||||
"jay-peg": "^1.1.1",
|
||||
"linebreak": "^1.1.0",
|
||||
"vite-compatible-readable-stream": "^3.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/png-js": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/png-js/-/png-js-3.0.0.tgz",
|
||||
"integrity": "sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserify-zlib": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/primitives": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/primitives/-/primitives-4.1.1.tgz",
|
||||
"integrity": "sha512-IuhxYls1luJb7NUWy6q5avb1XrNaVj9bTNI40U9qGRuS6n7Hje/8H8Qi99Z9UKFV74bBP3DOf3L1wV2qZVgVrQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-pdf/reconciler": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/reconciler/-/reconciler-2.0.0.tgz",
|
||||
"integrity": "sha512-7zaPRujpbHSmCpIrZ+b9HSTJHthcVZzX0Wx7RzvQGsGBUbHP4p6s5itXrAIOuQuPvDepoHGNOvf6xUuMVvdoyw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4.1.1",
|
||||
"scheduler": "0.25.0-rc-603e6108-20241029"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/render": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-4.3.2.tgz",
|
||||
"integrity": "sha512-el5KYM1sH/PKcO4tRCIm8/AIEmhtraaONbwCrBhFdehoGv6JtgnXiMxHGAvZbI5kEg051GbyP+XIU6f6YbOu6Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/fns": "3.1.2",
|
||||
"@react-pdf/primitives": "^4.1.1",
|
||||
"@react-pdf/textkit": "^6.1.0",
|
||||
"@react-pdf/types": "^2.9.2",
|
||||
"abs-svg-path": "^0.1.1",
|
||||
"color-string": "^1.9.1",
|
||||
"normalize-svg-path": "^1.1.0",
|
||||
"parse-svg-path": "^0.1.2",
|
||||
"svg-arc-to-cubic-bezier": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/renderer": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-4.3.2.tgz",
|
||||
"integrity": "sha512-EhPkj35gO9rXIyyx29W3j3axemvVY5RigMmlK4/6Ku0pXB8z9PEE/sz4ZBOShu2uot6V4xiCR3aG+t9IjJJlBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/fns": "3.1.2",
|
||||
"@react-pdf/font": "^4.0.4",
|
||||
"@react-pdf/layout": "^4.4.2",
|
||||
"@react-pdf/pdfkit": "^4.1.0",
|
||||
"@react-pdf/primitives": "^4.1.1",
|
||||
"@react-pdf/reconciler": "^2.0.0",
|
||||
"@react-pdf/render": "^4.3.2",
|
||||
"@react-pdf/types": "^2.9.2",
|
||||
"events": "^3.3.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"queue": "^6.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/stylesheet": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.1.2.tgz",
|
||||
"integrity": "sha512-E3ftGRYUQGKiN3JOgtGsLDo0hGekA6dmkmi/MYACytmPTKxQRBSO3126MebmCq+t1rgU9uRlREIEawJ+8nzSbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-pdf/fns": "3.1.2",
|
||||
"@react-pdf/types": "^2.9.2",
|
||||
"color-string": "^1.9.1",
|
||||
"hsl-to-hex": "^1.0.0",
|
||||
"media-engine": "^1.0.3",
|
||||
"postcss-value-parser": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/textkit": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/textkit/-/textkit-6.1.0.tgz",
|
||||
"integrity": "sha512-sFlzDC9CDFrJsnL3B/+NHrk9+Advqk7iJZIStiYQDdskbow8GF/AGYrpIk+vWSnh35YxaGbHkqXq53XOxnyrjQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-pdf/fns": "3.1.2",
|
||||
"bidi-js": "^1.0.2",
|
||||
"hyphen": "^1.6.4",
|
||||
"unicode-properties": "^1.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/types": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.9.2.tgz",
|
||||
"integrity": "sha512-dufvpKId9OajLLbgn9q7VLUmyo1Jf+iyGk2ZHmCL8nIDtL8N1Ejh9TH7+pXXrR0tdie1nmnEb5Bz9U7g4hI4/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-pdf/font": "^4.0.4",
|
||||
"@react-pdf/primitives": "^4.1.1",
|
||||
"@react-pdf/stylesheet": "^6.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.34.48",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz",
|
||||
|
|
@ -2090,6 +1938,27 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/pdfkit": {
|
||||
"version": "0.17.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.17.5.tgz",
|
||||
"integrity": "sha512-T3ZHnvF91HsEco5ClhBCOuBwobZfPcI2jaiSHybkkKYq4KhVIIurod94JVKvDIG0JXT6o3KiERC0X0//m8dyrg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pdfmake": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/pdfmake/-/pdfmake-0.3.2.tgz",
|
||||
"integrity": "sha512-2TZSL8puKJs/rHvMV1b8BhHD+qYyV9da8mVY83/x7ZR/NaEPXbm3+t5SwkwaH6QAIhY1zQVAaFDhHWL0haMstA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/pdfkit": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||
|
|
@ -2653,12 +2522,6 @@
|
|||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/abs-svg-path": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz",
|
||||
"integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
|
|
@ -2926,15 +2789,6 @@
|
|||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bidi-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
|
|
@ -2957,15 +2811,6 @@
|
|||
"base64-js": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/browserify-zlib": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
|
||||
"integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pako": "~1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
|
||||
|
|
@ -3272,18 +3117,9 @@
|
|||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/color-string": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
|
@ -3321,12 +3157,6 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
|
|
@ -3435,12 +3265,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex-xs": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
|
||||
"integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
|
||||
|
|
@ -3762,15 +3586,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/execa": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||
|
|
@ -4141,21 +3956,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/hsl-to-hex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hsl-to-hex/-/hsl-to-hex-1.0.0.tgz",
|
||||
"integrity": "sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hsl-to-rgb-for-reals": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hsl-to-rgb-for-reals": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz",
|
||||
"integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||
|
|
@ -4173,12 +3973,6 @@
|
|||
"node": ">=10.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hyphen": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/hyphen/-/hyphen-1.14.1.tgz",
|
||||
"integrity": "sha512-kvL8xYl5QMTh+LwohVN72ciOxC0OEV79IPdJSTwEXok9y9QHebXGdFgrED4sWfiax/ODx++CAMk3hMy4XPJPOw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||
|
|
@ -4235,6 +4029,7 @@
|
|||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
|
|
@ -4302,12 +4097,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-url": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
|
||||
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
|
|
@ -4402,15 +4191,6 @@
|
|||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jay-peg": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.1.1.tgz",
|
||||
"integrity": "sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"restructure": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest": {
|
||||
"version": "30.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz",
|
||||
|
|
@ -4990,10 +4770,17 @@
|
|||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/js-md5": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz",
|
||||
"integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
|
|
@ -5153,18 +4940,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
|
|
@ -5208,12 +4983,6 @@
|
|||
"tmpl": "1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/media-engine": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz",
|
||||
"integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
|
|
@ -5347,15 +5116,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-svg-path": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz",
|
||||
"integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"svg-arc-to-cubic-bezier": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-path": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||
|
|
@ -5369,15 +5129,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/obsidian": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz",
|
||||
|
|
@ -5498,12 +5249,6 @@
|
|||
"dev": true,
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
|
|
@ -5523,12 +5268,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-svg-path": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
|
||||
"integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
|
|
@ -5583,6 +5322,34 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/pdfkit": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.18.0.tgz",
|
||||
"integrity": "sha512-NvUwSDZ0eYEzqAiWwVQkRkjYUkZ48kcsHuCO31ykqPPIVkwoSDjDGiwIgHHNtsiwls3z3P/zy4q00hl2chg2Ug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/ciphers": "^1.0.0",
|
||||
"@noble/hashes": "^1.6.0",
|
||||
"fontkit": "^2.0.4",
|
||||
"js-md5": "^0.8.3",
|
||||
"linebreak": "^1.1.0",
|
||||
"png-js": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pdfmake": {
|
||||
"version": "0.3.7",
|
||||
"resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.3.7.tgz",
|
||||
"integrity": "sha512-SwTFcaH3kCJBlPFWi/YB34zRg6lpCxq90tkZ9GxfSi9/v4Tk96cv4IvOstA+CC40rdW1OzQIuNhD2DLD1RDVgA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"linebreak": "^1.1.0",
|
||||
"pdfkit": "^0.18.0",
|
||||
"xmldoc": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
|
|
@ -5682,11 +5449,10 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-value-parser": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"license": "MIT"
|
||||
"node_modules/png-js": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz",
|
||||
"integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g=="
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
|
|
@ -5743,23 +5509,6 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types/node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
|
|
@ -5788,15 +5537,6 @@
|
|||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/queue": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
|
||||
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "~2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.4",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||
|
|
@ -5855,15 +5595,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-cwd": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
|
||||
|
|
@ -5893,31 +5624,14 @@
|
|||
"integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.25.0-rc-603e6108-20241029",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz",
|
||||
"integrity": "sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==",
|
||||
"license": "MIT"
|
||||
"node_modules/sax": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
|
||||
"integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
|
|
@ -5968,21 +5682,6 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
|
||||
"integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle/node_modules/is-arrayish": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
|
||||
"integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
|
|
@ -6044,15 +5743,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-length": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
|
||||
|
|
@ -6248,12 +5938,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/svg-arc-to-cubic-bezier": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
|
||||
"integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.12",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz",
|
||||
|
|
@ -6621,12 +6305,6 @@
|
|||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||
|
|
@ -6655,20 +6333,6 @@
|
|||
"node": ">=10.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-compatible-readable-stream": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz",
|
||||
"integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
|
|
@ -6837,6 +6501,18 @@
|
|||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xmldoc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-2.0.3.tgz",
|
||||
"integrity": "sha512-6gRk4NY/Jvg67xn7OzJuxLRsGgiXBaPUQplVJ/9l99uIugxh4FTOewYz5ic8WScj7Xx/2WvhENiQKwkK9RpE4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sax": "^1.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
|
@ -6941,12 +6617,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/yoga-layout": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
|
||||
"integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
"devDependencies": {
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/pdfmake": "^0.3.2",
|
||||
"@types/react": "^19.2.4",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "8.57.2",
|
||||
|
|
@ -33,8 +34,8 @@
|
|||
"typescript": "5.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-pdf/renderer": "^4.3.2",
|
||||
"moment": "^2.30.1",
|
||||
"pdfmake": "^0.3.7",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"uuid": "^11.0.5",
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
import React from "react";
|
||||
import type { Moment } from "moment";
|
||||
import { Timekeep } from "@/timekeep/schema";
|
||||
import { getTotalDuration } from "@/timekeep";
|
||||
import { TimekeepSettings } from "@/settings";
|
||||
import { Page, View, Text, Document } from "@react-pdf/renderer";
|
||||
import {
|
||||
formatPdfDate,
|
||||
formatDurationLong,
|
||||
formatDurationShort,
|
||||
} from "@/utils";
|
||||
|
||||
import { createStyles } from "./styles";
|
||||
import TimesheetPdfTable from "./TimesheetPdfTable";
|
||||
import TimesheetPdfDetailField from "./TimesheetPdfDetailField";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
data: Timekeep;
|
||||
currentTime: Moment;
|
||||
footnote: string;
|
||||
settings: TimekeepSettings;
|
||||
};
|
||||
|
||||
export default function TimesheetPdf({
|
||||
title,
|
||||
data,
|
||||
currentTime,
|
||||
footnote,
|
||||
settings,
|
||||
}: Props) {
|
||||
// Get the total elapsed duration
|
||||
const duration = getTotalDuration(data.entries, currentTime);
|
||||
|
||||
const currentDate = formatPdfDate(currentTime, settings);
|
||||
const totalDuration = formatDurationLong(duration);
|
||||
const totalDurationShort = formatDurationShort(duration);
|
||||
|
||||
const styles = createStyles(settings.pdfFontFamily);
|
||||
|
||||
return (
|
||||
<Document>
|
||||
<Page size="A4" style={styles.page}>
|
||||
<View style={styles.heading}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
<Text style={styles.title}>Timesheet</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.details}>
|
||||
<TimesheetPdfDetailField
|
||||
name="Date"
|
||||
value={currentDate}
|
||||
styles={styles}
|
||||
/>
|
||||
<TimesheetPdfDetailField
|
||||
name="Total Duration"
|
||||
value={totalDuration}
|
||||
styles={styles}
|
||||
/>
|
||||
<TimesheetPdfDetailField
|
||||
name="Total Duration (hours)"
|
||||
value={totalDurationShort}
|
||||
styles={styles}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<TimesheetPdfTable
|
||||
data={data}
|
||||
currentTime={currentTime}
|
||||
totalDuration={totalDuration}
|
||||
settings={settings}
|
||||
styles={styles}
|
||||
/>
|
||||
|
||||
<Text style={styles.footNote} wrap={false}>
|
||||
{footnote}
|
||||
</Text>
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import React from "react";
|
||||
import { Text } from "@react-pdf/renderer";
|
||||
|
||||
import { Styles } from "./styles";
|
||||
|
||||
type Props = {
|
||||
name: string;
|
||||
value: string;
|
||||
styles: Styles;
|
||||
};
|
||||
|
||||
// Individual field within the details section
|
||||
export default function TimesheetPdfDetailField({
|
||||
name,
|
||||
value,
|
||||
styles,
|
||||
}: Props) {
|
||||
return (
|
||||
<Text style={styles.detailsFieldValue}>
|
||||
<Text style={styles.detailsFieldName}>{name}: </Text>
|
||||
{value}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
import React from "react";
|
||||
import type { Moment } from "moment";
|
||||
import { Timekeep } from "@/timekeep/schema";
|
||||
import { TimekeepSettings } from "@/settings";
|
||||
import { View, Text } from "@react-pdf/renderer";
|
||||
|
||||
import { Styles } from "./styles";
|
||||
import { TimesheetPdfTableRow } from "./TimesheetPdfTableRow";
|
||||
|
||||
type Props = {
|
||||
data: Timekeep;
|
||||
currentTime: Moment;
|
||||
totalDuration: string;
|
||||
settings: TimekeepSettings;
|
||||
styles: Styles;
|
||||
};
|
||||
|
||||
export default function TimesheetPdfTable({
|
||||
data,
|
||||
currentTime,
|
||||
totalDuration,
|
||||
settings,
|
||||
styles,
|
||||
}: Props) {
|
||||
// Render the table header
|
||||
const renderHeader = (
|
||||
<View style={[styles.tableRow, styles.tableRowHeader]} wrap={false}>
|
||||
<Text style={[styles.tableCell, styles.tableCellBlock]}>
|
||||
Block Name
|
||||
</Text>
|
||||
<Text style={[styles.tableCell, styles.tableCellTime]}>
|
||||
Start Time
|
||||
</Text>
|
||||
<Text style={[styles.tableCell, styles.tableCellTime]}>
|
||||
End Time
|
||||
</Text>
|
||||
<Text style={[styles.tableCell, styles.tableCellTime]}>
|
||||
Duration
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
// Render the rows of the table
|
||||
const renderEntries = data.entries.map((entry, index) => (
|
||||
<TimesheetPdfTableRow
|
||||
entry={entry}
|
||||
key={index}
|
||||
currentTime={currentTime}
|
||||
settings={settings}
|
||||
styles={styles}
|
||||
/>
|
||||
));
|
||||
|
||||
// Render the table footer
|
||||
const renderFooter = (
|
||||
<View style={[styles.tableRow, styles.tableRowHeader]} wrap={false}>
|
||||
<Text style={[styles.tableCell, styles.tableCellBlock]}>Total</Text>
|
||||
|
||||
<Text style={[styles.tableCell, styles.tableCellTime]} />
|
||||
<Text style={[styles.tableCell, styles.tableCellTime]} />
|
||||
|
||||
<Text style={[styles.tableCell, styles.tableCellDuration]}>
|
||||
{totalDuration}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.tableContainer}>
|
||||
{renderHeader}
|
||||
{renderEntries}
|
||||
{renderFooter}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import type { Moment } from "moment";
|
||||
import React, { Fragment } from "react";
|
||||
import { getEntryDuration } from "@/timekeep";
|
||||
import { TimekeepSettings } from "@/settings";
|
||||
import { TimeEntry } from "@/timekeep/schema";
|
||||
import { View, Text } from "@react-pdf/renderer";
|
||||
import { formatPdfRowDate, formatDurationLong } from "@/utils";
|
||||
|
||||
import { Styles } from "./styles";
|
||||
|
||||
type Props = {
|
||||
entry: TimeEntry;
|
||||
currentTime: Moment;
|
||||
settings: TimekeepSettings;
|
||||
styles: Styles;
|
||||
};
|
||||
|
||||
export function TimesheetPdfTableRow({
|
||||
entry,
|
||||
currentTime,
|
||||
settings,
|
||||
styles,
|
||||
}: Props) {
|
||||
const duration = getEntryDuration(entry, currentTime);
|
||||
const durationFormatted = formatDurationLong(duration);
|
||||
|
||||
// Render start and end timing for individual entries
|
||||
const renderTiming = entry.startTime !== null && (
|
||||
<>
|
||||
<Text style={[styles.tableCell, styles.tableCellTime]}>
|
||||
{formatPdfRowDate(entry.startTime, settings)}
|
||||
</Text>
|
||||
<Text style={[styles.tableCell, styles.tableCellTime]}>
|
||||
{formatPdfRowDate(entry.endTime ?? currentTime, settings)}
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
|
||||
// Render the child rows for groups
|
||||
const renderChildren = entry.subEntries != null && (
|
||||
<View style={[styles.tableIndent, styles.tableRowWrapper]}>
|
||||
{entry.subEntries.map((entry, index) => (
|
||||
<TimesheetPdfTableRow
|
||||
entry={entry}
|
||||
key={index}
|
||||
currentTime={currentTime}
|
||||
settings={settings}
|
||||
styles={styles}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<View style={styles.tableRow} wrap={false}>
|
||||
<Text style={[styles.tableCell, styles.tableCellBlock]}>
|
||||
{entry.name}
|
||||
</Text>
|
||||
{renderTiming}
|
||||
<Text style={[styles.tableCell, styles.tableCellTime]}>
|
||||
{durationFormatted}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{renderChildren}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,45 +1,7 @@
|
|||
import React from "react";
|
||||
import { Moment } from "moment";
|
||||
import { Timekeep } from "@/timekeep/schema";
|
||||
import { TimekeepSettings } from "@/settings";
|
||||
import RubikBold from "@/fonts/Rubik-Bold.ttf";
|
||||
import { pdf, Font } from "@react-pdf/renderer";
|
||||
// Embedded fonts
|
||||
import RobotoBold from "@/fonts/Roboto-Bold.ttf";
|
||||
import RubikRegular from "@/fonts/Rubik-Regular.ttf";
|
||||
import RobotoRegular from "@/fonts/Roboto-Regular.ttf";
|
||||
|
||||
import TimesheetPdf from "./TimesheetPdf";
|
||||
|
||||
// Register the embedded font (Roboto for a large range of supported languages/characters https://github.com/jacobtread/obsidian-timekeep/issues/1)
|
||||
Font.register({
|
||||
family: "Roboto",
|
||||
fonts: [
|
||||
{
|
||||
src: RobotoRegular,
|
||||
fontWeight: 400,
|
||||
},
|
||||
{
|
||||
src: RobotoBold,
|
||||
fontWeight: 700,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Register the Rubik font for Arabic character support (https://github.com/jacobtread/obsidian-timekeep/issues/61)
|
||||
Font.register({
|
||||
family: "Rubik",
|
||||
fonts: [
|
||||
{
|
||||
src: RubikRegular,
|
||||
fontWeight: 400,
|
||||
},
|
||||
{
|
||||
src: RubikBold,
|
||||
fontWeight: 700,
|
||||
},
|
||||
],
|
||||
});
|
||||
import { createPdfExport } from "@/components/pdf/renderer";
|
||||
|
||||
/**
|
||||
* Generates a PDF for the provided timekeep
|
||||
|
|
@ -54,18 +16,5 @@ export async function createPdf(
|
|||
settings: TimekeepSettings,
|
||||
currentTime: Moment
|
||||
): Promise<NodeJS.ReadableStream> {
|
||||
const document = React.createElement(TimesheetPdf, {
|
||||
data: timekeep,
|
||||
title: settings.pdfTitle,
|
||||
footnote: settings.pdfFootnote,
|
||||
currentTime,
|
||||
settings,
|
||||
});
|
||||
|
||||
// Create the PDF
|
||||
const createdPdf = pdf(document);
|
||||
|
||||
// Create a blob from the PDF
|
||||
const buffer = await createdPdf.toBuffer();
|
||||
return buffer;
|
||||
return createPdfExport(timekeep, settings, currentTime);
|
||||
}
|
||||
|
|
|
|||
378
src/components/pdf/renderer.ts
Normal file
378
src/components/pdf/renderer.ts
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
import type { TimeEntry, Timekeep } from "@/timekeep/schema";
|
||||
import type { TimekeepSettings } from "@/settings";
|
||||
import type { Moment } from "moment";
|
||||
|
||||
import pdfMake from "pdfmake";
|
||||
import type {
|
||||
DynamicContent,
|
||||
Table,
|
||||
TableCell,
|
||||
TDocumentDefinitions,
|
||||
TFontDictionary,
|
||||
} from "pdfmake/interfaces";
|
||||
|
||||
// Embedded fonts
|
||||
import RobotoBold from "@/fonts/Roboto-Bold.ttf";
|
||||
import RubikRegular from "@/fonts/Rubik-Regular.ttf";
|
||||
import RubikBold from "@/fonts/Rubik-Bold.ttf";
|
||||
import RobotoRegular from "@/fonts/Roboto-Regular.ttf";
|
||||
import { getEntryDuration, getTotalDuration } from "@/timekeep";
|
||||
import {
|
||||
formatDurationLong,
|
||||
formatDurationShort,
|
||||
formatPdfDate,
|
||||
formatPdfRowDate,
|
||||
} from "@/utils";
|
||||
import { Readable } from "stream";
|
||||
|
||||
const fonts: TFontDictionary = {
|
||||
Roboto: {
|
||||
normal: "Roboto-Regular.ttf",
|
||||
bold: "Roboto-Bold.ttf",
|
||||
},
|
||||
Rubik: {
|
||||
normal: "Rubik-Regular.ttf",
|
||||
bold: "Rubik-Bold.ttf",
|
||||
},
|
||||
};
|
||||
|
||||
function stripDataUrlPrefix(text: string): string {
|
||||
return text.substring("data:font/ttf;base64,".length);
|
||||
}
|
||||
|
||||
pdfMake.addVirtualFileSystem({
|
||||
"Roboto-Regular.ttf": stripDataUrlPrefix(RobotoRegular),
|
||||
"Roboto-Bold.ttf": stripDataUrlPrefix(RobotoBold),
|
||||
"Rubik-Regular.ttf": stripDataUrlPrefix(RubikRegular),
|
||||
"Rubik-Bold.ttf": stripDataUrlPrefix(RubikBold),
|
||||
});
|
||||
|
||||
pdfMake.addFonts(fonts);
|
||||
|
||||
export async function createPdfExport(
|
||||
timekeep: Timekeep,
|
||||
settings: TimekeepSettings,
|
||||
currentTime: Moment
|
||||
): Promise<NodeJS.ReadableStream> {
|
||||
const definition = createPdfDefinition(timekeep, settings, currentTime);
|
||||
const pdf = pdfMake.createPdf(definition, {});
|
||||
const stream = await pdf.getBuffer();
|
||||
return Readable.from([stream]);
|
||||
}
|
||||
|
||||
export function createPdfDefinition(
|
||||
timekeep: Timekeep,
|
||||
settings: TimekeepSettings,
|
||||
currentTime: Moment
|
||||
): TDocumentDefinitions {
|
||||
const duration = getTotalDuration(timekeep.entries, currentTime);
|
||||
const currentDate = formatPdfDate(currentTime, settings);
|
||||
const totalDuration = formatDurationLong(duration);
|
||||
const totalDurationShort = formatDurationShort(duration);
|
||||
|
||||
return {
|
||||
content: [
|
||||
createPdfHeader(
|
||||
settings.pdfTitle,
|
||||
currentDate,
|
||||
totalDuration,
|
||||
totalDurationShort
|
||||
),
|
||||
createPdfTable(timekeep, totalDuration, settings, currentTime),
|
||||
],
|
||||
footer: createPdfFooter(settings),
|
||||
defaultStyle: {
|
||||
font: settings.pdfFontFamily,
|
||||
},
|
||||
pageSize: "A4",
|
||||
pageMargins: 15,
|
||||
styles: {
|
||||
heading: {
|
||||
marginBottom: 5,
|
||||
columnGap: 10,
|
||||
},
|
||||
title: {
|
||||
bold: true,
|
||||
fontSize: 12,
|
||||
marginBottom: 5,
|
||||
},
|
||||
details: {
|
||||
marginBottom: 5,
|
||||
},
|
||||
footNote: {
|
||||
noWrap: true,
|
||||
fontSize: 6,
|
||||
bold: true,
|
||||
},
|
||||
pageNumber: {
|
||||
noWrap: true,
|
||||
fontSize: 6,
|
||||
},
|
||||
footer: {
|
||||
marginLeft: 15,
|
||||
marginRight: 15,
|
||||
},
|
||||
tableCell: {
|
||||
fontSize: 8,
|
||||
},
|
||||
tableCellTime: {
|
||||
alignment: "right",
|
||||
},
|
||||
tableCellHeader: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createPdfHeader(
|
||||
pdfTitle: string,
|
||||
currentDate: string,
|
||||
totalDuration: string,
|
||||
totalDurationShort: string
|
||||
): pdfMake.Content {
|
||||
return [
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
text: pdfTitle,
|
||||
alignment: "left",
|
||||
style: "title",
|
||||
width: "50%",
|
||||
},
|
||||
{
|
||||
text: "Timesheet",
|
||||
alignment: "right",
|
||||
style: "title",
|
||||
width: "50%",
|
||||
},
|
||||
],
|
||||
style: "heading",
|
||||
},
|
||||
{
|
||||
stack: [
|
||||
createPdfHeaderDetailField("Date", currentDate),
|
||||
createPdfHeaderDetailField("Total Duration", totalDuration),
|
||||
createPdfHeaderDetailField(
|
||||
"Total Duration (hours)",
|
||||
totalDurationShort
|
||||
),
|
||||
],
|
||||
|
||||
style: "details",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function createPdfHeaderDetailField(
|
||||
title: string,
|
||||
value: string
|
||||
): pdfMake.Content {
|
||||
return {
|
||||
text: [
|
||||
{ text: title + ": ", fontSize: 8, bold: true },
|
||||
{ text: value, fontSize: 8 },
|
||||
],
|
||||
marginBottom: 8,
|
||||
};
|
||||
}
|
||||
|
||||
function createPdfFooter(settings: TimekeepSettings): DynamicContent {
|
||||
return function (currentPage: number, pageCount: number) {
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
text: `${settings.pdfFootnote}`,
|
||||
style: "footNote",
|
||||
width: "50%",
|
||||
alignment: "left",
|
||||
},
|
||||
{
|
||||
text: `${currentPage} of ${pageCount}`,
|
||||
style: "pageNumber",
|
||||
width: "50%",
|
||||
alignment: "right",
|
||||
},
|
||||
],
|
||||
style: "footer",
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function createPdfTable(
|
||||
timekeep: Timekeep,
|
||||
totalDuration: string,
|
||||
settings: TimekeepSettings,
|
||||
currentTime: Moment
|
||||
): pdfMake.Content {
|
||||
const rows = createPdfTableRows(timekeep.entries, settings, currentTime);
|
||||
|
||||
return {
|
||||
table: {
|
||||
headerRows: 1,
|
||||
widths: ["*", 70, 70, 50],
|
||||
body: [
|
||||
[
|
||||
{
|
||||
text: "Block Name",
|
||||
style: ["tableCell", "tableCellHeader"],
|
||||
border: [true, false, false, true],
|
||||
},
|
||||
{
|
||||
text: "Start Time",
|
||||
style: ["tableCell", "tableCellHeader"],
|
||||
alignment: "center",
|
||||
border: [false, false, false, true],
|
||||
},
|
||||
{
|
||||
text: "End Time",
|
||||
style: ["tableCell", "tableCellHeader"],
|
||||
alignment: "center",
|
||||
border: [false, false, false, true],
|
||||
},
|
||||
{
|
||||
text: "Duration",
|
||||
style: ["tableCell", "tableCellHeader"],
|
||||
alignment: "right",
|
||||
border: [false, false, true, true],
|
||||
},
|
||||
],
|
||||
...rows.map((row) => row.row),
|
||||
[
|
||||
{ text: "Total", style: "tableCell" },
|
||||
{ text: "", style: "tableCell" },
|
||||
{ text: "", style: "tableCell" },
|
||||
{
|
||||
text: totalDuration,
|
||||
style: ["tableCell", "tableCellFooter"],
|
||||
bold: true,
|
||||
alignment: "right",
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
|
||||
layout: {
|
||||
fillColor: function (rowIndex, node, columnIndex) {
|
||||
// Header & Footer styling
|
||||
if (rowIndex === 0 || rowIndex === rows.length + 1) {
|
||||
return "#ececec";
|
||||
}
|
||||
|
||||
// Group row styling
|
||||
if (rowIndex > 0 && rowIndex - 1 < rows.length) {
|
||||
if (rows[rowIndex - 1].group) {
|
||||
return "#f4f4f4";
|
||||
}
|
||||
}
|
||||
|
||||
// Default styling
|
||||
return "#ffffff";
|
||||
},
|
||||
|
||||
vLineColor: function (columnIndex, node, rowIndex) {
|
||||
return "#ececec";
|
||||
},
|
||||
|
||||
hLineColor: function (rowIndex, node, columnIndex) {
|
||||
return "#ececec";
|
||||
},
|
||||
paddingBottom: cellPadding,
|
||||
paddingLeft: cellPadding,
|
||||
paddingTop: cellPadding,
|
||||
paddingRight: cellPadding,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const cellPadding = () => 8;
|
||||
|
||||
type TableEntryRow = {
|
||||
row: TableCell[];
|
||||
depth: number;
|
||||
group: boolean;
|
||||
};
|
||||
|
||||
function createPdfTableRows(
|
||||
entries: TimeEntry[],
|
||||
settings: TimekeepSettings,
|
||||
currentTime: Moment
|
||||
): TableEntryRow[] {
|
||||
type StackEntry = { entry: TimeEntry; depth: number };
|
||||
|
||||
const rows: TableEntryRow[] = [];
|
||||
const stack: StackEntry[] = entries.map((entry) => ({ entry, depth: 0 }));
|
||||
|
||||
while (stack.length > 0) {
|
||||
const { entry, depth } = stack.pop()!;
|
||||
|
||||
rows.push({
|
||||
group: entry.subEntries !== null && entry.subEntries.length > 0,
|
||||
row: createTableEntryCells(entry, depth, settings, currentTime),
|
||||
depth,
|
||||
});
|
||||
|
||||
if (entry.subEntries && entry.subEntries.length > 0) {
|
||||
for (let i = entry.subEntries.length - 1; i >= 0; i--) {
|
||||
stack.push({
|
||||
entry: entry.subEntries[i],
|
||||
depth: depth + 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
function createTableEntryCells(
|
||||
entry: TimeEntry,
|
||||
depth: number,
|
||||
settings: TimekeepSettings,
|
||||
currentTime: Moment
|
||||
): TableCell[] {
|
||||
const duration = getEntryDuration(entry, currentTime);
|
||||
const durationFormatted = formatDurationLong(duration);
|
||||
|
||||
let startTime: string;
|
||||
let endTime: string;
|
||||
|
||||
if (entry.startTime !== null) {
|
||||
startTime = formatPdfRowDate(entry.startTime, settings);
|
||||
endTime = formatPdfRowDate(entry.endTime ?? currentTime, settings);
|
||||
} else {
|
||||
startTime = "";
|
||||
endTime = "";
|
||||
}
|
||||
|
||||
const marginLeft = depth * 5;
|
||||
|
||||
if (depth > 0) {
|
||||
const bg = "#999";
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
text: entry.name,
|
||||
style: ["tableCell", "tableCellBlock"],
|
||||
marginLeft,
|
||||
border: [true, false, false, true],
|
||||
},
|
||||
{
|
||||
text: startTime,
|
||||
style: ["tableCell", "tableCellTime"],
|
||||
alignment: "center",
|
||||
},
|
||||
{
|
||||
text: endTime,
|
||||
style: ["tableCell", "tableCellTime"],
|
||||
alignment: "center",
|
||||
},
|
||||
{
|
||||
text: durationFormatted,
|
||||
style: ["tableCell", "tableCellTime"],
|
||||
alignment: "right",
|
||||
border: [false, false, true, true],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
import { StyleSheet } from "@react-pdf/renderer";
|
||||
|
||||
export type Styles = ReturnType<typeof createStyles>;
|
||||
|
||||
export function createStyles(fontFamily = "Roboto") {
|
||||
return StyleSheet.create({
|
||||
// Styles for a page
|
||||
page: {
|
||||
fontFamily,
|
||||
padding: 15,
|
||||
},
|
||||
|
||||
// Heading with the page title and timesheet title
|
||||
heading: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: 5,
|
||||
},
|
||||
|
||||
// User specified title and "Timesheet"
|
||||
title: {
|
||||
fontFamily,
|
||||
fontWeight: 700,
|
||||
fontSize: 12,
|
||||
marginBottom: 5,
|
||||
},
|
||||
|
||||
// Detail items at the top (Date, Total Duration, Short Duration)
|
||||
details: {
|
||||
gap: 4,
|
||||
marginBottom: 5,
|
||||
},
|
||||
|
||||
// Footer note
|
||||
footNote: {
|
||||
marginTop: 10,
|
||||
fontSize: 6,
|
||||
fontFamily,
|
||||
fontWeight: 700,
|
||||
},
|
||||
|
||||
// Name of a details field
|
||||
detailsFieldName: {
|
||||
fontFamily,
|
||||
fontWeight: 700,
|
||||
fontSize: 8,
|
||||
marginBottom: 5,
|
||||
},
|
||||
|
||||
// Value of a details field
|
||||
detailsFieldValue: {
|
||||
fontSize: 8,
|
||||
marginBottom: 5,
|
||||
},
|
||||
|
||||
// Container around the timesheet table
|
||||
tableContainer: {
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
borderWidth: 1,
|
||||
borderColor: "#ececec",
|
||||
},
|
||||
|
||||
// Row within the table
|
||||
tableRow: {
|
||||
flexDirection: "row",
|
||||
borderBottomColor: "#ececec",
|
||||
borderBottomWidth: 1,
|
||||
alignItems: "center",
|
||||
height: 24,
|
||||
fontSize: 8,
|
||||
width: "100%",
|
||||
},
|
||||
|
||||
// Header row
|
||||
tableRowHeader: {
|
||||
fontFamily,
|
||||
fontWeight: 700,
|
||||
backgroundColor: "#ececec",
|
||||
},
|
||||
|
||||
// Base table cell
|
||||
tableCell: {
|
||||
padding: 15,
|
||||
},
|
||||
|
||||
// Table cell that spans available width
|
||||
tableCellBlock: {
|
||||
width: "100%",
|
||||
},
|
||||
|
||||
// Table cell that contains a timestamp
|
||||
tableCellTime: {
|
||||
width: "200px",
|
||||
textAlign: "right",
|
||||
},
|
||||
|
||||
// Table cell that contains a duration
|
||||
tableCellDuration: {
|
||||
width: "400px",
|
||||
textAlign: "right",
|
||||
fontWeight: 700,
|
||||
fontFamily,
|
||||
},
|
||||
|
||||
// Wrapper for nested rows
|
||||
tableRowWrapper: {
|
||||
borderColor: "#f9f9f9",
|
||||
borderWidth: 5,
|
||||
},
|
||||
|
||||
// Table indentation container
|
||||
tableIndent: {
|
||||
borderLeftWidth: 5,
|
||||
borderLeftColor: "#999",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -27,9 +27,8 @@ describe("createEntry", () => {
|
|||
|
||||
describe("withEntry", () => {
|
||||
it("should add new entry to entries", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/adding_entry/addNewEntry"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/manipulating/adding_entry/addNewEntry");
|
||||
const output = withEntry(input, "New Entry", currentTime);
|
||||
expect(stripEntriesRuntimeData(output)).toEqual(
|
||||
stripEntriesRuntimeData(expected)
|
||||
|
|
@ -37,9 +36,8 @@ describe("withEntry", () => {
|
|||
});
|
||||
|
||||
it("should generate block name when empty", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/adding_entry/addEmptyBlockName"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/manipulating/adding_entry/addEmptyBlockName");
|
||||
|
||||
const output1 = withEntry(input, "", currentTime);
|
||||
expect(stripEntriesRuntimeData(output1)).toEqual(
|
||||
|
|
@ -54,9 +52,8 @@ describe("withEntry", () => {
|
|||
});
|
||||
|
||||
it("should maintain existing entries when adding to a list", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/adding_entry/addEntryToList"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/manipulating/adding_entry/addEntryToList");
|
||||
|
||||
const output = withEntry(input, "New Test Entry", currentTime);
|
||||
expect(stripEntriesRuntimeData(output)).toEqual(
|
||||
|
|
@ -67,9 +64,8 @@ describe("withEntry", () => {
|
|||
|
||||
describe("withSubEntry", () => {
|
||||
it("adding first entry should convert to group", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/adding_sub_entry/addConvertToGroup"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/manipulating/adding_sub_entry/addConvertToGroup");
|
||||
const output = withSubEntry(input, "New Entry", currentTime);
|
||||
expect(stripEntryRuntimeData(output)).toEqual(
|
||||
stripEntryRuntimeData(expected)
|
||||
|
|
@ -77,9 +73,8 @@ describe("withSubEntry", () => {
|
|||
});
|
||||
|
||||
it("adding first entry for folder should populate subentries", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/adding_sub_entry/addFolderPopulateSubEntries"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/manipulating/adding_sub_entry/addFolderPopulateSubEntries");
|
||||
const output = withSubEntry(input, "New Entry", currentTime);
|
||||
expect(stripEntryRuntimeData(output)).toEqual(
|
||||
stripEntryRuntimeData(expected)
|
||||
|
|
@ -87,9 +82,8 @@ describe("withSubEntry", () => {
|
|||
});
|
||||
|
||||
it("adding first entry for folder should extend subentries", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/adding_sub_entry/addFolderExtendSubEntries"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/manipulating/adding_sub_entry/addFolderExtendSubEntries");
|
||||
const output = withSubEntry(input, "New Entry 2", currentTime);
|
||||
expect(stripEntryRuntimeData(output)).toEqual(
|
||||
stripEntryRuntimeData(expected)
|
||||
|
|
@ -97,9 +91,8 @@ describe("withSubEntry", () => {
|
|||
});
|
||||
|
||||
it("adding to group should extend sub entries", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/adding_sub_entry/addToGroupExtendSubEntries"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/manipulating/adding_sub_entry/addToGroupExtendSubEntries");
|
||||
|
||||
const output = withSubEntry(input, "New Entry", currentTime);
|
||||
expect(stripEntryRuntimeData(output)).toEqual(
|
||||
|
|
@ -108,9 +101,8 @@ describe("withSubEntry", () => {
|
|||
});
|
||||
|
||||
it("empty name should generate a part name (single)", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/adding_sub_entry/emptyNameCreatePartNameSingle"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/manipulating/adding_sub_entry/emptyNameCreatePartNameSingle");
|
||||
|
||||
const output = withSubEntry(input, "", currentTime);
|
||||
expect(stripEntryRuntimeData(output)).toEqual(
|
||||
|
|
@ -119,9 +111,8 @@ describe("withSubEntry", () => {
|
|||
});
|
||||
|
||||
it("empty name should generate a part name (group)", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/adding_sub_entry/emptyNameCreatePartNameGroup"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/manipulating/adding_sub_entry/emptyNameCreatePartNameGroup");
|
||||
|
||||
const output = withSubEntry(input, "", currentTime);
|
||||
expect(stripEntryRuntimeData(output)).toEqual(
|
||||
|
|
|
|||
|
|
@ -38,9 +38,8 @@ const createCodeBlock = (
|
|||
|
||||
describe("extracting code blocks", () => {
|
||||
it("should extract codeblock contents", async () => {
|
||||
const { text, inputTimekeep1, inputTimekeep2 } = await import(
|
||||
"./__fixtures__/extracting/codeblockContents"
|
||||
);
|
||||
const { text, inputTimekeep1, inputTimekeep2 } =
|
||||
await import("./__fixtures__/extracting/codeblockContents");
|
||||
|
||||
const output = extractTimekeepCodeblocks(text);
|
||||
|
||||
|
|
@ -54,9 +53,8 @@ describe("extracting code blocks", () => {
|
|||
});
|
||||
|
||||
it("should ignore codeblocks that are not closed", async () => {
|
||||
const { text, inputTimekeep1 } = await import(
|
||||
"./__fixtures__/extracting/unclosedCodeBlock"
|
||||
);
|
||||
const { text, inputTimekeep1 } =
|
||||
await import("./__fixtures__/extracting/unclosedCodeBlock");
|
||||
const output = extractTimekeepCodeblocks(text);
|
||||
|
||||
expect(stripTimekeepRuntimeData(output[0])).toStrictEqual(
|
||||
|
|
|
|||
|
|
@ -11,27 +11,24 @@ import {
|
|||
|
||||
describe("getEntryById", () => {
|
||||
it("find top level entry", async () => {
|
||||
const { input, targetEntry, targetEntryId } = await import(
|
||||
"./__fixtures__/checking/findEntryById"
|
||||
);
|
||||
const { input, targetEntry, targetEntryId } =
|
||||
await import("./__fixtures__/checking/findEntryById");
|
||||
|
||||
const output = getEntryById(targetEntryId, input);
|
||||
expect(output).toEqual(targetEntry);
|
||||
});
|
||||
|
||||
it("find nested entry", async () => {
|
||||
const { input, targetEntry, targetEntryId } = await import(
|
||||
"./__fixtures__/checking/findEntryByIdNested"
|
||||
);
|
||||
const { input, targetEntry, targetEntryId } =
|
||||
await import("./__fixtures__/checking/findEntryByIdNested");
|
||||
|
||||
const output = getEntryById(targetEntryId, input);
|
||||
expect(output).toEqual(targetEntry);
|
||||
});
|
||||
|
||||
it("find entry non existent", async () => {
|
||||
const { input, targetEntryId } = await import(
|
||||
"./__fixtures__/checking/findEntryByIdMissing"
|
||||
);
|
||||
const { input, targetEntryId } =
|
||||
await import("./__fixtures__/checking/findEntryByIdMissing");
|
||||
|
||||
const output = getEntryById(targetEntryId, input);
|
||||
expect(output).toBeUndefined();
|
||||
|
|
@ -40,9 +37,8 @@ describe("getEntryById", () => {
|
|||
|
||||
describe("getPathToEntry", () => {
|
||||
it("path not found", async () => {
|
||||
const { targetEntry, entries, expected } = await import(
|
||||
"./__fixtures__/path/pathNotFound"
|
||||
);
|
||||
const { targetEntry, entries, expected } =
|
||||
await import("./__fixtures__/path/pathNotFound");
|
||||
const output = getPathToEntry(entries, targetEntry);
|
||||
expect(output).toEqual(expected);
|
||||
});
|
||||
|
|
@ -52,9 +48,8 @@ describe("getPathToEntry", () => {
|
|||
it("deep path found", () => {});
|
||||
|
||||
it("should find running entry path", async () => {
|
||||
const { input, runningEntry, path } = await import(
|
||||
"./__fixtures__/checking/findRunningEntryPath"
|
||||
);
|
||||
const { input, runningEntry, path } =
|
||||
await import("./__fixtures__/checking/findRunningEntryPath");
|
||||
|
||||
const output = getPathToEntry(input, runningEntry);
|
||||
expect(output).toEqual(path);
|
||||
|
|
@ -63,18 +58,16 @@ describe("getPathToEntry", () => {
|
|||
|
||||
describe("isEntryRunning", () => {
|
||||
it("should determine entry running state", async () => {
|
||||
const { running, notRunning } = await import(
|
||||
"./__fixtures__/checking/runningState"
|
||||
);
|
||||
const { running, notRunning } =
|
||||
await import("./__fixtures__/checking/runningState");
|
||||
|
||||
expect(isEntryRunning(running)).toBe(true);
|
||||
expect(isEntryRunning(notRunning)).toBe(false);
|
||||
});
|
||||
|
||||
it("should determine entry running state (nested)", async () => {
|
||||
const { runningNested, stoppedNested } = await import(
|
||||
"./__fixtures__/checking/runningState"
|
||||
);
|
||||
const { runningNested, stoppedNested } =
|
||||
await import("./__fixtures__/checking/runningState");
|
||||
|
||||
expect(isEntryRunning(runningNested)).toBe(true);
|
||||
expect(isEntryRunning(stoppedNested)).toBe(false);
|
||||
|
|
@ -83,9 +76,8 @@ describe("isEntryRunning", () => {
|
|||
|
||||
describe("getRunningEntry", () => {
|
||||
it("should find running entry", async () => {
|
||||
const { input, runningEntry } = await import(
|
||||
"./__fixtures__/checking/shouldFindRunningEntry"
|
||||
);
|
||||
const { input, runningEntry } =
|
||||
await import("./__fixtures__/checking/shouldFindRunningEntry");
|
||||
|
||||
const output = getRunningEntry(input);
|
||||
|
||||
|
|
@ -93,9 +85,8 @@ describe("getRunningEntry", () => {
|
|||
});
|
||||
|
||||
it("should find nested running entry", async () => {
|
||||
const { input, runningEntry } = await import(
|
||||
"./__fixtures__/checking/shouldFindRunningEntryNested"
|
||||
);
|
||||
const { input, runningEntry } =
|
||||
await import("./__fixtures__/checking/shouldFindRunningEntryNested");
|
||||
|
||||
const output = getRunningEntry(input);
|
||||
|
||||
|
|
@ -103,9 +94,8 @@ describe("getRunningEntry", () => {
|
|||
});
|
||||
|
||||
it("should not find running entry", async () => {
|
||||
const { input } = await import(
|
||||
"./__fixtures__/checking/shouldNotFindRunningEntry"
|
||||
);
|
||||
const { input } =
|
||||
await import("./__fixtures__/checking/shouldNotFindRunningEntry");
|
||||
|
||||
const output = getRunningEntry(input);
|
||||
|
||||
|
|
@ -115,17 +105,15 @@ describe("getRunningEntry", () => {
|
|||
|
||||
describe("isKeepRunning", () => {
|
||||
it("should show keep running", async () => {
|
||||
const { input } = await import(
|
||||
"./__fixtures__/checking/shouldBeRunning"
|
||||
);
|
||||
const { input } =
|
||||
await import("./__fixtures__/checking/shouldBeRunning");
|
||||
|
||||
expect(isKeepRunning(input)).toBe(true);
|
||||
});
|
||||
|
||||
it("should show keep not running", async () => {
|
||||
const { input } = await import(
|
||||
"./__fixtures__/checking/shouldNotBeRunning"
|
||||
);
|
||||
const { input } =
|
||||
await import("./__fixtures__/checking/shouldNotBeRunning");
|
||||
|
||||
expect(isKeepRunning(input)).toBe(false);
|
||||
});
|
||||
|
|
@ -133,9 +121,8 @@ describe("isKeepRunning", () => {
|
|||
|
||||
describe("getEntryDuration", () => {
|
||||
it("should get entry duration", async () => {
|
||||
const { input, currentTime, durationMs } = await import(
|
||||
"./__fixtures__/duration/shouldGetEntryDuration"
|
||||
);
|
||||
const { input, currentTime, durationMs } =
|
||||
await import("./__fixtures__/duration/shouldGetEntryDuration");
|
||||
|
||||
const output = getEntryDuration(input, currentTime);
|
||||
|
||||
|
|
@ -143,9 +130,8 @@ describe("getEntryDuration", () => {
|
|||
});
|
||||
|
||||
it("duration of non started entry should be zero", async () => {
|
||||
const { input, currentTime, durationMs } = await import(
|
||||
"./__fixtures__/duration/nonStartedZeroDuration"
|
||||
);
|
||||
const { input, currentTime, durationMs } =
|
||||
await import("./__fixtures__/duration/nonStartedZeroDuration");
|
||||
|
||||
const output = getEntryDuration(input, currentTime);
|
||||
|
||||
|
|
@ -153,9 +139,8 @@ describe("getEntryDuration", () => {
|
|||
});
|
||||
|
||||
it("duration should include children", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/duration/durationIncludeChildren"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/duration/durationIncludeChildren");
|
||||
|
||||
const output = getEntryDuration(input, currentTime);
|
||||
|
||||
|
|
@ -163,9 +148,8 @@ describe("getEntryDuration", () => {
|
|||
});
|
||||
|
||||
it("duration should use current as end for unfinished entries", async () => {
|
||||
const { input, endTime, durationMs } = await import(
|
||||
"./__fixtures__/duration/currentEndUnfinished"
|
||||
);
|
||||
const { input, endTime, durationMs } =
|
||||
await import("./__fixtures__/duration/currentEndUnfinished");
|
||||
|
||||
const output = getEntryDuration(input, endTime);
|
||||
|
||||
|
|
@ -175,9 +159,8 @@ describe("getEntryDuration", () => {
|
|||
|
||||
describe("getTotalDuration", () => {
|
||||
it("should get total duration", async () => {
|
||||
const { input, currentTime, expected } = await import(
|
||||
"./__fixtures__/duration/totalDuration"
|
||||
);
|
||||
const { input, currentTime, expected } =
|
||||
await import("./__fixtures__/duration/totalDuration");
|
||||
|
||||
const output = getTotalDuration(input, currentTime);
|
||||
|
||||
|
|
@ -187,17 +170,15 @@ describe("getTotalDuration", () => {
|
|||
|
||||
describe("getUniqueEntryHash", () => {
|
||||
it("hash should match when content matches", async () => {
|
||||
const { left, right } = await import(
|
||||
"./__fixtures__/hashing/hashMatches"
|
||||
);
|
||||
const { left, right } =
|
||||
await import("./__fixtures__/hashing/hashMatches");
|
||||
|
||||
expect(getUniqueEntryHash(left)).toBe(getUniqueEntryHash(right));
|
||||
});
|
||||
|
||||
it("hash shouldn't match when content is different", async () => {
|
||||
const { left, right } = await import(
|
||||
"./__fixtures__/hashing/hashDoesNotMatch"
|
||||
);
|
||||
const { left, right } =
|
||||
await import("./__fixtures__/hashing/hashDoesNotMatch");
|
||||
|
||||
expect(getUniqueEntryHash(left)).not.toBe(getUniqueEntryHash(right));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ import { getEntriesSorted } from "./sort";
|
|||
|
||||
describe("getEntriesSorted", () => {
|
||||
it("should be in reverse order", async () => {
|
||||
const { input, expected } = await import(
|
||||
"./__fixtures__/ordering/reverseOrder"
|
||||
);
|
||||
const { input, expected } =
|
||||
await import("./__fixtures__/ordering/reverseOrder");
|
||||
|
||||
const settings: TimekeepSettings = defaultSettings;
|
||||
settings.sortOrder = SortOrder.REVERSE_INSERTION;
|
||||
|
|
@ -21,9 +20,8 @@ describe("getEntriesSorted", () => {
|
|||
});
|
||||
|
||||
it("should be in newest first order", async () => {
|
||||
const { input, expected } = await import(
|
||||
"./__fixtures__/ordering/newestFirstOrder"
|
||||
);
|
||||
const { input, expected } =
|
||||
await import("./__fixtures__/ordering/newestFirstOrder");
|
||||
|
||||
const settings: TimekeepSettings = defaultSettings;
|
||||
settings.sortOrder = SortOrder.NEWEST_START;
|
||||
|
|
@ -33,9 +31,8 @@ describe("getEntriesSorted", () => {
|
|||
});
|
||||
|
||||
it("should be in newest last order", async () => {
|
||||
const { input, expected } = await import(
|
||||
"./__fixtures__/ordering/newestLastOrder"
|
||||
);
|
||||
const { input, expected } =
|
||||
await import("./__fixtures__/ordering/newestLastOrder");
|
||||
|
||||
const settings: TimekeepSettings = defaultSettings;
|
||||
settings.sortOrder = SortOrder.OLDEST_START;
|
||||
|
|
@ -45,9 +42,8 @@ describe("getEntriesSorted", () => {
|
|||
});
|
||||
|
||||
it("should be in newest last order with nulls first", async () => {
|
||||
const { input, expected } = await import(
|
||||
"./__fixtures__/ordering/newestLastNullsFirst"
|
||||
);
|
||||
const { input, expected } =
|
||||
await import("./__fixtures__/ordering/newestLastNullsFirst");
|
||||
|
||||
const settings: TimekeepSettings = defaultSettings;
|
||||
settings.sortOrder = SortOrder.OLDEST_START;
|
||||
|
|
@ -58,9 +54,8 @@ describe("getEntriesSorted", () => {
|
|||
});
|
||||
|
||||
it("order should not change", async () => {
|
||||
const { input, expected } = await import(
|
||||
"./__fixtures__/ordering/orderShouldNotChange"
|
||||
);
|
||||
const { input, expected } =
|
||||
await import("./__fixtures__/ordering/orderShouldNotChange");
|
||||
|
||||
const settings: TimekeepSettings = defaultSettings;
|
||||
settings.sortOrder = SortOrder.INSERTION;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ import { startNewEntry, startNewNestedEntry } from "./start";
|
|||
|
||||
describe("startNewEntry", () => {
|
||||
it("starting a new entry should stop any running entries", async () => {
|
||||
const { currentTime, stopped, name, input, expected } = await import(
|
||||
"./__fixtures__/manipulating/start_entry/startShouldStopRunning"
|
||||
);
|
||||
const { currentTime, stopped, name, input, expected } =
|
||||
await import("./__fixtures__/manipulating/start_entry/startShouldStopRunning");
|
||||
|
||||
const output = startNewEntry(name, currentTime, input);
|
||||
const stoppedEntry = output.find((entry) => entry.id === stopped);
|
||||
|
|
@ -19,9 +18,8 @@ describe("startNewEntry", () => {
|
|||
});
|
||||
|
||||
it("starting a new entry should add a new entry", async () => {
|
||||
const { currentTime, name, input, expected } = await import(
|
||||
"./__fixtures__/manipulating/start_entry/startShouldStopRunning"
|
||||
);
|
||||
const { currentTime, name, input, expected } =
|
||||
await import("./__fixtures__/manipulating/start_entry/startShouldStopRunning");
|
||||
|
||||
const output = startNewEntry(name, currentTime, input);
|
||||
expect(stripEntriesRuntimeData(output)).toEqual(
|
||||
|
|
@ -32,9 +30,8 @@ describe("startNewEntry", () => {
|
|||
|
||||
describe("startNewNestedEntry", () => {
|
||||
it("starting a new entry should stop any running entries", async () => {
|
||||
const { currentTime, targetEntry, input, expected } = await import(
|
||||
"./__fixtures__/manipulating/start_entry/startNotStartedEntry"
|
||||
);
|
||||
const { currentTime, targetEntry, input, expected } =
|
||||
await import("./__fixtures__/manipulating/start_entry/startNotStartedEntry");
|
||||
|
||||
const output = startNewNestedEntry(currentTime, targetEntry.id, input);
|
||||
expect(stripEntriesRuntimeData(output)).toEqual(
|
||||
|
|
@ -43,9 +40,8 @@ describe("startNewNestedEntry", () => {
|
|||
});
|
||||
|
||||
it("starting a new entry within a folder should create a subentry", async () => {
|
||||
const { currentTime, targetEntry, input, expected } = await import(
|
||||
"./__fixtures__/manipulating/start_entry/startNestedFolderEntry"
|
||||
);
|
||||
const { currentTime, targetEntry, input, expected } =
|
||||
await import("./__fixtures__/manipulating/start_entry/startNestedFolderEntry");
|
||||
|
||||
const output = startNewNestedEntry(currentTime, targetEntry.id, input);
|
||||
expect(stripEntriesRuntimeData(output)).toEqual(
|
||||
|
|
@ -54,9 +50,8 @@ describe("startNewNestedEntry", () => {
|
|||
});
|
||||
|
||||
it("starting a new entry should stop any running entries", async () => {
|
||||
const { currentTime, targetEntry, input, expected } = await import(
|
||||
"./__fixtures__/manipulating/start_entry/startNotStartedEntry"
|
||||
);
|
||||
const { currentTime, targetEntry, input, expected } =
|
||||
await import("./__fixtures__/manipulating/start_entry/startNotStartedEntry");
|
||||
|
||||
const output = startNewNestedEntry(currentTime, targetEntry.id, input);
|
||||
expect(stripEntriesRuntimeData(output)).toEqual(
|
||||
|
|
@ -65,9 +60,8 @@ describe("startNewNestedEntry", () => {
|
|||
});
|
||||
|
||||
it("starting a new entry should stop any running entries", async () => {
|
||||
const { currentTime, targetEntryId, input, expected } = await import(
|
||||
"./__fixtures__/manipulating/start_entry/startNestedNonExistent"
|
||||
);
|
||||
const { currentTime, targetEntryId, input, expected } =
|
||||
await import("./__fixtures__/manipulating/start_entry/startNestedNonExistent");
|
||||
|
||||
const output = startNewNestedEntry(currentTime, targetEntryId, input);
|
||||
expect(stripEntriesRuntimeData(output)).toEqual(
|
||||
|
|
@ -77,9 +71,7 @@ describe("startNewNestedEntry", () => {
|
|||
|
||||
it("starting a new entry should stop any running entries", async () => {
|
||||
const { currentTime, stopped, targetEntry, input, expected } =
|
||||
await import(
|
||||
"./__fixtures__/manipulating/start_entry/startNestedShouldStopRunning"
|
||||
);
|
||||
await import("./__fixtures__/manipulating/start_entry/startNestedShouldStopRunning");
|
||||
|
||||
const output = startNewNestedEntry(currentTime, targetEntry.id, input);
|
||||
|
||||
|
|
@ -97,9 +89,8 @@ describe("startNewNestedEntry", () => {
|
|||
});
|
||||
|
||||
it("starting a new entry should add a new entry", async () => {
|
||||
const { currentTime, targetEntry, input, expected } = await import(
|
||||
"./__fixtures__/manipulating/start_entry/startNestedShouldStopRunning"
|
||||
);
|
||||
const { currentTime, targetEntry, input, expected } =
|
||||
await import("./__fixtures__/manipulating/start_entry/startNestedShouldStopRunning");
|
||||
|
||||
const output = startNewNestedEntry(currentTime, targetEntry.id, input);
|
||||
expect(stripEntriesRuntimeData(output)).toEqual(
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ import {
|
|||
describe("updateEntry", () => {
|
||||
it("updating existing entry should succeed", async () => {
|
||||
const { entries, entryToUpdate, updatedEntry, expectedEntries } =
|
||||
await import(
|
||||
"./__fixtures__/manipulating/update_entry/updateEntry"
|
||||
);
|
||||
await import("./__fixtures__/manipulating/update_entry/updateEntry");
|
||||
|
||||
const updated = updateEntry(entries, entryToUpdate.id, updatedEntry);
|
||||
expect(updated).toEqual(expectedEntries);
|
||||
|
|
@ -21,9 +19,8 @@ describe("updateEntry", () => {
|
|||
|
||||
describe("setEntryCollapsed", () => {
|
||||
it("should update group collapse state when set to true", async () => {
|
||||
const { input } = await import(
|
||||
"./__fixtures__/manipulating/collapse/shouldUpdateCollapseTrue"
|
||||
);
|
||||
const { input } =
|
||||
await import("./__fixtures__/manipulating/collapse/shouldUpdateCollapseTrue");
|
||||
|
||||
const collapsed = setEntryCollapsed(input, true);
|
||||
|
||||
|
|
@ -33,9 +30,8 @@ describe("setEntryCollapsed", () => {
|
|||
});
|
||||
|
||||
it("collapsed state should be undefined when false", async () => {
|
||||
const { input } = await import(
|
||||
"./__fixtures__/manipulating/collapse/shouldUpdateCollapseFalse"
|
||||
);
|
||||
const { input } =
|
||||
await import("./__fixtures__/manipulating/collapse/shouldUpdateCollapseFalse");
|
||||
const collapsed = setEntryCollapsed(input, false);
|
||||
|
||||
expect(collapsed.subEntries).not.toBeNull();
|
||||
|
|
@ -43,9 +39,8 @@ describe("setEntryCollapsed", () => {
|
|||
});
|
||||
|
||||
it("should not set collapse state on single entry", async () => {
|
||||
const { input } = await import(
|
||||
"./__fixtures__/manipulating/collapse/shouldNotCollapseSingleEntry"
|
||||
);
|
||||
const { input } =
|
||||
await import("./__fixtures__/manipulating/collapse/shouldNotCollapseSingleEntry");
|
||||
|
||||
const collapsed = setEntryCollapsed(input, true);
|
||||
|
||||
|
|
@ -56,9 +51,8 @@ describe("setEntryCollapsed", () => {
|
|||
|
||||
describe("stopRunningEntries", () => {
|
||||
it("should stop running entries", async () => {
|
||||
const { input, endTime, expected } = await import(
|
||||
"./__fixtures__/manipulating/stopping_entries/stopRunningEntries"
|
||||
);
|
||||
const { input, endTime, expected } =
|
||||
await import("./__fixtures__/manipulating/stopping_entries/stopRunningEntries");
|
||||
|
||||
const output = stopRunningEntries(input, endTime);
|
||||
expect(output).toEqual(expected);
|
||||
|
|
@ -67,50 +61,44 @@ describe("stopRunningEntries", () => {
|
|||
|
||||
describe("removeEntry", () => {
|
||||
it("remove on single entry should stay same if not target", async () => {
|
||||
const { entries, entryToRemove, expectedEntries } = await import(
|
||||
"./__fixtures__/manipulating/remove_entry/removeSingleEntry"
|
||||
);
|
||||
const { entries, entryToRemove, expectedEntries } =
|
||||
await import("./__fixtures__/manipulating/remove_entry/removeSingleEntry");
|
||||
|
||||
const updated = removeEntry(entries, entryToRemove);
|
||||
expect(updated).toEqual(expectedEntries);
|
||||
});
|
||||
|
||||
it("should be able to remove entry", async () => {
|
||||
const { entries, entryToRemove, expectedEntries } = await import(
|
||||
"./__fixtures__/manipulating/remove_entry/removeEntrySuccess"
|
||||
);
|
||||
const { entries, entryToRemove, expectedEntries } =
|
||||
await import("./__fixtures__/manipulating/remove_entry/removeEntrySuccess");
|
||||
const updated = removeEntry(entries, entryToRemove);
|
||||
expect(updated).toEqual(expectedEntries);
|
||||
});
|
||||
|
||||
it("should be able to remove nested entry", async () => {
|
||||
const { entries, entryToRemove, expectedEntries } = await import(
|
||||
"./__fixtures__/manipulating/remove_entry/removeNestedEntry"
|
||||
);
|
||||
const { entries, entryToRemove, expectedEntries } =
|
||||
await import("./__fixtures__/manipulating/remove_entry/removeNestedEntry");
|
||||
const updated = removeEntry(entries, entryToRemove);
|
||||
expect(updated).toEqual(expectedEntries);
|
||||
});
|
||||
|
||||
it("should collapse groups with only one entry on remove", async () => {
|
||||
const { entries, entryToRemove, expectedEntries } = await import(
|
||||
"./__fixtures__/manipulating/remove_entry/removeEntryCollapse"
|
||||
);
|
||||
const { entries, entryToRemove, expectedEntries } =
|
||||
await import("./__fixtures__/manipulating/remove_entry/removeEntryCollapse");
|
||||
const updated = removeEntry(entries, entryToRemove);
|
||||
expect(updated).toEqual(expectedEntries);
|
||||
});
|
||||
|
||||
it("should collapse groups with only one entry on remove (single)", async () => {
|
||||
const { entries, entryToRemove, expectedEntries } = await import(
|
||||
"./__fixtures__/manipulating/remove_entry/removeEntryCollapseSingle"
|
||||
);
|
||||
const { entries, entryToRemove, expectedEntries } =
|
||||
await import("./__fixtures__/manipulating/remove_entry/removeEntryCollapseSingle");
|
||||
const updated = removeEntry(entries, entryToRemove);
|
||||
expect(updated).toEqual(expectedEntries);
|
||||
});
|
||||
|
||||
it("should not collapse folder on empty entries", async () => {
|
||||
const { entries, entryToRemove, expectedEntries } = await import(
|
||||
"./__fixtures__/manipulating/remove_entry/removeEntryFolder"
|
||||
);
|
||||
const { entries, entryToRemove, expectedEntries } =
|
||||
await import("./__fixtures__/manipulating/remove_entry/removeEntryFolder");
|
||||
const updated = removeEntry(entries, entryToRemove);
|
||||
expect(updated).toEqual(expectedEntries);
|
||||
});
|
||||
|
|
@ -118,9 +106,8 @@ describe("removeEntry", () => {
|
|||
|
||||
describe("removeSubEntry", () => {
|
||||
it("attempting to remove sub entry on non group should do nothing", async () => {
|
||||
const { parent, entryToRemove } = await import(
|
||||
"./__fixtures__/manipulating/remove_entry/removeEntry"
|
||||
);
|
||||
const { parent, entryToRemove } =
|
||||
await import("./__fixtures__/manipulating/remove_entry/removeEntry");
|
||||
const output = removeSubEntry(parent, entryToRemove);
|
||||
expect(output).toEqual(parent);
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue