Loading files
import { createFaceState, type FaceState, toPiuColorNumber, toPiuColorString } from 'face-state'
import {
type Container as PiuContainer,
type Content as PiuContent,
type Port as PiuPort,
type Style as PiuStyle,
type Text as PiuText,
Port,
} from 'piu/MC'
const defaultOptions = {
left: 0,
right: 0,
bottom: 4,
height: 28,
paddingX: 4,
paddingY: 2,
radius: 6,
text: '',
font: 'k8x12-12',
charWidth: 8,
lineHeight: 12,
}
const CLEAR_COLOR = 'transparent'
let textStyleCache: Map<string, PiuStyle> | null = null
type MultiRowBalloonOptions = {
name?: string
left?: number
right?: number
top?: number
bottom?: number
width?: number
height?: number
paddingX?: number
paddingY?: number
radius?: number
text?: string
font?: string
charWidth?: number
lineHeight?: number
}
type BodyText = PiuText
function getTextStyle(font: string, color: number | string): PiuStyle {
if (!textStyleCache) textStyleCache = new Map()
const styleColor = typeof color === 'number' ? toPiuColorString(color) : color
const key = `${font}:${styleColor}`
const cached = textStyleCache.get(key)
if (cached) return cached
const style = new Style({ font, color: styleColor, horizontal: 'left' })
textStyleCache.set(key, style)
return style
}
export const MultiRowBalloon = Container.template((opts: MultiRowBalloonOptions = {}) => {
const o = { ...defaultOptions, ...opts }
let background: PiuPort | null = null
let bodyText: BodyText | null = null
let currentPrimary: number | null = null
let currentSecondary: number | null = null
let currentText = o.text ?? ''
let layoutWidth = 0
let layoutHeight = 0
const left = opts.left ?? defaultOptions.left
const right = opts.right ?? defaultOptions.right
const top = opts.top
const bottom = opts.bottom ?? defaultOptions.bottom
const width = opts.width
const style = getTextStyle(o.font, '#000')
const resolveWidth = (self: PiuContainer) => {
const w = self.width
return w > 0 ? w : 320
}
const resolveHeight = (self: PiuContainer) => {
const h = self.height
return h > 0 ? h : o.height
}
type BalloonContainerOptions = {
name?: string
left?: number
right?: number
top?: number
bottom?: number
width?: number
height?: number
clip: boolean
Behavior: typeof Behavior
}
const containerOptions: BalloonContainerOptions = {
name: opts.name ?? 'MultiRowBalloon',
height: o.height,
clip: true,
Behavior: class extends Behavior {
ensureParts(self: PiuContainer) {
const w = resolveWidth(self)
const h = resolveHeight(self)
if (background && bodyText && layoutWidth === w && layoutHeight === h) return
if (background || bodyText) {
self.empty()
background = null
bodyText = null
}
layoutWidth = w
layoutHeight = h
background = new Port(null, {
left: 0,
top: 0,
width: w,
height: h,
Behavior: class extends Behavior {
onDraw(port: PiuPort) {
port.fillColor(CLEAR_COLOR, 0, 0, w, h)
const primary = currentPrimary ?? 0xffffff
const secondary = currentSecondary ?? 0x000000
const stroke = 2
const primaryColor = toPiuColorString(primary)
const secondaryColor = toPiuColorString(secondary)
port.fillColor(secondaryColor, 0, 0, w, h)
port.fillColor(primaryColor, 0, 0, w, stroke)
port.fillColor(primaryColor, 0, h - stroke, w, stroke)
port.fillColor(primaryColor, 0, 0, stroke, h)
port.fillColor(primaryColor, w - stroke, 0, stroke, h)
}
},
}) as PiuPort
bodyText = new Text(null, {
left: o.paddingX,
right: o.paddingX,
top: o.paddingY,
bottom: o.paddingY,
string: '',
style,
})
self.add(background as unknown as PiuContent)
self.add(bodyText)
this.updateText(self, currentText)
}
updatePalette(face: FaceState) {
if (!background || !bodyText) return
const primary = toPiuColorNumber(face.theme.primary)
const secondary = toPiuColorNumber(face.theme.secondary)
if (primary === currentPrimary && secondary === currentSecondary) return
currentPrimary = primary
currentSecondary = secondary
bodyText.style = getTextStyle(o.font, primary)
background.invalidate()
}
updateText(_self: PiuContainer, text: string) {
if (!bodyText) return
bodyText.string = text ?? ''
}
setText(self: PiuContainer, text: string) {
currentText = text ?? ''
this.ensureParts(self)
this.updateText(self, currentText)
}
clear(self: PiuContainer) {
this.setText(self, '')
}
onDisplaying(content: PiuContainer) {
this.ensureParts(content)
this.updatePalette(createFaceState())
}
onFaceState(content: PiuContainer, face: FaceState) {
this.ensureParts(content)
this.updatePalette(face)
}
},
}
if (width !== undefined) {
containerOptions.width = width
if (opts.left !== undefined) {
containerOptions.left = left
} else if (opts.right !== undefined) {
containerOptions.right = right
} else {
containerOptions.left = left
}
} else {
containerOptions.left = left
containerOptions.right = right
}
if (top !== undefined) {
containerOptions.top = top
} else {
containerOptions.bottom = bottom
}
return containerOptions
})
export type { MultiRowBalloonOptions }
c44ec7454This file is pinned to the selected release. Download the original to open it in your design tool.