Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
import { Drawer, type DrawerBehavior, type DrawerButtonViewSpec } from 'drawer'
import type {
Container as PiuContainer,
ContainerDictionary as PiuContainerDictionary,
Content as PiuContent,
} from 'piu/MC'
export type TemplateFunction<TData, TResult> = {
new (data?: TData, dictionary?: PiuContainerDictionary): TResult
template?<TNextData>(factory: (arg: TNextData) => PiuContainerDictionary): TemplateFunction<TNextData, TResult>
}
type CommonViewAnchors = {
MAIN?: PiuContainer
APP_BAR?: PiuContent
OVERLAY?: PiuContainer
}
export type CommonViewParams = CommonViewAnchors & {
main?: PiuContainer
appBar?: PiuContent
drawerButtons?: DrawerButtonViewSpec[]
}
/**
* Optional, Face-independent lifecycle hooks a swappable main content may implement.
* None of these touch FaceState/skin, so any PiuContainer qualifies as MainContent.
*/
export type MainContentBehavior = {
onShow?: (content: PiuContainer) => void
onHide?: (content: PiuContainer) => void
onDispose?: (content: PiuContainer) => void
}
/** A swappable main-area component: any PiuContainer with optional lifecycle hooks. */
export type MainContent = PiuContainer & { behavior?: MainContentBehavior }
export type CommonViewTemplateCtor = TemplateFunction<CommonViewParams, PiuContainer>
export class CommonViewBehavior extends Behavior {
container: PiuContainer | null = null
main: PiuContainer | null = null
appBar: PiuContent | null = null
overlay: PiuContainer | null = null
drawer: PiuContainer | null = null
drawerOpen = false
drawerButtons: DrawerButtonViewSpec[] = []
drawerStates: Map<string, boolean> | null = null
onCreate(container: PiuContainer, data: CommonViewParams) {
this.container = container
this.drawerStates = new Map()
const missing: string[] = []
if (!data.MAIN) missing.push('MAIN')
if (!data.APP_BAR) missing.push('APP_BAR')
if (!data.OVERLAY) missing.push('OVERLAY')
if (missing.length > 0) {
throw new Error(`[CommonView] missing anchors: ${missing.join(', ')}`)
}
this.main = data.MAIN as PiuContainer
this.appBar = data.APP_BAR as PiuContent
this.overlay = data.OVERLAY as PiuContainer
this.drawerButtons = [...(data.drawerButtons ?? [])]
this.drawer = new Drawer({ buttons: this.drawerButtons })
if (this.drawer && this.container) this.container.add(this.drawer)
this.setOverlayActive(false)
}
/**
* Replace the swappable MAIN component while keeping AppBar/Overlay/Drawer intact.
* The new content is inserted below the AppBar so the main area stays the bottom layer.
*/
setMain(content: MainContent): void {
if (!this.container || this.main === content) return
const previous = this.main
if (previous) {
this.container.remove(previous)
;(previous.behavior as MainContentBehavior | undefined)?.onHide?.(previous)
}
this.main = content
// Insert before the AppBar (always present via zero-height fallback) to preserve z-order.
const anchor = this.appBar ?? this.overlay
if (anchor) this.container.insert(content, anchor)
else this.container.add(content)
;(content.behavior as MainContentBehavior | undefined)?.onShow?.(content)
}
openDrawer(): void {
if (!this.drawer || this.drawerOpen) return
trace('[CommonView] openDrawer\n')
const behavior = this.drawer.behavior as DrawerBehavior | undefined
if (!behavior?.setOpen) return
this.drawerOpen = true
this.setOverlayActive(true)
behavior.setOpen(this.drawer, true)
}
closeDrawer(): void {
if (!this.drawer || !this.drawerOpen) return
trace('[CommonView] closeDrawer\n')
const behavior = this.drawer.behavior as DrawerBehavior | undefined
if (!behavior?.setOpen) return
this.drawerOpen = false
behavior.setOpen(this.drawer, false)
this.setOverlayActive(false)
}
toggleDrawer(): void {
if (this.drawerOpen) this.closeDrawer()
else this.openDrawer()
}
setDrawerButtons(buttons: DrawerButtonViewSpec[]): void {
this.drawerButtons = buttons.map((button) => this.withStoredDrawerState(button))
this.syncDrawerButtons()
}
addDrawerButton(button: DrawerButtonViewSpec): void {
const nextButton = this.withStoredDrawerState(button)
const index = this.drawerButtons.findIndex((item) => item.key === button.key)
if (index >= 0) this.drawerButtons[index] = nextButton
else this.drawerButtons.push(nextButton)
const drawer = this.drawer
const behavior = drawer?.behavior as DrawerBehavior | undefined
if (drawer && behavior?.addButton?.(drawer, nextButton)) return
this.syncDrawerButtons()
}
removeDrawerButton(key: string): void {
const index = this.drawerButtons.findIndex((item) => item.key === key)
if (index < 0) return
this.drawerButtons.splice(index, 1)
const drawer = this.drawer
const behavior = drawer?.behavior as DrawerBehavior | undefined
if (drawer && behavior?.removeButton?.(drawer, key)) return
this.syncDrawerButtons()
}
setDrawerButtonState(key: string, active: boolean): void {
const states = this.getDrawerStates()
states.set(key, active)
const drawer = this.drawer
const behavior = drawer?.behavior as DrawerBehavior | undefined
const updated = behavior?.setButtonState?.(drawer as PiuContainer, key, active)
const index = this.drawerButtons.findIndex((item) => item.key === key)
if (index >= 0) this.drawerButtons[index] = { ...this.drawerButtons[index], active }
if (!updated && index >= 0) this.syncDrawerButtons()
}
private syncDrawerButtons(): void {
const drawer = this.drawer
const behavior = drawer?.behavior as DrawerBehavior | undefined
if (drawer && behavior?.setButtons?.(drawer, this.drawerButtons)) {
this.applyStoredDrawerStates()
return
}
this.replaceDrawer()
}
private replaceDrawer(): void {
const container = this.container
if (!container) return
const wasOpen = this.drawerOpen
if (this.drawer) container.remove(this.drawer)
this.drawer = new Drawer({ buttons: this.drawerButtons })
if (this.drawer) {
container.add(this.drawer)
this.applyStoredDrawerStates()
const behavior = this.drawer.behavior as DrawerBehavior | undefined
if (behavior?.setOpen && wasOpen) {
this.drawerOpen = true
behavior.setOpen(this.drawer, true)
this.setOverlayActive(true)
} else {
this.drawerOpen = false
this.setOverlayActive(false)
}
}
}
private setOverlayActive(active: boolean) {
if (!this.overlay) return
type OverlayContainer = PiuContainer & { active?: boolean; visible?: boolean; backgroundTouch?: boolean }
const overlay = this.overlay as OverlayContainer
overlay.active = active
overlay.visible = true
overlay.backgroundTouch = active
}
private getDrawerStates(): Map<string, boolean> {
if (!this.drawerStates) this.drawerStates = new Map()
return this.drawerStates
}
private withStoredDrawerState(button: DrawerButtonViewSpec): DrawerButtonViewSpec {
const active = this.getDrawerStates().get(button.key)
return active === undefined ? button : { ...button, active }
}
private applyStoredDrawerStates(): void {
const drawer = this.drawer
const behavior = drawer?.behavior as DrawerBehavior | undefined
if (!drawer || !behavior?.setButtonState) return
for (const [key, active] of this.getDrawerStates().entries()) {
behavior.setButtonState(drawer, key, active)
}
}
}
export const CommonView: CommonViewTemplateCtor = Container.template(($: CommonViewParams) => {
const main = $.MAIN ?? $.main
if (!main) {
trace('[CommonView] ERROR: Missing main view instance\n')
return { top: 0, right: 0, bottom: 0, left: 0 }
}
if (!$.MAIN) {
$.MAIN = main
}
const appBar =
$.APP_BAR ??
$.appBar ??
new Content($, {
anchor: 'APP_BAR',
left: 0,
right: 0,
top: 0,
height: 0,
})
if (!$.APP_BAR) {
$.APP_BAR = appBar as PiuContent
}
const overlay = new Container($, {
anchor: 'OVERLAY',
left: 0,
right: 0,
top: 0,
bottom: 0,
active: false,
clip: false,
backgroundTouch: true,
Behavior: class extends Behavior {
onTouchBegan(_container: PiuContainer, _id: number, x: number, y: number) {
trace(`[CommonView] overlay touch began x=${x} y=${y}\n`)
}
onTouchMoved(_container: PiuContainer, _id: number, x: number, y: number) {
trace(`[CommonView] overlay touch moved x=${x} y=${y}\n`)
}
onTouchEnded(container: PiuContainer, _id: number, x: number, y: number) {
trace(`[CommonView] overlay touch ended x=${x} y=${y}\n`)
container.bubble('onDrawerClose')
}
},
})
return {
left: 0,
right: 0,
top: 0,
bottom: 0,
contents: [main, appBar, overlay],
Behavior: CommonViewBehavior,
}
}) as unknown as CommonViewTemplateCtor
4bc857bacThis file is pinned to the selected release. Download the original to open it in your design tool.