@vitus-labs/core
Version:
Core and utility functions for vitus-labs packages
339 lines (338 loc) • 18.2 kB
TypeScript
import * as React from "react";
import { ComponentType, FC, ReactNode, cloneElement, createElement } from "react";
//#region src/compose.d.ts
type ArityOneFn = (arg: any) => any;
/** Extracts the last function from a tuple type. */
type PickLastInTuple<T extends any[]> = T extends [...rest: infer _U, argn: infer L] ? L : any;
/** Parameter type of the rightmost (first-applied) function. */
type FirstFnParameterType<T extends any[]> = Parameters<PickLastInTuple<T>>[any];
/** Return type of the leftmost (last-applied) function. */
type LastFnReturnType<T extends any[]> = ReturnType<T[0]>;
/**
* Right-to-left function composition.
* `compose(f, g, h)(x)` === `f(g(h(x)))`.
*
* Used throughout the system to build HOC chains —
* `compose(attrsHoc, userHoc1, userHoc2)(Component)`.
*/
declare const compose: <T extends ArityOneFn[]>(...fns: T) => (p: FirstFnParameterType<T>) => LastFnReturnType<T>;
//#endregion
//#region src/html/htmlElementAttrs.d.ts
type Base = React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
interface HTMLElementAttrs {
a: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
abbr: Base;
address: Base;
area: React.DetailedHTMLProps<React.AreaHTMLAttributes<HTMLAreaElement>, HTMLAreaElement>;
article: Base;
aside: Base;
audio: React.DetailedHTMLProps<React.AudioHTMLAttributes<HTMLAudioElement>, HTMLAudioElement>;
b: Base;
bdi: Base;
bdo: Base;
big: Base;
blockquote: React.DetailedHTMLProps<React.BlockquoteHTMLAttributes<HTMLQuoteElement>, HTMLQuoteElement>;
body: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBodyElement>, HTMLBodyElement>;
br: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBRElement>, HTMLBRElement>;
button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
canvas: React.DetailedHTMLProps<React.CanvasHTMLAttributes<HTMLCanvasElement>, HTMLCanvasElement>;
caption: Base;
cite: React.DetailedHTMLProps<React.HTMLAttributes<HTMLQuoteElement>, HTMLQuoteElement>;
code: Base;
col: React.DetailedHTMLProps<React.ColHTMLAttributes<HTMLTableColElement>, HTMLTableColElement>;
colgroup: React.DetailedHTMLProps<React.ColgroupHTMLAttributes<HTMLTableColElement>, HTMLTableColElement>;
data: React.DetailedHTMLProps<React.DataHTMLAttributes<HTMLDataElement>, HTMLDataElement>;
datalist: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDataListElement>, HTMLDataListElement>;
dd: Base;
del: React.DetailedHTMLProps<React.DelHTMLAttributes<HTMLModElement>, HTMLModElement>;
details: Base;
dfn: Base;
dialog: React.DetailedHTMLProps<React.DialogHTMLAttributes<HTMLDialogElement>, HTMLDialogElement>;
div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
dl: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDListElement>, HTMLDListElement>;
dt: Base;
em: Base;
embed: React.DetailedHTMLProps<React.EmbedHTMLAttributes<HTMLEmbedElement>, HTMLEmbedElement>;
fieldset: React.DetailedHTMLProps<React.FieldsetHTMLAttributes<HTMLFieldSetElement>, HTMLFieldSetElement>;
figcaption: Base;
figure: Base;
footer: Base;
form: React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>;
h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h3: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h4: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h5: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
h6: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>;
header: Base;
hr: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHRElement>, HTMLHRElement>;
html: React.DetailedHTMLProps<React.HtmlHTMLAttributes<HTMLHtmlElement>, HTMLHtmlElement>;
i: Base;
iframe: React.DetailedHTMLProps<React.IframeHTMLAttributes<HTMLIFrameElement>, HTMLIFrameElement>;
img: React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>;
input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
ins: React.DetailedHTMLProps<React.InsHTMLAttributes<HTMLModElement>, HTMLModElement>;
kbd: Base;
label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>;
legend: React.DetailedHTMLProps<React.HTMLAttributes<HTMLLegendElement>, HTMLLegendElement>;
li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>;
main: Base;
map: React.DetailedHTMLProps<React.MapHTMLAttributes<HTMLMapElement>, HTMLMapElement>;
mark: Base;
meter: Base;
nav: Base;
object: React.DetailedHTMLProps<React.ObjectHTMLAttributes<HTMLObjectElement>, HTMLObjectElement>;
ol: React.DetailedHTMLProps<React.OlHTMLAttributes<HTMLOListElement>, HTMLOListElement>;
optgroup: React.DetailedHTMLProps<React.OptgroupHTMLAttributes<HTMLOptGroupElement>, HTMLOptGroupElement>;
option: React.DetailedHTMLProps<React.OptionHTMLAttributes<HTMLOptionElement>, HTMLOptionElement>;
output: Base;
p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>;
picture: Base;
pre: React.DetailedHTMLProps<React.HTMLAttributes<HTMLPreElement>, HTMLPreElement>;
progress: React.DetailedHTMLProps<React.ProgressHTMLAttributes<HTMLProgressElement>, HTMLProgressElement>;
q: React.DetailedHTMLProps<React.HTMLAttributes<HTMLQuoteElement>, HTMLQuoteElement>;
rp: Base;
rt: Base;
ruby: Base;
s: Base;
samp: Base;
section: Base;
select: React.DetailedHTMLProps<React.SelectHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>;
small: Base;
source: React.DetailedHTMLProps<React.SourceHTMLAttributes<HTMLSourceElement>, HTMLSourceElement>;
span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
strong: Base;
sub: Base;
summary: Base;
sup: Base;
svg: React.SVGProps<SVGSVGElement>;
table: React.DetailedHTMLProps<React.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>;
tbody: React.DetailedHTMLProps<React.HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>;
td: React.DetailedHTMLProps<React.TdHTMLAttributes<HTMLTableCellElement>, HTMLTableCellElement>;
template: React.DetailedHTMLProps<React.HTMLAttributes<HTMLTemplateElement>, HTMLTemplateElement>;
textarea: React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>;
tfoot: React.DetailedHTMLProps<React.HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>;
th: React.DetailedHTMLProps<React.ThHTMLAttributes<HTMLTableCellElement>, HTMLTableCellElement>;
thead: React.DetailedHTMLProps<React.HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>;
time: Base;
tr: React.DetailedHTMLProps<React.HTMLAttributes<HTMLTableRowElement>, HTMLTableRowElement>;
track: React.DetailedHTMLProps<React.TrackHTMLAttributes<HTMLTrackElement>, HTMLTrackElement>;
u: Base;
ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>;
var: Base;
video: React.DetailedHTMLProps<React.VideoHTMLAttributes<HTMLVideoElement>, HTMLVideoElement>;
wbr: Base;
}
//#endregion
//#region src/html/htmlTags.d.ts
declare const HTML_TAGS: readonly ["a", "abbr", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blockquote", "body", "br", "button", "canvas", "caption", "cite", "code", "col", "colgroup", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "meter", "nav", "object", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "section", "select", "small", "source", "span", "strong", "sub", "summary", "sup", "svg", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "u", "ul", "var", "video", "wbr"];
declare const HTML_TEXT_TAGS: readonly ["abbr", "b", "bdi", "bdo", "big", "blockquote", "cite", "code", "del", "div", "dl", "dt", "em", "figcaption", "h1", "h2", "h3", "h4", "h5", "h6", "i", "ins", "kbd", "label", "legend", "li", "p", "pre", "q", "rp", "rt", "s", "small", "span", "strong", "sub", "summary", "sup", "time", "u"];
type HTMLTags = (typeof HTML_TAGS)[number];
type HTMLTextTags = (typeof HTML_TEXT_TAGS)[number];
//#endregion
//#region src/html/index.d.ts
type HTMLTagAttrsByTag<T extends HTMLTags> = T extends HTMLTags ? HTMLElementAttrs[T] : {};
//#endregion
//#region src/config.d.ts
/**
* Augmentable interface representing the result of the engine's `css`
* tagged template. Empty by default — each connector package narrows it
* via module declaration merging:
*
* ```ts
* // @vitus-labs/connector-styler
* declare module '@vitus-labs/core' {
* interface CSSEngineResult extends StylerCSSResult {}
* }
* ```
*
* Consumers' types automatically resolve to the engine they `init()`'d with,
* without core depending on any specific engine package.
*/
interface CSSEngineResult {}
/**
* Describes the shape of a CSS-in-JS engine connector.
* Packages like `@vitus-labs/connector-styler`, `@vitus-labs/connector-emotion`,
* and `@vitus-labs/connector-styled-components` export this shape.
*
* Required properties (`css`, `styled`, `provider`) are consumed by config
* across all packages. Optional properties (`keyframes`, `createGlobalStyle`,
* `useTheme`) are exposed for direct use in application code.
*/
interface CSSEngineConnector {
/** Tagged template for composable CSS fragments. */
css: (strings: TemplateStringsArray, ...values: any[]) => CSSEngineResult;
/** Component factory: `styled(tag)`\`...\`` → React component. */
styled: ((tag: any, options?: any) => (strings: TemplateStringsArray, ...values: any[]) => any) & Record<string, any>;
/** ThemeProvider component wrapping children with a theme context. */
provider: FC<{
theme: any;
children: ReactNode;
}>;
/** Tagged template for @keyframes animations. */
keyframes?: (strings: TemplateStringsArray, ...values: any[]) => any;
/** Factory for injecting global CSS. */
createGlobalStyle?: (strings: TemplateStringsArray, ...values: any[]) => any;
/** Hook to read the current theme from the engine's context. */
useTheme?: () => any;
}
interface PlatformConfig {
component: ComponentType | HTMLTags;
textComponent: ComponentType | HTMLTags;
createMediaQueries?: (props: {
breakpoints: Record<string, number>;
rootSize: number;
css: CSSEngineConnector['css'];
}) => Record<string, (...args: any[]) => any>;
}
type InitConfig = Partial<CSSEngineConnector & PlatformConfig>;
/**
* Singleton configuration that bridges the UI system with the chosen
* CSS-in-JS engine. All packages reference `config.css`, `config.styled`,
* etc. — the engine is swapped via `init()` with a connector.
*
* The `css` and `styled` properties are **stable delegate functions** that
* can be safely destructured at module level (`const { styled, css } = config`).
* They internally read the latest engine reference set by `init()`.
*
* When destructured before `init()` is called:
* - `css` returns a thunk (function) that resolves at render time
* - `styled` returns a lazy component that creates the real component on first render
*/
declare class Configuration {
_css: CSSEngineConnector['css'] | null;
_styled: CSSEngineConnector['styled'] | null;
_provider: CSSEngineConnector['provider'] | null;
_keyframes: CSSEngineConnector['keyframes'] | null;
_createGlobalStyle: CSSEngineConnector['createGlobalStyle'] | null;
_useTheme: CSSEngineConnector['useTheme'] | null;
component: ComponentType | HTMLTags;
textComponent: ComponentType | HTMLTags;
createMediaQueries: PlatformConfig['createMediaQueries'];
/**
* Stable CSS delegate. When the engine is available, delegates immediately.
* When not (module load time before init), returns a thunk that resolves
* at render time — all CSS-in-JS engines treat functions as interpolations.
*/
css: (strings: TemplateStringsArray, ...values: any[]) => CSSEngineResult;
/**
* Stable styled delegate (Proxy). Supports `styled(tag)` and `styled.div`.
* When the engine is not yet available, returns a lazy component
* that creates the real styled component on first render.
*/
styled: CSSEngineConnector['styled'];
/** The external ThemeProvider from the configured engine. */
get ExternalProvider(): CSSEngineConnector['provider'] | null;
/** Keyframes factory from the configured engine, or null. */
get keyframes(): CSSEngineConnector['keyframes'] | null;
/** Global style factory from the configured engine, or null. */
get createGlobalStyle(): CSSEngineConnector['createGlobalStyle'] | null;
/** Theme hook from the configured engine, or null. */
get useTheme(): CSSEngineConnector['useTheme'] | null;
constructor();
/**
* Initialize or swap the CSS-in-JS engine. Must be called before any
* component renders (typically at the app entry point).
*
* @example
* ```typescript
* import { init } from '@vitus-labs/core'
* import * as connector from '@vitus-labs/connector-styler'
* init(connector)
* ```
*/
init: (props: InitConfig) => void;
}
declare const config: Configuration;
declare const init: (props: InitConfig) => void;
//#endregion
//#region src/types.d.ts
interface Breakpoints {
[key: string]: number;
}
type BreakpointKeys = keyof Breakpoints;
//#endregion
//#region src/context.d.ts
/**
* Internal React context shared across all @vitus-labs packages.
* Carries the theme object plus any extra provider props.
*/
declare const context: import("react").Context<any>;
type Theme = Partial<{
rootSize: number;
breakpoints: Breakpoints;
} & Record<string, any>>;
type ProviderType = Partial<{
theme: Theme;
children: ReactNode;
} & Record<string, any>>;
/**
* Dual-layer provider that feeds both the internal VitusLabs context
* and an optional external styling provider (e.g. styled-components'
* ThemeProvider). When no theme is supplied, renders children directly.
*/
declare const Provider: FC<ProviderType>;
//#endregion
//#region src/hoistNonReactStatics.d.ts
declare const hoistNonReactStatics: <T, S>(target: T, source: S, excludeList?: Record<string, true>) => T;
//#endregion
//#region src/isEmpty.d.ts
/**
* Type-safe emptiness check for objects, arrays, null, and undefined.
* Returns `true` for null, undefined, empty objects `{}`, and empty arrays `[]`.
* Non-object primitives (string, number) also return `true` as any.
*/
type IsEmpty = <T extends Record<number | string, any> | any[] | null | undefined>(param: T) => T extends null | undefined ? true : keyof T extends never ? true : T extends T[] ? T[number] extends never ? true : false : false;
declare const isEmpty: IsEmpty;
//#endregion
//#region src/isEqual.d.ts
/**
* Order-independent deep equality for plain objects, arrays, and primitives.
* Handles null, undefined, nested structures, and circular references via
* cycle detection. Does not handle Date, RegExp, Map, Set — not needed for
* theme/props comparison.
*/
declare const isEqual: (a: unknown, b: unknown) => boolean;
//#endregion
//#region src/render.d.ts
type CreateTypes = Parameters<typeof createElement>[0];
type CloneTypes = Parameters<typeof cloneElement>[0];
type RenderProps<T extends Record<string, unknown> | undefined> = (props: Partial<T>) => ReactNode;
/**
* Flexible element renderer that handles multiple content types:
* - Primitives (string, number) — returned as-is
* - Arrays and fragments — returned as-is
* - Component types (class/function) — created via `createElement`
* - Valid elements — cloned with `attachProps` if provided
* - Falsy values — return null
*/
type Render = <T extends Record<string, any> | undefined>(content?: CreateTypes | CloneTypes | ReactNode | ReactNode[] | RenderProps<T>, attachProps?: T) => ReturnType<typeof createElement> | ReturnType<typeof cloneElement> | null;
declare const render: Render;
//#endregion
//#region src/useStableValue.d.ts
/**
* Returns a referentially stable version of `value`. The returned reference
* only changes when the value is no longer deeply equal to the previous one.
*
* Use this to stabilize object/array props before passing them as hook
* dependencies, preventing unnecessary recalculations in useMemo/useEffect.
*
* Based on the useDeepCompareMemoize pattern from use-deep-compare.
*/
declare const useStableValue: <T>(value: T) => T;
//#endregion
//#region src/utils.d.ts
declare const omit: <T extends Record<string, any>>(obj: T | null | undefined, keys?: readonly (string | keyof T)[] | ReadonlySet<string | keyof T>) => Partial<T>;
declare const pick: <T extends Record<string, any>>(obj: T | null | undefined, keys?: readonly (string | keyof T)[]) => Partial<T>;
declare const get: (obj: any, path: string | string[], defaultValue?: any) => any;
declare const set: (obj: Record<string, any>, path: string | string[], value: any) => Record<string, any>;
declare const throttle: <T extends (...args: any[]) => any>(fn: T, wait?: number, options?: {
leading?: boolean;
trailing?: boolean;
}) => T & {
cancel: () => void;
};
declare const merge: <T extends Record<string, any>>(target: T, ...sources: Record<string, any>[]) => T;
//#endregion
export { type BreakpointKeys, type Breakpoints, type CSSEngineConnector, type CSSEngineResult, type HTMLElementAttrs, type HTMLTagAttrsByTag, type HTMLTags, type HTMLTextTags, HTML_TAGS, HTML_TEXT_TAGS, type IsEmpty, Provider, type Render, compose, config, context, get, hoistNonReactStatics, init, isEmpty, isEqual, merge, omit, pick, render, set, throttle, useStableValue };
//# sourceMappingURL=index2.d.ts.map