UNPKG

nitropage

Version:

A free and open source, extensible visual page builder based on SolidStart.

436 lines (389 loc) 10.3 kB
import type { Accessor, Component, ComponentProps, Context, FlowComponent, JSX, Owner, ParentComponent, ValidComponent, } from "solid-js"; import type { SetStoreFunction } from "solid-js/store"; import type { NitroMedia } from "@prisma/client"; export type BlueprintDataInputProps<V = any, C = any> = { element?: Element; setValue: (produce: (state: V) => V) => void; setOverride: (produce: (override: boolean) => boolean) => void; value?: V; data: BlueprintData<V, C>; override: boolean; key: string; context: "element" | "preset" | "project"; }; export type BlueprintDataInput<V = any, C = any> = Component< BlueprintDataInputProps<V, C> >; export type BlueprintDataWrapper = FlowComponent; export type ToolbarOption = { bold?: boolean; italic?: boolean; underline?: boolean; color?: boolean; background?: boolean; script?: boolean; align?: boolean; indent?: boolean; listOrdered?: boolean; listBullet?: boolean; link?: boolean; clean?: boolean; }; export type BlueprintData<V = any, C = any> = { id?: string; title?: () => string; rte?: { // disabled this option for now is I need to figure out a good api first // formats?: string[]; toolbar?: ToolbarOption; }; defaultValue: () => V; custom?: C; getter?: (value: V | undefined, state: State) => unknown; wrapper?: BlueprintDataWrapper; loadWrappers?: { id: string; wrapper: BlueprintDataWrapper }[]; context?: Context<any>; required?: boolean; input: BlueprintDataInput<V>; type?: String; }; export type FontFaceFile = { format: string; mime: string; name: string; }; export type FontFace = { id: string; files: FontFaceFile[]; weight?: number; style?: string; preload?: boolean; }; export type Font = { id?: string; publicId: string; family: string; external?: boolean; externalItalic?: boolean; externalWeights?: string[]; fallbacks: string[]; preload?: boolean; cdn?: boolean | null; faces: FontFace[]; driver?: string; }; export type InputFromBlueprintData<T extends BlueprintData> = T["input"]; export type BlueprintDataRecord = Record<string, BlueprintData>; type CustomProps<Props> = Omit< Props, "children" | "editor" | "dragging" | "ref" | "Slot" | "dragSlot" >; type ComponentData< T extends CustomBlueprintComponent = CustomBlueprintComponent, Props = CustomProps<ComponentProps<T>["data"]>, > = { [P in keyof Props]: Props[P]; }; export type BlueprintDataPropDefault<T extends BlueprintDataRecord> = { [P in keyof T]: ReturnType<T[P]["defaultValue"]>; }; export type BlueprintDataPropGetter<T extends BlueprintData> = T["getter"] extends Function ? ReturnType<T["getter"]> : ReturnType<T["defaultValue"]>; export type BlueprintDataRecordPropGetter<T extends BlueprintDataRecord> = { [P in keyof T]: BlueprintDataPropGetter<T[P]>; }; export type BlueprintDataPropsDefault< T extends BlueprintFn, R extends BaseBlueprint = Awaited<ReturnType<T>>, > = BlueprintDataPropDefault< R["data"] extends BlueprintDataRecord ? R["data"] : BlueprintDataRecord >; export type BlueprintDataPropsGetter< T extends BlueprintFn, R extends BaseBlueprint = Awaited<ReturnType<T>>, > = BlueprintDataRecordPropGetter< R["data"] extends BlueprintDataRecord ? R["data"] : BlueprintDataRecord >; export type BlueprintSlot = { /** * @deprecated */ max?: number; blueprint?: string; /** * @deprecated */ acceptBlueprint?: (blueprint: string) => boolean | void | undefined; }; export type BlueprintSlots = Record<string, BlueprintSlot>; export type BaseBlueprint< SD extends BlueprintDataRecord = BlueprintDataRecord, > = { component?: CustomBlueprintComponent; title?: () => string; category?: string; /** * @deprecated */ acceptParentType?: "root" | "element"; /** * @deprecated */ acceptParent?: (blueprint: string) => boolean | void | undefined; slots?: BlueprintSlots; data: SD; previewData?: () => Partial<BlueprintDataPropDefault<SD>>; }; export interface Blueprint extends BaseBlueprint { filename?: string; slots: BlueprintSlots; title: () => string; category: string; id: string; version?: number; } export type BlueprintFn = { (opts: { admin: boolean }): BaseBlueprint | Promise<BaseBlueprint>; _id?: string; }; export type ElementData = { value: any; override?: boolean }; export type ElementDataRecord = Record<string, ElementData>; export type Element = { id: string; createdAt?: Date; updatedAt?: Date; historyId?: string | null; blueprintId?: string | null; layoutId?: string | null; dynamic?: boolean; data: ElementDataRecord; presetId?: string | null; parentSlotId?: string | null; slots: Record<string, string>; }; export type ElementSlot = { id: string; key: string; layoutSlotId?: string | null; parentElementId: string; elements: string[]; }; export type LayoutSlot = { id: string; title: string; /** Slots consuming elements from this slot */ consumerSlots: string[]; /** Slots inserting elements into this slot */ elementSlots: string[]; }; export type Preset = { id: string; new?: boolean; blueprint: string; name: string; data: ElementDataRecord; }; export type SlotComponent< ElementProps = JSX.HTMLAttributes<HTMLElement>, S = string, > = Component< { optional?: boolean; component?: ValidComponent; key: S; direction?: "vertical" | "horizontal"; expand?: boolean; } & ElementProps >; export type RteComponent< B extends BaseBlueprint = BaseBlueprint, D extends BlueprintDataKeys<B> = BlueprintDataKeys<B>, > = Component<{ component?: ValidComponent; class?: string; key?: | D | ((data: BlueprintDataRecordPropGetter<B["data"]>) => string | undefined); }>; type DragSlotConfig = { slot?: string; direction?: "vertical" | "horizontal"; }; export type DragSlotFunction = ( el: HTMLElement, config: () => DragSlotConfig, ) => any; export type BlueprintComponentProps< B extends BaseBlueprint, S = GetBlueprintSlots<B>, > = { admin?: boolean; preview?: boolean; handles?: JSX.Element; blueprint: B; writing?: boolean; element: { id: string; }; // TODO: Maybe rename to rootProps elementProps: { "data-element": string | boolean; id?: string; ref?: (el: HTMLElement) => void; }; slotElements: (name: S) => string[]; Slot: SlotComponent<JSX.HTMLAttributes<HTMLElement>, S>; Rte: RteComponent<B>; lax?: boolean; }; export type GetBlueprintSlots<T extends BaseBlueprint> = T["slots"] extends Record<any, any> ? keyof T["slots"] : string; export type BlueprintDataKeys<T extends BaseBlueprint> = T["data"] extends Record<any, any> ? keyof T["data"] : string; export type CustomBlueprintComponent< T = {}, B extends BaseBlueprint = BaseBlueprint, > = Component<{ data: T } & BlueprintComponentProps<B>>; export type SidebarElementsTab = "element" | "preset" | "presets" | "project"; export type EditorUiState = { sidebar: { tab: "elements" | "page" | "project"; elementsTab: SidebarElementsTab; expandElementsTree: boolean; createElement: { type: "blueprints" | "layouts"; category: string; }; }; viewport: { tool: | "idle" | "writing" | "selecting" | "creating" | "locked" | "moving" | "deleting"; darkMode?: boolean; zoom: number; }; pageId: string; selectedRevisionId: string; saving: boolean; movingElementId: false | string; createMoveTarget: { elementId?: string; slot?: string; }; hoveredElementId: string | undefined; creatingElementId: string | undefined; deletingElementId: string | undefined; selectedElementId: string | undefined; selectedBlueprint: string | undefined; modification: { dirty: boolean; initialState?: string; initialSettings?: string; }; }; export interface RgbColor { r: number; g: number; b: number; } export type Colors = Record<string, { light: RgbColor; dark?: RgbColor }>; export type Color = { css?: string; rgb?: RgbColor; id?: string; invert?: boolean; alpha?: number; }; export type RteTextColors = Record<string, Color>; export type BlueprintDefaults = Record<string, Record<string, ElementData>>; export type State = { admin?: boolean; normalizationDate?: Date; title: string; cacheKey: number; urlPath: string; defaultFont?: string; notFound?: boolean; includeInFeed?: boolean; metaDescription?: string; metaImage?: string; favicon?: string; fonts: Font[]; colors: Colors; backgroundColor?: Color; richtext: { textColors: RteTextColors; }; // true -> blueprint needs to always be loaded // false -> blueprint is static, can be skipped during hydration blueprintIds?: Record<string, boolean>; blueprintDefaults: { project: BlueprintDefaults; }; elements: Record<string, Element>; slots: Record<string, ElementSlot>; layoutSlots: Record<string, LayoutSlot>; presets: Record<string, Preset>; pendingDeletes?: { presets: string[]; }; }; export type PageVisit = { dynamicBlueprintIds: string[]; blueprintIds: string[]; state?: State | undefined; layoutIds: string[]; }; export type BlueprintComponent<TS extends () => BlueprintFn> = CustomBlueprintComponent< BlueprintDataPropsGetter<ReturnType<TS>>, Awaited<ReturnType<ReturnType<TS>>> >; export type Config = { owner: Accessor<Owner>; page: ParentComponent; blueprints: Record<string, Blueprint>; getBlueprints?: () => BlueprintModules; initPromises: Record<string, Promise<void> | undefined>; }; export type InitConfigParams = { configParams: NPConfig; config: Config; setConfig: SetStoreFunction<Config>; admin: boolean; page?: PageVisit; }; export type NPConfig = { pageRoot: ParentComponent; blueprints: () => BlueprintModules; }; export type BlueprintModules = Record<string, () => Promise<any>>; type MediaBase = Omit< NitroMedia, "projectId" | "name" | "extension" | "createdAt" | "updatedAt" >; export type Media = MediaBase & { timestamp?: number; url: string; rawUrl: string; name?: string; extension?: string; }; export type MediaValue = { mediaIds: string[] };