@shadow-js/ui 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/cli.mjs +324 -247
- package/dist/dev/cli/schema.d.ts +0 -2
- package/dist/dev/locales/ar.d.ts +0 -2
- package/dist/dev/locales/ar.js +1 -2
- package/dist/dev/locales/en.d.ts +0 -2
- package/dist/dev/locales/en.js +1 -2
- package/dist/dev/registry/utils/utils.d.ts +0 -2
- package/dist/dev/registry/utils/utils.js +1 -2
- package/dist/dev/theme/theme-logic.d.ts +0 -2
- package/dist/dev/theme/theme-logic.js +1 -2
- package/dist/dev/theme/theme-provider.d.ts +0 -2
- package/dist/dev/theme/theme-provider.js +1 -2
- package/package.json +11 -11
- package/registry/carousel/carousel.tsx +79 -15
- package/registry/chart/chart.tsx +5 -1
- package/registry/input-group/input-group.tsx +2 -1
- package/registry/resizable/resizable.tsx +175 -34
- package/registry.json +7 -3
package/dist/dev/cli/schema.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/cli/schema.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* The shapes the CLI reads: the registry index (`registry.json`) and the project's
|
|
4
3
|
* `components.json`, with validators that turn untrusted JSON into typed values or a clear error.
|
|
@@ -69,5 +68,4 @@ export interface RegistryIndex {
|
|
|
69
68
|
/** Every installable item, in registry order. */
|
|
70
69
|
readonly items: readonly RegistryItem[];
|
|
71
70
|
}
|
|
72
|
-
//#endregion
|
|
73
71
|
//# sourceMappingURL=schema.d.ts.map
|
package/dist/dev/locales/ar.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/locales/ar.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* The Arabic messages of the UI kit, as a catalog of messages: every label, description and live-region text the components show, keyed
|
|
4
3
|
* by component. Pass it (or a copy with your own words) to the strings provider of your project, or into a `createI18n` catalog.
|
|
@@ -136,6 +135,5 @@ declare const ar: {
|
|
|
136
135
|
readonly notifications: "الإشعارات";
|
|
137
136
|
};
|
|
138
137
|
};
|
|
139
|
-
//#endregion
|
|
140
138
|
export { ar as default };
|
|
141
139
|
//# sourceMappingURL=ar.d.ts.map
|
package/dist/dev/locales/ar.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/locales/ar.ts
|
|
2
2
|
/**
|
|
3
3
|
* The Arabic messages of the UI kit, as a catalog of messages: every label, description and live-region text the components show, keyed
|
|
4
4
|
* by component. Pass it (or a copy with your own words) to the strings provider of your project, or into a `createI18n` catalog.
|
|
@@ -122,7 +122,6 @@ const ar = {
|
|
|
122
122
|
notifications: "الإشعارات"
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
|
-
//#endregion
|
|
126
125
|
export { ar as default };
|
|
127
126
|
|
|
128
127
|
//# sourceMappingURL=ar.js.map
|
package/dist/dev/locales/en.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/locales/en.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* The English messages of the UI kit, as a catalog of messages: every label, description and live-region text the components show, keyed
|
|
4
3
|
* by component. Pass it (or a copy with your own words) to the strings provider of your project, or into a `createI18n` catalog.
|
|
@@ -136,6 +135,5 @@ declare const en: {
|
|
|
136
135
|
readonly notifications: "Notifications";
|
|
137
136
|
};
|
|
138
137
|
};
|
|
139
|
-
//#endregion
|
|
140
138
|
export { en as default };
|
|
141
139
|
//# sourceMappingURL=en.d.ts.map
|
package/dist/dev/locales/en.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/locales/en.ts
|
|
2
2
|
/**
|
|
3
3
|
* The English messages of the UI kit, as a catalog of messages: every label, description and live-region text the components show, keyed
|
|
4
4
|
* by component. Pass it (or a copy with your own words) to the strings provider of your project, or into a `createI18n` catalog.
|
|
@@ -122,7 +122,6 @@ const en = {
|
|
|
122
122
|
notifications: "Notifications"
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
|
-
//#endregion
|
|
126
125
|
export { en as default };
|
|
127
126
|
|
|
128
127
|
//# sourceMappingURL=en.js.map
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region registry/utils/utils.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Anything a ShadowJS `class` attribute accepts: strings, numbers, nested (readonly) arrays and
|
|
4
3
|
* `{ name: condition }` objects. Falsy values are skipped.
|
|
@@ -25,5 +24,4 @@ export type ClassValue = string | number | bigint | boolean | null | undefined |
|
|
|
25
24
|
* @see {@link ClassValue}
|
|
26
25
|
*/
|
|
27
26
|
export declare function cn(...inputs: ClassValue[]): string;
|
|
28
|
-
//#endregion
|
|
29
27
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { clsx } from "clsx";
|
|
2
2
|
import { twMerge } from "tailwind-merge";
|
|
3
|
-
|
|
3
|
+
// registry/utils/utils.ts
|
|
4
4
|
/**
|
|
5
5
|
* Joins class names (strings, arrays, `{ name: condition }` objects) and resolves Tailwind
|
|
6
6
|
* conflicts, so the last utility wins: `cn("px-2", props.class)` lets the caller override `px-2`.
|
|
@@ -21,7 +21,6 @@ import { twMerge } from "tailwind-merge";
|
|
|
21
21
|
function cn(...inputs) {
|
|
22
22
|
return twMerge(clsx(...inputs));
|
|
23
23
|
}
|
|
24
|
-
//#endregion
|
|
25
24
|
export { cn };
|
|
26
25
|
|
|
27
26
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/theme/theme-logic.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Pure theme logic, shared by the provider and the no-flash script so both agree on what
|
|
4
3
|
* "system", "light" and "dark" mean. Nothing here touches `window` unless it is handed an object.
|
|
@@ -183,5 +182,4 @@ export declare function themeScript(options?: ThemeOptions): string;
|
|
|
183
182
|
export declare function themeScriptTag(options?: ThemeOptions & {
|
|
184
183
|
readonly nonce?: string | undefined;
|
|
185
184
|
}): string;
|
|
186
|
-
//#endregion
|
|
187
185
|
//# sourceMappingURL=theme-logic.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/theme/theme-logic.ts
|
|
2
2
|
/**
|
|
3
3
|
* The `localStorage` key that holds the theme choice when `storageKey` is not given.
|
|
4
4
|
* @example
|
|
@@ -178,7 +178,6 @@ function themeScript(options = {}) {
|
|
|
178
178
|
function themeScriptTag(options = {}) {
|
|
179
179
|
return `<script${options.nonce === void 0 ? "" : ` nonce="${options.nonce.replace(/[^A-Za-z0-9+/=_-]/g, "")}"`}>${themeScript(options)}<\/script>`;
|
|
180
180
|
}
|
|
181
|
-
//#endregion
|
|
182
181
|
export { DEFAULT_STORAGE_KEY, applyTheme, isTheme, readStoredTheme, resolveTheme, themeScript, themeScriptTag, writeStoredTheme };
|
|
183
182
|
|
|
184
183
|
//# sourceMappingURL=theme-logic.js.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ResolvedTheme, Theme, ThemeOptions } from "./theme-logic.js";
|
|
2
2
|
import { Accessor } from "@shadow-js/core";
|
|
3
3
|
import { JsxChild } from "@shadow-js/web/jsx-runtime";
|
|
4
|
-
//#region src/theme/theme-provider.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* What {@link useTheme} returns. Every value is an accessor: call it to read.
|
|
7
6
|
* @since 1.0.0
|
|
@@ -86,5 +85,4 @@ export declare function ThemeProvider(props: ThemeProviderProps): JsxChild;
|
|
|
86
85
|
* @since 1.0.0
|
|
87
86
|
*/
|
|
88
87
|
export declare function useTheme(): ThemeContextValue;
|
|
89
|
-
//#endregion
|
|
90
88
|
//# sourceMappingURL=theme-provider.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { applyTheme, isTheme, readStoredTheme, resolveTheme, writeStoredTheme } from "./theme-logic.js";
|
|
2
2
|
import { batch, createContext, onCleanup, onMount, untrack, useContext, useEffect, useMemo, useSignal } from "@shadow-js/core";
|
|
3
|
-
|
|
3
|
+
// src/theme/theme-provider.ts
|
|
4
4
|
const MISSING = {
|
|
5
5
|
theme: () => "system",
|
|
6
6
|
resolvedTheme: () => void 0,
|
|
@@ -144,7 +144,6 @@ function useTheme() {
|
|
|
144
144
|
if (value === MISSING) throw new Error("useTheme() must be used within <ThemeProvider>");
|
|
145
145
|
return value;
|
|
146
146
|
}
|
|
147
|
-
//#endregion
|
|
148
147
|
export { ThemeProvider, useTheme };
|
|
149
148
|
|
|
150
149
|
//# sourceMappingURL=theme-provider.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shadow-js/ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "shadcn-style components you add to your project with a CLI, styled with Tailwind v4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -60,18 +60,18 @@
|
|
|
60
60
|
"check": "npm run lint && npm run typecheck && npm run build && npm test && npm run test:perf && npm run test:gc"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@shadow-js/core": "^1.0.
|
|
64
|
-
"@shadow-js/primitives": "^1.0.
|
|
65
|
-
"@shadow-js/web": "^1.0.
|
|
63
|
+
"@shadow-js/core": "^1.0.2",
|
|
64
|
+
"@shadow-js/primitives": "^1.0.2",
|
|
65
|
+
"@shadow-js/web": "^1.0.2"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@biomejs/biome": "2.5.15",
|
|
69
|
-
"@shadow-js/compiler": "^1.0.
|
|
70
|
-
"@shadow-js/core": "^1.0.
|
|
71
|
-
"@shadow-js/forms": "^1.0.
|
|
72
|
-
"@shadow-js/primitives": "^1.0.
|
|
73
|
-
"@shadow-js/testing": "^1.0.
|
|
74
|
-
"@shadow-js/web": "^1.0.
|
|
69
|
+
"@shadow-js/compiler": "^1.0.2",
|
|
70
|
+
"@shadow-js/core": "^1.0.2",
|
|
71
|
+
"@shadow-js/forms": "^1.0.2",
|
|
72
|
+
"@shadow-js/primitives": "^1.0.2",
|
|
73
|
+
"@shadow-js/testing": "^1.0.2",
|
|
74
|
+
"@shadow-js/web": "^1.0.2",
|
|
75
75
|
"@tailwindcss/node": "4.3.3",
|
|
76
76
|
"@types/node": "^24.0.0",
|
|
77
77
|
"fast-check": "^4.10.2",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"vitest": "5.0.3"
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@shadow-js/table": "1.0.
|
|
87
|
+
"@shadow-js/table": "1.0.2",
|
|
88
88
|
"class-variance-authority": "0.7.1",
|
|
89
89
|
"clsx": "2.1.1",
|
|
90
90
|
"tailwind-merge": "3.7.0"
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { onCleanup, onMount, splitProps, useEffect, useSignal } from "@shadow-js/core";
|
|
2
2
|
import {
|
|
3
|
+
callHandler,
|
|
3
4
|
createPartContext,
|
|
4
5
|
type Direction,
|
|
6
|
+
mergeElementProps,
|
|
5
7
|
useDirectionAround,
|
|
6
8
|
useRefSignal,
|
|
7
9
|
} from "@shadow-js/primitives";
|
|
@@ -99,6 +101,7 @@ interface CarouselContextValue {
|
|
|
99
101
|
readonly setViewport: (element: HTMLElement | undefined) => void;
|
|
100
102
|
readonly claimSlide: () => number;
|
|
101
103
|
readonly registerCount: () => void;
|
|
104
|
+
readonly releaseSlide: () => void;
|
|
102
105
|
readonly pause: (paused: boolean) => void;
|
|
103
106
|
readonly labels: () => ResolvedLabels;
|
|
104
107
|
/** True once the carousel is mounted in the browser; the slide names wait for it (see CarouselItem). */
|
|
@@ -306,6 +309,12 @@ function Carousel(props: CarouselProps): JSX.Element {
|
|
|
306
309
|
const [ready, setReady] = useSignal(false);
|
|
307
310
|
let viewport: HTMLElement | undefined;
|
|
308
311
|
let claimed = 0;
|
|
312
|
+
// The slide count the "reInit" listeners were last told, and whether the carousel is gone.
|
|
313
|
+
let reported = 0;
|
|
314
|
+
let disposed = false;
|
|
315
|
+
onCleanup(() => {
|
|
316
|
+
disposed = true;
|
|
317
|
+
});
|
|
309
318
|
// The slide a programmatic move is heading for, until the scroll arrives there. While it is set,
|
|
310
319
|
// the intermediate positions of a smooth scroll do not move the index back.
|
|
311
320
|
let pending: number | undefined;
|
|
@@ -376,6 +385,27 @@ function Carousel(props: CarouselProps): JSX.Element {
|
|
|
376
385
|
if (typeof viewport.scrollTo !== "function") viewport.scrollLeft = position * sign;
|
|
377
386
|
}
|
|
378
387
|
};
|
|
388
|
+
/** Tells the "reInit" listeners when the slide count differs from what they last heard. */
|
|
389
|
+
const announceCount = (): void => {
|
|
390
|
+
if (!ready()) return;
|
|
391
|
+
const total = count();
|
|
392
|
+
if (total === reported) return;
|
|
393
|
+
reported = total;
|
|
394
|
+
// Slides were removed: the current one may be gone.
|
|
395
|
+
if (total > 0 && index() > total - 1) setIndex(total - 1);
|
|
396
|
+
emit("reInit");
|
|
397
|
+
};
|
|
398
|
+
// A list that is replaced removes every slide and adds new ones in the same turn, and a carousel
|
|
399
|
+
// that unmounts removes them all: wait for the final count before telling anyone, once.
|
|
400
|
+
let announcing = false;
|
|
401
|
+
const scheduleAnnounce = (): void => {
|
|
402
|
+
if (!ready() || announcing) return;
|
|
403
|
+
announcing = true;
|
|
404
|
+
queueMicrotask(() => {
|
|
405
|
+
announcing = false;
|
|
406
|
+
if (!disposed) announceCount();
|
|
407
|
+
});
|
|
408
|
+
};
|
|
379
409
|
const canPrevious = (): boolean => loops() || index() > 0;
|
|
380
410
|
const canNext = (): boolean => loops() || index() < count() - 1;
|
|
381
411
|
|
|
@@ -402,9 +432,13 @@ function Carousel(props: CarouselProps): JSX.Element {
|
|
|
402
432
|
return slot;
|
|
403
433
|
},
|
|
404
434
|
registerCount() {
|
|
405
|
-
const changed = claimed !== count();
|
|
406
435
|
setCount(claimed);
|
|
407
|
-
|
|
436
|
+
scheduleAnnounce();
|
|
437
|
+
},
|
|
438
|
+
releaseSlide() {
|
|
439
|
+
claimed = Math.max(0, claimed - 1);
|
|
440
|
+
setCount(claimed);
|
|
441
|
+
scheduleAnnounce();
|
|
408
442
|
},
|
|
409
443
|
pause: (value) => setPaused(value),
|
|
410
444
|
labels,
|
|
@@ -436,6 +470,7 @@ function Carousel(props: CarouselProps): JSX.Element {
|
|
|
436
470
|
onMount(() => {
|
|
437
471
|
setRegionElement(regionNode);
|
|
438
472
|
setReady(true);
|
|
473
|
+
reported = count();
|
|
439
474
|
const start = opts().startIndex;
|
|
440
475
|
if (start !== undefined && start > 0) scrollToIndex(start, true);
|
|
441
476
|
(local.setApi as ((api: CarouselApi) => void) | undefined)?.(api);
|
|
@@ -445,7 +480,8 @@ function Carousel(props: CarouselProps): JSX.Element {
|
|
|
445
480
|
// `autoplay` prop: changing it retimes the interval, and removing it stops the interval.
|
|
446
481
|
useEffect(() => {
|
|
447
482
|
const interval = local.autoplay as number | undefined;
|
|
448
|
-
|
|
483
|
+
// NaN and Infinity must not reach setInterval: a NaN delay fires every tick and an Infinity one wraps to 0.
|
|
484
|
+
if (interval === undefined || !Number.isFinite(interval) || interval <= 0) return undefined;
|
|
449
485
|
if (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches)
|
|
450
486
|
return undefined;
|
|
451
487
|
const timer = setInterval(() => {
|
|
@@ -489,12 +525,24 @@ function Carousel(props: CarouselProps): JSX.Element {
|
|
|
489
525
|
class={cn("relative", local.class)}
|
|
490
526
|
onKeyDown={(event: KeyboardEvent) => {
|
|
491
527
|
onKeyDown(event);
|
|
492
|
-
(local.onKeyDown
|
|
528
|
+
callHandler(local.onKeyDown, event);
|
|
529
|
+
}}
|
|
530
|
+
onPointerEnter={(event: PointerEvent) => {
|
|
531
|
+
callHandler(local.onPointerEnter, event);
|
|
532
|
+
setPaused(true);
|
|
533
|
+
}}
|
|
534
|
+
onPointerLeave={(event: PointerEvent) => {
|
|
535
|
+
callHandler(local.onPointerLeave, event);
|
|
536
|
+
setPaused(false);
|
|
537
|
+
}}
|
|
538
|
+
onFocusIn={(event: FocusEvent) => {
|
|
539
|
+
callHandler(local.onFocusIn, event);
|
|
540
|
+
setPaused(true);
|
|
541
|
+
}}
|
|
542
|
+
onFocusOut={(event: FocusEvent) => {
|
|
543
|
+
callHandler(local.onFocusOut, event);
|
|
544
|
+
setPaused(false);
|
|
493
545
|
}}
|
|
494
|
-
onPointerEnter={() => setPaused(true)}
|
|
495
|
-
onPointerLeave={() => setPaused(false)}
|
|
496
|
-
onFocusIn={() => setPaused(true)}
|
|
497
|
-
onFocusOut={() => setPaused(false)}
|
|
498
546
|
{...rest}
|
|
499
547
|
>
|
|
500
548
|
{local.children as JSX.Element}
|
|
@@ -551,15 +599,15 @@ function CarouselContent(props: ComponentProps<"div">): JSX.Element {
|
|
|
551
599
|
local.class,
|
|
552
600
|
)}
|
|
553
601
|
onScroll={(event: Event) => {
|
|
554
|
-
(local.onScroll
|
|
602
|
+
callHandler(local.onScroll, event);
|
|
555
603
|
carousel.scrolled();
|
|
556
604
|
}}
|
|
557
605
|
onWheel={(event: WheelEvent) => {
|
|
558
|
-
(local.onWheel
|
|
606
|
+
callHandler(local.onWheel, event);
|
|
559
607
|
carousel.userMoved();
|
|
560
608
|
}}
|
|
561
609
|
onPointerDown={(event: PointerEvent) => {
|
|
562
|
-
(local.onPointerDown
|
|
610
|
+
callHandler(local.onPointerDown, event);
|
|
563
611
|
carousel.userMoved();
|
|
564
612
|
if (event.button !== 0 || event.pointerType === "touch") return;
|
|
565
613
|
const element = event.currentTarget as HTMLElement;
|
|
@@ -630,12 +678,28 @@ function CarouselItem(props: ComponentProps<"div">): JSX.Element {
|
|
|
630
678
|
// before the next one is counted; so a slide is named only once the carousel is mounted, and
|
|
631
679
|
// the name then uses the final count (UI-CRS-18).
|
|
632
680
|
carousel.registerCount();
|
|
681
|
+
// A slide that goes away (a list that shrinks or is replaced) no longer counts.
|
|
682
|
+
onCleanup(() => carousel.releaseSlide());
|
|
683
|
+
let node: HTMLElement | undefined;
|
|
684
|
+
// The position among the slides that exist now, from the DOM order: the slot a slide claimed when
|
|
685
|
+
// it rendered is stale once another slide has been removed or inserted before it.
|
|
686
|
+
const position = (): number => {
|
|
687
|
+
carousel.count();
|
|
688
|
+
const parent = node?.parentElement;
|
|
689
|
+
if (node === undefined || parent === null || parent === undefined) return slot + 1;
|
|
690
|
+
const slides = Array.from(parent.children).filter(
|
|
691
|
+
(child) => child.getAttribute("data-slot") === "carousel-item",
|
|
692
|
+
);
|
|
693
|
+
const at = slides.indexOf(node);
|
|
694
|
+
return at === -1 ? slot + 1 : at + 1;
|
|
695
|
+
};
|
|
696
|
+
const merged = mergeElementProps({ ref: (element: HTMLElement) => (node = element) }, rest);
|
|
633
697
|
return (
|
|
634
698
|
<div
|
|
635
699
|
role="group"
|
|
636
700
|
aria-roledescription="slide"
|
|
637
701
|
aria-label={
|
|
638
|
-
carousel.ready() ? carousel.labels().slide(
|
|
702
|
+
carousel.ready() ? carousel.labels().slide(position(), carousel.count()) : undefined
|
|
639
703
|
}
|
|
640
704
|
data-slot="carousel-item"
|
|
641
705
|
class={cn(
|
|
@@ -643,7 +707,7 @@ function CarouselItem(props: ComponentProps<"div">): JSX.Element {
|
|
|
643
707
|
carousel.orientation() === "vertical" ? "pt-4" : "ps-4",
|
|
644
708
|
local.class,
|
|
645
709
|
)}
|
|
646
|
-
{...
|
|
710
|
+
{...merged}
|
|
647
711
|
/>
|
|
648
712
|
);
|
|
649
713
|
}
|
|
@@ -699,7 +763,7 @@ function CarouselPrevious(props: ComponentProps<"button">): JSX.Element {
|
|
|
699
763
|
)}
|
|
700
764
|
disabled={!carousel.canPrevious()}
|
|
701
765
|
onClick={(event: MouseEvent) => {
|
|
702
|
-
(local.onClick
|
|
766
|
+
callHandler(local.onClick, event);
|
|
703
767
|
carousel.previous();
|
|
704
768
|
}}
|
|
705
769
|
{...rest}
|
|
@@ -764,7 +828,7 @@ function CarouselNext(props: ComponentProps<"button">): JSX.Element {
|
|
|
764
828
|
)}
|
|
765
829
|
disabled={!carousel.canNext()}
|
|
766
830
|
onClick={(event: MouseEvent) => {
|
|
767
|
-
(local.onClick
|
|
831
|
+
callHandler(local.onClick, event);
|
|
768
832
|
carousel.next();
|
|
769
833
|
}}
|
|
770
834
|
{...rest}
|
package/registry/chart/chart.tsx
CHANGED
|
@@ -220,7 +220,11 @@ function ChartContainer(props: ChartContainerProps): JSX.Element {
|
|
|
220
220
|
"children",
|
|
221
221
|
]);
|
|
222
222
|
const uiLocale = useUiLocale();
|
|
223
|
-
|
|
223
|
+
// A NaN or Infinity width would put "NaN" into every path; fall back to the default instead.
|
|
224
|
+
const initial = local.initialWidth as number | undefined;
|
|
225
|
+
const [width, setWidth] = useSignal<number>(
|
|
226
|
+
initial !== undefined && Number.isFinite(initial) ? initial : 600,
|
|
227
|
+
);
|
|
224
228
|
const [element, setElement] = useRefSignal<HTMLElement>();
|
|
225
229
|
let node: HTMLElement | undefined;
|
|
226
230
|
const dir = useDirectionAround(() => local.dir as Direction | undefined, element);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import { callHandler } from "@shadow-js/primitives";
|
|
2
3
|
import type { ComponentProps, JSX } from "@shadow-js/web";
|
|
3
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
5
|
import { cn } from "@/lib/utils";
|
|
@@ -117,7 +118,7 @@ function InputGroupAddon(props: InputGroupAddonProps): JSX.Element {
|
|
|
117
118
|
const [local, rest] = splitProps(props, ["class", "align", "onClick"]);
|
|
118
119
|
// The application's own click handler runs first; the focus move is the default.
|
|
119
120
|
const onClick = (event: MouseEvent): void => {
|
|
120
|
-
(local.onClick
|
|
121
|
+
callHandler(local.onClick, event);
|
|
121
122
|
const target = event.target;
|
|
122
123
|
if (target instanceof Element && target.closest("button") !== null) return;
|
|
123
124
|
const group = (event.currentTarget as HTMLElement).closest('[data-slot="input-group"]');
|