Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 49 additions & 27 deletions src/components/avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {
type JSX,
splitProps,
Show,
children as getSolidChildren,
children as resolveChildren,
createMemo,
} from "solid-js";
import { Dynamic } from "solid-js/web";
import { twMerge } from "tailwind-merge";
Expand Down Expand Up @@ -46,8 +47,21 @@ export type AvatarProps<E extends ElementType = "div"> = Omit<

// Void elements rarely used here, but we'll allow generic `as`
const VoidElementList: ElementType[] = [
"area", "base", "br", "col", "embed", "hr", "img", "input", "link", "keygen",
"meta", "param", "source", "track", "wbr",
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"keygen",
"meta",
"param",
"source",
"track",
"wbr",
];

const Avatar = <E extends ElementType = "div">(
Expand All @@ -71,13 +85,15 @@ const Avatar = <E extends ElementType = "div">(
"className",
"style",
"children",
"dataTheme",
]
);

const Tag = local.as || "div";
const resolvedChildren = resolveChildren(() => local.children);
const Tag = createMemo(() => local.as || "div");

// Container classes
const containerClass = () =>
const containerClass = createMemo(() =>
twMerge(
"avatar",
local.class,
Expand All @@ -87,19 +103,21 @@ const Avatar = <E extends ElementType = "div">(
"avatar-offline": local.offline,
"avatar-placeholder": !local.src,
})
);
)
);

// Inner element dimensions
const customSizeStyle =
const customSizeStyle = createMemo(() =>
typeof local.size === "number"
? { width: `${local.size}px`, height: `${local.size}px` }
: undefined;
: undefined
);

// Shared inner classes
const baseInner = () => twMerge(local.innerClass);
const baseInner = createMemo(() => twMerge(local.innerClass));

// Image wrapper classes
const imgClasses = () =>
const imgClasses = createMemo(() =>
clsx(baseInner(), {
ring: local.border,
"ring-offset-base-100 ring-offset-2": local.border,
Expand All @@ -110,10 +128,11 @@ const Avatar = <E extends ElementType = "div">(
"w-24 h-24": local.size === "md",
"w-14 h-14": local.size === "sm",
"w-10 h-10": local.size === "xs",
});
})
);

// Placeholder wrapper classes
const placeholderClasses = () =>
const placeholderClasses = createMemo(() =>
clsx(baseInner(), {
"bg-neutral-focus": !local.color,
"text-neutral-content": !local.color || local.color === "neutral",
Expand All @@ -128,36 +147,39 @@ const Avatar = <E extends ElementType = "div">(
"w-24 h-24 text-xl": local.size === "md",
"w-14 h-14": local.size === "sm",
"w-10 h-10": local.size === "xs",
});
})
);

// Resolve children to detect single-string
const resolved = getSolidChildren(() => local.children)();
const isStringChild = typeof resolved === "string";
// Check if child is a single string
const isStringChild = createMemo(() => {
const child = resolvedChildren();
return typeof child === "string";
});

const renderContents = () => {
// If src => image avatar
return local.src ? (
<div class={imgClasses()} style={customSizeStyle}>
<div class={imgClasses()} style={customSizeStyle()}>
<img src={local.src} />
</div>
) : local.letters || isStringChild ? (
<div class={placeholderClasses()} style={customSizeStyle}>
<span>{local.letters ?? resolved}</span>
) : local.letters || isStringChild() ? (
<div class={placeholderClasses()} style={customSizeStyle()}>
<span>{local.letters ?? resolvedChildren()}</span>
</div>
) : (
<div class={imgClasses()} style={customSizeStyle}>
{local.children}
<div class={imgClasses()} style={customSizeStyle()}>
{resolvedChildren()}
</div>
);
};

// Render void tags (unlikely) or normal
if (VoidElementList.includes(Tag)) {
if (VoidElementList.includes(Tag())) {
return (
<Dynamic
component={Tag}
component={Tag()}
{...others}
data-theme={(others as any)["dataTheme"]}
data-theme={local.dataTheme}
class={containerClass()}
style={local.style}
/>
Expand All @@ -166,9 +188,9 @@ const Avatar = <E extends ElementType = "div">(

return (
<Dynamic
component={Tag}
component={Tag()}
{...others}
data-theme={(others as any)["dataTheme"]}
data-theme={local.dataTheme}
class={containerClass()}
style={local.style}
>
Expand Down
32 changes: 20 additions & 12 deletions src/components/codemockup/CodeMockup.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
import {
children as getChildren,
children as resolveChildren,
JSX,
mergeProps,
ParentProps,
splitProps,
createMemo,
ParentProps,
} from "solid-js";
import { twMerge } from "tailwind-merge";
import { IComponentBaseProps } from "../types";

type AppTheme = "light" | "dark";
type CodeMockupProps = JSX.HTMLAttributes<HTMLDivElement> & {
dataTheme?: AppTheme;
};
type CodeMockupProps = JSX.HTMLAttributes<HTMLDivElement> & IComponentBaseProps;

const CodeMockup = (props: ParentProps<CodeMockupProps>): JSX.Element => {
const merged = mergeProps({ class: "", "aria-label": "Code mockup" }, props);
const [local, rest] = splitProps(merged, ["class", "children", "dataTheme"]);
const [local, rest] = splitProps(props, [
"class",
"className",
"children",
"dataTheme",
"aria-label",
]);

const resolvedChildren = getChildren(() => local.children);
const resolvedChildren = resolveChildren(() => local.children);

const classes = createMemo(() =>
twMerge("mockup-code w-full", local.class, local.className)
);

return (
<div
class={twMerge("mockup-code w-full", local.class)}
class={classes()}
data-theme={local.dataTheme}
aria-label={local["aria-label"] || "Code mockup"}
{...rest}
>
{resolvedChildren()}
</div>
);
};

export default CodeMockup;
export default CodeMockup;
33 changes: 20 additions & 13 deletions src/components/diff/Diff.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
import { createMemo, splitProps, type JSX } from "solid-js";
import {
createMemo,
splitProps,
type JSX,
children as resolveChildren,
} from "solid-js";
import { twMerge } from "tailwind-merge";
import { IComponentBaseProps } from "../types";

export type DiffProps = JSX.HTMLAttributes<HTMLDivElement> & IComponentBaseProps & {
secondItem: JSX.Element;
};
export type DiffProps = JSX.HTMLAttributes<HTMLDivElement> &
IComponentBaseProps & {
secondItem: JSX.Element;
};

const Diff = (props: DiffProps) => {
const Diff = (props: DiffProps): JSX.Element => {
const [local, rest] = splitProps(props, [
"class",
"children",
"secondItem",
"className"
"className",
]);

const classes = createMemo(() => twMerge("diff aspect-[16/9]", local.class, local.className))
const resolvedChildren = resolveChildren(() => local.children);
const resolvedSecondItem = resolveChildren(() => local.secondItem);
const classes = createMemo(() =>
twMerge("diff aspect-[16/9]", local.class, local.className)
);

return (
<div
{...rest}
class={classes()}
>
<div class="diff-item-1">{local.children}</div>
<div class="diff-item-2">{local.secondItem}</div>
<div {...rest} class={classes()}>
<div class="diff-item-1">{resolvedChildren()}</div>
<div class="diff-item-2">{resolvedSecondItem()}</div>
<div class="diff-resizer" />
</div>
);
Expand Down
12 changes: 6 additions & 6 deletions src/components/dropdown/DropdownMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type JSX, splitProps } from "solid-js";
import { type JSX, splitProps, createMemo } from "solid-js";
import { twMerge } from "tailwind-merge";
import type { IComponentBaseProps } from "../types";

Expand All @@ -8,34 +8,34 @@ export type DropdownMenuProps = JSX.HTMLAttributes<HTMLUListElement> &
class?: string;
className?: string;
style?: JSX.CSSProperties;
"data-theme"?: string;
"aria-labelledby"?: string;
};

const DropdownMenu = (props: DropdownMenuProps): JSX.Element => {
const [local, others] = splitProps(props, [
"class",
"className",
"data-theme",
"dataTheme",
"style",
"id",
"aria-labelledby",
]);

const classes = () =>
const classes = createMemo(() =>
twMerge(
"dropdown-content menu p-2 shadow bg-base-100 rounded-box",
local.class,
local.className
);
)
);

return (
<ul
{...others}
id={local.id}
aria-labelledby={local["aria-labelledby"]}
tabindex={0}
data-theme={local["data-theme"]}
data-theme={local.dataTheme}
class={classes()}
style={local.style}
role="menu"
Expand Down
43 changes: 26 additions & 17 deletions src/components/flex/Flex.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type JSX, splitProps, children as resolveChildren } from "solid-js";
import {
type JSX,
splitProps,
children as resolveChildren,
createMemo,
} from "solid-js";
import { Dynamic } from "solid-js/web";
import clsx from "clsx";
import { twMerge } from "tailwind-merge";
Expand Down Expand Up @@ -149,7 +154,7 @@ const basisMap = {
xl: "basis-32",
};

const Flex = (props: FlexProps) => {
const Flex = (props: FlexProps): JSX.Element => {
const [local, rest] = splitProps(props, [
"as",
"class",
Expand All @@ -166,26 +171,30 @@ const Flex = (props: FlexProps) => {
"basis",
]);

const tag = local.as || "div";
const tag = createMemo(() => local.as || "div");
const resolvedChildren = resolveChildren(() => local.children);

const classes = clsx(
"flex",
mapResponsiveProp(local.direction, directionMap),
mapResponsiveProp(local.justify, justifyMap),
mapResponsiveProp(local.align, alignMap),
mapResponsiveProp(local.wrap, wrapMap),
mapResponsiveProp(local.gap, gapMap),
mapResponsiveProp(local.gapX, gapXMap),
mapResponsiveProp(local.gapY, gapYMap),
mapResponsiveProp(local.grow, growMap),
mapResponsiveProp(local.shrink, shrinkMap),
mapResponsiveProp(local.basis, basisMap),
local.class
const classes = createMemo(() =>
twMerge(
clsx(
"flex",
mapResponsiveProp(local.direction, directionMap),
mapResponsiveProp(local.justify, justifyMap),
mapResponsiveProp(local.align, alignMap),
mapResponsiveProp(local.wrap, wrapMap),
mapResponsiveProp(local.gap, gapMap),
mapResponsiveProp(local.gapX, gapXMap),
mapResponsiveProp(local.gapY, gapYMap),
mapResponsiveProp(local.grow, growMap),
mapResponsiveProp(local.shrink, shrinkMap),
mapResponsiveProp(local.basis, basisMap),
local.class
)
)
);

return (
<Dynamic component={tag} class={twMerge(classes)} {...rest}>
<Dynamic component={tag()} class={classes()} {...rest}>
{resolvedChildren()}
</Dynamic>
);
Expand Down
Loading