diff --git a/dist/common/ui.d.ts b/dist/common/ui.d.ts new file mode 100644 index 0000000..4af34ce --- /dev/null +++ b/dist/common/ui.d.ts @@ -0,0 +1,113 @@ +import { BoxProps } from '../components/Box'; +import { BooleanLike } from './react'; +type UnitMapper = (value: unknown) => string | undefined; +/** + * Coverts our rem-like spacing unit into a CSS unit. + */ +export declare const unit: UnitMapper; +/** + * Same as `unit`, but half the size for integers numbers. + */ +export declare const halfUnit: UnitMapper; +export type StringStyleMap = { + /** Align text inside the box. */ + align: string | BooleanLike; + /** A direct mapping to `position` CSS property. */ + position: string | BooleanLike; + /** Vertical align property. */ + verticalAlign: string | BooleanLike; + /** Sets background color. */ + backgroundColor: string | BooleanLike; + /** Applies an atomic `color-` class to the element. */ + color: string | BooleanLike; + /** Opacity, from 0 to 1. */ + opacity: string | BooleanLike; + /** Sets text color. */ + textColor: string | BooleanLike; + /** Margin on all sides. */ + m: string | BooleanLike; + /** Bottom margin. */ + mb: string | BooleanLike; + /** Left margin. */ + ml: string | BooleanLike; + /** Right margin. */ + mr: string | BooleanLike; + /** Top margin. */ + mt: string | BooleanLike; + /** Horizontal margin. */ + mx: string | BooleanLike; + /** Vertical margin. */ + my: string | BooleanLike; + /** Bottom margin. */ + bottom: string | BooleanLike; + /** Left margin. */ + left: string | BooleanLike; + /** Right margin. */ + right: string | BooleanLike; + /** Top margin. */ + top: string | BooleanLike; + /** Overflow property. */ + overflow: string | BooleanLike; + /** Overflow-X property. */ + overflowX: string | BooleanLike; + /** Overflow-Y property. */ + overflowY: string | BooleanLike; + /** Padding on all sides. */ + p: string | BooleanLike; + /** Bottom padding. */ + pb: string | BooleanLike; + /** Left padding. */ + pl: string | BooleanLike; + /** Right padding. */ + pr: string | BooleanLike; + /** Top padding. */ + pt: string | BooleanLike; + /** Horizontal padding. */ + px: string | BooleanLike; + /** Vertical padding. */ + py: string | BooleanLike; + /** Box height. */ + height: string | BooleanLike; + /** Box maximum height. */ + maxHeight: string | BooleanLike; + /** Box maximum width. */ + maxWidth: string | BooleanLike; + /** Box minimum height. */ + minHeight: string | BooleanLike; + /** Box minimum width. */ + minWidth: string | BooleanLike; + /** Box width. */ + width: string | BooleanLike; + /** Font family. */ + fontFamily: string | BooleanLike; + /** Font size. */ + fontSize: string | BooleanLike; + /** Font weight. */ + fontWeight: string | BooleanLike; + /** Directly affects the height of text lines. Useful for adjusting button height. */ + lineHeight: string | BooleanLike; + /** Align text inside the box. */ + textAlign: string | BooleanLike; +}; +export declare const stringStyleMap: Record; +export type BooleanStyleMap = { + /** Make text bold. */ + bold: boolean; + /** Fill positioned parent. */ + fillPositionedParent: boolean; + /** Forces the `Box` to appear as an `inline-block`. */ + inline: boolean; + /** Make text italic. */ + italic: boolean; + /** Stops text from wrapping. */ + nowrap: boolean; + /** Preserves line-breaks and spacing in text. */ + preserveWhitespace: boolean; +}; +export declare const booleanStyleMap: Record; +export declare function computeBoxProps(props: any): Record; +export declare function computeBoxClassName(props: BoxProps): string; +type StyleMap = StringStyleMap & BooleanStyleMap; +/** Super light implementation of tailwind-like class names. */ +export declare function computeTwClass(input: string | undefined): StyleMap; +export {}; diff --git a/dist/common/ui.js b/dist/common/ui.js new file mode 100644 index 0000000..5119b4a --- /dev/null +++ b/dist/common/ui.js @@ -0,0 +1,137 @@ +import { CSS_COLORS as u } from "./constants.js"; +import { classes as y } from "./react.js"; +const f = (o) => { + if (typeof o == "string") + return o.endsWith("px") ? `${Number.parseFloat(o) / 12}rem` : o; + if (typeof o == "number") + return `${o}rem`; +}, p = (o) => { + if (typeof o == "string") + return f(o); + if (typeof o == "number") + return f(o * 0.5); +}; +function w(o) { + return !h(o); +} +function h(o) { + return typeof o == "string" && u.includes(o); +} +const c = (o) => (t, i) => { + (typeof i == "number" || typeof i == "string") && (t[o] = i); +}, n = (o, t) => (i, e) => { + (typeof e == "number" || typeof e == "string") && (i[o] = t(e)); +}, l = (o, t) => (i, e) => { + e && (i[o] = t); +}, s = (o, t, i) => (e, r) => { + if (typeof r == "number" || typeof r == "string") + for (let m = 0; m < i.length; m++) + e[o + i[m]] = t(r); +}, a = (o) => (t, i) => { + w(i) && (t[o] = i); +}, d = { + align: c("textAlign"), + bottom: n("bottom", f), + fontFamily: c("fontFamily"), + fontSize: n("fontSize", f), + fontWeight: c("fontWeight"), + height: n("height", f), + left: n("left", f), + maxHeight: n("maxHeight", f), + maxWidth: n("maxWidth", f), + minHeight: n("minHeight", f), + minWidth: n("minWidth", f), + opacity: c("opacity"), + overflow: c("overflow"), + overflowX: c("overflowX"), + overflowY: c("overflowY"), + position: c("position"), + right: n("right", f), + textAlign: c("textAlign"), + top: n("top", f), + verticalAlign: c("verticalAlign"), + width: n("width", f), + lineHeight: (o, t) => { + typeof t == "number" ? o.lineHeight = t : typeof t == "string" && (o.lineHeight = f(t)); + }, + // Margin + m: s("margin", p, [ + "Top", + "Bottom", + "Left", + "Right" + ]), + mb: n("marginBottom", p), + ml: n("marginLeft", p), + mr: n("marginRight", p), + mt: n("marginTop", p), + mx: s("margin", p, ["Left", "Right"]), + my: s("margin", p, ["Top", "Bottom"]), + // Padding + p: s("padding", p, [ + "Top", + "Bottom", + "Left", + "Right" + ]), + pb: n("paddingBottom", p), + pl: n("paddingLeft", p), + pr: n("paddingRight", p), + pt: n("paddingTop", p), + px: s("padding", p, ["Left", "Right"]), + py: s("padding", p, ["Top", "Bottom"]), + // Color props + color: a("color"), + textColor: a("color"), + backgroundColor: a("backgroundColor") +}, b = { + bold: l("fontWeight", "bold"), + fillPositionedParent: (o, t) => { + t && (o.position = "absolute", o.top = 0, o.bottom = 0, o.left = 0, o.right = 0); + }, + inline: l("display", "inline-block"), + italic: l("fontStyle", "italic"), + nowrap: l("whiteSpace", "nowrap"), + preserveWhitespace: l("whiteSpace", "pre-wrap") +}; +function T(o) { + const t = {}, i = {}; + for (const e in o) { + if (e === "style") + continue; + const r = o[e], m = d[e] || b[e]; + m ? m(i, r) : t[e] = r; + } + return t.style = { ...i, ...o.style }, t; +} +function S(o) { + const t = o.textColor || o.color, { backgroundColor: i } = o; + return y([ + h(t) && `color-${t}`, + h(i) && `color-bg-${i}` + ]); +} +function P(o) { + const t = {}; + if (!o) return t; + const i = o.split(" "); + for (const e of i) { + const [r, m] = e.split("-"); + if (r) + if (r in d) { + if (m === "") continue; + const g = Number(m); + !Number.isNaN(g) && Number.isFinite(g) ? t[r] = g : t[r] = m; + } else r in b ? t[r] = !0 : console.warn(`Unknown prop ${r}`); + } + return t; +} +export { + b as booleanStyleMap, + S as computeBoxClassName, + T as computeBoxProps, + P as computeTwClass, + p as halfUnit, + d as stringStyleMap, + f as unit +}; diff --git a/dist/components/Box.d.ts b/dist/components/Box.d.ts index 6b53e20..6d721ae 100644 --- a/dist/components/Box.d.ts +++ b/dist/components/Box.d.ts @@ -1,8 +1,7 @@ import { CSSProperties, KeyboardEventHandler, MouseEventHandler, ReactNode, UIEventHandler } from 'react'; import { BooleanLike } from '../common/react'; -type BooleanProps = Partial>; -type StringProps = Partial>; -export type EventHandlers = Partial<{ +import { BooleanStyleMap, StringStyleMap } from '../common/ui'; +type EventHandlers = { onClick: MouseEventHandler; onContextMenu: MouseEventHandler; onDoubleClick: MouseEventHandler; @@ -14,78 +13,91 @@ export type EventHandlers = Partial<{ onMouseOver: MouseEventHandler; onMouseUp: MouseEventHandler; onScroll: UIEventHandler; -}>; -export type BoxProps = Partial<{ +}; +type InternalProps = { + /** The component used for the root node. */ as: string; + /** The content of the component. */ children: ReactNode; + /** Class name to pass into the component. */ className: string | BooleanLike; + /** The unique id of the component. */ id: string; + /** The inline style of the component. */ style: CSSProperties; -}> & BooleanProps & StringProps & EventHandlers; + /** + * ### tw + * A shorthand classname syntax based loosely on tailwind. + * + * This takes all Box style props with a dash separator for params, e.g.'mb-4' or the prop name alone e.g. 'bold'. + * + * It's compatible with regular Box props, even on the same component, but it will take precedence. + * + * ### Example: + * ```tsx + * + * // Is equivalent to + * + * ``` + * + * ### Caveats: + * 1. You can't use this for custom props from other components. + * + * 2. There is no type info or safety for this method. Like the old days, it simply won't work if you use it incorrectly. + * + * 3. This should be a static string with minimal interpolation. If you need more logic, prefer the props approach. + */ + tw: string; +}; +export type BoxProps = Partial; type DangerDoNotUse = { dangerouslySetInnerHTML?: { __html: any; }; }; /** - * Coverts our rem-like spacing unit into a CSS unit. - */ -export declare function unit(value: unknown): string | undefined; -/** - * Same as `unit`, but half the size for integers numbers. + * # Box + * + * The Box component serves as a wrapper component for most of the CSS utility + * needs. It creates a new DOM element, a `
` by default that can be changed + * with the `as` property. Let's say you want to use a `` instead: + * + * @example + * ```tsx + * + *