*/}
-
- {/* Keep these classes on wrapping element */}
-
-
-
- {/* Children */}
- {children && (
-
- {children}
-
- )}
+ {/* Label */}
+
+ {lead && {lead}}
+ {children}
-
+
);
};
-/** The list of `
` components. */
-const TabPanels: React.FC = ({
+const TabsContent: FC = ({
// Root
base = '',
classes = '',
// Children
children
}) => {
- return {children}
;
+ return (
+
+ {children}
+
+ );
};
-/** The individual Panel component. */
-const TabsPanel: React.FC = ({
- id = '',
- value,
- group,
- // A11y
- labelledBy,
+const TabsPanel: FC = ({
// Root
+ base = '',
classes = '',
// Children
- children
+ children,
+ // Zag
+ ...zagProps
}) => {
+ const ctx = useContext(TabsContext);
+
return (
- value === group &&
- children && (
-
- {children}
-
- )
+
+ {children}
+
);
};
+// Export ---
+
export const Tabs = Object.assign(TabsRoot, {
+ /** A group of tab controls. */
List: TabsList,
+ /** An individual tab control. */
Control: TabsControl,
- Panels: TabPanels,
+ /** A group of tab panels. */
+ Content: TabsContent,
+ /** An individual tab panel. */
Panel: TabsPanel
});
diff --git a/packages/skeleton-react/src/lib/components/Tabs/schema.json b/packages/skeleton-react/src/lib/components/Tabs/schema.json
index 3f538e20b..39d07c7bb 100644
--- a/packages/skeleton-react/src/lib/components/Tabs/schema.json
+++ b/packages/skeleton-react/src/lib/components/Tabs/schema.json
@@ -1,10 +1,71 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
+ "Api": {
+ "properties": {
+ "focusedValue": {
+ "description": "The value of the tab that is currently focused.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The current value of the tabs.",
+ "type": "string"
+ }
+ },
+ "propertyOrder": [
+ "value",
+ "focusedValue",
+ "setValue",
+ "clearValue",
+ "setIndicatorRect",
+ "syncTabIndex",
+ "focus",
+ "selectNext",
+ "selectPrev",
+ "getTriggerState",
+ "getRootProps",
+ "getListProps",
+ "getTriggerProps",
+ "getContentProps",
+ "getIndicatorProps"
+ ],
+ "required": ["focusedValue", "value"],
+ "type": "object"
+ },
+ "IntlTranslations": {
+ "properties": {
+ "listLabel": {
+ "type": "string"
+ }
+ },
+ "propertyOrder": ["listLabel"],
+ "type": "object"
+ },
"Iterable": {
"propertyOrder": ["__@iterator@83"],
"type": "object"
},
+ "Partial<{root:string;trigger:string;list:string;content:string;indicator:string;}>": {
+ "properties": {
+ "content": {
+ "type": "string"
+ },
+ "indicator": {
+ "type": "string"
+ },
+ "list": {
+ "type": "string"
+ },
+ "root": {
+ "type": "string"
+ },
+ "trigger": {
+ "type": "string"
+ }
+ },
+ "propertyOrder": ["root", "trigger", "list", "content", "indicator"],
+ "type": "object"
+ },
"React.ReactElement>": {
"description": "Represents a JSX element.\n\nWhere {@link ReactNode} represents everything that can be rendered, `ReactElement`\nonly represents JSX.",
"properties": {
@@ -84,271 +145,250 @@
"required": ["children", "key", "props", "type"],
"type": "object"
},
- "TabPanelsProps": {
+ "TabsContentProps": {
"properties": {
"base": {
- "description": "Sets wrapping panel base styles.",
+ "description": "Set base classes for the panel group element.",
"type": "string"
},
"children": {
"$ref": "#/definitions/React.ReactNode"
},
"classes": {
- "description": "Provide arbitrary CSS classes to the wrapping panel element.",
+ "description": "Provide arbitrary classes for the panel group element.",
"type": "string"
}
},
"propertyOrder": ["base", "classes", "children"],
"type": "object"
},
- "TabsControlProps": {
+ "TabsContextState": {
"properties": {
- "active": {
- "description": "Sets the active control styles.",
- "type": "string"
- },
- "background": {
- "description": "Sets background styles.",
- "type": "string"
+ "api": {
+ "$ref": "#/definitions/Api"
},
+ "fluid": {
+ "type": "boolean"
+ }
+ },
+ "propertyOrder": ["fluid", "api"],
+ "required": ["api", "fluid"],
+ "type": "object"
+ },
+ "TabsControlProps": {
+ "properties": {
"base": {
- "description": "Sets base styles.",
- "type": "string"
- },
- "border": {
- "description": "Sets border styles.",
+ "description": "Set base classes for the control element.",
"type": "string"
},
"children": {
"$ref": "#/definitions/React.ReactNode"
},
"classes": {
- "description": "Provide arbitrary CSS classes.",
- "type": "string"
- },
- "contentBase": {
- "description": "Sets tab content base styles.",
- "type": "string"
- },
- "contentBg": {
- "description": "Sets the tab content background styles.",
- "type": "string"
- },
- "contentClasses": {
- "description": "Provide arbitrary CSS classes for the tab content.",
- "type": "string"
- },
- "contentFlex": {
- "description": "Sets tab content flex styles.",
- "type": "string"
- },
- "contentGap": {
- "description": "Sets the tab content gap styles.",
- "type": "string"
- },
- "contentPadding": {
- "description": "Sets the tab content padding styles.",
- "type": "string"
- },
- "contentRounded": {
- "description": "Sets the tab content rounded styles.",
+ "description": "Provide arbitrary classes for the control element.",
"type": "string"
},
- "controls": {
- "description": "Sets ARIA controls value to define which panel this tab controls.",
- "type": "string"
+ "disabled": {
+ "description": "Whether the tab is disabled",
+ "type": "boolean"
},
- "cursor": {
- "description": "Sets cursor styles.",
+ "labelBase": {
+ "description": "Set base classes for the label element.",
"type": "string"
},
- "flex": {
- "description": "Sets flex styles.",
+ "labelClasses": {
+ "description": "Provide arbitrary classes for the label element.",
"type": "string"
},
- "gap": {
- "description": "Sets vertical gap styles.",
- "type": "string"
+ "lead": {
+ "$ref": "#/definitions/React.ReactNode",
+ "description": "The lead node for the control element."
},
- "group": {
- "description": "Provide the tab control radio group.",
+ "padding": {
+ "description": "Set padding classes for the control element.",
"type": "string"
},
- "id": {
- "description": "Provide a unique ID.",
+ "stateActive": {
+ "description": "Set active classes for the control element.",
"type": "string"
},
- "inactive": {
- "description": "Sets the inactive control styles.",
+ "stateInactive": {
+ "description": "Set inactive classes for the control element.",
"type": "string"
},
- "label": {
- "description": "Sets the A11y label.",
+ "stateLabelActive": {
+ "description": "Set active classes for the label element.",
"type": "string"
},
- "name": {
- "description": "Provide the tab control name.",
+ "stateLabelInactive": {
+ "description": "Set inactive classes for the label element.",
"type": "string"
},
- "onChange": {
- "description": "Triggers on Tab Control group change.",
- "propertyOrder": [],
- "type": "object"
- },
- "onClick": {
- "description": "Triggers on Tab Control click.",
- "propertyOrder": [],
- "type": "object"
- },
- "onKeydown": {
- "description": "Triggers on Tab Control key down.",
- "propertyOrder": [],
- "type": "object"
- },
- "onKeyup": {
- "description": "Triggers on Tab Control key up.",
- "propertyOrder": [],
- "type": "object"
- },
- "padding": {
- "description": "Sets padding styles.",
- "type": "string"
- },
- "rounded": {
- "description": "Sets rounded styles.",
- "type": "string"
- },
- "text": {
- "description": "Sets text size styles.",
+ "translateX": {
+ "description": "Set x-axis translate classes for the control element.",
"type": "string"
},
- "title": {
- "description": "Provide a hoverable title attribute.",
- "type": "string"
- },
- "width": {
- "description": "Sets width styles.",
+ "value": {
+ "description": "The value of the tab",
"type": "string"
}
},
"propertyOrder": [
- "id",
- "name",
- "group",
- "title",
- "label",
- "controls",
"base",
- "width",
- "active",
- "inactive",
- "flex",
- "background",
- "border",
- "text",
"padding",
- "rounded",
- "gap",
- "cursor",
+ "translateX",
"classes",
- "contentBase",
- "contentFlex",
- "contentGap",
- "contentBg",
- "contentPadding",
- "contentRounded",
- "contentClasses",
- "onClick",
- "onKeydown",
- "onKeyup",
- "onChange",
- "children"
+ "labelBase",
+ "labelClasses",
+ "stateInactive",
+ "stateActive",
+ "stateLabelInactive",
+ "stateLabelActive",
+ "lead",
+ "children",
+ "value",
+ "disabled"
],
- "required": ["group", "name"],
+ "required": ["value"],
"type": "object"
},
"TabsListProps": {
"properties": {
"base": {
- "description": "Sets the base styles.",
+ "description": "Set base classes for the list element.",
"type": "string"
},
"border": {
- "description": "Sets the border styles.",
+ "description": "Set border classes for the list element.",
"type": "string"
},
"children": {
"$ref": "#/definitions/React.ReactNode"
},
"classes": {
- "description": "Provide arbitrary CSS classes.",
+ "description": "Provide arbitrary classes for the list element.",
"type": "string"
},
"gap": {
- "description": "Sets the gap spacing.",
+ "description": "Set gap classes for the list element.",
"type": "string"
},
"justify": {
- "description": "Sets the justification styles.",
+ "description": "Set justify classes for the list element.",
+ "type": "string"
+ },
+ "margin": {
+ "description": "Set margin classes for the list element.",
"type": "string"
}
},
- "propertyOrder": ["base", "justify", "gap", "border", "classes", "children"],
+ "propertyOrder": ["base", "justify", "border", "margin", "gap", "classes", "children"],
"type": "object"
},
"TabsPanelProps": {
"properties": {
+ "base": {
+ "description": "Set base classes for the panel element.",
+ "type": "string"
+ },
"children": {
"$ref": "#/definitions/React.ReactNode"
},
"classes": {
- "description": "Provide arbitrary CSS classes.",
- "type": "string"
- },
- "group": {
- "description": "Provide the tab control radio group.",
- "type": "string"
- },
- "id": {
- "description": "Provide a unique ID.",
- "type": "string"
- },
- "labelledBy": {
- "description": "Sets the A11y labelledby.",
+ "description": "Provide arbitrary classes for the panel element.",
"type": "string"
},
"value": {
- "description": "Provide the tab panel value.",
+ "description": "The value of the tab",
"type": "string"
}
},
- "propertyOrder": ["id", "value", "group", "labelledBy", "classes", "children"],
- "required": ["group", "value"],
+ "propertyOrder": ["base", "classes", "children", "value"],
+ "required": ["value"],
"type": "object"
},
- "TabsProps": {
+ "TabsRootProps": {
"properties": {
+ "activationMode": {
+ "default": "automatic",
+ "description": "The activation mode of the tabs. Can be `manual` or `automatic`\n- `manual`: Tabs are activated when clicked or press `enter` key.\n- `automatic`: Tabs are activated when receiving focus",
+ "enum": ["automatic", "manual"],
+ "type": "string"
+ },
"base": {
- "description": "Sets base styles.",
+ "description": "Set base classes for the root element.",
"type": "string"
},
"children": {
"$ref": "#/definitions/React.ReactNode"
},
"classes": {
- "description": "Provide arbitrary CSS classes.",
+ "description": "Provide arbitrary classes for the root element.",
"type": "string"
},
- "id": {
- "description": "Provide a unique ID.",
+ "composite": {
+ "description": "Whether the tab is composite",
+ "type": "boolean"
+ },
+ "dir": {
+ "default": "ltr",
+ "description": "The document's text/writing direction.",
+ "enum": ["ltr", "rtl"],
"type": "string"
},
- "spaceY": {
- "description": "Set vertical spacing between list and panels.",
+ "fluid": {
+ "description": "Set tabs to stretch to fill the available width.",
+ "type": "boolean"
+ },
+ "getRootNode": {
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.",
+ "propertyOrder": [],
+ "type": "object"
+ },
+ "ids": {
+ "$ref": "#/definitions/Partial<{root:string;trigger:string;list:string;content:string;indicator:string;}>",
+ "description": "The ids of the elements in the tabs. Useful for composition."
+ },
+ "loopFocus": {
+ "default": true,
+ "description": "Whether the keyboard navigation will loop from last tab to first, and vice versa.",
+ "type": "boolean"
+ },
+ "onFocusChange": {
+ "description": "Callback to be called when the focused tab changes",
+ "propertyOrder": [],
+ "type": "object"
+ },
+ "onValueChange": {
+ "description": "Triggers when the value state is changed.",
+ "propertyOrder": [],
+ "type": "object"
+ },
+ "translations": {
+ "$ref": "#/definitions/IntlTranslations",
+ "description": "Specifies the localized strings that identifies the accessibility elements and their states"
+ },
+ "value": {
+ "description": "The selected tab id",
"type": "string"
}
},
- "propertyOrder": ["id", "base", "spaceY", "classes", "children"],
+ "propertyOrder": [
+ "fluid",
+ "base",
+ "classes",
+ "onValueChange",
+ "children",
+ "composite",
+ "value",
+ "dir",
+ "getRootNode",
+ "ids",
+ "translations",
+ "loopFocus",
+ "activationMode",
+ "onFocusChange"
+ ],
"type": "object"
}
}
diff --git a/packages/skeleton-react/src/lib/components/Tabs/types.ts b/packages/skeleton-react/src/lib/components/Tabs/types.ts
index 1435904e1..6647203fb 100644
--- a/packages/skeleton-react/src/lib/components/Tabs/types.ts
+++ b/packages/skeleton-react/src/lib/components/Tabs/types.ts
@@ -1,124 +1,90 @@
-import React from 'react';
+import React, { ReactNode } from 'react';
+import * as tabs from '@zag-js/tabs';
+
+// Context ---
+
+export interface TabsContextState {
+ fluid: boolean;
+ api: ReturnType;
+}
// Components ---
-export interface TabsProps extends React.PropsWithChildren {
- /** Provide a unique ID. */
- id?: string;
+export interface TabsRootProps extends React.PropsWithChildren, Omit {
+ /** Set tabs to stretch to fill the available width. */
+ fluid?: boolean;
// Root ---
- /** Sets base styles. */
+ /** Set base classes for the root element. */
base?: string;
- /** Set vertical spacing between list and panels. */
- spaceY?: string;
- /** Provide arbitrary CSS classes. */
+ /** Provide arbitrary classes for the root element. */
classes?: string;
+
+ // Events ---
+ /** Triggers when the value state is changed. */
+ onValueChange?: (value: string) => void;
}
export interface TabsListProps extends React.PropsWithChildren {
- /** Sets the base styles. */
+ // Root ---
+ /** Set base classes for the list element. */
base?: string;
- /** Sets the justification styles. */
+ /** Set justify classes for the list element. */
justify?: string;
- /** Sets the gap spacing. */
- gap?: string;
- /** Sets the border styles. */
+ /** Set border classes for the list element. */
border?: string;
- /** Provide arbitrary CSS classes. */
+ /** Set margin classes for the list element. */
+ margin?: string;
+ /** Set gap classes for the list element. */
+ gap?: string;
+ /** Provide arbitrary classes for the list element. */
classes?: string;
}
-export interface TabsControlProps extends React.PropsWithChildren {
- /** Provide a unique ID. */
- id?: string;
- /** Provide the tab control name. */
- name: string;
- /** Provide the tab control radio group. */
- group: string;
- /** Provide a hoverable title attribute. */
- title?: string;
-
- // A11y ---
- /** Sets the A11y label. */
- label?: string;
- /** Sets ARIA controls value to define which panel this tab controls. */
- controls?: string;
-
+export interface TabsControlProps extends React.PropsWithChildren, tabs.TriggerProps {
// Root ---
- /** Sets base styles. */
+ /** Set base classes for the control element. */
base?: string;
- /** Sets width styles. */
- width?: string;
- /** Sets the active control styles. */
- active?: string;
- /** Sets the inactive control styles. */
- inactive?: string;
- /** Sets flex styles. */
- flex?: string;
- /** Sets background styles. */
- background?: string;
- /** Sets border styles. */
- border?: string;
- /** Sets text size styles. */
- text?: string;
- /** Sets padding styles. */
+ /** Set padding classes for the control element. */
padding?: string;
- /** Sets rounded styles. */
- rounded?: string;
- /** Sets vertical gap styles. */
- gap?: string;
- /** Sets cursor styles. */
- cursor?: string;
- /** Provide arbitrary CSS classes. */
+ /** Set x-axis translate classes for the control element. */
+ translateX?: string;
+ /** Provide arbitrary classes for the control element. */
classes?: string;
- // Content
- /** Sets tab content base styles. */
- contentBase?: string;
- /** Sets tab content flex styles. */
- contentFlex?: string;
- /** Sets the tab content gap styles. */
- contentGap?: string;
- /** Sets the tab content background styles. */
- contentBg?: string;
- /** Sets the tab content padding styles. */
- contentPadding?: string;
- /** Sets the tab content rounded styles. */
- contentRounded?: string;
- /** Provide arbitrary CSS classes for the tab content. */
- contentClasses?: string;
+ // Label ---
+ /** Set base classes for the label element. */
+ labelBase?: string;
+ /** Provide arbitrary classes for the label element. */
+ labelClasses?: string;
- // Events ---
- /** Triggers on Tab Control click. */
- onClick?: (event: React.MouseEvent) => void;
- /** Triggers on Tab Control key down. */
- onKeydown?: (event: React.KeyboardEvent) => void;
- /** Triggers on Tab Control key up. */
- onKeyup?: (event: React.KeyboardEvent) => void;
- /** Triggers on Tab Control group change. */
- onChange?: (group: string) => void;
+ // State ---
+ /** Set inactive classes for the control element. */
+ stateInactive?: string;
+ /** Set active classes for the control element. */
+ stateActive?: string;
+ /** Set inactive classes for the label element. */
+ stateLabelInactive?: string;
+ /** Set active classes for the label element. */
+ stateLabelActive?: string;
+
+ // Nodes ---
+ /** The lead node for the control element. */
+ lead?: ReactNode;
}
-export interface TabPanelsProps extends React.PropsWithChildren {
- /** Sets wrapping panel base styles. */
+export interface TabsContentProps extends React.PropsWithChildren {
+ // Root ---
+ /** Set base classes for the panel group element. */
base?: string;
- /** Provide arbitrary CSS classes to the wrapping panel element. */
+ /** Provide arbitrary classes for the panel group element. */
classes?: string;
}
-export interface TabsPanelProps extends React.PropsWithChildren {
- /** Provide a unique ID. */
- id?: string;
- /** Provide the tab panel value. */
- value: string;
- /** Provide the tab control radio group. */
- group: string;
-
- // A11y ---
- /** Sets the A11y labelledby. */
- labelledBy?: string;
-
+export interface TabsPanelProps extends React.PropsWithChildren, tabs.ContentProps {
// Root ---
- /** Provide arbitrary CSS classes. */
+ /** Set base classes for the panel element. */
+ base?: string;
+ /** Provide arbitrary classes for the panel element. */
classes?: string;
}
diff --git a/packages/skeleton-react/src/lib/internal/nodes.tsx b/packages/skeleton-react/src/lib/internal/nodes.tsx
new file mode 100644
index 000000000..aae46e355
--- /dev/null
+++ b/packages/skeleton-react/src/lib/internal/nodes.tsx
@@ -0,0 +1,34 @@
+export const starEmpty = (
+
+);
+
+export const starHalf = (
+
+);
+
+export const starFull = (
+
+);
diff --git a/packages/skeleton-react/src/lib/internal/noop.ts b/packages/skeleton-react/src/lib/internal/noop.ts
new file mode 100644
index 000000000..247aaa144
--- /dev/null
+++ b/packages/skeleton-react/src/lib/internal/noop.ts
@@ -0,0 +1,2 @@
+// eslint-disable-next-line @typescript-eslint/no-empty-function
+export function noop() {}
diff --git a/packages/skeleton-react/src/routes/components/accordions.tsx b/packages/skeleton-react/src/routes/components/accordions.tsx
index 97f7abd0f..eed32b413 100644
--- a/packages/skeleton-react/src/routes/components/accordions.tsx
+++ b/packages/skeleton-react/src/routes/components/accordions.tsx
@@ -1,52 +1,71 @@
-import { Skull } from 'lucide-react';
+import { Club, Diamond, Heart, Spade } from 'lucide-react';
import { useState } from 'react';
import { Accordion } from '$lib/components/Accordion/Accordion.js';
export function Component() {
+ const [valueDefault, setValueDefault] = useState(['club']);
+ const [valueMultiple, setValueMultiple] = useState(['club']);
+ const [valueCollapsible, setValueCollapsible] = useState(['club']);
+
const lorem =
'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Sit esse nisi eligendi fuga! Quas nisi repellat adipisci animi repellendus incidunt laborum sunt qui nesciunt, ducimus saepe sapiente sed ut labore. Lorem ipsum dolor, sit amet consectetur adipisicing elit. Sit esse nisi eligendi fuga! Quas nisi repellat adipisci animi repellendus incidunt laborum sunt qui nesciunt, ducimus saepe sapiente sed ut labore.';
- const [multiple, setMultiple] = useState(false);
- const [value, setValue] = useState(['reactItem1']);
-
- const onInputChangeHandler = (event: React.ChangeEvent) => {
- setMultiple(event.target.checked);
- };
-
+ const exampleItems = [
+ { icon: Club, value: 'club', title: 'Club', content: lorem },
+ { icon: Diamond, value: 'diamond', title: 'Diamond', content: lorem },
+ { icon: Heart, value: 'heart', title: 'Heart', content: lorem },
+ { icon: Spade, value: 'space', title: 'Spade', content: lorem }
+ ];
return (
- {/* Set Multiple */}
-
-
- {/* Current State */}
-
Open: {value.join(', ')}
-
- {/* Component */}
-
-
- }>React Control 1
- React Panel 1 - {lorem}
-
-
-
- React Control 2
- React Panel 2 - {lorem}
-
-
-
- React Control 3 (Disabled)
- React Panel 3 - {lorem}
-
-
-
- React Control 4
- React Panel 4 - {lorem}
-
-
+
+
+ {JSON.stringify(valueDefault)}
+
+ {exampleItems.map((item, i) => (
+
+
+ }>{item.title}
+ {item.content}
+
+ {i < exampleItems.length - 1 &&
}
+
+ ))}
+
+
+
+ Collapsible
+ {JSON.stringify(valueCollapsible)}
+
+ {exampleItems.map((item, i) => (
+
+
+ }>{item.title}
+ {item.content}
+
+ {i < exampleItems.length - 1 &&
}
+
+ ))}
+
+
+
+ Multiple
+ {JSON.stringify(valueMultiple)}
+
+ {exampleItems.map((item, i) => (
+
+
+ }>{item.title}
+ {item.content}
+
+ {i < exampleItems.length - 1 &&
}
+
+ ))}
+
+
);
}
diff --git a/packages/skeleton-react/src/routes/components/avatars.tsx b/packages/skeleton-react/src/routes/components/avatars.tsx
index 0cedbdf1b..71691fb28 100644
--- a/packages/skeleton-react/src/routes/components/avatars.tsx
+++ b/packages/skeleton-react/src/routes/components/avatars.tsx
@@ -4,92 +4,43 @@ import { Avatar } from '$lib/components/Avatar/Avatar.js';
const imgSrc =
'https://images.unsplash.com/photo-1617296538902-887900d9b592?ixid=M3w0Njc5ODF8MHwxfGFsbHx8fHx8fHx8fDE2ODc5NzExMDB8&ixlib=rb-4.0.3&w=128&h=128&auto=format&fit=crop';
-const verticalImgSrc =
- 'https://images.unsplash.com/photo-1617296538902-887900d9b592?ixid=M3w0Njc5ODF8MHwxfGFsbHx8fHx8fHx8fDE2ODc5NzExMDB8&ixlib=rb-4.0.3&w=64&h=128&auto=format&fit=crop';
-const horizontalImgSrc =
- 'https://images.unsplash.com/photo-1509557965875-b88c97052f0e?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D';
-
-function imageSrcAvatar() {
- return (
- <>
- imageSrc
-
- >
- );
-}
-
-function initialsAvatar() {
- return (
- <>
- Initials
-
- SK
-
- >
- );
-}
-
-function iconAvatar() {
- return (
- <>
- Icon
-
-
-
- >
- );
-}
-
-function filterAvatar() {
- return (
- <>
- {/* Filter example */}
- {/* NoirLight: `filter: url(#NoirLight)` */}
-
- Filter
-
- >
- );
-}
-
-function nonSquareAspectRatioAvatar() {
- return (
- <>
- non-square aspect-ratio
-
- >
- );
-}
export function Component() {
return (
- <>
- Avatars
- {imageSrcAvatar()}
- {initialsAvatar()}
- {iconAvatar()}
- {filterAvatar()}
- {nonSquareAspectRatioAvatar()}
- >
+
+
+
+
+ Fallback
+ SK
+ Icon
+
+
+
+ Filter
+ {/* NoirLight: `filter: url(#NoirLight)` */}
+
+
+
+
);
}
diff --git a/packages/skeleton-react/src/routes/components/progress-rings.tsx b/packages/skeleton-react/src/routes/components/progress-rings.tsx
index 1e942ac12..f34c252b8 100644
--- a/packages/skeleton-react/src/routes/components/progress-rings.tsx
+++ b/packages/skeleton-react/src/routes/components/progress-rings.tsx
@@ -4,7 +4,7 @@ import { useState } from 'react';
import { ProgressRing } from '$lib/components/ProgressRing/ProgressRing.js';
export function Component() {
- const [value, setValue] = useState(50);
+ const [value, setValue] = useState(40);
const [max, setMax] = useState(100);
return (
@@ -33,7 +33,7 @@ export function Component() {
);
diff --git a/packages/skeleton-react/src/routes/components/progress.tsx b/packages/skeleton-react/src/routes/components/progress.tsx
index 6c6c009e9..6a652e8e2 100644
--- a/packages/skeleton-react/src/routes/components/progress.tsx
+++ b/packages/skeleton-react/src/routes/components/progress.tsx
@@ -54,11 +54,13 @@ export function Component() {
Indeterminate
- indeterminate (default)
-
- custom-indeterminate
+
{/* NOTE: `custom-indeterminate` defined in app.pcss */}
-
+
RTL
diff --git a/packages/skeleton-react/src/routes/components/ratings.tsx b/packages/skeleton-react/src/routes/components/ratings.tsx
index 7b2c41c49..119fb7c69 100644
--- a/packages/skeleton-react/src/routes/components/ratings.tsx
+++ b/packages/skeleton-react/src/routes/components/ratings.tsx
@@ -1,134 +1,44 @@
-import { Bone, Skull, Star } from 'lucide-react';
+import { Bone, Skull } from 'lucide-react';
import { useState } from 'react';
+import { Rating } from '$lib/index.js';
-import { Rating } from '$lib/components/Rating/Rating';
-
-function StaticRatings(value: number) {
- return (
-
-
- } iconFull={} />
-
-
{value}
-
- );
-}
+export function Component() {
+ const [value, setValue] = useState(3);
-function CustomIcons(value: number, setValue: React.Dispatch>) {
return (
- <>
+
+
+
value: {value}
- Custom Icons
-
-
- setValue(val)}
- iconEmpty={}
- iconFull={}
- />
-
-
{value.toFixed(2)}
-
+
- >
- );
-}
-
-function InteractiveRating(value: number, setValue: React.Dispatch
>) {
- return (
- <>
- Interactive
-
-
- setValue(val)}
- iconEmpty={}
- iconFull={}
- />
-
-
{value.toFixed(2)}
-
+ Icons
+ } iconFull={}>
+
+
+
+
+
- >
- );
-}
-
-function Steps(step: number, value: number, setValue: React.Dispatch>) {
- return (
- <>
-
-
- setValue(val)}
- iconEmpty={}
- iconFull={}
- />
-
-
{value.toFixed(2)}
-
- >
- );
-}
-function RTL(value: number, setValue: React.Dispatch>) {
- return (
- <>
RTL
-
-
- setValue(val)}
- iconEmpty={}
- iconFull={}
- />
-
-
{value.toFixed(2)}
-
+
- >
- );
-}
-
-export function Component() {
- const [interactiveValue, setInteractiveValue] = useState(3);
- return (
- <>
-
-
-
-
- {StaticRatings(5)}
- {StaticRatings(0)}
- {StaticRatings(2.5)}
- {StaticRatings(3.75)}
- {StaticRatings(1.25)}
-
-
- {CustomIcons(interactiveValue, setInteractiveValue)}
- {InteractiveRating(interactiveValue, setInteractiveValue)}
-
-
- Steps
- {Steps(2, interactiveValue, setInteractiveValue)}
- {Steps(3, interactiveValue, setInteractiveValue)}
-
-
- {RTL(interactiveValue, setInteractiveValue)}
-
- >
+
);
}
diff --git a/packages/skeleton-react/src/routes/components/segment-control.tsx b/packages/skeleton-react/src/routes/components/segment-control.tsx
index f2cd8357a..fc98dfe68 100644
--- a/packages/skeleton-react/src/routes/components/segment-control.tsx
+++ b/packages/skeleton-react/src/routes/components/segment-control.tsx
@@ -13,47 +13,76 @@ export function Component() {
{align}
-
-
+
+
-
+
-
+
-
+
{size}
-
-
- sm
-
-
- md
-
-
+
+ sm
+ md
+
lg
+ {/* Vertical */}
Vertical
{size}
-
-
- sm
+
+ sm
+ md
+
+ lg
+
+
+
+ {/* Disabled */}
+
+ Disabled
+
+
+
+
+
+
-
- md
+
+
-
- lg
+
+
+
+
+
+ {/* Read-Only */}
+
+ Read-Only
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/skeleton-react/src/routes/components/switch.tsx b/packages/skeleton-react/src/routes/components/switch.tsx
index dcc780d9f..d66fde697 100644
--- a/packages/skeleton-react/src/routes/components/switch.tsx
+++ b/packages/skeleton-react/src/routes/components/switch.tsx
@@ -18,60 +18,51 @@ export function Component() {
{JSON.stringify({ disturb, notifications, disabled, icons, lightswitch, compact }, null, 2)}
-
+
-
+
-
+
-
+
-
+
-
+
);
diff --git a/packages/skeleton-react/src/routes/components/tabs.tsx b/packages/skeleton-react/src/routes/components/tabs.tsx
index 9650d435d..f4d49c583 100644
--- a/packages/skeleton-react/src/routes/components/tabs.tsx
+++ b/packages/skeleton-react/src/routes/components/tabs.tsx
@@ -1,10 +1,12 @@
-import { Plane, Hotel, Box } from 'lucide-react';
+import { Plane as IconPlane, Sailboat as IconBoat, Car as IconCar } from 'lucide-react';
import { useState } from 'react';
import { Tabs } from '$lib/components/Tabs/Tabs.js';
export function Component() {
- const [group, setGroup] = useState('flight');
+ const [group, setGroup] = useState('item-1');
+ const lorem =
+ 'Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nostrum veniam reprehenderit eum, reiciendis obcaecati, excepturi nemo ipsa fugit suscipit autem vitae numquam et cumque praesentium vero eos minus itaque. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nostrum veniam reprehenderit eum, reiciendis obcaecati, excepturi nemo.';
return (
@@ -13,237 +15,123 @@ export function Component() {
{/* Default */}
-
+ group: {JSON.stringify(group, null, 2)}
+
- console.log('onClick')}
- onKeydown={() => console.log('onKeyDown')}
- >
- Flight
-
-
- Hotel
-
-
- Explore
-
+ Control-1
+ Control-2
+ Control-3
-
-
- Flight Panel
-
-
- Hotel Panel
-
-
- Explore Panel
-
-
+
+ Panel-1 - {lorem}
+ Panel-2 - {lorem}
+ Panel-3 - {lorem}
+
{/* Icon + Label */}
Icon + Label
-
+
-
-
- Flight
+ }>
+ Control-1
-
-
- Hotel
+ }>
+ Control-2
-
-
- Explore
+ }>
+ Control-3
-
-
- Flight Panel
-
-
- Hotel Panel
-
-
- Explore Panel
-
-
+
+ Panel-1 - {lorem}
+ Panel-2 - {lorem}
+ Panel-3 - {lorem}
+
{/* Icon Only */}
Icon Only
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
- Flight Panel
-
-
- Hotel Panel
-
-
- Explore Panel
-
-
+
+ Panel-1 - {lorem}
+ Panel-2 - {lorem}
+ Panel-3 - {lorem}
+
{/* Fluid */}
Fluid
-
+
-
- Flight
-
-
- Hotel
-
-
- Explore
-
+ Control-1
+ Control-2
+ Control-3
-
-
- Flight Panel
-
-
- Hotel Panel
-
-
- Explore Panel
-
-
+
+ Panel-1 - {lorem}
+ Panel-2 - {lorem}
+ Panel-3 - {lorem}
+
- {/* Justified */}
+ {/* Justify */}
Justify
-
+
-
- Flight
-
-
- Hotel
-
-
- Explore
-
+ Control-1
+ Control-2
+ Control-3
-
-
- Flight Panel
-
-
- Hotel Panel
-
-
- Explore Panel
-
-
+
+ Panel-1 - {lorem}
+ Panel-2 - {lorem}
+ Panel-3 - {lorem}
+
-
+
-
- Flight
-
-
- Hotel
-
-
- Explore
-
-
-
-
- Flight Panel
-
-
- Hotel Panel
-
-
- Explore Panel
-
-
-
-
- {/* Focus */}
-
- Focus
-
-
-
- Flight
-
-
- Hotel
-
-
- Explore
-
+ Control-1
+ Control-2
+ Control-3
-
-
-
-
-
-
-
-
-
-
-
+
+ Panel-1 - {lorem}
+ Panel-2 - {lorem}
+ Panel-3 - {lorem}
+
{/* RTL */}
RTL
-
+
-
- Flight
-
-
- Hotel
-
-
- Explore
-
+ Control-1
+ Control-2
+ Control-3
-
-
- Flight Panel{' '}
-
-
- Hotel Panel{' '}
-
-
- Explore Panel
-
-
+
+ Panel-1 - {lorem}
+ Panel-2 - {lorem}
+ Panel-3 - {lorem}
+
diff --git a/packages/skeleton-svelte/package.json b/packages/skeleton-svelte/package.json
index 755dee178..db0ab4b3c 100644
--- a/packages/skeleton-svelte/package.json
+++ b/packages/skeleton-svelte/package.json
@@ -13,7 +13,6 @@
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test": "pnpm run \"/test:/\"",
- "test:e2e": "playwright test",
"test:unit": "vitest",
"sync": "svelte-kit sync"
},
@@ -36,7 +35,7 @@
"./dist/**/*.d.cts"
],
"peerDependencies": {
- "svelte": "^5.0.0-next.136"
+ "svelte": "^5.0.0-next.193"
},
"devDependencies": {
"@playwright/test": "^1.45.3",
@@ -48,6 +47,14 @@
"@tailwindcss/forms": "^0.5.7",
"@testing-library/jest-dom": "^6.4.7",
"@testing-library/svelte": "^4.2.3",
+ "@zag-js/accordion": "^0.65.0",
+ "@zag-js/avatar": "^0.65.0",
+ "@zag-js/progress": "^0.65.0",
+ "@zag-js/radio-group": "^0.65.0",
+ "@zag-js/rating-group": "0.65.0",
+ "@zag-js/svelte": "^0.65.0",
+ "@zag-js/switch": "^0.65.0",
+ "@zag-js/tabs": "^0.65.0",
"autoprefixer": "^10.4.19",
"jsdom": "^24.1.1",
"lucide-svelte": "^0.341.0",
diff --git a/packages/skeleton-svelte/src/lib/components/Accordion/Accordion.svelte b/packages/skeleton-svelte/src/lib/components/Accordion/Accordion.svelte
index 7e23685d4..7bdece61f 100644
--- a/packages/skeleton-svelte/src/lib/components/Accordion/Accordion.svelte
+++ b/packages/skeleton-svelte/src/lib/components/Accordion/Accordion.svelte
@@ -1,9 +1,12 @@
-
+
-
+
{@render children()}
diff --git a/packages/skeleton-svelte/src/lib/components/Accordion/AccordionItem.svelte b/packages/skeleton-svelte/src/lib/components/Accordion/AccordionItem.svelte
index 2c625187c..5911f084c 100644
--- a/packages/skeleton-svelte/src/lib/components/Accordion/AccordionItem.svelte
+++ b/packages/skeleton-svelte/src/lib/components/Accordion/AccordionItem.svelte
@@ -1,80 +1,90 @@
-
+
-
+
-
+
+
+
+
- {#if panel && ctx.isOpen(id)}
+ {#if ctx.api.value.includes(zagProps.value)}
- {@render panel()}
+ {@render panel?.()}
{/if}
diff --git a/packages/skeleton-svelte/src/lib/components/Accordion/context.ts b/packages/skeleton-svelte/src/lib/components/Accordion/context.ts
index f7ceb92b7..7d480f85a 100644
--- a/packages/skeleton-svelte/src/lib/components/Accordion/context.ts
+++ b/packages/skeleton-svelte/src/lib/components/Accordion/context.ts
@@ -1,10 +1,5 @@
import { createContext } from '$lib/internal/create-context.js';
import type { AccordionContext } from './types.js';
-export const [setAccordionContext, getAccordionContext, key] = createContext
({
- open: () => {},
- close: () => {},
- toggle: () => {},
- isOpen: () => false,
- animDuration: 0
-});
+// @ts-expect-error - Defaults for context don't make sense, `createContext` should just throw TBH
+export const [setAccordionContext, getAccordionContext, key] = createContext();
diff --git a/packages/skeleton-svelte/src/lib/components/Accordion/types.ts b/packages/skeleton-svelte/src/lib/components/Accordion/types.ts
index 06d91d0c0..aaab405da 100644
--- a/packages/skeleton-svelte/src/lib/components/Accordion/types.ts
+++ b/packages/skeleton-svelte/src/lib/components/Accordion/types.ts
@@ -1,12 +1,10 @@
-import { type Snippet } from 'svelte';
+import type { Snippet } from 'svelte';
+import * as accordion from '@zag-js/accordion';
// Accordion Context ---
export interface AccordionContext {
- open: (id: string) => void;
- close: (id: string) => void;
- toggle: (id: string) => void;
- isOpen: (id: string) => boolean;
+ api: ReturnType;
animDuration: number;
iconOpen?: Snippet;
iconClosed?: Snippet;
@@ -14,11 +12,7 @@ export interface AccordionContext {
// Accordion ---
-export interface AccordionProps {
- /** Enables opening multiple items at once. */
- multiple?: boolean;
- /** Takes an array list of open items. */
- value?: string[];
+export interface AccordionProps extends Omit {
/** The slide animation duration in milliseconds. */
animDuration?: number;
@@ -47,11 +41,9 @@ export interface AccordionProps {
// Accordion Item ---
-export interface AccordionItemProps {
- /** Set a unique ID for the item. */
- id: string;
- /** Set a disabled state for the item. */
- disabled?: boolean;
+export interface AccordionItemProps extends accordion.ItemProps {
+ /** The element used as the header. */
+ headingElement?: string;
// Root ---
/** Sets base styles. */
@@ -61,10 +53,6 @@ export interface AccordionItemProps {
/** Provide arbitrary CSS classes. */
classes?: string;
- // Events ---
- /** Triggers on item open or close. */
- onclick?: (event: MouseEvent) => void;
-
// Control ---
/** Sets control's base styles. */
controlBase?: string;
@@ -77,9 +65,23 @@ export interface AccordionItemProps {
/** Provide arbitrary CSS classes to the control. */
controlClasses?: string;
- // Icons ---
- /** Set the base styles for the state icons. */
- iconsBase?: string;
+ // Lead ---
+ /** Sets the lead's base styles */
+ leadBase?: string;
+ /** Provide arbitrary CSS classes to the lead. */
+ leadClasses?: string;
+
+ // Content ---
+ /** Sets the lead's base styles */
+ contentBase?: string;
+ /** Provide arbitrary CSS classes to the content. */
+ contentClasses?: string;
+
+ // Indicator ---
+ /** Sets the lead's base styles */
+ indicatorBase?: string;
+ /** Provide arbitrary CSS classes to the indicator. */
+ indicatorClasses?: string;
// Panel ---
/** Set the panel's base styles. */
@@ -95,7 +97,7 @@ export interface AccordionItemProps {
/** The control's default slot. */
control: Snippet;
/** The control's lead icon slot. */
- controlLead?: Snippet;
+ lead?: Snippet;
/** The panels's default slot. */
panel?: Snippet;
}
diff --git a/packages/skeleton-svelte/src/lib/components/Avatar/Avatar.svelte b/packages/skeleton-svelte/src/lib/components/Avatar/Avatar.svelte
index 0137cc783..3433bbe9a 100644
--- a/packages/skeleton-svelte/src/lib/components/Avatar/Avatar.svelte
+++ b/packages/skeleton-svelte/src/lib/components/Avatar/Avatar.svelte
@@ -1,10 +1,14 @@
-
+
-