This commit is contained in:
eewing
2026-02-17 14:10:16 -06:00
parent 2bca5834c5
commit cf73cd3b4c
11246 changed files with 1690552 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Hunter Johnston
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+59
View File
@@ -0,0 +1,59 @@
# Bits UI
<!-- automd:badges license name="bits-ui" color="pink" github="huntabyte/bits-ui" -->
[![npm version](https://flat.badgen.net/npm/v/bits-ui?color=pink)](https://npmjs.com/package/bits-ui)
[![npm downloads](https://flat.badgen.net/npm/dm/bits-ui?color=pink)](https://npmjs.com/package/bits-ui)
[![license](https://flat.badgen.net/github/license/huntabyte/bits-ui?color=pink)](https://github.com/huntabyte/bits-ui/blob/main/LICENSE)
[![](https://dcbadge.vercel.app/api/server/fdXy3Sk8Gq?style=flat)](https://discord.gg/fdXy3Sk8Gq)
<img style="max-width: 100%" alt="hero" src="https://github.com/user-attachments/assets/19cac792-6a93-4289-b9c7-647794a7de79" />
**Bits UI** the headless components for Svelte.
Flexible, unstyled, and accessible primitives that provide the foundation for building your own high-quality component library.
## Documentation
Visit https://bits-ui.com/docs to view the documentation.
## Credits
- [Bitworks](https://bitworks.cz) - The design team behind the Bits UI documentation and example components.
- [Melt UI](https://melt-ui.com) - A powerful builder API that inspired the internal architecture of Bits UI.
- [Radix UI](https://radix-ui.com) - The incredible headless component APIs that we've taken heavy inspiration from.
- [React Spectrum](https://react-spectrum.adobe.com) - An incredible collection of headless components we've taken inspiration from.
## Sponsors
This project is supported by the following beautiful people/organizations:
<p align="center">
<a href="https://github.com/sponsors/huntabyte">
<img src='https://github.com/huntabyte/static/blob/main/sponsors.svg?raw=true' alt="Logos from Sponsors" />
</a>
</p>
## License
<!-- automd:contributors license=MIT author="huntabyte" github="huntabyte/bits-ui" -->
Published under the [MIT](https://github.com/huntabyte/bits-ui/blob/main/LICENSE) license.
Built by [@huntabyte](https://github.com/huntabyte) and [community](https://github.com/huntabyte/bits-ui/graphs/contributors). Designed by [Pavel Stianko](https://x.com/pavel_stianko) 💛
<br><br>
<a href="https://github.com/huntabyte/bits-ui/graphs/contributors">
<img src="https://contrib.rocks/image?repo=huntabyte/bits-ui" />
</a>
<!-- /automd -->
## Community
Join the Discord server to ask questions, find collaborators, or just say hi!
<a href="https://discord.gg/fdXy3Sk8Gq" alt="Svecosystem Discord community">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://invidget.switchblade.xyz/fdXy3Sk8Gq">
<img alt="Svecosystem Discord community" src="https://invidget.switchblade.xyz/fdXy3Sk8Gq?theme=light">
</picture>
</a>
+19
View File
@@ -0,0 +1,19 @@
// oxlint-disable no-var
import type { ReadableBox } from "svelte-toolbelt";
import type { DismissibleLayerState } from "./bits/utilities/dismissible-layer/use-dismissable-layer.svelte.ts";
import type { InteractOutsideBehaviorType } from "./bits/utilities/dismissible-layer/types.ts";
import type { EscapeLayerState } from "./bits/utilities/escape-layer/use-escape-layer.svelte.ts";
import type { EscapeBehaviorType } from "./bits/utilities/escape-layer/types.ts";
import type { TextSelectionLayerState } from "./bits/utilities/text-selection-layer/use-text-selection-layer.svelte.ts";
declare global {
var bitsDismissableLayers: Map<DismissibleLayerState, ReadableBox<InteractOutsideBehaviorType>>;
var bitsEscapeLayers: Map<EscapeLayerState, ReadableBox<EscapeBehaviorType>>;
var bitsTextSelectionLayers: Map<TextSelectionLayerState, ReadableBox<boolean>>;
var bitsIdCounter: { current: number };
var bitsBodyLockStackCount: {
readonly map: SvelteMap<string, boolean>;
resetBodyStyle: () => void;
};
var bitsAnimationsDisabled: boolean;
}
+159
View File
@@ -0,0 +1,159 @@
import { type Box, type ReadableBoxedValues, type WritableBoxedValues } from "svelte-toolbelt";
import type { BitsKeyboardEvent, BitsMouseEvent, RefAttachment, WithRefOpts } from "../../internal/types.js";
import type { Orientation } from "../../shared/index.js";
import { RovingFocusGroup } from "../../internal/roving-focus-group.js";
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
interface AccordionBaseStateOpts extends WithRefOpts, ReadableBoxedValues<{
disabled: boolean;
orientation: Orientation;
loop: boolean;
}> {
}
interface AccordionSingleStateOpts extends AccordionBaseStateOpts, WritableBoxedValues<{
value: string;
}> {
}
interface AccordionMultiStateOpts extends AccordionBaseStateOpts, WritableBoxedValues<{
value: string[];
}> {
}
type AccordionRoot = AccordionSingleState | AccordionMultiState;
interface AccordionItemStateOpts extends WithRefOpts, ReadableBoxedValues<{
value: string;
disabled: boolean;
}> {
rootState: AccordionRoot;
}
interface AccordionTriggerStateOpts extends WithRefOpts, ReadableBoxedValues<{
disabled: boolean | null | undefined;
tabindex: number;
}> {
}
interface AccordionContentStateOpts extends WithRefOpts, ReadableBoxedValues<{
forceMount: boolean;
hiddenUntilFound: boolean;
}> {
}
interface AccordionHeaderStateOpts extends WithRefOpts, ReadableBoxedValues<{
level: 1 | 2 | 3 | 4 | 5 | 6;
}> {
}
interface AccordionRootStateOpts extends WithRefOpts, ReadableBoxedValues<{
disabled: boolean;
orientation: Orientation;
loop: boolean;
}> {
type: "single" | "multiple";
value: Box<string> | Box<string[]>;
}
declare abstract class AccordionBaseState {
readonly opts: AccordionBaseStateOpts;
readonly rovingFocusGroup: RovingFocusGroup;
abstract readonly isMulti: boolean;
readonly attachment: RefAttachment;
constructor(opts: AccordionBaseStateOpts);
abstract includesItem(item: string): boolean;
abstract toggleItem(item: string): void;
readonly props: {
readonly id: string;
readonly "data-orientation": Orientation;
readonly "data-disabled": "" | undefined;
};
}
declare class AccordionSingleState extends AccordionBaseState {
readonly opts: AccordionSingleStateOpts;
readonly isMulti: false;
constructor(opts: AccordionSingleStateOpts);
includesItem(item: string): boolean;
toggleItem(item: string): void;
}
declare class AccordionMultiState extends AccordionBaseState {
#private;
readonly isMulti: true;
constructor(props: AccordionMultiStateOpts);
includesItem(item: string): boolean;
toggleItem(item: string): void;
}
export declare class AccordionRootState {
static create(props: AccordionRootStateOpts): AccordionRoot;
}
export declare class AccordionItemState {
static create(props: Omit<AccordionItemStateOpts, "rootState">): AccordionItemState;
readonly opts: AccordionItemStateOpts;
readonly root: AccordionRoot;
readonly isActive: boolean;
readonly isDisabled: boolean;
readonly attachment: RefAttachment;
contentNode: HTMLElement | null;
contentPresence: PresenceManager;
constructor(opts: AccordionItemStateOpts);
updateValue(): void;
readonly props: {
readonly id: string;
readonly "data-state": "open" | "closed";
readonly "data-disabled": "" | undefined;
readonly "data-orientation": Orientation;
};
}
export declare class AccordionTriggerState {
#private;
readonly opts: AccordionTriggerStateOpts;
readonly itemState: AccordionItemState;
readonly attachment: RefAttachment;
constructor(opts: AccordionTriggerStateOpts, itemState: AccordionItemState);
static create(props: AccordionTriggerStateOpts): AccordionTriggerState;
onclick(e: BitsMouseEvent): void;
onkeydown(e: BitsKeyboardEvent): void;
readonly props: {
readonly id: string;
readonly disabled: boolean;
readonly "aria-expanded": "true" | "false";
readonly "aria-disabled": "true" | "false";
readonly "data-disabled": "" | undefined;
readonly "data-state": "open" | "closed";
readonly "data-orientation": Orientation;
readonly tabindex: number;
readonly onclick: (e: BitsMouseEvent) => void;
readonly onkeydown: (e: BitsKeyboardEvent) => void;
};
}
export declare class AccordionContentState {
#private;
readonly opts: AccordionContentStateOpts;
readonly item: AccordionItemState;
readonly attachment: RefAttachment;
readonly open: boolean;
constructor(opts: AccordionContentStateOpts, item: AccordionItemState);
static create(props: AccordionContentStateOpts): AccordionContentState;
get shouldRender(): boolean;
readonly snippetProps: {
open: boolean;
};
readonly props: {
readonly hidden: boolean | "until-found" | undefined;
readonly id: string;
readonly "data-state": "open" | "closed";
readonly "data-disabled": "" | undefined;
readonly "data-orientation": Orientation;
readonly style: {
readonly "--bits-accordion-content-height": `${number}px`;
readonly "--bits-accordion-content-width": `${number}px`;
};
};
}
export declare class AccordionHeaderState {
readonly opts: AccordionHeaderStateOpts;
readonly item: AccordionItemState;
readonly attachment: RefAttachment;
constructor(opts: AccordionHeaderStateOpts, item: AccordionItemState);
static create(props: AccordionHeaderStateOpts): AccordionHeaderState;
readonly props: {
readonly id: string;
readonly role: "heading";
readonly "aria-level": 1 | 2 | 3 | 4 | 5 | 6;
readonly "data-heading-level": 1 | 2 | 3 | 4 | 5 | 6;
readonly "data-state": "open" | "closed";
readonly "data-orientation": Orientation;
};
}
export {};
+284
View File
@@ -0,0 +1,284 @@
import { afterTick, attachRef, boxWith, } from "svelte-toolbelt";
import { Context, watch } from "runed";
import { boolToStr, boolToEmptyStrOrUndef, getDataOpenClosed } from "../../internal/attrs.js";
import { kbd } from "../../internal/kbd.js";
import { createBitsAttrs } from "../../internal/attrs.js";
import { RovingFocusGroup } from "../../internal/roving-focus-group.js";
import { on } from "svelte/events";
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
const accordionAttrs = createBitsAttrs({
component: "accordion",
parts: ["root", "trigger", "content", "item", "header"],
});
const AccordionRootContext = new Context("Accordion.Root");
const AccordionItemContext = new Context("Accordion.Item");
class AccordionBaseState {
opts;
rovingFocusGroup;
attachment;
constructor(opts) {
this.opts = opts;
this.rovingFocusGroup = new RovingFocusGroup({
rootNode: this.opts.ref,
candidateAttr: accordionAttrs.trigger,
loop: this.opts.loop,
orientation: this.opts.orientation,
});
this.attachment = attachRef(this.opts.ref);
}
props = $derived.by(() => ({
id: this.opts.id.current,
"data-orientation": this.opts.orientation.current,
"data-disabled": boolToEmptyStrOrUndef(this.opts.disabled.current),
[accordionAttrs.root]: "",
...this.attachment,
}));
}
class AccordionSingleState extends AccordionBaseState {
opts;
isMulti = false;
constructor(opts) {
super(opts);
this.opts = opts;
this.includesItem = this.includesItem.bind(this);
this.toggleItem = this.toggleItem.bind(this);
}
includesItem(item) {
return this.opts.value.current === item;
}
toggleItem(item) {
this.opts.value.current = this.includesItem(item) ? "" : item;
}
}
class AccordionMultiState extends AccordionBaseState {
#value;
isMulti = true;
constructor(props) {
super(props);
this.#value = props.value;
this.includesItem = this.includesItem.bind(this);
this.toggleItem = this.toggleItem.bind(this);
}
includesItem(item) {
return this.#value.current.includes(item);
}
toggleItem(item) {
this.#value.current = this.includesItem(item)
? this.#value.current.filter((v) => v !== item)
: [...this.#value.current, item];
}
}
export class AccordionRootState {
static create(props) {
const { type, ...rest } = props;
const rootState = type === "single"
? new AccordionSingleState(rest)
: new AccordionMultiState(rest);
return AccordionRootContext.set(rootState);
}
}
export class AccordionItemState {
static create(props) {
return AccordionItemContext.set(new AccordionItemState({ ...props, rootState: AccordionRootContext.get() }));
}
opts;
root;
isActive = $derived.by(() => this.root.includesItem(this.opts.value.current));
isDisabled = $derived.by(() => this.opts.disabled.current || this.root.opts.disabled.current);
attachment;
contentNode = $state(null);
contentPresence;
constructor(opts) {
this.opts = opts;
this.root = opts.rootState;
this.updateValue = this.updateValue.bind(this);
this.attachment = attachRef(this.opts.ref);
this.contentPresence = new PresenceManager({
ref: boxWith(() => this.contentNode),
open: boxWith(() => this.isActive),
});
}
updateValue() {
this.root.toggleItem(this.opts.value.current);
}
props = $derived.by(() => ({
id: this.opts.id.current,
"data-state": getDataOpenClosed(this.isActive),
"data-disabled": boolToEmptyStrOrUndef(this.isDisabled),
"data-orientation": this.root.opts.orientation.current,
[accordionAttrs.item]: "",
...this.attachment,
}));
}
export class AccordionTriggerState {
opts;
itemState;
#root;
#isDisabled = $derived.by(() => this.opts.disabled.current ||
this.itemState.opts.disabled.current ||
this.#root.opts.disabled.current);
attachment;
constructor(opts, itemState) {
this.opts = opts;
this.itemState = itemState;
this.#root = itemState.root;
this.onclick = this.onclick.bind(this);
this.onkeydown = this.onkeydown.bind(this);
this.attachment = attachRef(this.opts.ref);
}
static create(props) {
return new AccordionTriggerState(props, AccordionItemContext.get());
}
onclick(e) {
if (this.#isDisabled || e.button !== 0) {
e.preventDefault();
return;
}
this.itemState.updateValue();
}
onkeydown(e) {
if (this.#isDisabled)
return;
if (e.key === kbd.SPACE || e.key === kbd.ENTER) {
e.preventDefault();
this.itemState.updateValue();
return;
}
this.#root.rovingFocusGroup.handleKeydown(this.opts.ref.current, e);
}
props = $derived.by(() => ({
id: this.opts.id.current,
disabled: this.#isDisabled,
"aria-expanded": boolToStr(this.itemState.isActive),
"aria-disabled": boolToStr(this.#isDisabled),
"data-disabled": boolToEmptyStrOrUndef(this.#isDisabled),
"data-state": getDataOpenClosed(this.itemState.isActive),
"data-orientation": this.#root.opts.orientation.current,
[accordionAttrs.trigger]: "",
tabindex: this.opts.tabindex.current,
onclick: this.onclick,
onkeydown: this.onkeydown,
...this.attachment,
}));
}
export class AccordionContentState {
opts;
item;
attachment;
#originalStyles = undefined;
#isMountAnimationPrevented = false;
#dimensions = $state({ width: 0, height: 0 });
open = $derived.by(() => {
if (this.opts.hiddenUntilFound.current)
return this.item.isActive;
return this.opts.forceMount.current || this.item.isActive;
});
constructor(opts, item) {
this.opts = opts;
this.item = item;
this.#isMountAnimationPrevented = this.item.isActive;
this.attachment = attachRef(this.opts.ref, (v) => (this.item.contentNode = v));
// Prevent mount animations on initial render
$effect(() => {
const rAF = requestAnimationFrame(() => {
this.#isMountAnimationPrevented = false;
});
return () => cancelAnimationFrame(rAF);
});
watch.pre([() => this.opts.ref.current, () => this.opts.hiddenUntilFound.current], ([node, hiddenUntilFound]) => {
if (!node || !hiddenUntilFound)
return;
const handleBeforeMatch = () => {
if (this.item.isActive)
return;
// we need to defer opening until after browser completes search highlighting
// otherwise the browser will immediately open the accordion
// and the search highlighting will not be visible
requestAnimationFrame(() => {
this.item.updateValue();
});
};
return on(node, "beforematch", handleBeforeMatch);
});
// Handle dimension updates
watch([() => this.open, () => this.opts.ref.current], this.#updateDimensions);
}
static create(props) {
return new AccordionContentState(props, AccordionItemContext.get());
}
#updateDimensions = ([_, node]) => {
if (!node)
return;
afterTick(() => {
const element = this.opts.ref.current;
if (!element)
return;
// store original styles on first run
this.#originalStyles ??= {
transitionDuration: element.style.transitionDuration,
animationName: element.style.animationName,
};
// temporarily disable animations for measurement
element.style.transitionDuration = "0s";
element.style.animationName = "none";
const rect = element.getBoundingClientRect();
this.#dimensions = { width: rect.width, height: rect.height };
// restore animations if not initial mount
if (!this.#isMountAnimationPrevented && this.#originalStyles) {
element.style.transitionDuration = this.#originalStyles.transitionDuration;
element.style.animationName = this.#originalStyles.animationName;
}
});
};
get shouldRender() {
return this.item.contentPresence.shouldRender;
}
snippetProps = $derived.by(() => ({ open: this.item.isActive }));
props = $derived.by(() => ({
id: this.opts.id.current,
"data-state": getDataOpenClosed(this.item.isActive),
"data-disabled": boolToEmptyStrOrUndef(this.item.isDisabled),
"data-orientation": this.item.root.opts.orientation.current,
[accordionAttrs.content]: "",
style: {
"--bits-accordion-content-height": `${this.#dimensions.height}px`,
"--bits-accordion-content-width": `${this.#dimensions.width}px`,
},
hidden: this.opts.hiddenUntilFound.current && !this.item.isActive
? "until-found"
: undefined,
...(this.opts.hiddenUntilFound.current && !this.shouldRender
? {}
: {
hidden: this.opts.hiddenUntilFound.current
? !this.shouldRender
: this.opts.forceMount.current
? undefined
: !this.shouldRender,
}),
...this.attachment,
}));
}
export class AccordionHeaderState {
opts;
item;
attachment;
constructor(opts, item) {
this.opts = opts;
this.item = item;
this.attachment = attachRef(this.opts.ref);
}
static create(props) {
return new AccordionHeaderState(props, AccordionItemContext.get());
}
props = $derived.by(() => ({
id: this.opts.id.current,
role: "heading",
"aria-level": this.opts.level.current,
"data-heading-level": this.opts.level.current,
"data-state": getDataOpenClosed(this.item.isActive),
"data-orientation": this.item.root.opts.orientation.current,
[accordionAttrs.header]: "",
...this.attachment,
}));
}
@@ -0,0 +1,41 @@
<script lang="ts">
import { mergeProps, boxWith } from "svelte-toolbelt";
import { AccordionContentState } from "../accordion.svelte.js";
import type { AccordionContentProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
child,
ref = $bindable(null),
id = createId(uid),
forceMount = false,
children,
hiddenUntilFound = false,
...restProps
}: AccordionContentProps = $props();
const contentState = AccordionContentState.create({
forceMount: boxWith(() => forceMount),
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
hiddenUntilFound: boxWith(() => hiddenUntilFound),
});
const mergedProps = $derived(mergeProps(restProps, contentState.props));
</script>
{#if child}
{@render child({
props: mergedProps,
...contentState.snippetProps,
})}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { AccordionContentProps } from "../types.js";
declare const AccordionContent: import("svelte").Component<AccordionContentProps, {}, "ref">;
type AccordionContent = ReturnType<typeof AccordionContent>;
export default AccordionContent;
@@ -0,0 +1,36 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { AccordionHeaderProps } from "../types.js";
import { AccordionHeaderState } from "../accordion.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
id = createId(uid),
level = 2,
children,
child,
ref = $bindable(null),
...restProps
}: AccordionHeaderProps = $props();
const headerState = AccordionHeaderState.create({
id: boxWith(() => id),
level: boxWith(() => level),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, headerState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { AccordionHeaderProps } from "../types.js";
declare const AccordionHeader: import("svelte").Component<AccordionHeaderProps, {}, "ref">;
type AccordionHeader = ReturnType<typeof AccordionHeader>;
export default AccordionHeader;
@@ -0,0 +1,39 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { AccordionItemProps } from "../types.js";
import { AccordionItemState } from "../accordion.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
const defaultId = createId(uid);
let {
id = defaultId,
disabled = false,
value = defaultId,
children,
child,
ref = $bindable(null),
...restProps
}: AccordionItemProps = $props();
const itemState = AccordionItemState.create({
value: boxWith(() => value),
disabled: boxWith(() => disabled),
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, itemState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { AccordionItemProps } from "../types.js";
declare const AccordionItem: import("svelte").Component<AccordionItemProps, {}, "ref">;
type AccordionItem = ReturnType<typeof AccordionItem>;
export default AccordionItem;
@@ -0,0 +1,38 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { AccordionTriggerProps } from "../types.js";
import { AccordionTriggerState } from "../accordion.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
disabled = false,
ref = $bindable(null),
id = createId(uid),
tabindex = 0,
children,
child,
...restProps
}: AccordionTriggerProps = $props();
const triggerState = AccordionTriggerState.create({
disabled: boxWith(() => disabled),
id: boxWith(() => id),
tabindex: boxWith(() => tabindex ?? 0),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, triggerState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<button type="button" {...mergedProps}>
{@render children?.()}
</button>
{/if}
@@ -0,0 +1,4 @@
import type { AccordionTriggerProps } from "../types.js";
declare const AccordionTrigger: import("svelte").Component<AccordionTriggerProps, {}, "ref">;
type AccordionTrigger = ReturnType<typeof AccordionTrigger>;
export default AccordionTrigger;
+69
View File
@@ -0,0 +1,69 @@
<script lang="ts">
import { type WritableBox, boxWith, mergeProps } from "svelte-toolbelt";
import { AccordionRootState } from "../accordion.svelte.js";
import type { AccordionRootProps } from "../types.js";
import { noop } from "../../../internal/noop.js";
import { watch } from "runed";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
disabled = false,
children,
child,
type,
value = $bindable(),
ref = $bindable(null),
id = createId(uid),
onValueChange = noop,
loop = true,
orientation = "vertical",
...restProps
}: AccordionRootProps = $props();
function handleDefaultValue() {
if (value !== undefined) return;
value = type === "single" ? "" : [];
}
// SSR
handleDefaultValue();
watch.pre(
() => value,
() => {
handleDefaultValue();
}
);
const rootState = AccordionRootState.create({
type,
value: boxWith(
() => value!,
(v) => {
value = v;
// oxlint-disable-next-line no-explicit-any
onValueChange(v as any);
}
) as WritableBox<string> | WritableBox<string[]>,
id: boxWith(() => id),
disabled: boxWith(() => disabled),
loop: boxWith(() => loop),
orientation: boxWith(() => orientation),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, rootState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { AccordionRootProps } from "../types.js";
declare const Accordion: import("svelte").Component<AccordionRootProps, {}, "value" | "ref">;
type Accordion = ReturnType<typeof Accordion>;
export default Accordion;
+6
View File
@@ -0,0 +1,6 @@
export { default as Root } from "./components/accordion.svelte";
export { default as Item } from "./components/accordion-item.svelte";
export { default as Header } from "./components/accordion-header.svelte";
export { default as Trigger } from "./components/accordion-trigger.svelte";
export { default as Content } from "./components/accordion-content.svelte";
export type { AccordionRootProps as RootProps, AccordionItemProps as ItemProps, AccordionHeaderProps as HeaderProps, AccordionTriggerProps as TriggerProps, AccordionContentProps as ContentProps, } from "./types.js";
+5
View File
@@ -0,0 +1,5 @@
export { default as Root } from "./components/accordion.svelte";
export { default as Item } from "./components/accordion-item.svelte";
export { default as Header } from "./components/accordion-header.svelte";
export { default as Trigger } from "./components/accordion-trigger.svelte";
export { default as Content } from "./components/accordion-content.svelte";
+1
View File
@@ -0,0 +1 @@
export * as Accordion from "./exports.js";
+1
View File
@@ -0,0 +1 @@
export * as Accordion from "./exports.js";
+121
View File
@@ -0,0 +1,121 @@
import type { OnChangeFn, WithChild, WithChildNoChildrenSnippetProps, Without } from "../../internal/types.js";
import type { BitsPrimitiveButtonAttributes, BitsPrimitiveDivAttributes } from "../../shared/attributes.js";
import type { Orientation } from "../../shared/index.js";
export type BaseAccordionRootPropsWithoutHTML = {
/**
* Whether the accordion is disabled or not.
*
* @default false
*/
disabled?: boolean;
/**
* Whether to loop through the accordion items when navigating
* with the arrow keys.
*
* @default true
*/
loop?: boolean;
/**
* The orientation of the accordion.
*
* @default "vertical"
*/
orientation?: Orientation;
};
export type AccordionRootSinglePropsWithoutHTML = BaseAccordionRootPropsWithoutHTML & {
/**
* The type of accordion. If set to `'multiple'`, the accordion will
* allow multiple items to be open at the same time. If set to
* `'single'`, the accordion will only allow a single item to be open.
*
* @required
*/
type: "single";
/**
* The value of the currently open accordion item.
*
* @bindable
* @default ""
*/
value?: string;
/**
* A callback function called when the value changes.
*/
onValueChange?: OnChangeFn<string>;
};
export type AccordionRootMultiplePropsWithoutHTML = BaseAccordionRootPropsWithoutHTML & {
/**
* The type of accordion. If set to `'multiple'`, the accordion will
* allow multiple items to be open at the same time. If set to
* `'single'`, the accordion will only allow a single item to be open.
*
* @required
*/
type: "multiple";
/**
* The value of the currently open accordion item.
*
* @bindable
* @default []
*/
value?: string[];
/**
* A callback function called when the value changes.
*/
onValueChange?: OnChangeFn<string[]>;
};
export type AccordionRootPropsWithoutHTML = WithChild<AccordionRootSinglePropsWithoutHTML> | WithChild<AccordionRootMultiplePropsWithoutHTML>;
export type AccordionRootProps = AccordionRootPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, AccordionRootPropsWithoutHTML>;
export type AccordionRootSingleProps = AccordionRootSinglePropsWithoutHTML & Without<BitsPrimitiveDivAttributes, AccordionRootSinglePropsWithoutHTML>;
export type AccordionMultipleProps = AccordionRootMultiplePropsWithoutHTML & Without<BitsPrimitiveDivAttributes, AccordionRootMultiplePropsWithoutHTML>;
export type AccordionTriggerPropsWithoutHTML = WithChild;
export type AccordionTriggerProps = AccordionTriggerPropsWithoutHTML & Without<BitsPrimitiveButtonAttributes, AccordionTriggerPropsWithoutHTML>;
export type AccordionContentSnippetProps = {
/**
* Whether the accordion content is active/open or not.
*/
open: boolean;
};
export type AccordionContentPropsWithoutHTML = WithChildNoChildrenSnippetProps<{
/**
* Whether to forcefully mount the content, regardless of the open state.
* This is useful if you want to use Svelte transitions for the content.
*
* @default true
*/
forceMount?: boolean;
/**
* Whether to allow the browser to expand the content when searching for content
* within the panel via the browser's built-in search functionality.
*
* When `true`, this prop will override the `forceMount` prop.
*
* @default false
*/
hiddenUntilFound?: boolean;
}, AccordionContentSnippetProps>;
export type AccordionContentProps = AccordionContentPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, AccordionContentPropsWithoutHTML>;
export type AccordionItemPropsWithoutHTML = WithChild<{
/**
* The value of the accordion item. This is used to identify if the item is open or closed.
* If not provided, a unique ID will be generated for this value.
*/
value?: string;
/**
* Whether the accordion item is disabled, which prevents users from interacting with it.
*
* @default false
*/
disabled?: boolean;
}>;
export type AccordionItemProps = AccordionItemPropsWithoutHTML & BitsPrimitiveDivAttributes;
export type AccordionHeaderPropsWithoutHTML = WithChild<{
/**
* The level of the accordion header, applied to the element's `aria-level` attribute.
* This is used to indicate the hierarchical relationship between the accordion items.
*
* @default `3`
*/
level?: 1 | 2 | 3 | 4 | 5 | 6;
}>;
export type AccordionHeaderProps = AccordionHeaderPropsWithoutHTML & BitsPrimitiveDivAttributes;
+1
View File
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,34 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { AlertDialogActionProps } from "../types.js";
import { DialogActionState } from "../../dialog/dialog.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
id = createId(uid),
ref = $bindable(null),
...restProps
}: AlertDialogActionProps = $props();
const actionState = DialogActionState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, actionState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<button {...mergedProps}>
{@render children?.()}
</button>
{/if}
@@ -0,0 +1,3 @@
declare const AlertDialogAction: import("svelte").Component<import("../types.js").AlertDialogTriggerProps, {}, "ref">;
type AlertDialogAction = ReturnType<typeof AlertDialogAction>;
export default AlertDialogAction;
@@ -0,0 +1,36 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { AlertDialogCancelProps } from "../types.js";
import { AlertDialogCancelState } from "../../dialog/dialog.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
children,
child,
disabled = false,
...restProps
}: AlertDialogCancelProps = $props();
const cancelState = AlertDialogCancelState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
disabled: boxWith(() => Boolean(disabled)),
});
const mergedProps = $derived(mergeProps(restProps, cancelState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<button {...mergedProps}>
{@render children?.()}
</button>
{/if}
@@ -0,0 +1,3 @@
declare const AlertDialogCancel: import("svelte").Component<import("../types.js").AlertDialogTriggerProps, {}, "ref">;
type AlertDialogCancel = ReturnType<typeof AlertDialogCancel>;
export default AlertDialogCancel;
@@ -0,0 +1,103 @@
<script lang="ts">
import { afterSleep, boxWith, mergeProps } from "svelte-toolbelt";
import type { AlertDialogContentProps } from "../types.js";
import DismissibleLayer from "../../utilities/dismissible-layer/dismissible-layer.svelte";
import EscapeLayer from "../../utilities/escape-layer/escape-layer.svelte";
import FocusScope from "../../utilities/focus-scope/focus-scope.svelte";
import TextSelectionLayer from "../../utilities/text-selection-layer/text-selection-layer.svelte";
import { createId } from "../../../internal/create-id.js";
import { noop } from "../../../internal/noop.js";
import ScrollLock from "../../utilities/scroll-lock/scroll-lock.svelte";
import { DialogContentState } from "../../dialog/dialog.svelte.js";
const uid = $props.id();
let {
id = createId(uid),
children,
child,
ref = $bindable(null),
forceMount = false,
interactOutsideBehavior = "ignore",
onCloseAutoFocus = noop,
onEscapeKeydown = noop,
onOpenAutoFocus = noop,
onInteractOutside = noop,
preventScroll = true,
trapFocus = true,
restoreScrollDelay = null,
...restProps
}: AlertDialogContentProps = $props();
const contentState = DialogContentState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, contentState.props));
</script>
{#if contentState.shouldRender || forceMount}
<FocusScope
ref={contentState.opts.ref}
loop
{trapFocus}
enabled={contentState.root.opts.open.current}
{onCloseAutoFocus}
onOpenAutoFocus={(e) => {
onOpenAutoFocus(e);
if (e.defaultPrevented) return;
e.preventDefault();
afterSleep(0, () => contentState.opts.ref.current?.focus());
}}
>
{#snippet focusScope({ props: focusScopeProps })}
<EscapeLayer
{...mergedProps}
enabled={contentState.root.opts.open.current}
ref={contentState.opts.ref}
onEscapeKeydown={(e) => {
onEscapeKeydown(e);
if (e.defaultPrevented) return;
contentState.root.handleClose();
}}
>
<DismissibleLayer
{...mergedProps}
ref={contentState.opts.ref}
enabled={contentState.root.opts.open.current}
{interactOutsideBehavior}
onInteractOutside={(e) => {
onInteractOutside(e);
if (e.defaultPrevented) return;
contentState.root.handleClose();
}}
>
<TextSelectionLayer
{...mergedProps}
ref={contentState.opts.ref}
enabled={contentState.root.opts.open.current}
>
{#if child}
{#if contentState.root.opts.open.current}
<ScrollLock {preventScroll} {restoreScrollDelay} />
{/if}
{@render child({
props: mergeProps(mergedProps, focusScopeProps),
...contentState.snippetProps,
})}
{:else}
<ScrollLock {preventScroll} />
<div {...mergeProps(mergedProps, focusScopeProps)}>
{@render children?.()}
</div>
{/if}
</TextSelectionLayer>
</DismissibleLayer>
</EscapeLayer>
{/snippet}
</FocusScope>
{/if}
@@ -0,0 +1,3 @@
declare const AlertDialogContent: import("svelte").Component<import("../../dialog/types.js").DialogContentProps, {}, "ref">;
type AlertDialogContent = ReturnType<typeof AlertDialogContent>;
export default AlertDialogContent;
@@ -0,0 +1,27 @@
<script lang="ts">
import { boxWith } from "svelte-toolbelt";
import type { AlertDialogRootProps } from "../types.js";
import { noop } from "../../../internal/noop.js";
import { DialogRootState } from "../../dialog/dialog.svelte.js";
let {
open = $bindable(false),
onOpenChange = noop,
onOpenChangeComplete = noop,
children,
}: AlertDialogRootProps = $props();
DialogRootState.create({
variant: boxWith(() => "alert-dialog"),
open: boxWith(
() => open,
(v) => {
open = v;
onOpenChange(v);
}
),
onOpenChangeComplete: boxWith(() => onOpenChangeComplete),
});
</script>
{@render children?.()}
@@ -0,0 +1,3 @@
declare const AlertDialog: import("svelte").Component<import("../types.js").AlertDialogRootPropsWithoutHTML, {}, "open">;
type AlertDialog = ReturnType<typeof AlertDialog>;
export default AlertDialog;
+10
View File
@@ -0,0 +1,10 @@
export { default as Root } from "./components/alert-dialog.svelte";
export { default as Title } from "../dialog/components/dialog-title.svelte";
export { default as Action } from "./components/alert-dialog-action.svelte";
export { default as Cancel } from "./components/alert-dialog-cancel.svelte";
export { default as Portal } from "../utilities/portal/portal.svelte";
export { default as Content } from "./components/alert-dialog-content.svelte";
export { default as Overlay } from "../dialog/components/dialog-overlay.svelte";
export { default as Trigger } from "../dialog/components/dialog-trigger.svelte";
export { default as Description } from "../dialog/components/dialog-description.svelte";
export type { AlertDialogRootProps as RootProps, AlertDialogTitleProps as TitleProps, AlertDialogActionProps as ActionProps, AlertDialogCancelProps as CancelProps, AlertDialogPortalProps as PortalProps, AlertDialogContentProps as ContentProps, AlertDialogOverlayProps as OverlayProps, AlertDialogTriggerProps as TriggerProps, AlertDialogDescriptionProps as DescriptionProps, } from "./types.js";
+9
View File
@@ -0,0 +1,9 @@
export { default as Root } from "./components/alert-dialog.svelte";
export { default as Title } from "../dialog/components/dialog-title.svelte";
export { default as Action } from "./components/alert-dialog-action.svelte";
export { default as Cancel } from "./components/alert-dialog-cancel.svelte";
export { default as Portal } from "../utilities/portal/portal.svelte";
export { default as Content } from "./components/alert-dialog-content.svelte";
export { default as Overlay } from "../dialog/components/dialog-overlay.svelte";
export { default as Trigger } from "../dialog/components/dialog-trigger.svelte";
export { default as Description } from "../dialog/components/dialog-description.svelte";
+1
View File
@@ -0,0 +1 @@
export * as AlertDialog from "./exports.js";
+1
View File
@@ -0,0 +1 @@
export * as AlertDialog from "./exports.js";
+4
View File
@@ -0,0 +1,4 @@
import type { DialogContentProps, DialogContentPropsWithoutHTML } from "../../types.js";
export type { DialogRootPropsWithoutHTML as AlertDialogRootPropsWithoutHTML, DialogRootProps as AlertDialogRootProps, DialogClosePropsWithoutHTML as AlertDialogActionPropsWithoutHTML, DialogCloseProps as AlertDialogActionProps, DialogClosePropsWithoutHTML as AlertDialogCancelPropsWithoutHTML, DialogCloseProps as AlertDialogCancelProps, DialogPortalPropsWithoutHTML as AlertDialogPortalPropsWithoutHTML, DialogPortalProps as AlertDialogPortalProps, DialogOverlayPropsWithoutHTML as AlertDialogOverlayPropsWithoutHTML, DialogOverlayProps as AlertDialogOverlayProps, DialogTitlePropsWithoutHTML as AlertDialogTitlePropsWithoutHTML, DialogTitleProps as AlertDialogTitleProps, DialogDescriptionPropsWithoutHTML as AlertDialogDescriptionPropsWithoutHTML, DialogDescriptionProps as AlertDialogDescriptionProps, DialogTriggerPropsWithoutHTML as AlertDialogTriggerPropsWithoutHTML, DialogTriggerProps as AlertDialogTriggerProps, } from "../dialog/types.js";
export type AlertDialogContentPropsWithoutHTML = Omit<DialogContentPropsWithoutHTML, "onInteractOutside">;
export type AlertDialogContentProps = DialogContentProps;
+1
View File
@@ -0,0 +1 @@
export {};
+23
View File
@@ -0,0 +1,23 @@
import { type ReadableBoxedValues } from "svelte-toolbelt";
import type { RefAttachment, WithRefOpts } from "../../internal/types.js";
interface AspectRatioRootStateOpts extends WithRefOpts, ReadableBoxedValues<{
ratio: number;
}> {
}
export declare class AspectRatioRootState {
static create(opts: AspectRatioRootStateOpts): AspectRatioRootState;
readonly opts: AspectRatioRootStateOpts;
readonly attachment: RefAttachment;
constructor(opts: AspectRatioRootStateOpts);
readonly props: {
readonly id: string;
readonly style: {
readonly position: "absolute";
readonly top: 0;
readonly right: 0;
readonly bottom: 0;
readonly left: 0;
};
};
}
export {};
+29
View File
@@ -0,0 +1,29 @@
import { attachRef } from "svelte-toolbelt";
import { createBitsAttrs } from "../../internal/attrs.js";
const aspectRatioAttrs = createBitsAttrs({
component: "aspect-ratio",
parts: ["root"],
});
export class AspectRatioRootState {
static create(opts) {
return new AspectRatioRootState(opts);
}
opts;
attachment;
constructor(opts) {
this.opts = opts;
this.attachment = attachRef(this.opts.ref);
}
props = $derived.by(() => ({
id: this.opts.id.current,
style: {
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
},
[aspectRatioAttrs.root]: "",
...this.attachment,
}));
}
@@ -0,0 +1,38 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { AspectRatioRootProps } from "../types.js";
import { AspectRatioRootState } from "../aspect-ratio.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
ref = $bindable(null),
id = createId(uid),
ratio = 1,
children,
child,
...restProps
}: AspectRatioRootProps = $props();
const rootState = AspectRatioRootState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
ratio: boxWith(() => ratio),
});
const mergedProps = $derived(mergeProps(restProps, rootState.props));
</script>
<div style:position="relative" style:width="100%" style:padding-bottom="{ratio ? 100 / ratio : 0}%">
{#if child}
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
</div>
@@ -0,0 +1,4 @@
import type { AspectRatioRootProps } from "../types.js";
declare const AspectRatio: import("svelte").Component<AspectRatioRootProps, {}, "ref">;
type AspectRatio = ReturnType<typeof AspectRatio>;
export default AspectRatio;
+2
View File
@@ -0,0 +1,2 @@
export { default as Root } from "./components/aspect-ratio.svelte";
export type { AspectRatioRootProps as RootProps } from "./types.js";
+1
View File
@@ -0,0 +1 @@
export { default as Root } from "./components/aspect-ratio.svelte";
+1
View File
@@ -0,0 +1 @@
export * as AspectRatio from "./exports.js";
+1
View File
@@ -0,0 +1 @@
export * as AspectRatio from "./exports.js";
+11
View File
@@ -0,0 +1,11 @@
import type { WithChild, Without } from "../../internal/types.js";
import type { BitsPrimitiveDivAttributes } from "../../shared/attributes.js";
export type AspectRatioRootPropsWithoutHTML = WithChild<{
/**
* The aspect ratio of the image.
*
* @defaultValue 1
*/
ratio?: number;
}>;
export type AspectRatioRootProps = AspectRatioRootPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, AspectRatioRootPropsWithoutHTML>;
+1
View File
@@ -0,0 +1 @@
export {};
+65
View File
@@ -0,0 +1,65 @@
import { DOMContext, type ReadableBox, type WritableBox, type ReadableBoxedValues } from "svelte-toolbelt";
import type { HTMLImgAttributes } from "svelte/elements";
import type { AvatarImageLoadingStatus } from "./types.js";
import type { RefAttachment, WithRefOpts } from "../../internal/types.js";
type CrossOrigin = HTMLImgAttributes["crossorigin"];
type ReferrerPolicy = HTMLImgAttributes["referrerpolicy"];
type AvatarImageSrc = string | null | undefined;
interface AvatarRootStateOpts extends WithRefOpts {
delayMs: ReadableBox<number>;
loadingStatus: WritableBox<AvatarImageLoadingStatus>;
}
export declare class AvatarRootState {
static create(opts: AvatarRootStateOpts): AvatarRootState;
readonly opts: AvatarRootStateOpts;
readonly domContext: DOMContext;
readonly attachment: RefAttachment;
constructor(opts: AvatarRootStateOpts);
loadImage(src: string, crossorigin?: CrossOrigin, referrerPolicy?: ReferrerPolicy): (() => void) | undefined;
props: {
readonly id: string;
readonly "data-status": AvatarImageLoadingStatus;
};
}
interface AvatarImageStateOpts extends WithRefOpts, ReadableBoxedValues<{
src: AvatarImageSrc;
crossOrigin: CrossOrigin;
referrerPolicy: ReferrerPolicy;
}> {
}
export declare class AvatarImageState {
static create(opts: AvatarImageStateOpts): AvatarImageState;
readonly opts: AvatarImageStateOpts;
readonly root: AvatarRootState;
readonly attachment: RefAttachment;
constructor(opts: AvatarImageStateOpts, root: AvatarRootState);
readonly props: {
readonly id: string;
readonly style: {
readonly display: "none" | "block";
};
readonly "data-status": AvatarImageLoadingStatus;
readonly src: AvatarImageSrc;
readonly crossorigin: "" | "anonymous" | "use-credentials" | null | undefined;
readonly referrerpolicy: globalThis.ReferrerPolicy | null | undefined;
};
}
interface AvatarFallbackStateOpts extends WithRefOpts {
}
export declare class AvatarFallbackState {
static create(opts: AvatarFallbackStateOpts): AvatarFallbackState;
readonly opts: AvatarFallbackStateOpts;
readonly root: AvatarRootState;
readonly attachment: RefAttachment;
constructor(opts: AvatarFallbackStateOpts, root: AvatarRootState);
readonly style: {
display: string;
} | undefined;
readonly props: {
readonly style: {
display: string;
} | undefined;
readonly "data-status": AvatarImageLoadingStatus;
};
}
export {};
+105
View File
@@ -0,0 +1,105 @@
import { DOMContext, attachRef, } from "svelte-toolbelt";
import { Context, watch } from "runed";
import { createBitsAttrs } from "../../internal/attrs.js";
const avatarAttrs = createBitsAttrs({
component: "avatar",
parts: ["root", "image", "fallback"],
});
const AvatarRootContext = new Context("Avatar.Root");
export class AvatarRootState {
static create(opts) {
return AvatarRootContext.set(new AvatarRootState(opts));
}
opts;
domContext;
attachment;
constructor(opts) {
this.opts = opts;
this.domContext = new DOMContext(this.opts.ref);
this.loadImage = this.loadImage.bind(this);
this.attachment = attachRef(this.opts.ref);
}
loadImage(src, crossorigin, referrerPolicy) {
if (this.opts.loadingStatus.current === "loaded")
return;
let imageTimerId;
const image = new Image();
image.src = src;
if (crossorigin !== undefined)
image.crossOrigin = crossorigin;
if (referrerPolicy)
image.referrerPolicy = referrerPolicy;
this.opts.loadingStatus.current = "loading";
image.onload = () => {
imageTimerId = this.domContext.setTimeout(() => {
this.opts.loadingStatus.current = "loaded";
}, this.opts.delayMs.current);
};
image.onerror = () => {
this.opts.loadingStatus.current = "error";
};
return () => {
if (!imageTimerId)
return;
this.domContext.clearTimeout(imageTimerId);
};
}
props = $derived.by(() => ({
id: this.opts.id.current,
[avatarAttrs.root]: "",
"data-status": this.opts.loadingStatus.current,
...this.attachment,
}));
}
export class AvatarImageState {
static create(opts) {
return new AvatarImageState(opts, AvatarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
watch.pre([() => this.opts.src.current, () => this.opts.crossOrigin.current], ([src, crossOrigin]) => {
if (!src) {
this.root.opts.loadingStatus.current = "error";
return;
}
this.root.loadImage(src, crossOrigin, this.opts.referrerPolicy.current);
});
}
props = $derived.by(() => ({
id: this.opts.id.current,
style: {
display: this.root.opts.loadingStatus.current === "loaded" ? "block" : "none",
},
"data-status": this.root.opts.loadingStatus.current,
[avatarAttrs.image]: "",
src: this.opts.src.current,
crossorigin: this.opts.crossOrigin.current,
referrerpolicy: this.opts.referrerPolicy.current,
...this.attachment,
}));
}
export class AvatarFallbackState {
static create(opts) {
return new AvatarFallbackState(opts, AvatarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
}
style = $derived.by(() => this.root.opts.loadingStatus.current === "loaded" ? { display: "none" } : undefined);
props = $derived.by(() => ({
style: this.style,
"data-status": this.root.opts.loadingStatus.current,
[avatarAttrs.fallback]: "",
...this.attachment,
}));
}
@@ -0,0 +1,34 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { AvatarFallbackProps } from "../types.js";
import { AvatarFallbackState } from "../avatar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
id = createId(uid),
ref = $bindable(null),
...restProps
}: AvatarFallbackProps = $props();
const fallbackState = AvatarFallbackState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, fallbackState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<span {...mergedProps}>
{@render children?.()}
</span>
{/if}
@@ -0,0 +1,4 @@
import type { AvatarFallbackProps } from "../types.js";
declare const AvatarFallback: import("svelte").Component<AvatarFallbackProps, {}, "ref">;
type AvatarFallback = ReturnType<typeof AvatarFallback>;
export default AvatarFallback;
+37
View File
@@ -0,0 +1,37 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { AvatarImageProps } from "../types.js";
import { AvatarImageState } from "../avatar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
src,
child,
id = createId(uid),
ref = $bindable(null),
crossorigin = undefined,
referrerpolicy = undefined,
...restProps
}: AvatarImageProps = $props();
const imageState = AvatarImageState.create({
src: boxWith(() => src),
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
crossOrigin: boxWith(() => crossorigin),
referrerPolicy: boxWith(() => referrerpolicy),
});
const mergedProps = $derived(mergeProps(restProps, imageState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<img {...mergedProps} {src} />
{/if}
@@ -0,0 +1,4 @@
import type { AvatarImageProps } from "../types.js";
declare const AvatarImage: import("svelte").Component<AvatarImageProps, {}, "ref">;
type AvatarImage = ReturnType<typeof AvatarImage>;
export default AvatarImage;
+47
View File
@@ -0,0 +1,47 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { AvatarRootProps } from "../types.js";
import { AvatarRootState } from "../avatar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
delayMs = 0,
loadingStatus = $bindable("loading"),
onLoadingStatusChange,
child,
children,
id = createId(uid),
ref = $bindable(null),
...restProps
}: AvatarRootProps = $props();
const rootState = AvatarRootState.create({
delayMs: boxWith(() => delayMs),
loadingStatus: boxWith(
() => loadingStatus,
(v) => {
if (loadingStatus !== v) {
loadingStatus = v;
onLoadingStatusChange?.(v);
}
}
),
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, rootState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
+4
View File
@@ -0,0 +1,4 @@
import type { AvatarRootProps } from "../types.js";
declare const Avatar: import("svelte").Component<AvatarRootProps, {}, "ref" | "loadingStatus">;
type Avatar = ReturnType<typeof Avatar>;
export default Avatar;
+4
View File
@@ -0,0 +1,4 @@
export { default as Root } from "./components/avatar.svelte";
export { default as Image } from "./components/avatar-image.svelte";
export { default as Fallback } from "./components/avatar-fallback.svelte";
export type { AvatarRootProps as RootProps, AvatarImageProps as ImageProps, AvatarFallbackProps as FallbackProps, } from "./types.js";
+3
View File
@@ -0,0 +1,3 @@
export { default as Root } from "./components/avatar.svelte";
export { default as Image } from "./components/avatar-image.svelte";
export { default as Fallback } from "./components/avatar-fallback.svelte";
+1
View File
@@ -0,0 +1 @@
export * as Avatar from "./exports.js";
+1
View File
@@ -0,0 +1 @@
export * as Avatar from "./exports.js";
+32
View File
@@ -0,0 +1,32 @@
import type { OnChangeFn, WithChild, Without } from "../../internal/types.js";
import type { BitsPrimitiveDivAttributes, BitsPrimitiveImgAttributes, BitsPrimitiveSpanAttributes } from "../../shared/attributes.js";
export type AvatarImageLoadingStatus = "loading" | "loaded" | "error";
export type AvatarRootPropsWithoutHTML = WithChild<{
/**
* The delay in milliseconds to wait before showing the avatar once
* the image has loaded. This can be used to prevent sudden flickering
* of the image if it loads quickly.
*
* @default 0
*/
delayMs?: number;
/**
* The loading status of the image.
*
* If you are confident that the image exists and will load successfully, you can
* set this to `"loaded"` to skip the loading process (which shows the fallback)
* and immediately show the image.
*
* @default "loading"
*/
loadingStatus?: AvatarImageLoadingStatus;
/**
* A callback invoked when the loading status of the image changes.
*/
onLoadingStatusChange?: OnChangeFn<AvatarImageLoadingStatus>;
}>;
export type AvatarRootProps = AvatarRootPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, AvatarRootPropsWithoutHTML>;
export type AvatarImagePropsWithoutHTML = WithChild;
export type AvatarImageProps = AvatarImagePropsWithoutHTML & Without<BitsPrimitiveImgAttributes, AvatarImagePropsWithoutHTML>;
export type AvatarFallbackPropsWithoutHTML = WithChild;
export type AvatarFallbackProps = AvatarFallbackPropsWithoutHTML & Without<BitsPrimitiveSpanAttributes, AvatarFallbackPropsWithoutHTML>;
+1
View File
@@ -0,0 +1 @@
export {};
+27
View File
@@ -0,0 +1,27 @@
<script lang="ts">
import type { ButtonRootProps } from "../types.js";
let {
href,
type,
children,
disabled = false,
ref = $bindable(null),
...restProps
}: ButtonRootProps = $props();
</script>
<svelte:element
this={href ? "a" : "button"}
data-button-root
type={href ? undefined : type}
href={href && !disabled ? href : undefined}
disabled={href ? undefined : disabled}
aria-disabled={href ? disabled : undefined}
role={href && disabled ? "link" : undefined}
tabindex={href && disabled ? -1 : 0}
bind:this={ref}
{...restProps}
>
{@render children?.()}
</svelte:element>
+4
View File
@@ -0,0 +1,4 @@
import type { ButtonRootProps } from "../types.js";
declare const Button: import("svelte").Component<ButtonRootProps, {}, "ref">;
type Button = ReturnType<typeof Button>;
export default Button;
+2
View File
@@ -0,0 +1,2 @@
export { default as Root } from "./components/button.svelte";
export type { ButtonRootProps as RootProps } from "./types.js";
+1
View File
@@ -0,0 +1 @@
export { default as Root } from "./components/button.svelte";
+1
View File
@@ -0,0 +1 @@
export * as Button from "./exports.js";
+1
View File
@@ -0,0 +1 @@
export * as Button from "./exports.js";
+18
View File
@@ -0,0 +1,18 @@
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
import type { WithoutChildren } from "svelte-toolbelt";
import type { WithChildren } from "../../shared/index.js";
export type ButtonRootPropsWithoutHTML = WithChildren<{
ref?: HTMLElement | null;
}>;
type AnchorElement = ButtonRootPropsWithoutHTML & WithoutChildren<Omit<HTMLAnchorAttributes, "href" | "type">> & {
href: HTMLAnchorAttributes["href"];
type?: never;
disabled?: HTMLButtonAttributes["disabled"];
};
type ButtonElement = ButtonRootPropsWithoutHTML & WithoutChildren<Omit<HTMLButtonAttributes, "type" | "href">> & {
type?: HTMLButtonAttributes["type"];
href?: never;
disabled?: HTMLButtonAttributes["disabled"];
};
export type ButtonRootProps = AnchorElement | ButtonElement;
export {};
+1
View File
@@ -0,0 +1 @@
export {};
+416
View File
@@ -0,0 +1,416 @@
import { type DateValue } from "@internationalized/date";
import { DOMContext, type ReadableBoxedValues, type WritableBoxedValues } from "svelte-toolbelt";
import { Context } from "runed";
import type { RangeCalendarRootState } from "../range-calendar/range-calendar.svelte.js";
import type { BitsKeyboardEvent, BitsMouseEvent, RefAttachment, WithRefOpts } from "../../internal/types.js";
import type { DateMatcher, Month } from "../../shared/index.js";
import { type Announcer } from "../../internal/date-time/announcer.js";
import { type Formatter } from "../../internal/date-time/formatter.js";
import { calendarAttrs } from "../../internal/date-time/calendar-helpers.svelte.js";
import type { WeekStartsOn } from "../../shared/date/types.js";
interface CalendarRootStateOpts extends WithRefOpts, WritableBoxedValues<{
value: DateValue | undefined | DateValue[];
placeholder: DateValue;
}>, ReadableBoxedValues<{
preventDeselect: boolean;
minValue: DateValue | undefined;
maxValue: DateValue | undefined;
disabled: boolean;
pagedNavigation: boolean;
weekStartsOn: WeekStartsOn | undefined;
weekdayFormat: Intl.DateTimeFormatOptions["weekday"];
isDateDisabled: DateMatcher;
isDateUnavailable: DateMatcher;
fixedWeeks: boolean;
numberOfMonths: number;
locale: string;
calendarLabel: string;
type: "single" | "multiple";
readonly: boolean;
disableDaysOutsideMonth: boolean;
initialFocus: boolean;
maxDays: number | undefined;
/**
* This is strictly used by the `DatePicker` component to close the popover when a date
* is selected. It is not intended to be used by the user.
*/
onDateSelect?: () => void;
monthFormat: Intl.DateTimeFormatOptions["month"] | ((month: number) => string);
yearFormat: Intl.DateTimeFormatOptions["year"] | ((year: number) => string);
}> {
defaultPlaceholder: DateValue;
}
export declare const CalendarRootContext: Context<RangeCalendarRootState | CalendarRootState>;
export declare class CalendarRootState {
#private;
static create(opts: CalendarRootStateOpts): RangeCalendarRootState | CalendarRootState;
readonly opts: CalendarRootStateOpts;
readonly visibleMonths: DateValue[];
readonly formatter: Formatter;
readonly accessibleHeadingId: string;
readonly domContext: DOMContext;
readonly attachment: RefAttachment;
months: Month<DateValue>[];
announcer: Announcer;
constructor(opts: CalendarRootStateOpts);
setMonths(months: Month<DateValue>[]): void;
/**
* This derived state holds an array of localized day names for the current
* locale and calendar view. It dynamically syncs with the 'weekStartsOn' option,
* updating its content when the option changes. Using this state to render the
* calendar's days of the week is strongly recommended, as it guarantees that
* the days are correctly formatted for the current locale and calendar view.
*/
readonly weekdays: string[];
readonly initialPlaceholderYear: number;
readonly defaultYears: number[];
/**
* Navigates to the next page of the calendar.
*/
nextPage(): void;
/**
* Navigates to the previous page of the calendar.
*/
prevPage(): void;
nextYear(): void;
prevYear(): void;
setYear(year: number): void;
setMonth(month: number): void;
isNextButtonDisabled: boolean;
isPrevButtonDisabled: boolean;
isInvalid: boolean;
readonly headingValue: string;
readonly fullCalendarLabel: string;
isOutsideVisibleMonths(date: DateValue): boolean;
isDateDisabled(date: DateValue): boolean;
isDateSelected(date: DateValue): boolean;
shiftFocus(node: HTMLElement, add: number): void;
handleCellClick(_: Event, date: DateValue): void;
handleMultipleUpdate(prev: DateValue[] | undefined, date: DateValue): DateValue[] | undefined;
handleSingleUpdate(prev: DateValue | undefined, date: DateValue): DateValue | undefined;
onkeydown(event: BitsKeyboardEvent): void;
readonly snippetProps: {
months: Month<DateValue>[];
weekdays: string[];
};
getBitsAttr: (typeof calendarAttrs)["getAttr"];
readonly props: {
readonly onkeydown: (event: BitsKeyboardEvent) => void;
readonly id: string;
readonly role: "application";
readonly "aria-label": string;
readonly "data-invalid": "" | undefined;
readonly "data-disabled": "" | undefined;
readonly "data-readonly": "" | undefined;
};
}
interface CalendarHeadingStateOpts extends WithRefOpts {
}
export declare class CalendarHeadingState {
static create(opts: CalendarHeadingStateOpts): CalendarHeadingState;
readonly opts: CalendarHeadingStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly attachment: RefAttachment;
constructor(opts: CalendarHeadingStateOpts, root: CalendarRootState | RangeCalendarRootState);
readonly props: {
readonly id: string;
readonly "aria-hidden": "true" | undefined;
readonly "data-disabled": "" | undefined;
readonly "data-readonly": "" | undefined;
};
}
interface CalendarCellStateOpts extends WithRefOpts, ReadableBoxedValues<{
date: DateValue;
month: DateValue;
}> {
}
export declare class CalendarCellState {
static create(opts: CalendarCellStateOpts): CalendarCellState;
readonly opts: CalendarCellStateOpts;
readonly root: CalendarRootState;
readonly cellDate: Date;
readonly isUnavailable: boolean;
readonly isDateToday: boolean;
readonly isOutsideMonth: boolean;
readonly isOutsideVisibleMonths: boolean;
readonly isDisabled: boolean;
readonly isFocusedDate: boolean;
readonly isSelectedDate: boolean;
readonly labelText: string;
readonly attachment: RefAttachment;
constructor(opts: CalendarCellStateOpts, root: CalendarRootState);
readonly snippetProps: {
disabled: boolean;
unavailable: boolean;
selected: boolean;
day: string;
};
readonly ariaDisabled: boolean;
readonly sharedDataAttrs: {
readonly "data-unavailable": "" | undefined;
readonly "data-today": "" | undefined;
readonly "data-outside-month": "" | undefined;
readonly "data-outside-visible-months": "" | undefined;
readonly "data-focused": "" | undefined;
readonly "data-selected": "" | undefined;
readonly "data-value": string;
readonly "data-type": string;
readonly "data-disabled": "" | undefined;
};
readonly props: {
readonly "data-unavailable": "" | undefined;
readonly "data-today": "" | undefined;
readonly "data-outside-month": "" | undefined;
readonly "data-outside-visible-months": "" | undefined;
readonly "data-focused": "" | undefined;
readonly "data-selected": "" | undefined;
readonly "data-value": string;
readonly "data-type": string;
readonly "data-disabled": "" | undefined;
readonly id: string;
readonly role: "gridcell";
readonly "aria-selected": "true" | "false";
readonly "aria-disabled": "true" | "false";
};
}
interface CalendarDayStateOpts extends WithRefOpts {
}
export declare class CalendarDayState {
#private;
static create(opts: CalendarDayStateOpts): CalendarDayState;
readonly opts: CalendarDayStateOpts;
readonly cell: CalendarCellState;
readonly attachment: RefAttachment;
constructor(opts: CalendarDayStateOpts, cell: CalendarCellState);
onclick(e: BitsMouseEvent): void;
readonly snippetProps: {
disabled: boolean;
unavailable: boolean;
selected: boolean;
day: string;
};
readonly props: {
readonly tabindex: 0 | -1 | undefined;
readonly "data-bits-day": "";
readonly onclick: (e: BitsMouseEvent) => void;
readonly "data-unavailable": "" | undefined;
readonly "data-today": "" | undefined;
readonly "data-outside-month": "" | undefined;
readonly "data-outside-visible-months": "" | undefined;
readonly "data-focused": "" | undefined;
readonly "data-selected": "" | undefined;
readonly "data-value": string;
readonly "data-type": string;
readonly "data-disabled": "" | undefined;
readonly id: string;
readonly role: "button";
readonly "aria-label": string;
readonly "aria-disabled": "true" | "false";
};
}
interface CalendarNextButtonStateOpts extends WithRefOpts {
}
export declare class CalendarNextButtonState {
static create(opts: CalendarNextButtonStateOpts): CalendarNextButtonState;
readonly opts: CalendarNextButtonStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly isDisabled: boolean;
readonly attachment: RefAttachment;
constructor(opts: CalendarNextButtonStateOpts, root: CalendarRootState | RangeCalendarRootState);
onclick(_: BitsMouseEvent): void;
readonly props: {
readonly id: string;
readonly role: "button";
readonly type: "button";
readonly "aria-label": "Next";
readonly "aria-disabled": "true" | "false";
readonly "data-disabled": "" | undefined;
readonly disabled: boolean;
readonly onclick: (_: BitsMouseEvent) => void;
};
}
interface CalendarPrevButtonStateOpts extends WithRefOpts {
}
export declare class CalendarPrevButtonState {
static create(opts: CalendarPrevButtonStateOpts): CalendarPrevButtonState;
readonly opts: CalendarPrevButtonStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly isDisabled: boolean;
readonly attachment: RefAttachment;
constructor(opts: CalendarPrevButtonStateOpts, root: CalendarRootState | RangeCalendarRootState);
onclick(_: BitsMouseEvent): void;
readonly props: {
readonly id: string;
readonly role: "button";
readonly type: "button";
readonly "aria-label": "Previous";
readonly "aria-disabled": "true" | "false";
readonly "data-disabled": "" | undefined;
readonly disabled: boolean;
readonly onclick: (_: BitsMouseEvent) => void;
};
}
interface CalendarGridStateOpts extends WithRefOpts {
}
export declare class CalendarGridState {
static create(opts: CalendarGridStateOpts): CalendarGridState;
readonly opts: CalendarGridStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly attachment: RefAttachment;
constructor(opts: CalendarGridStateOpts, root: CalendarRootState | RangeCalendarRootState);
readonly props: {
readonly id: string;
readonly tabindex: -1;
readonly role: "grid";
readonly "aria-readonly": "true" | "false";
readonly "aria-disabled": "true" | "false";
readonly "data-readonly": "" | undefined;
readonly "data-disabled": "" | undefined;
};
}
interface CalendarGridBodyStateOpts extends WithRefOpts {
}
export declare class CalendarGridBodyState {
static create(opts: CalendarGridBodyStateOpts): CalendarGridBodyState;
readonly opts: CalendarGridBodyStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly attachment: RefAttachment;
constructor(opts: CalendarGridBodyStateOpts, root: CalendarRootState | RangeCalendarRootState);
readonly props: {
readonly id: string;
readonly "data-disabled": "" | undefined;
readonly "data-readonly": "" | undefined;
};
}
interface CalendarGridHeadStateOpts extends WithRefOpts {
}
export declare class CalendarGridHeadState {
static create(opts: CalendarGridHeadStateOpts): CalendarGridHeadState;
readonly opts: CalendarGridHeadStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly attachment: RefAttachment;
constructor(opts: CalendarGridHeadStateOpts, root: CalendarRootState | RangeCalendarRootState);
readonly props: {
readonly id: string;
readonly "data-disabled": "" | undefined;
readonly "data-readonly": "" | undefined;
};
}
interface CalendarGridRowStateOpts extends WithRefOpts {
}
export declare class CalendarGridRowState {
static create(opts: CalendarGridRowStateOpts): CalendarGridRowState;
readonly opts: CalendarGridRowStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly attachment: RefAttachment;
constructor(opts: CalendarGridRowStateOpts, root: CalendarRootState | RangeCalendarRootState);
readonly props: {
readonly id: string;
readonly "data-disabled": "" | undefined;
readonly "data-readonly": "" | undefined;
};
}
interface CalendarHeadCellStateOpts extends WithRefOpts {
}
export declare class CalendarHeadCellState {
static create(opts: CalendarHeadCellStateOpts): CalendarHeadCellState;
readonly opts: CalendarHeadCellStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly attachment: RefAttachment;
constructor(opts: CalendarHeadCellStateOpts, root: CalendarRootState | RangeCalendarRootState);
readonly props: {
readonly id: string;
readonly "data-disabled": "" | undefined;
readonly "data-readonly": "" | undefined;
};
}
interface CalendarHeaderStateOpts extends WithRefOpts {
}
export declare class CalendarHeaderState {
static create(opts: CalendarHeaderStateOpts): CalendarHeaderState;
readonly opts: CalendarHeaderStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly attachment: RefAttachment;
constructor(opts: CalendarHeaderStateOpts, root: CalendarRootState | RangeCalendarRootState);
readonly props: {
readonly id: string;
readonly "data-disabled": "" | undefined;
readonly "data-readonly": "" | undefined;
};
}
interface CalendarMonthSelectStateOpts extends WithRefOpts, ReadableBoxedValues<{
months: number[];
monthFormat: Intl.DateTimeFormatOptions["month"] | ((month: number) => string);
disabled: boolean;
}> {
}
export declare class CalendarMonthSelectState {
static create(opts: CalendarMonthSelectStateOpts): CalendarMonthSelectState;
readonly opts: CalendarMonthSelectStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly attachment: RefAttachment;
constructor(opts: CalendarMonthSelectStateOpts, root: CalendarRootState | RangeCalendarRootState);
readonly monthItems: {
value: number;
label: string;
}[];
readonly currentMonth: number;
readonly isDisabled: boolean;
readonly snippetProps: {
monthItems: {
value: number;
label: string;
}[];
selectedMonthItem: {
value: number;
label: string;
};
};
onchange(event: Event): void;
readonly props: {
readonly id: string;
readonly value: number;
readonly disabled: boolean;
readonly "data-disabled": "" | undefined;
readonly onchange: (event: Event) => void;
};
}
interface CalendarYearSelectStateOpts extends WithRefOpts, ReadableBoxedValues<{
years: number[] | undefined;
yearFormat: Intl.DateTimeFormatOptions["year"] | ((year: number) => string);
disabled: boolean;
}> {
}
export declare class CalendarYearSelectState {
static create(opts: CalendarYearSelectStateOpts): CalendarYearSelectState;
readonly opts: CalendarYearSelectStateOpts;
readonly root: CalendarRootState | RangeCalendarRootState;
readonly attachment: RefAttachment;
constructor(opts: CalendarYearSelectStateOpts, root: CalendarRootState | RangeCalendarRootState);
readonly years: number[];
readonly yearItems: {
value: number;
label: string;
}[];
readonly currentYear: number;
readonly isDisabled: boolean;
readonly snippetProps: {
yearItems: {
value: number;
label: string;
}[];
selectedYearItem: {
value: number;
label: string;
};
};
onchange(event: Event): void;
readonly props: {
readonly id: string;
readonly value: number;
readonly disabled: boolean;
readonly "data-disabled": "" | undefined;
readonly onchange: (event: Event) => void;
};
}
export {};
+870
View File
@@ -0,0 +1,870 @@
import { getLocalTimeZone, isSameDay, isSameMonth, isToday, } from "@internationalized/date";
import { DEV } from "esm-env";
import { onMount, untrack } from "svelte";
import { attachRef, DOMContext, } from "svelte-toolbelt";
import { Context, watch } from "runed";
import { boolToStr, boolToStrTrueOrUndef, boolToEmptyStrOrUndef } from "../../internal/attrs.js";
import { useId } from "../../internal/use-id.js";
import { getAnnouncer } from "../../internal/date-time/announcer.js";
import { createFormatter } from "../../internal/date-time/formatter.js";
import { calendarAttrs, createAccessibleHeading, createMonths, getCalendarElementProps, getCalendarHeadingValue, getDateWithPreviousTime, getDefaultYears, getIsNextButtonDisabled, getIsPrevButtonDisabled, getWeekdays, handleCalendarKeydown, handleCalendarNextPage, handleCalendarPrevPage, shiftCalendarFocus, useEnsureNonDisabledPlaceholder, useMonthViewOptionsSync, useMonthViewPlaceholderSync, } from "../../internal/date-time/calendar-helpers.svelte.js";
import { getDateValueType, isBefore, toDate } from "../../internal/date-time/utils.js";
export const CalendarRootContext = new Context("Calendar.Root | RangeCalender.Root");
export class CalendarRootState {
static create(opts) {
return CalendarRootContext.set(new CalendarRootState(opts));
}
opts;
visibleMonths = $derived.by(() => this.months.map((month) => month.value));
formatter;
accessibleHeadingId = useId();
domContext;
attachment;
months = $state([]);
announcer;
constructor(opts) {
this.opts = opts;
this.attachment = attachRef(this.opts.ref);
this.domContext = new DOMContext(opts.ref);
this.announcer = getAnnouncer(null);
this.formatter = createFormatter({
initialLocale: this.opts.locale.current,
monthFormat: this.opts.monthFormat,
yearFormat: this.opts.yearFormat,
});
this.setMonths = this.setMonths.bind(this);
this.nextPage = this.nextPage.bind(this);
this.prevPage = this.prevPage.bind(this);
this.prevYear = this.prevYear.bind(this);
this.nextYear = this.nextYear.bind(this);
this.setYear = this.setYear.bind(this);
this.setMonth = this.setMonth.bind(this);
this.isOutsideVisibleMonths = this.isOutsideVisibleMonths.bind(this);
this.isDateDisabled = this.isDateDisabled.bind(this);
this.isDateSelected = this.isDateSelected.bind(this);
this.shiftFocus = this.shiftFocus.bind(this);
this.handleCellClick = this.handleCellClick.bind(this);
this.handleMultipleUpdate = this.handleMultipleUpdate.bind(this);
this.handleSingleUpdate = this.handleSingleUpdate.bind(this);
this.onkeydown = this.onkeydown.bind(this);
this.getBitsAttr = this.getBitsAttr.bind(this);
onMount(() => {
this.announcer = getAnnouncer(this.domContext.getDocument());
});
this.months = createMonths({
dateObj: this.opts.placeholder.current,
weekStartsOn: this.opts.weekStartsOn.current,
locale: this.opts.locale.current,
fixedWeeks: this.opts.fixedWeeks.current,
numberOfMonths: this.opts.numberOfMonths.current,
});
this.#setupInitialFocusEffect();
this.#setupAccessibleHeadingEffect();
this.#setupFormatterEffect();
/**
* Updates the displayed months based on changes in the placeholder value.
*/
useMonthViewPlaceholderSync({
placeholder: this.opts.placeholder,
getVisibleMonths: () => this.visibleMonths,
weekStartsOn: this.opts.weekStartsOn,
locale: this.opts.locale,
fixedWeeks: this.opts.fixedWeeks,
numberOfMonths: this.opts.numberOfMonths,
setMonths: (months) => (this.months = months),
});
/**
* Updates the displayed months based on changes in the options values,
* which determines the month to show in the calendar.
*/
useMonthViewOptionsSync({
fixedWeeks: this.opts.fixedWeeks,
locale: this.opts.locale,
numberOfMonths: this.opts.numberOfMonths,
placeholder: this.opts.placeholder,
setMonths: this.setMonths,
weekStartsOn: this.opts.weekStartsOn,
});
/**
* Update the accessible heading's text content when the `fullCalendarLabel`
* changes.
*/
watch(() => this.fullCalendarLabel, (label) => {
const node = this.domContext.getElementById(this.accessibleHeadingId);
if (!node)
return;
node.textContent = label;
});
/**
* Synchronize the placeholder value with the current value.
*/
watch(() => this.opts.value.current, () => {
const value = this.opts.value.current;
if (Array.isArray(value) && value.length) {
const lastValue = value[value.length - 1];
if (lastValue && this.opts.placeholder.current !== lastValue) {
this.opts.placeholder.current = lastValue;
}
}
else if (!Array.isArray(value) &&
value &&
this.opts.placeholder.current !== value) {
this.opts.placeholder.current = value;
}
});
useEnsureNonDisabledPlaceholder({
placeholder: opts.placeholder,
defaultPlaceholder: opts.defaultPlaceholder,
isDateDisabled: opts.isDateDisabled,
maxValue: opts.maxValue,
minValue: opts.minValue,
ref: opts.ref,
});
}
setMonths(months) {
this.months = months;
}
/**
* This derived state holds an array of localized day names for the current
* locale and calendar view. It dynamically syncs with the 'weekStartsOn' option,
* updating its content when the option changes. Using this state to render the
* calendar's days of the week is strongly recommended, as it guarantees that
* the days are correctly formatted for the current locale and calendar view.
*/
weekdays = $derived.by(() => {
return getWeekdays({
months: this.months,
formatter: this.formatter,
weekdayFormat: this.opts.weekdayFormat.current,
});
});
initialPlaceholderYear = $derived.by(() => untrack(() => this.opts.placeholder.current.year));
defaultYears = $derived.by(() => {
return getDefaultYears({
minValue: this.opts.minValue.current,
maxValue: this.opts.maxValue.current,
placeholderYear: this.initialPlaceholderYear,
});
});
#setupInitialFocusEffect() {
$effect(() => {
const initialFocus = untrack(() => this.opts.initialFocus.current);
if (initialFocus) {
// focus the first `data-focused` day node
const firstFocusedDay = this.opts.ref.current?.querySelector(`[data-focused]`);
if (firstFocusedDay) {
firstFocusedDay.focus();
}
}
});
}
#setupAccessibleHeadingEffect() {
$effect(() => {
if (!this.opts.ref.current)
return;
const removeHeading = createAccessibleHeading({
calendarNode: this.opts.ref.current,
label: this.fullCalendarLabel,
accessibleHeadingId: this.accessibleHeadingId,
});
return removeHeading;
});
}
#setupFormatterEffect() {
$effect.pre(() => {
if (this.formatter.getLocale() === this.opts.locale.current)
return;
this.formatter.setLocale(this.opts.locale.current);
});
}
/**
* Navigates to the next page of the calendar.
*/
nextPage() {
handleCalendarNextPage({
fixedWeeks: this.opts.fixedWeeks.current,
locale: this.opts.locale.current,
numberOfMonths: this.opts.numberOfMonths.current,
pagedNavigation: this.opts.pagedNavigation.current,
setMonths: this.setMonths,
setPlaceholder: (date) => (this.opts.placeholder.current = date),
weekStartsOn: this.opts.weekStartsOn.current,
months: this.months,
});
}
/**
* Navigates to the previous page of the calendar.
*/
prevPage() {
handleCalendarPrevPage({
fixedWeeks: this.opts.fixedWeeks.current,
locale: this.opts.locale.current,
numberOfMonths: this.opts.numberOfMonths.current,
pagedNavigation: this.opts.pagedNavigation.current,
setMonths: this.setMonths,
setPlaceholder: (date) => (this.opts.placeholder.current = date),
weekStartsOn: this.opts.weekStartsOn.current,
months: this.months,
});
}
nextYear() {
this.opts.placeholder.current = this.opts.placeholder.current.add({ years: 1 });
}
prevYear() {
this.opts.placeholder.current = this.opts.placeholder.current.subtract({ years: 1 });
}
setYear(year) {
this.opts.placeholder.current = this.opts.placeholder.current.set({ year });
}
setMonth(month) {
this.opts.placeholder.current = this.opts.placeholder.current.set({ month });
}
isNextButtonDisabled = $derived.by(() => {
return getIsNextButtonDisabled({
maxValue: this.opts.maxValue.current,
months: this.months,
disabled: this.opts.disabled.current,
});
});
isPrevButtonDisabled = $derived.by(() => {
return getIsPrevButtonDisabled({
minValue: this.opts.minValue.current,
months: this.months,
disabled: this.opts.disabled.current,
});
});
isInvalid = $derived.by(() => {
const value = this.opts.value.current;
const isDateDisabled = this.opts.isDateDisabled.current;
const isDateUnavailable = this.opts.isDateUnavailable.current;
if (Array.isArray(value)) {
if (!value.length)
return false;
for (const date of value) {
if (isDateDisabled(date))
return true;
if (isDateUnavailable(date))
return true;
}
}
else {
if (!value)
return false;
if (isDateDisabled(value))
return true;
if (isDateUnavailable(value))
return true;
}
return false;
});
headingValue = $derived.by(() => {
this.opts.monthFormat.current;
this.opts.yearFormat.current;
return getCalendarHeadingValue({
months: this.months,
formatter: this.formatter,
locale: this.opts.locale.current,
});
});
fullCalendarLabel = $derived.by(() => {
return `${this.opts.calendarLabel.current} ${this.headingValue}`;
});
isOutsideVisibleMonths(date) {
return !this.visibleMonths.some((month) => isSameMonth(date, month));
}
isDateDisabled(date) {
if (this.opts.isDateDisabled.current(date) || this.opts.disabled.current)
return true;
const minValue = this.opts.minValue.current;
const maxValue = this.opts.maxValue.current;
if (minValue && isBefore(date, minValue))
return true;
if (maxValue && isBefore(maxValue, date))
return true;
return false;
}
isDateSelected(date) {
const value = this.opts.value.current;
if (Array.isArray(value)) {
return value.some((d) => isSameDay(d, date));
}
else if (!value) {
return false;
}
return isSameDay(value, date);
}
shiftFocus(node, add) {
return shiftCalendarFocus({
node,
add,
placeholder: this.opts.placeholder,
calendarNode: this.opts.ref.current,
isPrevButtonDisabled: this.isPrevButtonDisabled,
isNextButtonDisabled: this.isNextButtonDisabled,
months: this.months,
numberOfMonths: this.opts.numberOfMonths.current,
});
}
#isMultipleSelectionValid(selectedDates) {
// only validate for multiple type and when maxDays is set
if (this.opts.type.current !== "multiple")
return true;
if (!this.opts.maxDays.current)
return true;
const selectedCount = selectedDates.length;
if (this.opts.maxDays.current && selectedCount > this.opts.maxDays.current)
return false;
return true;
}
handleCellClick(_, date) {
if (this.opts.readonly.current ||
this.opts.isDateDisabled.current?.(date) ||
this.opts.isDateUnavailable.current?.(date)) {
return;
}
const prev = this.opts.value.current;
const multiple = this.opts.type.current === "multiple";
if (multiple) {
if (Array.isArray(prev) || prev === undefined) {
this.opts.value.current = this.handleMultipleUpdate(prev, date);
}
}
else if (!Array.isArray(prev)) {
const next = this.handleSingleUpdate(prev, date);
if (!next) {
this.announcer.announce("Selected date is now empty.", "polite", 5000);
}
else {
this.announcer.announce(`Selected Date: ${this.formatter.selectedDate(next, false)}`, "polite");
}
this.opts.value.current = getDateWithPreviousTime(next, prev);
if (next !== undefined) {
this.opts.onDateSelect?.current?.();
}
}
}
handleMultipleUpdate(prev, date) {
if (!prev) {
const newSelection = [date];
return this.#isMultipleSelectionValid(newSelection) ? newSelection : [date];
}
if (!Array.isArray(prev)) {
if (DEV)
throw new Error("Invalid value for multiple prop.");
return;
}
const index = prev.findIndex((d) => isSameDay(d, date));
const preventDeselect = this.opts.preventDeselect.current;
if (index === -1) {
// adding a new date - check if it would be valid
const newSelection = [...prev, date];
if (this.#isMultipleSelectionValid(newSelection)) {
return newSelection;
}
else {
// reset to just the newly selected date when constraints are violated
return [date];
}
}
else if (preventDeselect) {
return prev;
}
else {
const next = prev.filter((d) => !isSameDay(d, date));
if (!next.length) {
this.opts.placeholder.current = date;
return undefined;
}
return next;
}
}
handleSingleUpdate(prev, date) {
if (Array.isArray(prev)) {
if (DEV)
throw new Error("Invalid value for single prop.");
}
if (!prev)
return date;
const preventDeselect = this.opts.preventDeselect.current;
if (!preventDeselect && isSameDay(prev, date)) {
this.opts.placeholder.current = date;
return undefined;
}
return date;
}
onkeydown(event) {
handleCalendarKeydown({
event,
handleCellClick: this.handleCellClick,
shiftFocus: this.shiftFocus,
placeholderValue: this.opts.placeholder.current,
});
}
snippetProps = $derived.by(() => ({
months: this.months,
weekdays: this.weekdays,
}));
getBitsAttr = (part) => {
return calendarAttrs.getAttr(part);
};
props = $derived.by(() => ({
...getCalendarElementProps({
fullCalendarLabel: this.fullCalendarLabel,
id: this.opts.id.current,
isInvalid: this.isInvalid,
disabled: this.opts.disabled.current,
readonly: this.opts.readonly.current,
}),
[this.getBitsAttr("root")]: "",
//
onkeydown: this.onkeydown,
...this.attachment,
}));
}
export class CalendarHeadingState {
static create(opts) {
return new CalendarHeadingState(opts, CalendarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
}
props = $derived.by(() => ({
id: this.opts.id.current,
"aria-hidden": boolToStrTrueOrUndef(true),
"data-disabled": boolToEmptyStrOrUndef(this.root.opts.disabled.current),
"data-readonly": boolToEmptyStrOrUndef(this.root.opts.readonly.current),
[this.root.getBitsAttr("heading")]: "",
...this.attachment,
}));
}
const CalendarCellContext = new Context("Calendar.Cell | RangeCalendar.Cell");
export class CalendarCellState {
static create(opts) {
return CalendarCellContext.set(new CalendarCellState(opts, CalendarRootContext.get()));
}
opts;
root;
cellDate = $derived.by(() => toDate(this.opts.date.current));
isUnavailable = $derived.by(() => this.root.opts.isDateUnavailable.current(this.opts.date.current));
isDateToday = $derived.by(() => isToday(this.opts.date.current, getLocalTimeZone()));
isOutsideMonth = $derived.by(() => !isSameMonth(this.opts.date.current, this.opts.month.current));
isOutsideVisibleMonths = $derived.by(() => this.root.isOutsideVisibleMonths(this.opts.date.current));
isDisabled = $derived.by(() => this.root.isDateDisabled(this.opts.date.current) ||
(this.isOutsideMonth && this.root.opts.disableDaysOutsideMonth.current));
isFocusedDate = $derived.by(() => isSameDay(this.opts.date.current, this.root.opts.placeholder.current));
isSelectedDate = $derived.by(() => this.root.isDateSelected(this.opts.date.current));
labelText = $derived.by(() => this.root.formatter.custom(this.cellDate, {
weekday: "long",
month: "long",
day: "numeric",
year: "numeric",
}));
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
}
snippetProps = $derived.by(() => ({
disabled: this.isDisabled,
unavailable: this.isUnavailable,
selected: this.isSelectedDate,
day: `${this.opts.date.current.day}`,
}));
ariaDisabled = $derived.by(() => {
return (this.isDisabled ||
(this.isOutsideMonth && this.root.opts.disableDaysOutsideMonth.current) ||
this.isUnavailable);
});
sharedDataAttrs = $derived.by(() => ({
"data-unavailable": boolToEmptyStrOrUndef(this.isUnavailable),
"data-today": this.isDateToday ? "" : undefined,
"data-outside-month": this.isOutsideMonth ? "" : undefined,
"data-outside-visible-months": this.isOutsideVisibleMonths ? "" : undefined,
"data-focused": this.isFocusedDate ? "" : undefined,
"data-selected": boolToEmptyStrOrUndef(this.isSelectedDate),
"data-value": this.opts.date.current.toString(),
"data-type": getDateValueType(this.opts.date.current),
"data-disabled": boolToEmptyStrOrUndef(this.isDisabled ||
(this.isOutsideMonth && this.root.opts.disableDaysOutsideMonth.current)),
}));
props = $derived.by(() => ({
id: this.opts.id.current,
role: "gridcell",
"aria-selected": boolToStr(this.isSelectedDate),
"aria-disabled": boolToStr(this.ariaDisabled),
...this.sharedDataAttrs,
[this.root.getBitsAttr("cell")]: "",
...this.attachment,
}));
}
export class CalendarDayState {
static create(opts) {
return new CalendarDayState(opts, CalendarCellContext.get());
}
opts;
cell;
attachment;
constructor(opts, cell) {
this.opts = opts;
this.cell = cell;
this.onclick = this.onclick.bind(this);
this.attachment = attachRef(this.opts.ref);
}
#tabindex = $derived.by(() => (this.cell.isOutsideMonth && this.cell.root.opts.disableDaysOutsideMonth.current) ||
this.cell.isDisabled
? undefined
: this.cell.isFocusedDate
? 0
: -1);
onclick(e) {
if (this.cell.isDisabled)
return;
this.cell.root.handleCellClick(e, this.cell.opts.date.current);
}
snippetProps = $derived.by(() => ({
disabled: this.cell.isDisabled,
unavailable: this.cell.isUnavailable,
selected: this.cell.isSelectedDate,
day: `${this.cell.opts.date.current.day}`,
}));
props = $derived.by(() => ({
id: this.opts.id.current,
role: "button",
"aria-label": this.cell.labelText,
"aria-disabled": boolToStr(this.cell.ariaDisabled),
...this.cell.sharedDataAttrs,
tabindex: this.#tabindex,
[this.cell.root.getBitsAttr("day")]: "",
// Shared logic for range calendar and calendar
"data-bits-day": "",
//
onclick: this.onclick,
...this.attachment,
}));
}
export class CalendarNextButtonState {
static create(opts) {
return new CalendarNextButtonState(opts, CalendarRootContext.get());
}
opts;
root;
isDisabled = $derived.by(() => this.root.isNextButtonDisabled);
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.onclick = this.onclick.bind(this);
this.attachment = attachRef(this.opts.ref);
}
onclick(_) {
if (this.isDisabled)
return;
this.root.nextPage();
}
props = $derived.by(() => ({
id: this.opts.id.current,
role: "button",
type: "button",
"aria-label": "Next",
"aria-disabled": boolToStr(this.isDisabled),
"data-disabled": boolToEmptyStrOrUndef(this.isDisabled),
disabled: this.isDisabled,
[this.root.getBitsAttr("next-button")]: "",
//
onclick: this.onclick,
...this.attachment,
}));
}
export class CalendarPrevButtonState {
static create(opts) {
return new CalendarPrevButtonState(opts, CalendarRootContext.get());
}
opts;
root;
isDisabled = $derived.by(() => this.root.isPrevButtonDisabled);
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.onclick = this.onclick.bind(this);
this.attachment = attachRef(this.opts.ref);
}
onclick(_) {
if (this.isDisabled)
return;
this.root.prevPage();
}
props = $derived.by(() => ({
id: this.opts.id.current,
role: "button",
type: "button",
"aria-label": "Previous",
"aria-disabled": boolToStr(this.isDisabled),
"data-disabled": boolToEmptyStrOrUndef(this.isDisabled),
disabled: this.isDisabled,
[this.root.getBitsAttr("prev-button")]: "",
//
onclick: this.onclick,
...this.attachment,
}));
}
export class CalendarGridState {
static create(opts) {
return new CalendarGridState(opts, CalendarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
}
props = $derived.by(() => ({
id: this.opts.id.current,
tabindex: -1,
role: "grid",
"aria-readonly": boolToStr(this.root.opts.readonly.current),
"aria-disabled": boolToStr(this.root.opts.disabled.current),
"data-readonly": boolToEmptyStrOrUndef(this.root.opts.readonly.current),
"data-disabled": boolToEmptyStrOrUndef(this.root.opts.disabled.current),
[this.root.getBitsAttr("grid")]: "",
...this.attachment,
}));
}
export class CalendarGridBodyState {
static create(opts) {
return new CalendarGridBodyState(opts, CalendarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
}
props = $derived.by(() => ({
id: this.opts.id.current,
"data-disabled": boolToEmptyStrOrUndef(this.root.opts.disabled.current),
"data-readonly": boolToEmptyStrOrUndef(this.root.opts.readonly.current),
[this.root.getBitsAttr("grid-body")]: "",
...this.attachment,
}));
}
export class CalendarGridHeadState {
static create(opts) {
return new CalendarGridHeadState(opts, CalendarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
}
props = $derived.by(() => ({
id: this.opts.id.current,
"data-disabled": boolToEmptyStrOrUndef(this.root.opts.disabled.current),
"data-readonly": boolToEmptyStrOrUndef(this.root.opts.readonly.current),
[this.root.getBitsAttr("grid-head")]: "",
...this.attachment,
}));
}
export class CalendarGridRowState {
static create(opts) {
return new CalendarGridRowState(opts, CalendarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
}
props = $derived.by(() => ({
id: this.opts.id.current,
"data-disabled": boolToEmptyStrOrUndef(this.root.opts.disabled.current),
"data-readonly": boolToEmptyStrOrUndef(this.root.opts.readonly.current),
[this.root.getBitsAttr("grid-row")]: "",
...this.attachment,
}));
}
export class CalendarHeadCellState {
static create(opts) {
return new CalendarHeadCellState(opts, CalendarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
}
props = $derived.by(() => ({
id: this.opts.id.current,
"data-disabled": boolToEmptyStrOrUndef(this.root.opts.disabled.current),
"data-readonly": boolToEmptyStrOrUndef(this.root.opts.readonly.current),
[this.root.getBitsAttr("head-cell")]: "",
...this.attachment,
}));
}
export class CalendarHeaderState {
static create(opts) {
return new CalendarHeaderState(opts, CalendarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.attachment = attachRef(this.opts.ref);
}
props = $derived.by(() => ({
id: this.opts.id.current,
"data-disabled": boolToEmptyStrOrUndef(this.root.opts.disabled.current),
"data-readonly": boolToEmptyStrOrUndef(this.root.opts.readonly.current),
[this.root.getBitsAttr("header")]: "",
...this.attachment,
}));
}
export class CalendarMonthSelectState {
static create(opts) {
return new CalendarMonthSelectState(opts, CalendarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.onchange = this.onchange.bind(this);
this.attachment = attachRef(this.opts.ref);
}
monthItems = $derived.by(() => {
this.root.opts.locale.current;
const monthNumbers = this.opts.months.current;
const monthFormat = this.opts.monthFormat.current;
const months = [];
for (const month of monthNumbers) {
// create a date with the current year and the month to get localized name
const date = this.root.opts.placeholder.current.set({ month });
let label;
if (typeof monthFormat === "function") {
label = monthFormat(month);
}
else {
label = this.root.formatter.custom(toDate(date), { month: monthFormat });
}
months.push({
value: month,
label,
});
}
return months;
});
currentMonth = $derived.by(() => this.root.opts.placeholder.current.month);
isDisabled = $derived.by(() => this.root.opts.disabled.current || this.opts.disabled.current);
snippetProps = $derived.by(() => {
return {
monthItems: this.monthItems,
selectedMonthItem: this.monthItems.find((month) => month.value === this.currentMonth),
};
});
onchange(event) {
if (this.isDisabled)
return;
const target = event.target;
const month = parseInt(target.value, 10);
if (!isNaN(month)) {
this.root.opts.placeholder.current = this.root.opts.placeholder.current.set({ month });
}
}
props = $derived.by(() => ({
id: this.opts.id.current,
value: this.currentMonth,
disabled: this.isDisabled,
"data-disabled": boolToEmptyStrOrUndef(this.isDisabled),
[this.root.getBitsAttr("month-select")]: "",
//
onchange: this.onchange,
...this.attachment,
}));
}
export class CalendarYearSelectState {
static create(opts) {
return new CalendarYearSelectState(opts, CalendarRootContext.get());
}
opts;
root;
attachment;
constructor(opts, root) {
this.opts = opts;
this.root = root;
this.onchange = this.onchange.bind(this);
this.attachment = attachRef(this.opts.ref);
}
years = $derived.by(() => {
if (this.opts.years.current && this.opts.years.current.length)
return this.opts.years.current;
return this.root.defaultYears;
});
yearItems = $derived.by(() => {
this.root.opts.locale.current;
const yearFormat = this.opts.yearFormat.current;
const localYears = [];
for (const year of this.years) {
// create a date with the year to get localized formatting
const date = this.root.opts.placeholder.current.set({ year });
let label;
if (typeof yearFormat === "function") {
label = yearFormat(year);
}
else {
label = this.root.formatter.custom(toDate(date), { year: yearFormat });
}
localYears.push({
value: year,
label,
});
}
return localYears;
});
currentYear = $derived.by(() => this.root.opts.placeholder.current.year);
isDisabled = $derived.by(() => this.root.opts.disabled.current || this.opts.disabled.current);
snippetProps = $derived.by(() => {
return {
yearItems: this.yearItems,
selectedYearItem: this.yearItems.find((year) => year.value === this.currentYear),
};
});
onchange(event) {
if (this.isDisabled)
return;
const target = event.target;
const year = parseInt(target.value, 10);
if (!isNaN(year)) {
this.root.opts.placeholder.current = this.root.opts.placeholder.current.set({ year });
}
}
props = $derived.by(() => ({
id: this.opts.id.current,
value: this.currentYear,
disabled: this.isDisabled,
"data-disabled": boolToEmptyStrOrUndef(this.isDisabled),
[this.root.getBitsAttr("year-select")]: "",
//
onchange: this.onchange,
...this.attachment,
}));
}
@@ -0,0 +1,41 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { CalendarCellState } from "../calendar.svelte.js";
import type { CalendarCellProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
date,
month,
...restProps
}: CalendarCellProps = $props();
const cellState = CalendarCellState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
date: boxWith(() => date),
month: boxWith(() => month),
});
const mergedProps = $derived(mergeProps(restProps, cellState.props));
</script>
{#if child}
{@render child({
props: mergedProps,
...cellState.snippetProps,
})}
{:else}
<td {...mergedProps}>
{@render children?.(cellState.snippetProps)}
</td>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarCellProps } from "../types.js";
declare const CalendarCell: import("svelte").Component<CalendarCellProps, {}, "ref">;
type CalendarCell = ReturnType<typeof CalendarCell>;
export default CalendarCell;
+41
View File
@@ -0,0 +1,41 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { CalendarDayState } from "../calendar.svelte.js";
import type { CalendarDayProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
...restProps
}: CalendarDayProps = $props();
const dayState = CalendarDayState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, dayState.props));
</script>
{#if child}
{@render child({
props: mergedProps,
...dayState.snippetProps,
})}
{:else}
<div {...mergedProps}>
{#if children}
{@render children?.(dayState.snippetProps)}
{:else}
{dayState.cell.opts.date.current.day}
{/if}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarDayProps } from "../types.js";
declare const CalendarDay: import("svelte").Component<CalendarDayProps, {}, "ref">;
type CalendarDay = ReturnType<typeof CalendarDay>;
export default CalendarDay;
@@ -0,0 +1,34 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { CalendarGridBodyProps } from "../types.js";
import { CalendarGridBodyState } from "../calendar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
...restProps
}: CalendarGridBodyProps = $props();
const gridBodyState = CalendarGridBodyState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, gridBodyState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<tbody {...mergedProps}>
{@render children?.()}
</tbody>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarGridBodyProps } from "../types.js";
declare const CalendarGridBody: import("svelte").Component<CalendarGridBodyProps, {}, "ref">;
type CalendarGridBody = ReturnType<typeof CalendarGridBody>;
export default CalendarGridBody;
@@ -0,0 +1,34 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { CalendarGridHeadState } from "../calendar.svelte.js";
import type { CalendarGridHeadProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
...restProps
}: CalendarGridHeadProps = $props();
const gridHeadState = CalendarGridHeadState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, gridHeadState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<thead {...mergedProps}>
{@render children?.()}
</thead>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarGridHeadProps } from "../types.js";
declare const CalendarGridHead: import("svelte").Component<CalendarGridHeadProps, {}, "ref">;
type CalendarGridHead = ReturnType<typeof CalendarGridHead>;
export default CalendarGridHead;
@@ -0,0 +1,34 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { CalendarGridRowState } from "../calendar.svelte.js";
import type { CalendarGridRowProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
...restProps
}: CalendarGridRowProps = $props();
const gridRowState = CalendarGridRowState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, gridRowState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<tr {...mergedProps}>
{@render children?.()}
</tr>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarGridRowProps } from "../types.js";
declare const CalendarGridRow: import("svelte").Component<CalendarGridRowProps, {}, "ref">;
type CalendarGridRow = ReturnType<typeof CalendarGridRow>;
export default CalendarGridRow;
@@ -0,0 +1,34 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { CalendarGridState } from "../calendar.svelte.js";
import type { CalendarGridProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
...restProps
}: CalendarGridProps = $props();
const gridState = CalendarGridState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, gridState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<table {...mergedProps}>
{@render children?.()}
</table>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarGridProps } from "../types.js";
declare const CalendarGrid: import("svelte").Component<CalendarGridProps, {}, "ref">;
type CalendarGrid = ReturnType<typeof CalendarGrid>;
export default CalendarGrid;
@@ -0,0 +1,34 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { CalendarHeadCellState } from "../calendar.svelte.js";
import type { CalendarHeadCellProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
...restProps
}: CalendarHeadCellProps = $props();
const headCellState = CalendarHeadCellState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, headCellState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<th {...mergedProps}>
{@render children?.()}
</th>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarHeadCellProps } from "../types.js";
declare const CalendarHeadCell: import("svelte").Component<CalendarHeadCellProps, {}, "ref">;
type CalendarHeadCell = ReturnType<typeof CalendarHeadCell>;
export default CalendarHeadCell;
@@ -0,0 +1,34 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { CalendarHeaderState } from "../calendar.svelte.js";
import type { CalendarHeaderProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
...restProps
}: CalendarHeaderProps = $props();
const headerState = CalendarHeaderState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, headerState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<header {...mergedProps}>
{@render children?.()}
</header>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarHeaderProps } from "../types.js";
declare const CalendarHeader: import("svelte").Component<CalendarHeaderProps, {}, "ref">;
type CalendarHeader = ReturnType<typeof CalendarHeader>;
export default CalendarHeader;
@@ -0,0 +1,38 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { CalendarHeadingProps } from "../types.js";
import { CalendarHeadingState } from "../calendar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
...restProps
}: CalendarHeadingProps = $props();
const headingState = CalendarHeadingState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, headingState.props));
</script>
{#if child}
{@render child({ props: mergedProps, headingValue: headingState.root.headingValue })}
{:else}
<div {...mergedProps}>
{#if children}
{@render children?.({ headingValue: headingState.root.headingValue })}
{:else}
{headingState.root.headingValue}
{/if}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarHeadingProps } from "../types.js";
declare const CalendarHeading: import("svelte").Component<CalendarHeadingProps, {}, "ref">;
type CalendarHeading = ReturnType<typeof CalendarHeading>;
export default CalendarHeading;
@@ -0,0 +1,54 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { CalendarMonthSelectProps } from "../types.js";
import { CalendarMonthSelectState } from "../calendar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
monthFormat = "long",
disabled = false,
"aria-label": ariaLabel = "Select a month",
...restProps
}: CalendarMonthSelectProps = $props();
const monthSelectState = CalendarMonthSelectState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
months: boxWith(() => months),
monthFormat: boxWith(() => monthFormat),
disabled: boxWith(() => Boolean(disabled)),
});
const mergedProps = $derived(
mergeProps(restProps, monthSelectState.props, { "aria-label": ariaLabel })
);
</script>
{#if child}
{@render child({ props: mergedProps, ...monthSelectState.snippetProps })}
{:else}
<select {...mergedProps}>
{#if children}
{@render children?.(monthSelectState.snippetProps)}
{:else}
{#each monthSelectState.monthItems as month (month.value)}
<option
value={month.value}
selected={month.value === monthSelectState.currentMonth}
>
{month.label}
</option>
{/each}
{/if}
</select>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarMonthSelectProps } from "../types.js";
declare const CalendarMonthSelect: import("svelte").Component<CalendarMonthSelectProps, {}, "ref">;
type CalendarMonthSelect = ReturnType<typeof CalendarMonthSelect>;
export default CalendarMonthSelect;
@@ -0,0 +1,36 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { CalendarNextButtonState } from "../calendar.svelte.js";
import type { CalendarNextButtonProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
id = createId(uid),
ref = $bindable(null),
// for safari
tabindex = 0,
...restProps
}: CalendarNextButtonProps = $props();
const nextButtonState = CalendarNextButtonState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, nextButtonState.props, { tabindex }));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<button {...mergedProps}>
{@render children?.()}
</button>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarNextButtonProps } from "../types.js";
declare const CalendarNextButton: import("svelte").Component<CalendarNextButtonProps, {}, "ref">;
type CalendarNextButton = ReturnType<typeof CalendarNextButton>;
export default CalendarNextButton;
@@ -0,0 +1,36 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { CalendarPrevButtonProps } from "../types.js";
import { CalendarPrevButtonState } from "../calendar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
id = createId(uid),
ref = $bindable(null),
// for safari
tabindex = 0,
...restProps
}: CalendarPrevButtonProps = $props();
const prevButtonState = CalendarPrevButtonState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, prevButtonState.props, { tabindex }));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<button {...mergedProps}>
{@render children?.()}
</button>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarPrevButtonProps } from "../types.js";
declare const CalendarPrevButton: import("svelte").Component<CalendarPrevButtonProps, {}, "ref">;
type CalendarPrevButton = ReturnType<typeof CalendarPrevButton>;
export default CalendarPrevButton;
@@ -0,0 +1,51 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { CalendarYearSelectProps } from "../types.js";
import { CalendarYearSelectState } from "../calendar.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
children,
child,
ref = $bindable(null),
id = createId(uid),
years,
yearFormat = "numeric",
disabled = false,
"aria-label": ariaLabel = "Select a year",
...restProps
}: CalendarYearSelectProps = $props();
const yearSelectState = CalendarYearSelectState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
years: boxWith(() => years),
yearFormat: boxWith(() => yearFormat),
disabled: boxWith(() => Boolean(disabled)),
});
const mergedProps = $derived(
mergeProps(restProps, yearSelectState.props, { "aria-label": ariaLabel })
);
</script>
{#if child}
{@render child({ props: mergedProps, ...yearSelectState.snippetProps })}
{:else}
<select {...mergedProps}>
{#if children}
{@render children?.(yearSelectState.snippetProps)}
{:else}
{#each yearSelectState.yearItems as year (year.value)}
<option value={year.value} selected={year.value === yearSelectState.currentYear}>
{year.label}
</option>
{/each}
{/if}
</select>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarYearSelectProps } from "../types.js";
declare const CalendarYearSelect: import("svelte").Component<CalendarYearSelectProps, {}, "ref">;
type CalendarYearSelect = ReturnType<typeof CalendarYearSelect>;
export default CalendarYearSelect;
+133
View File
@@ -0,0 +1,133 @@
<script lang="ts">
import { watch } from "runed";
import { boxWith, mergeProps } from "svelte-toolbelt";
import { type DateValue } from "@internationalized/date";
import { CalendarRootState } from "../calendar.svelte.js";
import type { CalendarRootProps } from "../types.js";
import { useId } from "../../../internal/use-id.js";
import { noop } from "../../../internal/noop.js";
import { getDefaultDate } from "../../../internal/date-time/utils.js";
import { resolveLocaleProp } from "../../utilities/config/prop-resolvers.js";
let {
child,
children,
id = useId(),
ref = $bindable(null),
value = $bindable(),
onValueChange = noop,
placeholder = $bindable(),
onPlaceholderChange = noop,
weekdayFormat = "narrow",
weekStartsOn,
pagedNavigation = false,
isDateDisabled = () => false,
isDateUnavailable = () => false,
fixedWeeks = false,
numberOfMonths = 1,
locale,
calendarLabel = "Event",
disabled = false,
readonly = false,
minValue = undefined,
maxValue = undefined,
preventDeselect = false,
type,
disableDaysOutsideMonth = true,
initialFocus = false,
maxDays,
monthFormat = "long",
yearFormat = "numeric",
...restProps
}: CalendarRootProps = $props();
const defaultPlaceholder = getDefaultDate({
defaultValue: value,
minValue,
maxValue,
});
function handleDefaultPlaceholder() {
if (placeholder !== undefined) return;
placeholder = defaultPlaceholder;
}
// SSR
handleDefaultPlaceholder();
watch.pre(
() => placeholder,
() => {
handleDefaultPlaceholder();
}
);
function handleDefaultValue() {
if (value !== undefined) return;
value = type === "single" ? undefined : [];
}
// SSR
handleDefaultValue();
watch.pre(
() => value,
() => {
handleDefaultValue();
}
);
const rootState = CalendarRootState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
weekdayFormat: boxWith(() => weekdayFormat),
weekStartsOn: boxWith(() => weekStartsOn),
pagedNavigation: boxWith(() => pagedNavigation),
isDateDisabled: boxWith(() => isDateDisabled),
isDateUnavailable: boxWith(() => isDateUnavailable),
fixedWeeks: boxWith(() => fixedWeeks),
numberOfMonths: boxWith(() => numberOfMonths),
locale: resolveLocaleProp(() => locale),
calendarLabel: boxWith(() => calendarLabel),
readonly: boxWith(() => readonly),
disabled: boxWith(() => disabled),
minValue: boxWith(() => minValue),
maxValue: boxWith(() => maxValue),
disableDaysOutsideMonth: boxWith(() => disableDaysOutsideMonth),
initialFocus: boxWith(() => initialFocus),
maxDays: boxWith(() => maxDays),
placeholder: boxWith(
() => placeholder as DateValue,
(v) => {
placeholder = v;
onPlaceholderChange(v as DateValue);
}
),
preventDeselect: boxWith(() => preventDeselect),
value: boxWith(
() => value,
(v) => {
value = v;
// oxlint-disable-next-line no-explicit-any
onValueChange(v as any);
}
),
type: boxWith(() => type),
monthFormat: boxWith(() => monthFormat),
yearFormat: boxWith(() => yearFormat),
defaultPlaceholder,
});
const mergedProps = $derived(mergeProps(restProps, rootState.props));
</script>
{#if child}
{@render child({ props: mergedProps, ...rootState.snippetProps })}
{:else}
<div {...mergedProps}>
{@render children?.(rootState.snippetProps)}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { CalendarRootProps } from "../types.js";
declare const Calendar: import("svelte").Component<CalendarRootProps, {}, "value" | "placeholder" | "ref">;
type Calendar = ReturnType<typeof Calendar>;
export default Calendar;
+15
View File
@@ -0,0 +1,15 @@
export { default as Root } from "./components/calendar.svelte";
export { default as Day } from "./components/calendar-day.svelte";
export { default as Grid } from "./components/calendar-grid.svelte";
export { default as GridBody } from "./components/calendar-grid-body.svelte";
export { default as Cell } from "./components/calendar-cell.svelte";
export { default as GridHead } from "./components/calendar-grid-head.svelte";
export { default as HeadCell } from "./components/calendar-head-cell.svelte";
export { default as GridRow } from "./components/calendar-grid-row.svelte";
export { default as Header } from "./components/calendar-header.svelte";
export { default as Heading } from "./components/calendar-heading.svelte";
export { default as MonthSelect } from "./components/calendar-month-select.svelte";
export { default as NextButton } from "./components/calendar-next-button.svelte";
export { default as PrevButton } from "./components/calendar-prev-button.svelte";
export { default as YearSelect } from "./components/calendar-year-select.svelte";
export type { CalendarRootProps as RootProps, CalendarPrevButtonProps as PrevButtonProps, CalendarNextButtonProps as NextButtonProps, CalendarHeadingProps as HeadingProps, CalendarHeaderProps as HeaderProps, CalendarGridProps as GridProps, CalendarGridHeadProps as GridHeadProps, CalendarHeadCellProps as HeadCellProps, CalendarGridBodyProps as GridBodyProps, CalendarGridRowProps as GridRowProps, CalendarCellProps as CellProps, CalendarDayProps as DayProps, CalendarMonthSelectProps as MonthSelectProps, CalendarYearSelectProps as YearSelectProps, } from "./types.js";
+14
View File
@@ -0,0 +1,14 @@
export { default as Root } from "./components/calendar.svelte";
export { default as Day } from "./components/calendar-day.svelte";
export { default as Grid } from "./components/calendar-grid.svelte";
export { default as GridBody } from "./components/calendar-grid-body.svelte";
export { default as Cell } from "./components/calendar-cell.svelte";
export { default as GridHead } from "./components/calendar-grid-head.svelte";
export { default as HeadCell } from "./components/calendar-head-cell.svelte";
export { default as GridRow } from "./components/calendar-grid-row.svelte";
export { default as Header } from "./components/calendar-header.svelte";
export { default as Heading } from "./components/calendar-heading.svelte";
export { default as MonthSelect } from "./components/calendar-month-select.svelte";
export { default as NextButton } from "./components/calendar-next-button.svelte";
export { default as PrevButton } from "./components/calendar-prev-button.svelte";
export { default as YearSelect } from "./components/calendar-year-select.svelte";
+1
View File
@@ -0,0 +1 @@
export * as Calendar from "./exports.js";

Some files were not shown because too many files have changed in this diff Show More