Skip to content

Commit 1f2af88

Browse files
committed
more passes at a consistent web component lifecycle
1 parent 7548681 commit 1f2af88

10 files changed

Lines changed: 122 additions & 37 deletions

File tree

components/column-selector/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ export default class ColumnSelector extends HTMLElement {
3434
* Authorization gate - checks permissions before rendering.
3535
* Removes component if user lacks LINE SELECTOR view access.
3636
*/
37-
async authgate() {
37+
authgate() {
3838
if (!CheckPermissions.checkViewAccess("LINE", "SELECTOR")) {
3939
this.remove()
4040
return
4141
}
42-
await this.findColumnsData()
42+
this.findColumnsData()
4343
}
4444

4545
disconnectedCallback() {

components/create-column/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,12 @@ class TpenCreateColumn extends HTMLElement {
247247
* Authorization gate - checks permissions before loading page.
248248
* Shows permission message if user lacks LINE SELECTOR all access.
249249
*/
250-
async authgate() {
250+
authgate() {
251251
if (!CheckPermissions.checkAllAccess("LINE", "SELECTOR")) {
252252
this.container.innerHTML = `<div class="error-message">You do not have permission to manage columns.</div>`
253253
return
254254
}
255-
await this.loadPage(this.pageID)
255+
this.loadPage(this.pageID)
256256
}
257257

258258
disconnectedCallback() {

components/new-action.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
import TPEN from "../api/TPEN.js"
2+
import { CleanupRegistry } from '../utilities/CleanupRegistry.js'
23

4+
/**
5+
* NewAction - Displays quick action links for creating projects, importing manifests, etc.
6+
* @element tpen-new-action
7+
*/
38
class NewAction extends HTMLElement {
9+
/** @type {CleanupRegistry} Registry for cleanup handlers */
10+
cleanup = new CleanupRegistry()
11+
412
constructor() {
513
super()
614
this.attachShadow({ mode: 'open' })
15+
}
16+
17+
connectedCallback() {
18+
TPEN.attachAuthentication(this)
19+
this.render()
20+
this.addEventListeners()
21+
}
22+
23+
disconnectedCallback() {
24+
this.cleanup.run()
25+
}
26+
27+
render() {
728
this.shadowRoot.innerHTML = `
8-
<style>
29+
<style>
930
.new-action {
1031
display: grid;
1132
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
@@ -60,8 +81,10 @@ class NewAction extends HTMLElement {
6081
</a>
6182
</div>
6283
`
84+
}
6385

64-
this.shadowRoot.getElementById("link-tpen-2.8").addEventListener("click", this.TPEN2ImportHandler.bind(this))
86+
addEventListeners() {
87+
this.cleanup.onElement(this.shadowRoot.getElementById("link-tpen-2.8"), "click", this.TPEN2ImportHandler.bind(this))
6588
}
6689

6790
TPEN2ImportHandler = async (event) => {

components/page-tool/index.js

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import TPEN from '../../api/TPEN.js'
22
import CheckPermissions from "../check-permissions/checkPermissions.js"
33
import { onProjectReady } from "../../utilities/projectReady.js"
4+
import { CleanupRegistry } from '../../utilities/CleanupRegistry.js'
45

6+
/**
7+
* PageTool - Provides image manipulation tools like contrast, brightness, grayscale, and invert.
8+
* Requires TOOL ANY view access.
9+
* @element tpen-page-tool
10+
*/
511
export default class PageTool extends HTMLElement {
12+
/** @type {CleanupRegistry} Registry for cleanup handlers */
13+
cleanup = new CleanupRegistry()
614
#drawerContent
715
#drawerToggleBtn
816

@@ -39,6 +47,7 @@ export default class PageTool extends HTMLElement {
3947
}
4048

4149
connectedCallback() {
50+
TPEN.attachAuthentication(this)
4251
this._unsubProject = onProjectReady(this, this.authgate.bind(this))
4352
}
4453

@@ -53,12 +62,12 @@ export default class PageTool extends HTMLElement {
5362

5463
disconnectedCallback() {
5564
try { this._unsubProject?.() } catch {}
56-
if (this._escapeHandler) window.removeEventListener('keydown', this._escapeHandler)
65+
this.cleanup.run()
5766
}
5867

5968
addEventListeners() {
60-
this.drawerToggleBtn?.addEventListener('click', () => this.toggleDrawer())
61-
this.shadowRoot.querySelector('.drawer-close-btn')?.addEventListener('click', () => this.closeDrawer())
69+
this.cleanup.onElement(this.drawerToggleBtn, 'click', () => this.toggleDrawer())
70+
this.cleanup.onElement(this.shadowRoot.querySelector('.drawer-close-btn'), 'click', () => this.closeDrawer())
6271
}
6372

6473
toggleDrawer() {
@@ -476,20 +485,20 @@ export default class PageTool extends HTMLElement {
476485
</div>
477486
`
478487

479-
this.shadowRoot.querySelector('.grayscale-btn')?.addEventListener('click', () => this.toggleGrayscale())
480-
this.shadowRoot.querySelector('.invert-btn')?.addEventListener('click', () => this.toggleInvert())
481-
this.shadowRoot.querySelector('.contrast-slider')?.addEventListener('input', (e) => this.setContrast(e))
482-
this.shadowRoot.querySelector('.brightness-slider')?.addEventListener('input', (e) => this.setBrightness(e))
483-
this.shadowRoot.querySelector('.reset-btn')?.addEventListener('click', () => this.resetFilters())
488+
this.cleanup.onElement(this.shadowRoot.querySelector('.grayscale-btn'), 'click', () => this.toggleGrayscale())
489+
this.cleanup.onElement(this.shadowRoot.querySelector('.invert-btn'), 'click', () => this.toggleInvert())
490+
this.cleanup.onElement(this.shadowRoot.querySelector('.contrast-slider'), 'input', (e) => this.setContrast(e))
491+
this.cleanup.onElement(this.shadowRoot.querySelector('.brightness-slider'), 'input', (e) => this.setBrightness(e))
492+
this.cleanup.onElement(this.shadowRoot.querySelector('.reset-btn'), 'click', () => this.resetFilters())
484493
if (CheckPermissions.checkEditAccess("LINE", "SELECTOR")) {
485494
const linesBtn = this.shadowRoot.querySelector('.lines-btn')
486495
const columnsBtn = this.shadowRoot.querySelector('.columns-btn')
487496
this.shadowRoot.querySelector('.parsing-section').style.display = "block"
488497
linesBtn.style.display = "block"
489498
columnsBtn.style.display = "block"
490-
linesBtn.addEventListener('click', () =>
499+
this.cleanup.onElement(linesBtn, 'click', () =>
491500
document.location.href = `/annotator?projectID=${TPEN.activeProject._id}&pageID=${TPEN.screen.pageInQuery}`)
492-
columnsBtn.addEventListener('click', () =>
501+
this.cleanup.onElement(columnsBtn, 'click', () =>
493502
document.location.href = `/manage-columns?projectID=${TPEN.activeProject._id}&pageID=${TPEN.screen.pageInQuery}`)
494503
}
495504
}

components/quicktype-tool/index.js

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,26 @@ import { escapeHtml } from "/js/utils.js"
33
const eventDispatcher = TPEN.eventDispatcher
44
import CheckPermissions from "../check-permissions/checkPermissions.js"
55
import { onProjectReady } from "../../utilities/projectReady.js"
6+
import { CleanupRegistry } from '../../utilities/CleanupRegistry.js'
67
import "./quicktype-editor-dialog.js"
78

9+
/**
10+
* QuickTypeTool - Provides keyboard shortcut buttons for inserting special characters.
11+
* Requires PROJECT CONTENT edit access for shortcuts, PROJECT OPTIONS edit access for editing.
12+
* @element tpen-quicktype-tool
13+
*/
814
class QuickTypeTool extends HTMLElement {
15+
/** @type {CleanupRegistry} Registry for cleanup handlers */
16+
cleanup = new CleanupRegistry()
17+
918
constructor() {
1019
super()
1120
this.attachShadow({ mode: "open" })
1221
this._invalidToastShown = false
1322
}
1423

1524
connectedCallback() {
25+
TPEN.attachAuthentication(this)
1626
this._unsubProject = onProjectReady(this, () => {
1727
this.render()
1828
this.addEventListeners()
@@ -22,7 +32,7 @@ class QuickTypeTool extends HTMLElement {
2232

2333
disconnectedCallback() {
2434
try { this._unsubProject?.() } catch { }
25-
try { this._unsubEditorSaved?.() } catch { }
35+
this.cleanup.run()
2636
}
2737

2838
initializeDialog() {
@@ -33,7 +43,7 @@ class QuickTypeTool extends HTMLElement {
3343
}
3444

3545
// Listen for save events to refresh the panel
36-
this._unsubEditorSaved = eventDispatcher.on("quicktype-editor-saved", (event) => {
46+
this.cleanup.onEvent(eventDispatcher, "quicktype-editor-saved", (event) => {
3747
// Refresh the tool panel with updated shortcuts
3848
TPEN.activeProject.interfaces.quicktype = event.detail.quicktype
3949
this.render()
@@ -42,19 +52,20 @@ class QuickTypeTool extends HTMLElement {
4252
}
4353

4454
addEventListeners() {
45-
const charPanel = this.shadowRoot.querySelector('.char-panel')
4655
const editCharBtn = this.shadowRoot.querySelector('.edit-char-btn')
4756

48-
editCharBtn.addEventListener('click', () => {
49-
const dialog = document.querySelector('tpen-quicktype-editor-dialog')
50-
if (dialog) {
51-
const quicktype = TPEN.activeProject?.interfaces?.quicktype ?? []
52-
dialog.open(quicktype)
53-
}
54-
})
57+
if (editCharBtn) {
58+
this.cleanup.onElement(editCharBtn, 'click', () => {
59+
const dialog = document.querySelector('tpen-quicktype-editor-dialog')
60+
if (dialog) {
61+
const quicktype = TPEN.activeProject?.interfaces?.quicktype ?? []
62+
dialog.open(quicktype)
63+
}
64+
})
65+
}
5566

5667
this.shadowRoot.querySelectorAll('.char-button').forEach(btn => {
57-
btn.addEventListener('click', () => {
68+
this.cleanup.onElement(btn, 'click', () => {
5869
const char = btn.textContent
5970
const iface = document.querySelector('[data-interface-type="transcription"]')
6071
const block = iface?.shadowRoot?.querySelector('tpen-transcription-block')?.shadowRoot
@@ -283,14 +294,23 @@ class QuickTypeTool extends HTMLElement {
283294

284295
customElements.define('tpen-quicktype-tool', QuickTypeTool)
285296

297+
/**
298+
* QuickTypeToolButton - Toggle button for the QuickType panel.
299+
* Requires TOOL ANY view access.
300+
* @element tpen-quicktype-tool-button
301+
*/
286302
class QuickTypeToolButton extends HTMLElement {
303+
/** @type {CleanupRegistry} Registry for cleanup handlers */
304+
cleanup = new CleanupRegistry()
305+
287306
constructor() {
288307
super()
289308
this.attachShadow({ mode: "open" })
290309
}
291310

292311
connectedCallback() {
293-
this._unsubProject = onProjectReady(this, this.authgate)
312+
TPEN.attachAuthentication(this)
313+
this._unsubProject = onProjectReady(this, this.authgate.bind(this))
294314
}
295315

296316
authgate() {
@@ -304,12 +324,13 @@ class QuickTypeToolButton extends HTMLElement {
304324

305325
disconnectedCallback() {
306326
try { this._unsubProject?.() } catch { }
327+
this.cleanup.run()
307328
}
308329

309330
addEventListeners() {
310331
const quicktypeBtn = this.shadowRoot.querySelector('.quicktype-btn')
311332

312-
quicktypeBtn.addEventListener('click', () => {
333+
this.cleanup.onElement(quicktypeBtn, 'click', () => {
313334
const iface = document.querySelector('[data-interface-type="transcription"]')
314335
const charPanel = iface?.shadowRoot
315336
?.querySelector('tpen-workspace-tools')?.shadowRoot

components/simple-transcription/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,21 @@ export default class SimpleTranscriptionInterface extends HTMLElement {
147147
}
148148
}
149149

150-
async authgate() {
150+
authgate() {
151151
if (!CheckPermissions.checkViewAccess("ANY", "CONTENT")) {
152152
this.renderPermissionError()
153153
return
154154
}
155155
this.render()
156156
this.addEventListeners()
157157
this.setupResizableSplit()
158+
this.initializeAsync()
159+
}
160+
161+
/**
162+
* Performs async initialization after authgate passes.
163+
*/
164+
async initializeAsync() {
158165
const pageID = TPEN.screen?.pageInQuery
159166
await this.updateTranscriptionImages(pageID)
160167

components/splitscreen-tool/index.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,25 @@ import CheckPermissions from "../check-permissions/checkPermissions.js"
22
import TPEN from "../../api/TPEN.js"
33
const eventDispatcher = TPEN.eventDispatcher
44
import { onProjectReady } from "../../utilities/projectReady.js"
5+
import { CleanupRegistry } from '../../utilities/CleanupRegistry.js'
56

7+
/**
8+
* SplitscreenTool - Dropdown selector for activating split-screen tools.
9+
* Requires TOOL ANY view access.
10+
* @element tpen-splitscreen-tool
11+
*/
612
export default class SplitscreenTool extends HTMLElement {
13+
/** @type {CleanupRegistry} Registry for cleanup handlers */
14+
cleanup = new CleanupRegistry()
15+
716
constructor() {
817
super()
918
this.attachShadow({ mode: "open" })
1019
}
1120

1221
connectedCallback() {
13-
this._unsubProject = onProjectReady(this, this.authgate)
22+
TPEN.attachAuthentication(this)
23+
this._unsubProject = onProjectReady(this, this.authgate.bind(this))
1424
}
1525

1626
authgate() {
@@ -25,16 +35,17 @@ export default class SplitscreenTool extends HTMLElement {
2535

2636
disconnectedCallback() {
2737
try { this._unsubProject?.() } catch {}
38+
this.cleanup.run()
2839
}
2940

3041
addEventListeners() {
3142
const dropdown = this.shadowRoot.querySelector('.dropdown-select')
3243
if (dropdown) {
33-
dropdown.addEventListener('click', (e) => {
44+
this.cleanup.onElement(dropdown, 'click', (e) => {
3445
e.target.dataset.prev = e.target.value
3546
})
3647

37-
dropdown.addEventListener('change', (e) => {
48+
this.cleanup.onElement(dropdown, 'change', (e) => {
3849
const value = e.target.value
3950
this.dispatchEvent(new CustomEvent('splitscreen-toggle', {
4051
bubbles: true,

components/transcription-block/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,20 @@ export default class TranscriptionBlock extends HTMLElement {
8585
TPEN.eventDispatcher.on('tpen-active-line-updated', this._activeLineUpdatedHandler)
8686
}
8787

88-
async authgate() {
88+
authgate() {
8989
if (!CheckPermissions.checkViewAccess("ANY", "CONTENT")) {
9090
this.remove()
9191
return
9292
}
9393
this.render()
9494
this.addEventListeners()
95+
this.initializeAsync()
96+
}
97+
98+
/**
99+
* Performs async initialization after authgate passes.
100+
*/
101+
async initializeAsync() {
95102
const pageID = TPEN.screen?.pageInQuery
96103
this.#page = await vault.get(pageID, 'annotationpage', true)
97104
const projectPage = TPEN.activeProject.layers.flatMap(layer => layer.pages || []).find(p => p.id.split('/').pop() === pageID.split('/').pop())

interfaces/manage-columns/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,13 +294,13 @@ class TpenManageColumns extends HTMLElement {
294294
* Authorization gate - checks permissions before rendering the interface.
295295
* Renders permission error if user lacks LINE SELECTOR access.
296296
*/
297-
async authgate() {
297+
authgate() {
298298
if (!CheckPermissions.checkAllAccess("LINE", "SELECTOR")) {
299299
renderPermissionError(this.shadowRoot, TPEN.screen?.projectInQuery ?? '')
300300
return
301301
}
302302
this.addEventListeners()
303-
await this.loadPage(this.pageID)
303+
this.loadPage(this.pageID)
304304
}
305305

306306
/**

interfaces/transcription/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,21 @@ export default class TranscriptionInterface extends HTMLElement {
4949
window.addEventListener('message', this.messageHandler)
5050
}
5151

52-
async authgate() {
52+
authgate() {
5353
if (!CheckPermissions.checkViewAccess("ANY", "CONTENT")) {
5454
this.renderPermissionError()
5555
return
5656
}
5757
this.render()
5858
this.addEventListeners()
5959
this.setupResizableSplit()
60+
this.initializeAsync()
61+
}
62+
63+
/**
64+
* Performs async initialization after authgate passes.
65+
*/
66+
async initializeAsync() {
6067
const pageID = TPEN.screen?.pageInQuery
6168
await this.updateTranscriptionImages(pageID)
6269
this.updateLines()

0 commit comments

Comments
 (0)