From 7f7b7e03564aff8afee8d909830bb17c839c6ade Mon Sep 17 00:00:00 2001 From: Phil Stenning Date: Sat, 25 Jun 2022 12:36:53 +0100 Subject: [PATCH] refactor: add typings for typescript Some of the typings were missing and lots of red lines appeared in vs-code, their should now be no errors shown by the ts compiler. --- app/javascript/src/bulk_edit.ts | 36 ++++++++--- app/javascript/src/globals.d.ts | 10 +++ app/javascript/src/model_edit.ts | 3 +- app/javascript/src/preview.ts | 107 +++++++++++++++++++++++-------- package.json | 8 +++ tsconfig.json | 19 +++--- yarn.lock | 39 +++++++++++ 7 files changed, 175 insertions(+), 47 deletions(-) create mode 100644 app/javascript/src/globals.d.ts diff --git a/app/javascript/src/bulk_edit.ts b/app/javascript/src/bulk_edit.ts index d5051e9a9..05416353a 100644 --- a/app/javascript/src/bulk_edit.ts +++ b/app/javascript/src/bulk_edit.ts @@ -1,4 +1,4 @@ -function updateTagOptions (tags, input, addTags = true): void { +function updateTagOptions (tags: string[], input, addTags = true): void { tags.forEach((tag) => { if (addTags) { input.addOption({ value: tag, text: tag }) @@ -11,23 +11,37 @@ function updateTagOptions (tags, input, addTags = true): void { function getTags (modelId: string): string[] { const selector = `[data-bulk-item-tags="${modelId}"]` - const tagLinks = document.querySelectorAll(selector) - return Array.prototype.slice.call(tagLinks).map((tag) => (tag.innerText)) + const tagLinks = document.querySelectorAll( + selector + ) + return Array.prototype.slice + .call(tagLinks) + .map((tag: HTMLAnchorElement) => tag.innerHTML) } function handleCheckboxChange (event): void { + const target = event.target as HTMLInputElement + const { name } = target event.preventDefault() - if (event.target.name === 'bulk-select-all') { - document.querySelectorAll('[data-bulk-item]').forEach((cb): boolean => { - cb.checked = !(cb.checked as boolean) - }) + console.log({ target }, { name }) + // the bulk select checkbox has been selected + if (target.name === 'bulk-select-all') { + document + .querySelectorAll('[data-bulk-item]') + .forEach((checkbox: HTMLInputElement) => { + checkbox.checked = target.checked + }) } else { - const modelId = event.target.getAttribute('data-bulk-item') as string + // a single checkbox item has been selected. + const modelId = target.getAttribute('data-bulk-item') as string if (modelId != null) { const tags = getTags(modelId) + if (tags.length > 0 && window.tagInputs != null) { + const { tagInputs } = window + console.log({ tagInputs }) window.tagInputs.forEach((input) => { - updateTagOptions(tags, input[0].selectize, event.target.checked) + updateTagOptions(tags, input[0].selectize, target.checked) }) } } @@ -35,7 +49,9 @@ function handleCheckboxChange (event): void { } document.addEventListener('turbolinks:load', () => { - const bulkEditTable = document.querySelector('[data-bulk-edit]') + const bulkEditTable = document.querySelector( + '[data-bulk-edit]' + ) if (bulkEditTable != null) { bulkEditTable.removeEventListener('change', handleCheckboxChange) bulkEditTable.addEventListener('change', handleCheckboxChange) diff --git a/app/javascript/src/globals.d.ts b/app/javascript/src/globals.d.ts new file mode 100644 index 000000000..e959b38ab --- /dev/null +++ b/app/javascript/src/globals.d.ts @@ -0,0 +1,10 @@ +import { ObjectPreview } from './preview' + +declare global { + interface Window { + tagInputs: Array> + } + interface HTMLCanvasElement { + renderer: ObjectPreview + } +} diff --git a/app/javascript/src/model_edit.ts b/app/javascript/src/model_edit.ts index f81fb01c8..49aa0565d 100644 --- a/app/javascript/src/model_edit.ts +++ b/app/javascript/src/model_edit.ts @@ -4,8 +4,7 @@ document.addEventListener('turbolinks:load', () => { function () { const tagInput = $(this).selectize({ create: true, - sortField: 'text', - maxItems: null + sortField: 'text' }) window.tagInputs.push(tagInput) } diff --git a/app/javascript/src/preview.ts b/app/javascript/src/preview.ts index 4bad80562..d3dc720c5 100644 --- a/app/javascript/src/preview.ts +++ b/app/javascript/src/preview.ts @@ -6,7 +6,28 @@ import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader.js' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' class ObjectPreview { - constructor (canvas, progressIndicator, url, format, yUp, gridSizeX, gridSizeZ) { + canvas: HTMLCanvasElement + url: string + format: string + yUp: boolean + gridSizeX: number + gridSizeZ: number + scene: THREE.Scene + renderer: THREE.WebGLRenderer + camera: THREE.PerspectiveCamera + controls: OrbitControls + gridHelper: THREE.GridHelper + frame: number + progressIndicator: HTMLDivElement + constructor ( + canvas: HTMLCanvasElement, + progressIndicator: HTMLDivElement, + url: string, + format: string, + yUp: boolean, + gridSizeX: number, + gridSizeZ: number + ) { this.canvas = canvas this.progressIndicator = progressIndicator this.url = url @@ -14,16 +35,28 @@ class ObjectPreview { this.yUp = yUp this.gridSizeX = gridSizeX this.gridSizeZ = gridSizeZ - const observer = new window.IntersectionObserver(this.onIntersectionChanged.bind(this), {}) + const observer = new window.IntersectionObserver( + this.onIntersectionChanged.bind(this), + {} + ) observer.observe(canvas) } setup (): void { this.scene = new THREE.Scene() this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas }) - this.camera = new THREE.PerspectiveCamera(45, this.canvas.clientWidth / this.canvas.clientHeight, 0.1, 1000) + this.camera = new THREE.PerspectiveCamera( + 45, + this.canvas.clientWidth / this.canvas.clientHeight, + 0.1, + 1000 + ) this.camera.position.z = 50 - this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight, false) + this.renderer.setSize( + this.canvas.clientWidth, + this.canvas.clientHeight, + false + ) this.controls = new OrbitControls(this.camera, this.renderer.domElement) this.controls.enableDamping = true this.controls.enablePan = false @@ -37,8 +70,8 @@ class ObjectPreview { } } - load (url, format): void { - let loader = null + load (url: string, format: string): void { + let loader: OBJLoader | STLLoader | ThreeMFLoader | PLYLoader | null = null switch (format) { case 'obj': loader = new OBJLoader() @@ -54,7 +87,8 @@ class ObjectPreview { break } if (loader !== null) { - loader.load(url, + loader.load( + url, this.onLoad.bind(this), this.onProgress.bind(this), this.onLoadError.bind(this) @@ -63,7 +97,8 @@ class ObjectPreview { } onProgress (xhr): void { - const percentage = Math.floor(xhr.loaded / xhr.total * 100).toString() + '%' + const percentage = + Math.floor((xhr.loaded / xhr.total) * 100).toString() + '%' this.progressIndicator.style.width = percentage this.progressIndicator.ariaValueNow = percentage this.progressIndicator.textContent = percentage @@ -74,7 +109,7 @@ class ObjectPreview { const material = new THREE.MeshNormalMaterial({ flatShading: true }) - let object = null + let object: THREE.Mesh | null = null if (model.type === 'BufferGeometry') { object = new THREE.Mesh(model, material) } else { @@ -85,14 +120,29 @@ class ObjectPreview { }) object = model } + + if (object == null) return // Transform to screen coords from print - if (this.yUp === false) { + if (!this.yUp) { const coordSystemTransform = new THREE.Matrix4() coordSystemTransform.set( - 1, 0, 0, 0, // x -> x - 0, 0, 1, 0, // z -> y - 0, -1, 0, 0, // y -> -z - 0, 0, 0, 1) + 1, + 0, + 0, + 0, // x -> x + 0, + 0, + 1, + 0, // z -> y + 0, + -1, + 0, + 0, // y -> -z + 0, + 0, + 0, + 1 + ) object.applyMatrix4(coordSystemTransform) } // Calculate bounding volumes @@ -101,24 +151,25 @@ class ObjectPreview { bbox.getCenter(centre) const bsphere = new THREE.Sphere() bbox.getBoundingSphere(bsphere) - const modelheight = bbox.max.y - bbox.min.y + const modelHeight = bbox.max.y - bbox.min.y // Configure camera this.camera.position.z = this.camera.position.x = bsphere.radius * 1.63 this.camera.position.y = bsphere.radius * 0.75 - this.controls.target = new THREE.Vector3(0, modelheight / 2, 0) + this.controls.target = new THREE.Vector3(0, modelHeight / 2, 0) // Centre the model object.position.set(-centre.x, -bbox.min.y, -centre.z) this.scene.add(object) // Add the grid - this.gridHelper = new THREE.GridHelper(this.gridSizeX, this.gridSizeX / 10, 'magenta', 'cyan') + this.gridHelper = new THREE.GridHelper( + this.gridSizeX, + this.gridSizeX / 10, + 'magenta', + 'cyan' + ) this.scene.add(this.gridHelper) // Render first frame this.onAnimationFrame() - - bbox.dispose() - centre.dispose() - bsphere.dispose() } onLoadError (): void { @@ -160,12 +211,14 @@ document.addEventListener('turbolinks:load', () => { canvas.height = canvas.width canvas.renderer = new ObjectPreview( canvas, - div.getElementsByClassName('progress-bar')[0], - canvas.dataset.previewUrl, - canvas.dataset.format, - (canvas.dataset.yUp === 'true'), - canvas.dataset.gridSizeX, - canvas.dataset.gridSizeZ + div.getElementsByClassName('progress-bar')[0] as HTMLDivElement, + canvas.dataset.previewUrl ?? '/', + canvas.dataset.format ?? '', + canvas.dataset.yUp === 'true', + parseInt(canvas.dataset.gridSizeX ?? '10', 10), + parseInt(canvas.dataset.gridSizeZ ?? '10', 10) ) }) }) + +export { ObjectPreview } diff --git a/package.json b/package.json index 62ddce9ed..b1f59b7bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { "name": "van-dam", "private": true, + "scripts": { + "lint:ts": "ts-standard --fix" + }, "dependencies": { "@babel/preset-typescript": "^7.17.12", "@nathanvda/cocoon": "^1.2.14", @@ -23,6 +26,11 @@ "version": "0.1.0", "devDependencies": { "@babel/eslint-parser": "^7.18.2", + "@types/jquery": "^3.5.14", + "@types/node": "^18.0.0", + "@types/selectize": "^0.12.35", + "@types/three": "^0.141.0", + "@types/webpack-env": "^1.17.0", "@typescript-eslint/parser": "^4.33.0", "eslint": "^7.32.0", "husky": "^8.0.1", diff --git a/tsconfig.json b/tsconfig.json index 3b4f37b8d..f4f82c48d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,18 +8,21 @@ "moduleResolution": "node", "baseUrl": ".", "paths": { - "*": ["node_modules/*", "app/javascript/*"] + "*": ["node_modules/*", "app/javascript/*"] }, "sourceMap": true, "strictNullChecks": true, "target": "es5", - "noEmit": true + "noEmit": true, + "types": [ + "./app/javascript/src/globals", + "node", + "webpack-env", + "three", + "jquery", + "selectize" + ] }, - "exclude": [ - "**/*.spec.ts", - "node_modules", - "vendor", - "public" - ], + "exclude": ["**/*.spec.ts", "node_modules", "vendor", "public"], "compileOnSave": false } diff --git a/yarn.lock b/yarn.lock index d6da7ab54..fd9085a9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1540,6 +1540,13 @@ dependencies: "@types/node" "*" +"@types/jquery@^3.5.14": + version "3.5.14" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.14.tgz#ac8e11ee591e94d4d58da602cb3a5a8320dee577" + integrity sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg== + dependencies: + "@types/sizzle" "*" + "@types/json-schema@^7.0.5": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" @@ -1565,6 +1572,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18" integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw== +"@types/node@^18.0.0": + version "18.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" + integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -1590,6 +1602,11 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065" integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g== +"@types/selectize@^0.12.35": + version "0.12.35" + resolved "https://registry.yarnpkg.com/@types/selectize/-/selectize-0.12.35.tgz#61cc9353f21e09b267a5a8b133cf819706562d39" + integrity sha512-OtutSd9mfyl2qjxNABguUGhwRmHcJRYqRNQEUVBC6QIw0P0DKbhsAF5RUhid1L0GdYGNef8l73kSB1KVpazXqA== + "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" @@ -1605,6 +1622,11 @@ "@types/mime" "^1" "@types/node" "*" +"@types/sizzle@*": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" + integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== + "@types/sockjs@^0.3.33": version "0.3.33" resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" @@ -1612,6 +1634,23 @@ dependencies: "@types/node" "*" +"@types/three@^0.141.0": + version "0.141.0" + resolved "https://registry.yarnpkg.com/@types/three/-/three-0.141.0.tgz#d9d81a54b28ebc2a56931dfd4d9c54d25c20d6c8" + integrity sha512-OJdKDgTPVBUgc+s74DYoy4aLznbFFC38Xm4ElmU1YwGNgR7GGFVvFCX7lpVgOsT6S1zSJtGdajTsOYE8/xY9nA== + dependencies: + "@types/webxr" "*" + +"@types/webpack-env@^1.17.0": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.17.0.tgz#f99ce359f1bfd87da90cc4a57cab0a18f34a48d0" + integrity sha512-eHSaNYEyxRA5IAG0Ym/yCyf86niZUIF/TpWKofQI/CVfh5HsMEUyfE2kwFxha4ow0s5g0LfISQxpDKjbRDrizw== + +"@types/webxr@*": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.4.0.tgz#ad06c96a324293e0d5175d13dd5ded5931f90ba3" + integrity sha512-LQvrACV3Pj17GpkwHwXuTd733gfY+D7b9mKdrTmLdO7vo7P/o6209Qqtk63y/FCv/lspdmi0pWz6Qe/ull9kQg== + "@types/ws@^8.5.1": version "8.5.3" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"