terrier-engine 4.71.7 → 4.72.0
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.
- package/data-dive/queries/tables.ts +1 -1
- package/package.json +1 -1
- package/terrier/tabs.ts +108 -16
|
@@ -228,7 +228,7 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
228
228
|
}
|
|
229
229
|
})
|
|
230
230
|
|
|
231
|
-
parent.div(".tt-panel.table-panel", panel => {
|
|
231
|
+
parent.div(".tt-panel.table-panel.stay-as-panel", panel => {
|
|
232
232
|
panel.div('.title', title => {
|
|
233
233
|
const t = this.table as any
|
|
234
234
|
if ('join_type' in t) {
|
package/package.json
CHANGED
package/terrier/tabs.ts
CHANGED
|
@@ -181,26 +181,32 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
|
|
|
181
181
|
}
|
|
182
182
|
parent.div('tt-tab-container', this.state.side, container => {
|
|
183
183
|
container.div('.tt-flex.tt-tab-list', tabList => {
|
|
184
|
-
tabList.class(`tablist-${this.id}`)
|
|
185
184
|
if (this._beforeActions.length) {
|
|
186
185
|
this.theme.renderActions(tabList, this._beforeActions, { defaultClass: 'action' })
|
|
187
186
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
187
|
+
// The tabs live inside a scroll viewport/track so that they can be shifted
|
|
188
|
+
// horizontally (for top/bottom sides) without moving the before/after actions.
|
|
189
|
+
tabList.div('.tt-tab-scroll', scroll => {
|
|
190
|
+
scroll.div('.tt-tab-track', track => {
|
|
191
|
+
track.class(`tablist-${this.id}`)
|
|
192
|
+
for (const tabKey of this.tabOrder) {
|
|
193
|
+
const tab = this.tabs.get(tabKey)!
|
|
194
|
+
if (tab.state == 'hidden') continue
|
|
195
|
+
|
|
196
|
+
track.a('.tab', a => {
|
|
197
|
+
if (tab.tabClasses?.length) a.class(...tab.tabClasses)
|
|
198
|
+
a.attrs({ draggable: this.state.reorderable })
|
|
199
|
+
a.data({ key: tab.key })
|
|
200
|
+
a.class(tab.state || 'enabled')
|
|
201
|
+
if (tab.key === currentTabKey) a.class('active')
|
|
202
|
+
if (tab.icon) this.theme.renderIcon(a, tab.icon, tab.iconColor ? tab.iconColor : 'secondary')
|
|
203
|
+
a.span({ text: tab.title })
|
|
204
|
+
a.emitClick(this.changeTabKey, { tabKey: tab.key })
|
|
205
|
+
if (tab.click) a.emitClick(tab.click.key, tab.click.data || {})
|
|
206
|
+
})
|
|
207
|
+
}
|
|
202
208
|
})
|
|
203
|
-
}
|
|
209
|
+
})
|
|
204
210
|
if (this._afterActions.length) {
|
|
205
211
|
tabList.div('.spacer')
|
|
206
212
|
this.theme.renderActions(tabList, this._afterActions, { defaultClass: 'action' })
|
|
@@ -217,6 +223,92 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
|
|
|
217
223
|
})
|
|
218
224
|
}
|
|
219
225
|
|
|
226
|
+
private tabScrollObserver?: ResizeObserver
|
|
227
|
+
|
|
228
|
+
update(elem: HTMLElement) {
|
|
229
|
+
super.update(elem)
|
|
230
|
+
this.layoutHorizontalTabs()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* For top/bottom tab containers, wires up the horizontal scroll behavior: keeps the selected
|
|
235
|
+
* tab in view, translates vertical mouse-wheel gestures into horizontal scrolling, and keeps
|
|
236
|
+
* the selected tab visible as the container is resized.
|
|
237
|
+
*/
|
|
238
|
+
private layoutHorizontalTabs() {
|
|
239
|
+
const container = this.element?.querySelector('.tt-tab-container')
|
|
240
|
+
const isHorizontal = !!container && (container.classList.contains('top') || container.classList.contains('bottom'))
|
|
241
|
+
if (!isHorizontal) {
|
|
242
|
+
this.tabScrollObserver?.disconnect()
|
|
243
|
+
this.tabScrollObserver = undefined
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const viewport = container!.querySelector('.tt-tab-scroll') as HTMLElement | null
|
|
248
|
+
if (!viewport) return
|
|
249
|
+
|
|
250
|
+
// The viewport is recreated on every full render, so the observer has to be re-pointed and
|
|
251
|
+
// the wheel handler re-attached each time update() runs. The dataset flag prevents attaching
|
|
252
|
+
// the listener more than once when the same element survives a stale update.
|
|
253
|
+
if (!this.tabScrollObserver) {
|
|
254
|
+
this.tabScrollObserver = new ResizeObserver(() => this.applyTabScrollOffset())
|
|
255
|
+
}
|
|
256
|
+
this.tabScrollObserver.disconnect()
|
|
257
|
+
this.tabScrollObserver.observe(viewport)
|
|
258
|
+
|
|
259
|
+
if (!viewport.dataset.wheelBound) {
|
|
260
|
+
viewport.dataset.wheelBound = 'true'
|
|
261
|
+
viewport.addEventListener('wheel', this.onTabWheel, { passive: false })
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
this.applyTabScrollOffset()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Lets a plain (vertical) mouse wheel scroll the tabs horizontally, so users without a
|
|
269
|
+
* trackpad can still scroll left and right. Only intercepts when the tabs actually overflow.
|
|
270
|
+
*/
|
|
271
|
+
private onTabWheel = (e: WheelEvent) => {
|
|
272
|
+
if (e.ctrlKey) return // allow browser zoom
|
|
273
|
+
const viewport = e.currentTarget as HTMLElement
|
|
274
|
+
if (viewport.scrollWidth <= viewport.clientWidth) return // nothing to scroll
|
|
275
|
+
const delta = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.deltaY
|
|
276
|
+
if (!delta) return
|
|
277
|
+
viewport.scrollLeft += delta
|
|
278
|
+
e.preventDefault()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Scrolls the viewport so the selected tab sits near the left edge, offset slightly so the
|
|
283
|
+
* preceding tab remains partially visible. Only scrolls when the tabs actually overflow their
|
|
284
|
+
* container; otherwise they're left in place.
|
|
285
|
+
*/
|
|
286
|
+
private applyTabScrollOffset() {
|
|
287
|
+
const viewport = this.element?.querySelector('.tt-tab-scroll') as HTMLElement | null
|
|
288
|
+
const track = this.element?.querySelector('.tt-tab-track') as HTMLElement | null
|
|
289
|
+
if (!viewport || !track) return
|
|
290
|
+
|
|
291
|
+
const active = track.querySelector('.tab.active') as HTMLElement | null
|
|
292
|
+
const contentWidth = track.scrollWidth
|
|
293
|
+
const viewportWidth = viewport.clientWidth
|
|
294
|
+
|
|
295
|
+
// If all the tabs fit (or nothing's selected), don't move them.
|
|
296
|
+
if (!active || contentWidth <= viewportWidth) {
|
|
297
|
+
viewport.scrollLeft = 0
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Line up the left edge of the viewport just before the active tab, leaving a sliver of the
|
|
302
|
+
// preceding tab visible. Clamp so we never scroll past the last tab (no empty gap at the end).
|
|
303
|
+
const prev = active.previousElementSibling as HTMLElement | null
|
|
304
|
+
let offset = active.offsetLeft
|
|
305
|
+
if (prev) {
|
|
306
|
+
offset = prev.offsetLeft + prev.offsetWidth - Math.min(prev.offsetWidth, 32)
|
|
307
|
+
}
|
|
308
|
+
const maxOffset = contentWidth - viewportWidth
|
|
309
|
+
viewport.scrollLeft = Math.max(0, Math.min(offset, maxOffset))
|
|
310
|
+
}
|
|
311
|
+
|
|
220
312
|
}
|
|
221
313
|
|
|
222
314
|
const Tabs = {
|