terrier-engine 4.52.3 → 4.52.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -49,8 +49,6 @@ export default class DiveEditor extends ContentPart<DiveEditorState> {
49
49
  static readonly diveChangedKey = Messages.untypedKey()
50
50
 
51
51
  queries = new Map<string, Query>()
52
- // Array of map keys to give the queries a sort order.
53
- queryOrder = new Array<string>()
54
52
 
55
53
  async init() {
56
54
  this.queryTabs = this.makePart(TabContainerPart, { side: 'top', reorderable: true })
@@ -82,7 +80,6 @@ export default class DiveEditor extends ContentPart<DiveEditorState> {
82
80
  this.queries = new Map()
83
81
  for (const query of this.state.dive.query_data?.queries || []) {
84
82
  this.queries.set(query.id, query)
85
- this.queryOrder.push(query.id)
86
83
  this.addQueryTab(query)
87
84
  }
88
85
 
@@ -92,12 +89,6 @@ export default class DiveEditor extends ContentPart<DiveEditorState> {
92
89
  this.queryTabs.updateTab({ key: query.id, title: query.name })
93
90
  })
94
91
 
95
- // Reorder queries in the list when the tab sort order is updated.
96
- this.listenMessage(this.queryTabs.tabsModifiedKey, m => {
97
- const { newOrder } = m.data
98
- this.queryOrder = newOrder
99
- })
100
-
101
92
  this.onClick(this.newQueryKey, _ => {
102
93
  this.app.showModal(NewQueryModal, { editor: this as DiveEditor, schema: this.state.schema })
103
94
  })
@@ -188,7 +179,7 @@ export default class DiveEditor extends ContentPart<DiveEditorState> {
188
179
 
189
180
  return {
190
181
  ...this.state.dive,
191
- query_data: { queries: this.queryOrder.map(id => queries.get(id)!) }
182
+ query_data: { queries: this.queryTabs.getTabOrder().map(id => queries.get(id)!) }
192
183
  }
193
184
  }
194
185
 
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "files": [
5
5
  "*"
6
6
  ],
7
- "version": "4.52.3",
7
+ "version": "4.52.4",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "https://github.com/Terrier-Tech/terrier-engine"
@@ -21,7 +21,7 @@ export type LoadOnScrollOptions<TState> = {
21
21
  loadNextStates: (existingStates: TState[]) => Promise<TState[] | undefined>
22
22
  // The root element whose viewport is scrolling. If not provided, the document viewport is used.
23
23
  // Usually not required, but if using intersectThreshold on a scrollable element, might be necessary.
24
- intersectRootSelector: string
24
+ intersectRootSelector?: string
25
25
  // Percentage of the last item in the collection that must be visible before the next state is loaded.
26
26
  // If not provided, a default value of 25% is used. Must be between 0 and 1.
27
27
  intersectThreshold?: number
package/terrier/tabs.ts CHANGED
@@ -38,10 +38,11 @@ export type TabContainerState = {
38
38
 
39
39
  export class TabContainerPart extends TerrierPart<TabContainerState> {
40
40
 
41
- private tabs = {} as Record<string, TabDefinition>
41
+ private tabs = new Map as Map<string, TabDefinition>
42
+ private tabOrder = [] as string[]
42
43
  changeTabKey = Messages.typedKey<{ tabKey: string }>()
43
44
  changeSideKey = Messages.typedKey<{ side: TabSide }>()
44
- tabsModifiedKey = Messages.typedKey<{ newOrder: string[] }>()
45
+ tabsModifiedKey = Messages.untypedKey()
45
46
 
46
47
  async init() {
47
48
  this.state = Object.assign({ reorderable: false }, this.state)
@@ -59,22 +60,29 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
59
60
 
60
61
  if (this.state.reorderable) {
61
62
  this.makePlugin(SortablePlugin, {
62
- zoneClass: 'tt-tab-list',
63
+ zoneClass: `tablist-${this.id}`,
63
64
  targetClass: 'tab',
64
- onSorted: (_, evt) => {
65
- this.onTabsModified(evt.toChildren)
65
+ onSorted: () => {
66
+ // Get only our tab container, as other tab contains may exist inside this one.
67
+ const ourTabList = this.element?.getElementsByClassName(`tablist-${this.id}`)[0]
68
+ const tabElements = Array.from(ourTabList?.getElementsByClassName('tab') || []) as HTMLElement[]
69
+ this.tabOrder = tabElements.map(tabElement => tabElement.dataset?.key!)
70
+ this.#onTabsModified()
71
+ this.dirty()
66
72
  }
67
73
  })
68
74
  }
69
75
  }
70
76
 
71
- onTabsModified(tabElementsMaybe?: HTMLElement[]) {
72
- const tabElements = tabElementsMaybe ??
73
- Array.from(this.element?.querySelectorAll('.tt-tab-list') ?? [])
74
- const newOrder = tabElements.map(tabElement => tabElement.dataset.key)
75
- this.emitMessage(this.tabsModifiedKey, { newOrder })
77
+ #onTabsModified() {
78
+ this.emitMessage(this.tabsModifiedKey, null)
76
79
  }
77
80
 
81
+ /**
82
+ * Gets the current tab order as an array of keys.
83
+ */
84
+ getTabOrder = () => [...this.tabOrder]
85
+
78
86
  /**
79
87
  * Adds or overwrites an existing tab.
80
88
  * @param tab initial parameters for the tab
@@ -86,13 +94,15 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
86
94
  constructor: { new(p: PartParent, id: string, state: PartStateType): PartType; },
87
95
  state: InferredPartStateType
88
96
  ): PartType {
89
- const existingTab = this.tabs[tab.key] ?? {}
90
- this.tabs[tab.key] = Object.assign(existingTab, {
91
- state: 'enabled',
92
- }, tab)
93
- this.onTabsModified()
97
+ const existingTab = this.tabs.get(tab.key) ?? {}
94
98
  const part = this.makePart(constructor, state)
95
- existingTab.part = part
99
+ this.tabs.set(tab.key, Object.assign(existingTab, {
100
+ state: 'enabled',
101
+ part
102
+ }, tab))
103
+ if (!this.tabOrder.includes(tab.key))
104
+ this.tabOrder.push(tab.key)
105
+ this.#onTabsModified()
96
106
  this.dirty()
97
107
  return part
98
108
  }
@@ -102,7 +112,7 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
102
112
  * @param tab
103
113
  */
104
114
  updateTab(tab: TabParams): void {
105
- const existingTab = this.tabs[tab.key]
115
+ const existingTab = this.tabs.get(tab.key)
106
116
  if (!existingTab) throw `Tab with key '${tab.key}' does not exist!`
107
117
  Object.assign(existingTab, tab)
108
118
  this.dirty()
@@ -113,13 +123,14 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
113
123
  * @param key
114
124
  */
115
125
  removeTab(key: string) {
116
- const tab = this.tabs[key]
126
+ const tab = this.tabs.get(key)
117
127
  if (!tab) return log.warn(`No tab ${key} to remove!`)
118
128
 
119
129
  log.info(`Removing tab ${key}`, tab)
120
- delete this.tabs[key]
130
+ this.tabs.delete(key)
131
+ this.tabOrder.splice(this.tabOrder.indexOf(key), 1)
121
132
  this.removeChild(tab.part)
122
- this.onTabsModified()
133
+ this.#onTabsModified()
123
134
  this.state.currentTab = undefined
124
135
  this.dirty()
125
136
  }
@@ -129,10 +140,10 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
129
140
  * @param tabKey
130
141
  */
131
142
  showTab(tabKey: string) {
132
- if (!(tabKey in this.tabs)) {
143
+ if (!this.tabs.has(tabKey)) {
133
144
  throw `Unknown tab key ${tabKey}`
134
145
  }
135
- if (this.tabs[tabKey].state != 'enabled') return // tab exists but is not enabled
146
+ if (this.tabs.get(tabKey)?.state != 'enabled') return // tab exists but is not enabled
136
147
  if (this.state.currentTab === tabKey) return // tab is already selected
137
148
 
138
149
  this.state.currentTab = tabKey
@@ -143,7 +154,7 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
143
154
  * Gets the current tab key.
144
155
  */
145
156
  get currentTagKey(): string | undefined {
146
- return this.state.currentTab || Object.keys(this.tabs)[0]
157
+ return this.state.currentTab || this.tabOrder[0]
147
158
  }
148
159
 
149
160
 
@@ -161,20 +172,20 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
161
172
  this.dirty()
162
173
  }
163
174
 
164
-
165
-
166
175
  render(parent: PartTag) {
167
176
  let currentTabKey = this.state.currentTab
168
177
  if (!currentTabKey) {
169
178
  log.debug("no current tab specified, selecting first enabled tab")
170
- currentTabKey = Object.values(this.tabs).find(t => t.state == 'enabled')?.key
179
+ currentTabKey = this.tabs.values().find(t => t.state == 'enabled')?.key
171
180
  }
172
181
  parent.div('tt-tab-container', this.state.side, container => {
173
182
  container.div('.tt-flex.tt-tab-list', tabList => {
183
+ tabList.class(`tablist-${this.id}`)
174
184
  if (this._beforeActions.length) {
175
185
  this.theme.renderActions(tabList, this._beforeActions, { defaultClass: 'action' })
176
186
  }
177
- for (const tab of Object.values(this.tabs)) {
187
+ for (const tabKey of this.tabOrder) {
188
+ const tab = this.tabs.get(tabKey)!
178
189
  if (tab.state == 'hidden') continue
179
190
 
180
191
  tabList.a('.tab', a => {
@@ -195,7 +206,7 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
195
206
  })
196
207
 
197
208
  if (currentTabKey) {
198
- const currentTab = this.tabs[currentTabKey]
209
+ const currentTab = this.tabs.get(currentTabKey)!
199
210
  container.div('.tt-tab-content', panel => {
200
211
  if (currentTab.classes?.length) panel.class(...currentTab.classes)
201
212
  panel.part(currentTab.part as StatelessPart)