import { contentAppend, Widget, WidgetConfig, WidgetStyleConfig } from "./Widget"; import { ClassProp, ModProp, StyleProp } from "./Prop"; import { StaticText } from "./StaticText"; import { Text } from "./Text"; import { innerTextTrim } from "../util/innerTextTrim"; import { isString } from "../util/isString"; import { isArray } from "../util/isArray"; import { exploreChildren } from "./layout/exploreChildren"; import { Instance } from "./Instance"; import { CxChild, RenderingContext } from "./RenderingContext"; import { Create } from "../util"; export type ChildNode = Create | string | number | boolean | null | undefined; export interface ContainerConfig extends WidgetConfig { /** Keep whitespace in text based children. Default is `false`. See also `trimWhitespace`. */ ws?: boolean; /** Remove all whitespace in text based children. Default is `true`. See also `preserveWhitespace`. */ trimWhitespace?: boolean; /** Keep whitespace in text based children. Default is `false`. See also `trimWhitespace`. */ preserveWhitespace?: boolean; /** List of child elements. */ items?: ChildNode | ChildNode[]; /** List of child elements. */ children?: ChildNode | ChildNode[]; plainText?: boolean; // Prevent overriding container methods via config add?: never; clear?: never; } export interface StyledContainerConfig extends ContainerConfig, WidgetStyleConfig {} // Base class for extending with custom Config types export class ContainerBase< Config extends ContainerConfig = ContainerConfig, InstanceType extends Instance = Instance, > extends Widget { declare public ws?: boolean; declare public preserveWhitespace?: boolean; declare public trimWhitespace?: boolean; declare public items: Widget[]; declare public children?: Widget[]; declare public layout?: Container | null; declare public useParentLayout?: boolean; declare public itemDefaults?: any; declare public plainText?: boolean; public init(context?: any): void { if (typeof this.ws !== "undefined") this.preserveWhitespace = this.ws; if (this.preserveWhitespace) this.trimWhitespace = false; let items = this.items || this.children || []; delete this.children; this.items = []; if (this.layout) { let layout = Container.create(this.layout as Create, { items }); layout.init(context); this.layout = null; if ("noLayout" in layout && (layout as any).noLayout) { this.useParentLayout = true; this.add(items); } else { this.add(layout); this.layout = layout; } } else { this.add(items); } super.init(); } protected exploreItems(context: RenderingContext, instance: Instance, items: CxChild[]): void { instance.children = exploreChildren(context, instance, items, instance.cached.children, null, instance.store); if (instance.cache("children", instance.children)) instance.markShouldUpdate(context); } public explore(context: any, instance: any): void { super.explore(context, instance); this.exploreItems(context, instance, this.items!); } public render(context: any, instance: any, key: any): any { return this.renderChildren(context, instance, key); } protected renderChildren(_context: any, instance: any, key?: string): any { let preserveComplexContent = this.useParentLayout; function append(result: any[], r: any): void { if (r == null) return; //react element if (!r.hasOwnProperty("content")) { contentAppend(result, r); return; } if (r.useParentLayout) return r.content.forEach((x: any) => append(result, x)); if (r.atomic || preserveComplexContent) { result.push(r); } else { let first = true; for (let k in r) if (contentAppend(result, r[k], !first)) first = false; } } let result: React.ReactNode[] = []; for (let i = 0; i < instance.children.length; i++) { append(result, instance.children[i].vdom); } if (this.useParentLayout) return { useParentLayout: true, content: result, }; return result; } public clear(): void { if (this.layout) this.layout.clear(); else this.items = []; } public add(...args: any[]): void { if (this.layout) return this.layout.add(...args); for (let a of args) { if (!a) continue; if (isArray(a)) { for (let c of a) this.add(c); } else if (isString(a)) { if (this.trimWhitespace) a = innerTextTrim(a); if (a) this.addText(a); } else if (a.isComponent) this.items.push(this.wrapItem(a)); else { this.add(Widget.create(a, this.itemDefaults, {})); } } } protected wrapItem(item: any): any { return item; } protected addText(text: string): void { if (this.plainText || text.indexOf("{") == -1 || text.indexOf("}") == -1) this.add(Widget.create(StaticText, { text: text }, {})); else this.add(Widget.create(Text, { text: { tpl: text } }, {})); } public find(filter: any, options?: any): any[] { if (!options) options = {}; if (!filter || !this.items) return []; let alias = filter; if (isString(filter)) filter = (w: any) => w.componentAlias == alias; if (filter.isComponentType) filter = (w: any) => w instanceof alias; let results = []; for (let i = 0; i < this.items.length; i++) { let w = this.items[i]; if (w && !w.initialized) w.init(); if (filter(w)) { results.push(w); if (options.first) break; } if (w && (w as any).find) results.push(...(w as any).find(filter, options)); } return results; } public findFirst(filter: any, options?: any): any { return this.find(filter, { ...options, first: true })[0]; } } ContainerBase.prototype.trimWhitespace = true; ContainerBase.prototype.plainText = true; ContainerBase.prototype.styled = false; // Closed type for direct usage - preserves ControllerProp ThisType export class Container extends ContainerBase {} // Base class for styled containers with custom Config types export class StyledContainerBase< Config extends StyledContainerConfig = StyledContainerConfig, InstanceType extends Instance = Instance, > extends ContainerBase { declare public class?: ClassProp; declare public className?: ClassProp; declare public style?: StyleProp; declare public styles?: StyleProp; declare public baseClass?: string; declare public mod?: ModProp; } StyledContainerBase.prototype.styled = true; // Closed type for direct usage export class StyledContainer extends StyledContainerBase {}