reordenando archivos

main
dev 6 months ago
parent a8ca6b0b4d
commit c17c0fbf75

@ -1,6 +1,6 @@
<script lang="ts">
import { DateFieldHiddenInputState } from "../date-field.svelte.js";
import HiddenInput from "$lib/bits/utilities/hidden-input.svelte";
import HiddenInput from "../../internal/hidden-input.svelte";
const hiddenInputState = DateFieldHiddenInputState.create();
</script>

@ -1,9 +1,9 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { boxWith, mergeProps } from "../../utilities";
import { DateFieldInputState } from "../date-field.svelte.js";
import type { DateFieldInputProps } from "../types.js";
import DateFieldHiddenInput from "./date-field-hidden-input.svelte";
import { createId } from "$lib/internal/create-id.js";
import { createId } from "../../utilities/create-id.js";
const uid = $props.id();

@ -1,8 +1,8 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { boxWith, mergeProps } from "../../utilities";
import { DateFieldLabelState } from "../date-field.svelte.js";
import type { DateFieldLabelProps } from "../types.js";
import { createId } from "$lib/internal/create-id.js";
import { createId } from "../../utilities/create-id.js";
const uid = $props.id();

@ -1,8 +1,8 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { boxWith, mergeProps } from "../../utilities";
import { DateFieldSegmentState } from "../date-field.svelte.js";
import type { DateFieldSegmentProps } from "../types.js";
import { createId } from "$lib/internal/create-id.js";
import { createId } from "../../utilities/create-id.js";
const uid = $props.id();
@ -24,7 +24,7 @@
});
const mergedProps = $derived(
mergeProps(restProps, segmentState.props as Record<string, unknown>)
mergeProps(restProps, segmentState?.props as Record<string, unknown>)
);
</script>

@ -1,11 +1,11 @@
<script lang="ts">
import { watch } from "runed";
import { boxWith } from "svelte-toolbelt";
import { boxWith } from "../../utilities";
import { DateFieldRootState } from "../date-field.svelte.js";
import type { DateFieldRootProps } from "../types.js";
import { noop } from "$lib/internal/noop.js";
import { getDefaultDate } from "$lib/internal/date-time/utils.js";
import { resolveLocaleProp } from "$lib/bits/utilities/config/prop-resolvers.js";
import { noop } from "../../utilities/noop.ts";
import { getDefaultDate } from "../utilities/date/get-default-date.js";
import { resolveLocaleProp } from "../../utilities/config/prop-resolvers.ts";
let {
disabled = false,

@ -1,5 +1,4 @@
<script lang="ts">
import { boxWith, mergeProps } from "../../utilities/index";
import type { FieldLabelProps } from "../types";
import { FieldRootContext } from "../field.svelte";
import { Label } from "../../label";

@ -7,6 +7,8 @@ export { default as Error } from "./components/Field.Error.svelte";
export { default as Prefix } from "./components/Field.Prefix.svelte";
export { default as Suffix } from "./components/Field.Suffix.svelte";
export type {
FieldRootProps as RootProps,
FieldLabelProps as LabelProps,

@ -1 +1,2 @@
export * from "./exports";
export * as Field from "./exports";

@ -0,0 +1,30 @@
<script lang="ts">
import { mergeProps } from "../../utilities/index.ts";
import type { ArrowProps } from "./types.js";
import { useId } from "../../internal/use-id.ts";
let {
id = useId(),
children,
child,
width = 10,
height = 5,
...restProps
}: ArrowProps = $props();
const mergedProps = $derived(mergeProps(restProps, { id }));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<span {...mergedProps}>
{#if children}
{@render children?.()}
{:else}
<svg {width} {height} viewBox="0 0 30 10" preserveAspectRatio="none" data-arrow="">
<polygon points="0,0 30,0 15,10" fill="currentColor" />
</svg>
{/if}
</span>
{/if}

@ -0,0 +1,2 @@
export { default as Arrow } from "./arrow.svelte";
export type { ArrowProps } from "./types.ts";

@ -0,0 +1,20 @@
import type { WithChild } from "$lib/internal/types.js";
import type { BitsPrimitiveSpanAttributes } from "$lib/shared/attributes.js";
export type ArrowPropsWithoutHTML = WithChild<{
/**
* The width of the arrow in pixels.
*
* @defaultValue 10
*/
width?: number;
/**
* The height of the arrow in pixels.
*
* @defaultValue 5
*/
height?: number;
}>;
export type ArrowProps = ArrowPropsWithoutHTML & BitsPrimitiveSpanAttributes;

@ -0,0 +1,105 @@
import { Context } from "runed";
import { boxWith, type ReadableBox, type ReadableBoxedValues } from "svelte-toolbelt";
import type { BitsConfigPropsWithoutChildren } from "$lib/bits/utilities/config/types.js";
type BitsConfigStateProps = ReadableBoxedValues<BitsConfigPropsWithoutChildren>;
export const BitsConfigContext = new Context<BitsConfigState>("BitsConfig");
/**
* Gets the current Bits UI configuration state from the context.
*
* Returns a default configuration (where all values are `undefined`) if no configuration is found.
*/
export function getBitsConfig() {
const fallback = new BitsConfigState(null, {});
return BitsConfigContext.getOr(fallback).opts;
}
/**
* Creates and sets a new Bits UI configuration state that inherits from parent configs.
*
* @param opts - Configuration options for this level
* @returns The configuration state instance
*
* @example
* ```typescript
* // In a component that wants to set a default portal target
* const config = useBitsConfig({ defaultPortalTo: box("#some-element") });
*
* // Child components will inherit this config and can override specific values
* const childConfig = useBitsConfig({ someOtherProp: box("value") });
* // childConfig still has defaultPortalTo="#some-element" from parent
* ```
*/
export function useBitsConfig(opts: BitsConfigStateProps) {
return BitsConfigContext.set(new BitsConfigState(BitsConfigContext.getOr(null), opts));
}
/**
* Configuration state that inherits from parent configurations.
*
* @example
* Config resolution:
* ```
* Level 1: { defaultPortalTo: "#some-element", theme: "dark" }
* Level 2: { spacing: "large" } // inherits defaultPortalTo="#some-element", theme="dark"
* Level 3: { theme: "light" } // inherits defaultPortalTo="#some-element", spacing="large", overrides theme="light"
* ```
*/
export class BitsConfigState {
readonly opts: Required<BitsConfigStateProps>;
constructor(parent: BitsConfigState | null, opts: BitsConfigStateProps) {
const resolveConfigOption = createConfigResolver(parent, opts);
this.opts = {
defaultPortalTo: resolveConfigOption((config) => config.defaultPortalTo),
defaultLocale: resolveConfigOption((config) => config.defaultLocale),
};
}
}
type ConfigOptionGetter<T> = (config: BitsConfigStateProps) => ReadableBox<T> | undefined;
type ConfigOptionResolver = <T>(getter: ConfigOptionGetter<T>) => ReadableBox<T | undefined>;
/**
* Returns a config resolver that resolves a given config option's value.
*
* The resolver creates reactive boxes that resolve config option values using this priority:
* 1. Current level's value (if defined)
* 2. Parent level's value (if defined and current is undefined)
* 3. `undefined` (if no value is found in either parent or child)
*
* @param parent - Parent configuration state (null if this is root level)
* @param currentOpts - Current level's configuration options
*
* @example
* ```typescript
* // Given this hierarchy:
* // Root: { defaultPortalTo: "#some-element" }
* // Child: { someOtherProp: "value" } // no defaultPortalTo specified
*
* const resolveConfigOption = createConfigResolver(parent, opts);
* const portalTo = resolveConfigOption(config => config.defaultPortalTo);
*
* // portalTo.current === "#some-element" (inherited from parent)
* // even when child didn't specify `defaultPortalTo`
* ```
*/
function createConfigResolver(
parent: BitsConfigState | null,
currentOpts: BitsConfigStateProps
): ConfigOptionResolver {
return <T>(getter: ConfigOptionGetter<T>) => {
const configOption = boxWith(() => {
// try current opts first
const value = getter(currentOpts)?.current;
if (value !== undefined) return value;
// if no parent, return undefined
if (parent === null) return undefined;
// get value from parent (which already has its own chain resolved)
return getter(parent.opts)?.current;
});
return configOption;
};
}

@ -0,0 +1,14 @@
<script lang="ts">
import type { AcelConfigProps } from "../types.ts";
import { useBitsConfig } from "../bits-config.js";
import { boxWith } from "svelte-toolbelt";
let { children, defaultPortalTo, defaultLocale }: BitsConfigProps = $props();
useBitsConfig({
defaultPortalTo: boxWith(() => defaultPortalTo),
defaultLocale: boxWith(() => defaultLocale),
});
</script>
{@render children?.()}

@ -0,0 +1,2 @@
export { default as BitsConfig } from "./components/acel-config.svelte";
export { getBitsConfig, BitsConfigState } from "./bits-config.js";

@ -0,0 +1 @@
export * from "./exports.js";

@ -0,0 +1,41 @@
import { boxWith, type Getter, type ReadableBox } from "svelte-toolbelt";
import { type BitsConfigState, getBitsConfig } from "./bits-config.js";
/**
* Creates a generic prop resolver that follows a standard priority chain:
* 1. The getter's prop value (if defined)
* 2. The config default value (if no getter prop value is defined)
* 3. The fallback value (if no config value found)
*/
function createPropResolver<T>(
configOption: (config: BitsConfigState["opts"]) => { current: T | undefined },
fallback: T
) {
return (getProp: Getter<T | undefined>): ReadableBox<T> => {
const config = getBitsConfig();
return boxWith(() => {
// 1. return the prop's value, if provided
const propValue = getProp();
if (propValue !== undefined) return propValue;
// 2. return the resolved config option value, if available
const option = configOption(config).current;
if (option !== undefined) return option;
// 3. return the fallback if no other value is found
return fallback;
});
};
}
/**
* Resolves a locale value using the prop, the config default, or a fallback.
*
* Default value: `"en"`
*/
export const resolveLocaleProp = createPropResolver((config) => config.defaultLocale, "en");
/**
* Resolves a portal's `to` value using the prop, the config default, or a fallback.
*
* Default value: `"body"`
*/
export const resolvePortalToProp = createPropResolver((config) => config.defaultPortalTo, "body");

@ -0,0 +1,15 @@
import type { WithChildren } from "$lib/internal/types.js";
import type { PortalTarget } from "$lib/bits/utilities/portal/types.js";
export type BitsConfigPropsWithoutChildren = {
/**
* The default portal `to`/target to use for the `Portal` components throughout the app.
*/
defaultPortalTo?: PortalTarget;
/**
* The default locale to use for the components that support localization.
*/
defaultLocale?: string;
};
export type BitsConfigProps = WithChildren<BitsConfigPropsWithoutChildren>;

@ -0,0 +1,20 @@
<script lang="ts">
import { mergeProps, srOnlyStylesString } from "../utilities";
import type { HTMLInputAttributes } from "svelte/elements";
let { value = $bindable(), ...restProps }: HTMLInputAttributes = $props();
const mergedProps = $derived(
mergeProps(restProps, {
"aria-hidden": "true",
tabindex: -1,
style: srOnlyStylesString,
})
);
</script>
{#if mergedProps.type === "checkbox"}
<input {...mergedProps} {value} />
{:else}
<input bind:value {...mergedProps} />
{/if}

@ -0,0 +1,2 @@
export { default as VisuallyHidden } from "./visually-hidden.svelte";
export type * from "./types.js";

@ -0,0 +1,4 @@
import type { WithChild } from "../../utilities/types.ts";
import type { AcelPrimitiveSpanAttributes } from "../../utilities/attributes.ts";
export type VisuallyHiddenProps = WithChild<AcelPrimitiveSpanAttributes>;

@ -0,0 +1,31 @@
<script lang="ts">
import { mergeProps } from "../../utilities/index.ts";
import type { VisuallyHiddenProps } from "./types.js";
import type { StyleProperties } from "../../utilities/index.ts";
let { children, child, ...restProps }: VisuallyHiddenProps = $props();
const style: StyleProperties = {
position: "absolute",
border: 0,
width: "1px",
display: "inline-block",
height: "1px",
padding: 0,
margin: "-1px",
overflow: "hidden",
clip: "rect(0 0 0 0)",
whiteSpace: "nowrap",
wordWrap: "normal",
};
const mergedProps = $derived(mergeProps(restProps, { style }));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<span {...mergedProps}>
{@render children?.()}
</span>
{/if}

@ -29,7 +29,7 @@ import { createAcelAttrs, boolToEmptyStrOrUndef } from "../attrs";
import { chunk, isValidIndex } from "../arrays";
import { isBrowser, isHTMLElement } from "../dom/is-browser";
import { kbd } from "../keyboard/kbd";
import type { DateMatcher, Month } from "../types";
import type { DateMatcher, Month } from "./date-types.ts";
import { watch } from "runed";
/**

@ -1,509 +0,0 @@
import { describe, expect, it, vi } from "vitest";
import { mergeProps } from "../merge-props.ts";
describe("mergeProps", () => {
it("should merge objects", () => {
const props1 = { a: 1, b: 2 };
const props2 = { c: 3 };
const result = mergeProps(props1, props2);
expect(result).toEqual({ a: 1, b: 2, c: 3 });
});
it("should override values with the last one", () => {
const props1 = { a: 1 };
const props2 = { a: 2 };
const result = mergeProps(props1, props2);
expect(result).toEqual({ a: 2 });
});
it("should compose event handlers", () => {
const handler1 = vi.fn();
const handler2 = vi.fn();
const props1 = { onclick: handler1 };
const props2 = { onclick: handler2 };
const result = mergeProps(props1, props2);
result.onclick(new Event("click"));
expect(handler1).toHaveBeenCalled();
expect(handler2).toHaveBeenCalled();
});
it("should chain non-event handler functions", () => {
const handler1 = vi.fn();
const handler2 = vi.fn();
const props1 = { foo: handler1 };
const props2 = { foo: handler2 };
const result = mergeProps(props1, props2);
result.foo();
expect(handler1).toHaveBeenCalled();
expect(handler2).toHaveBeenCalled();
});
it("should handle chaining with one property being undefined", () => {
const handler1 = vi.fn();
const props1 = { foo: handler1 };
const props2 = { foo: undefined };
const result = mergeProps(props1, props2);
// @ts-expect-error - we're testing to see if the undefined value is handled
result.foo();
expect(handler1).toHaveBeenCalled();
});
it("should merge class strings", () => {
const props1 = { class: "foo" };
const props2 = { class: "bar" };
const result = mergeProps(props1, props2);
expect(result).toEqual({ class: "foo bar" });
});
it("should merge style objects", () => {
const props1 = { style: { color: "red" } };
const props2 = { style: { fontSize: "16px" } };
const result = mergeProps(props1, props2);
expect(result).toEqual({ style: "color: red; font-size: 16px;" });
});
it("should merge class strings with style objects", () => {
const props1 = { style: { fontSize: "16px", pointerEvents: "auto", color: "red" } };
const props2 = { style: "pointer-events: none; font-size: 48px;" };
const props3 = { style: { backgroundColor: "blue" } };
const props4 = { style: "background-color: red;" };
const result = mergeProps(props1, props2, props3, props4);
expect(result).toEqual({
style: "font-size: 48px; pointer-events: none; color: red; background-color: red;"
});
});
it("should merge multiple css variables", () => {
const props1 = {
style: {
"--foo": "red",
"--bar": "blue"
}
};
const props2 = {
style: {
"--foo": "green",
"--baz": "yellow"
}
};
const result = mergeProps(props1, props2);
expect(result.style).toEqual("--foo: green; --bar: blue; --baz: yellow;");
});
it("should merge style strings", () => {
const props1 = { style: "color: red;" };
const props2 = { style: "font-size: 16px;" };
const result = mergeProps(props1, props2);
expect(result).toEqual({ style: "color: red; font-size: 16px;" });
});
it("should handle sparse arrays", () => {
const props1 = { a: 1 };
const props2 = { b: 2 };
const result = mergeProps(undefined, props1, null, props2);
expect(result).toEqual({ a: 1, b: 2 });
});
it("should handle nested objects", () => {
const props1 = { obj: { a: 1 } };
const props2 = { obj: { b: 2 } };
const result = mergeProps(props1, props2);
expect(result).toEqual({ obj: { b: 2 } });
});
it("should handle the hidden attribute", () => {
const props1 = { hidden: true };
const props2 = { hidden: false };
const props3 = { hidden: "until-found" };
const result = mergeProps(props1, props2);
expect(result).toEqual({ hidden: undefined });
const result2 = mergeProps(props2, props1);
expect(result2).toEqual({ hidden: true });
const result3 = mergeProps(props1, props3);
expect(result3).toEqual({ hidden: "until-found" });
});
it("should handle the disabled attribute", () => {
const props1 = { disabled: true };
const props2 = { disabled: false };
const result = mergeProps(props1, props2);
expect(result).toEqual({ disabled: undefined });
const result2 = mergeProps(props2, props1);
expect(result2).toEqual({ disabled: true });
});
it("should chain non-function event handlers", () => {
const handler1 = vi.fn();
const handler2 = vi.fn();
const props1 = { onValueChange: handler1 };
const props2 = { onValueChange: handler2 };
const result = mergeProps(props1, props2);
result.onValueChange();
expect(handler1).toHaveBeenCalled();
expect(handler2).toHaveBeenCalled();
});
it("should handle merging of styles if styles only provided in props2", () => {
const props1 = {};
const props2 = {
style: "color: blue;"
};
const result = mergeProps(props1, props2);
expect(result).toEqual({ style: "color: blue;" });
});
it("should merge classnames via clsx", () => {
const props1 = { class: "text-lg font-bold" };
const props2 = { class: ["bg-blue-500", "hover:bg-blue-600"] };
const result = mergeProps(props1, props2);
expect(result).toEqual({ class: "text-lg font-bold bg-blue-500 hover:bg-blue-600" });
});
it("should handle symbol as keys", () => {
const symbol = Symbol("foo");
const symbol2 = Symbol("foo2");
const props1 = { [symbol]: "bar" };
const props2 = { [symbol]: "baz", [symbol2]: "qux" };
const result = mergeProps(props1, props2);
expect(result).toEqual({ [symbol]: "baz", [symbol2]: "qux" });
});
it("should handle multiple symbols with same description", () => {
const symbol1 = Symbol("foo");
const symbol2 = Symbol("foo"); // same description, different symbol
const props1 = { [symbol1]: "bar" };
const props2 = { [symbol2]: "baz" };
const result = mergeProps(props1, props2);
expect(result).toEqual({ [symbol1]: "bar", [symbol2]: "baz" });
});
it("should handle mixed string and symbol keys", () => {
const symbol = Symbol("foo");
const props1 = { [symbol]: "bar", a: 1 };
const props2 = { [symbol]: "baz", b: 2 };
const result = mergeProps(props1, props2);
expect(result).toEqual({ [symbol]: "baz", a: 1, b: 2 });
});
it("should handle undefined symbol values", () => {
const symbol = Symbol("foo");
const props1 = { [symbol]: "bar" };
const props2 = { [symbol]: undefined };
const result = mergeProps(props1, props2);
expect(result).toEqual({ [symbol]: "bar" });
});
it("should handle null symbol values", () => {
const symbol = Symbol("foo");
const props1 = { [symbol]: "bar" };
const props2 = { [symbol]: null };
const result = mergeProps(props1, props2);
expect(result).toEqual({ [symbol]: null });
});
it("should handle complex nested objects with symbols", () => {
const symbol = Symbol("foo");
const props1 = {
[symbol]: { nested: { value: 1 } },
obj: { a: 1 }
};
const props2 = {
[symbol]: { nested: { value: 2 } },
obj: { b: 2 }
};
const result = mergeProps(props1, props2);
expect(result).toEqual({
[symbol]: { nested: { value: 2 } },
obj: { b: 2 }
});
});
it("should handle symbols with function values", () => {
const symbol = Symbol("foo");
const handler1 = vi.fn();
const handler2 = vi.fn();
const props1 = { [symbol]: handler1 };
const props2 = { [symbol]: handler2 };
const result = mergeProps(props1, props2);
(result[symbol] as () => void)();
expect(handler1).not.toHaveBeenCalled();
expect(handler2).toHaveBeenCalled();
});
it("should handle symbols with style objects", () => {
const symbol = Symbol("style");
const props1 = { [symbol]: { color: "red" } };
const props2 = { [symbol]: { fontSize: "16px" } };
const result = mergeProps(props1, props2);
expect(result).toEqual({ [symbol]: { fontSize: "16px" } });
});
it("should handle symbols with class values", () => {
const symbol = Symbol("class");
const props1 = { [symbol]: "foo" };
const props2 = { [symbol]: "bar" };
const result = mergeProps(props1, props2);
expect(result).toEqual({ [symbol]: "bar" });
});
it("should handle multiple props with multiple symbols", () => {
const symbol1 = Symbol("foo");
const symbol2 = Symbol("bar");
const symbol3 = Symbol("baz");
const props1 = { [symbol1]: "a", [symbol2]: "b" };
const props2 = { [symbol2]: "c", [symbol3]: "d" };
const props3 = { [symbol1]: "e", [symbol3]: "f" };
const result = mergeProps(props1, props2, props3);
expect(result).toEqual({
[symbol1]: "e",
[symbol2]: "c",
[symbol3]: "f"
});
});
it("should handle CSS variables and camelCase in styles", () => {
const props1 = {
style: {
"--custom-color": "red",
backgroundColor: "blue",
"--spacing-unit": "8px",
"--border-radius": "4px"
}
};
const props2 = {
style: {
"--custom-color": "green",
backgroundColor: "yellow",
"--border-width": "2px"
}
};
const result = mergeProps(props1, props2);
expect(result.style).toEqual(
"--custom-color: green; background-color: yellow; --spacing-unit: 8px; --border-radius: 4px; --border-width: 2px;"
);
});
it("should handle complex class combinations with arrays, objects, and conditionals", () => {
const props1 = {
class: [
"base",
{ "conditional-true": true, "conditional-false": false },
["nested", "array"],
undefined,
null
]
};
const props2 = {
class: {
"override-true": true,
"override-false": false
}
};
const result = mergeProps(props1, props2);
expect(result.class).toEqual("base conditional-true nested array override-true");
});
it("should handle multiple event handlers with different event types", () => {
const clickHandler1 = vi.fn();
const clickHandler2 = vi.fn();
const inputHandler1 = vi.fn();
const inputHandler2 = vi.fn();
const keydownHandler1 = vi.fn();
const keydownHandler2 = vi.fn();
const props1 = {
onclick: clickHandler1,
oninput: inputHandler1,
onkeydown: keydownHandler1
};
const props2 = {
onclick: clickHandler2,
oninput: inputHandler2,
onkeydown: keydownHandler2
};
const result = mergeProps(props1, props2);
result.onclick(new Event("click"));
result.oninput(new Event("input"));
result.onkeydown(new Event("keydown"));
expect(clickHandler1).toHaveBeenCalled();
expect(clickHandler2).toHaveBeenCalled();
expect(inputHandler1).toHaveBeenCalled();
expect(inputHandler2).toHaveBeenCalled();
expect(keydownHandler1).toHaveBeenCalled();
expect(keydownHandler2).toHaveBeenCalled();
});
it("should handle complex function chaining with different return types", () => {
const fn1 = vi.fn().mockReturnValue(1);
const fn2 = vi.fn().mockReturnValue("string");
const fn3 = vi.fn().mockReturnValue({ obj: true });
const props1 = { complexFn: fn1 };
const props2 = { complexFn: fn2 };
const props3 = { complexFn: fn3 };
const result = mergeProps(props1, props2, props3);
result.complexFn();
expect(fn1).toHaveBeenCalled();
expect(fn2).toHaveBeenCalled();
expect(fn3).toHaveBeenCalled();
expect(fn3).toHaveReturnedWith({ obj: true });
});
it("should handle style merging with !important and vendor prefixes", () => {
const props1 = {
style: {
color: "red !important",
WebkitTransition: "all 0.3s",
MozTransition: "all 0.3s",
msTransition: "all 0.3s"
}
};
const props2 = {
style: {
color: "blue",
WebkitTransform: "scale(1.1)",
MozTransform: "scale(1.1)",
msTransform: "scale(1.1)"
}
};
const result = mergeProps(props1, props2);
expect(result.style).toEqual(
"color: blue; -webkit-transition: all 0.3s; -moz-transition: all 0.3s; ms-transition: all 0.3s; -webkit-transform: scale(1.1); -moz-transform: scale(1.1); ms-transform: scale(1.1);"
);
});
it("should handle merging of custom event handlers with different parameter types", () => {
const handler1 = vi.fn();
const handler2 = vi.fn();
const handler3 = vi.fn();
const props1 = { onCustomEvent: handler1 };
const props2 = { onCustomEvent: handler2 };
const props3 = { onCustomEvent: handler3 };
const result = mergeProps(props1, props2, props3);
// Test with different parameter types
result.onCustomEvent("string");
result.onCustomEvent(123);
result.onCustomEvent({ complex: "object" });
result.onCustomEvent(new Event("custom"));
expect(handler1).toHaveBeenCalledTimes(4);
expect(handler2).toHaveBeenCalledTimes(4);
expect(handler3).toHaveBeenCalledTimes(4);
});
it("should handle merging of style with calc() and var() functions", () => {
const props1 = {
style: {
width: "calc(100% - 20px)",
"--custom-width": "50px",
height: "var(--custom-height, 100px)"
}
};
const props2 = {
style: {
"--custom-width": "75px",
"--custom-height": "200px",
margin: "calc(var(--custom-width) / 2)"
}
};
const result = mergeProps(props1, props2);
expect(result.style).toEqual(
"width: calc(100% - 20px); --custom-width: 75px; height: var(--custom-height, 100px); --custom-height: 200px; margin: calc(var(--custom-width) / 2);"
);
});
it("should handle style merging with complex units and values", () => {
const props1 = {
style: {
width: "100vw",
height: "calc(100vh - 60px)",
transform: "translate3d(0, 0, 0) rotate(45deg)",
background: "linear-gradient(45deg, #ff0000, #00ff00)",
boxShadow: "0 0 10px rgba(0,0,0,0.5)",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
}
};
const props2 = {
style: {
transform: "scale(1.1)",
background: "radial-gradient(circle, #0000ff, #ff00ff)",
boxShadow: "inset 0 0 5px rgba(255,255,255,0.5)"
}
};
const result = mergeProps(props1, props2);
expect(result.style).toEqual(
"width: 100vw; height: calc(100vh - 60px); transform: scale(1.1); background: radial-gradient(circle, #0000ff, #ff00ff); box-shadow: inset 0 0 5px rgba(255,255,255,0.5); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);"
);
});
it("should handle style merging with empty and invalid values", () => {
const props1 = {
style: {
color: "",
backgroundColor: null,
fontSize: undefined,
border: "1px solid black",
invalid: 123
}
};
const props2 = {
style: {
color: "red",
backgroundColor: "blue",
fontSize: "16px",
invalid: "string"
}
};
const result = mergeProps(props1, props2);
expect(result.style).toEqual(
"color: red; background-color: blue; font-size: 16px; border: 1px solid black; invalid: string;"
);
});
it("should handle style merging with shorthand properties", () => {
const props1 = {
style: {
margin: "10px",
padding: "5px 10px",
border: "1px solid black",
background: "red"
}
};
const props2 = {
style: {
marginTop: "20px",
paddingLeft: "15px",
borderColor: "blue",
backgroundColor: "green"
}
};
const result = mergeProps(props1, props2);
expect(result.style).toEqual(
"margin: 10px; padding: 5px 10px; border: 1px solid black; background: red; margin-top: 20px; padding-left: 15px; border-color: blue; background-color: green;"
);
});
});

@ -1,7 +1,6 @@
<script lang="ts">
import { mergeProps } from "svelte-toolbelt";
import type { VisuallyHiddenProps } from "./types.js";
import type { StyleProperties } from "$lib/shared/index.js";
import { mergeProps, type StyleProperties } from "@/uix/acel/utilities/index.js";
let { children, child, ...restProps }: VisuallyHiddenProps = $props();

Loading…
Cancel
Save

Powered by TurnKey Linux.