@stfrigerio/sito-template 0.1.35 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ import React, { ReactNode } from 'react';
2
+ /**
3
+ * Props for the EmptyState component
4
+ * @interface EmptyStateProps
5
+ */
6
+ export interface EmptyStateProps {
7
+ /** Optional icon (usually a lucide-react icon) rendered above the message */
8
+ icon?: ReactNode;
9
+ /** Optional heading shown above the message */
10
+ title?: string;
11
+ /** The main message describing why the state is empty and what to do next */
12
+ message: string;
13
+ /** Optional action slot (e.g. a Button CTA) rendered under the message */
14
+ action?: ReactNode;
15
+ /** Size variant — `compact` fits inline slots, `default` fills a panel, `large` fills the viewport */
16
+ size?: 'compact' | 'default' | 'large';
17
+ }
18
+ /**
19
+ * EmptyState Component
20
+ *
21
+ * @component
22
+ * @description
23
+ * A composable placeholder for empty lists, empty sidebars, and
24
+ * "nothing to show" panels. Renders an icon, optional title, message,
25
+ * and optional action CTA in a vertically-centered stack. Pure layout —
26
+ * no fetching, no state.
27
+ *
28
+ * @example
29
+ * // Minimal: just a message
30
+ * <EmptyState message="No documents yet." />
31
+ *
32
+ * @example
33
+ * // With an icon
34
+ * import { FileText } from 'lucide-react';
35
+ * <EmptyState
36
+ * icon={<FileText size={32} />}
37
+ * message="Select a document from the sidebar to start editing."
38
+ * />
39
+ *
40
+ * @example
41
+ * // Full: icon + title + message + action
42
+ * <EmptyState
43
+ * icon={<FolderKanban size={40} />}
44
+ * title="No projects"
45
+ * message="Create your first project to get started."
46
+ * action={<Button onClick={handleCreate}>New project</Button>}
47
+ * />
48
+ *
49
+ * @param {EmptyStateProps} props - The props for the EmptyState component
50
+ * @returns {JSX.Element} The rendered EmptyState component
51
+ */
52
+ export declare const EmptyState: React.FC<EmptyStateProps>;
53
+ //# sourceMappingURL=EmptyState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../../../../src/components/atoms/EmptyState/EmptyState.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGzC;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,6EAA6E;IAC7E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,sGAAsG;IACtG,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAgBhD,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { EmptyState } from './EmptyState';
2
+ export type { EmptyStateProps } from './EmptyState';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/atoms/EmptyState/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,61 @@
1
+ import React, { ReactNode } from 'react';
2
+ /**
3
+ * Props for the Modal component
4
+ * @interface ModalProps
5
+ */
6
+ export interface ModalProps {
7
+ /** Whether the modal is currently visible */
8
+ open: boolean;
9
+ /** Title shown in the modal header */
10
+ title: string;
11
+ /** Callback fired when the user requests to close the modal (backdrop click, Escape, or X button) */
12
+ onClose: () => void;
13
+ /** Modal body content */
14
+ children: ReactNode;
15
+ /** Width variant. `default` fits typical forms; `compact` is narrower; `wide` is for editors */
16
+ size?: 'default' | 'compact' | 'wide';
17
+ /** Optional slot rendered in the header next to the close button (e.g. action buttons) */
18
+ actions?: ReactNode;
19
+ /** When true, the body is padded; when false, the body is flush to the edges */
20
+ padding?: boolean;
21
+ }
22
+ /**
23
+ * Modal Component
24
+ *
25
+ * @component
26
+ * @description
27
+ * An accessible overlay dialog with a backdrop, title header, optional
28
+ * header actions, and a flexible body. Renders into a React portal on
29
+ * `document.body`, animates in/out with Framer Motion, closes on Escape
30
+ * key or backdrop click, and clicks inside the dialog don't propagate
31
+ * to the backdrop.
32
+ *
33
+ * @example
34
+ * // Basic usage
35
+ * <Modal open={isOpen} title="Confirm" onClose={() => setIsOpen(false)}>
36
+ * <p>Are you sure?</p>
37
+ * </Modal>
38
+ *
39
+ * @example
40
+ * // With header actions and wide size
41
+ * <Modal
42
+ * open={isOpen}
43
+ * title="Edit profile"
44
+ * size="wide"
45
+ * actions={<button onClick={save}>Save</button>}
46
+ * onClose={handleClose}
47
+ * >
48
+ * <ProfileForm />
49
+ * </Modal>
50
+ *
51
+ * @example
52
+ * // Flush body (no padding) for editors and full-bleed content
53
+ * <Modal open={isOpen} title="Document" padding={false} onClose={handleClose}>
54
+ * <DocEditor content={content} onSave={save} />
55
+ * </Modal>
56
+ *
57
+ * @param {ModalProps} props - The props for the Modal component
58
+ * @returns {React.ReactPortal | null} A portal rendering the modal, or null on the server
59
+ */
60
+ export declare const Modal: React.FC<ModalProps>;
61
+ //# sourceMappingURL=Modal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../../src/components/atoms/Modal/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAa,MAAM,OAAO,CAAC;AAMpD;;;GAGG;AACH,MAAM,WAAW,UAAU;IAC1B,6CAA6C;IAC7C,IAAI,EAAE,OAAO,CAAC;IACd,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,qGAAqG;IACrG,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,yBAAyB;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,gGAAgG;IAChG,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IACtC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CAsEtC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { Modal } from './Modal';
2
+ export type { ModalProps } from './Modal';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/atoms/Modal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}
@@ -2,6 +2,10 @@ export { Button } from './Button';
2
2
  export type { ButtonProps } from './Button';
3
3
  export { Card } from './Card';
4
4
  export type { CardProps } from './Card';
5
+ export { EmptyState } from './EmptyState';
6
+ export type { EmptyStateProps } from './EmptyState';
7
+ export { Modal } from './Modal';
8
+ export type { ModalProps } from './Modal';
5
9
  export { Checkbox } from './Checkbox';
6
10
  export type { CheckboxProps } from './Checkbox';
7
11
  export { DateInput } from './DateInput';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAIxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAGpE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGvE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI1C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAGpE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGvE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.esm.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React, { createContext, useContext, useState, useEffect, useRef, useCallback, useMemo, memo } from 'react';
3
3
  import { motion, AnimatePresence, LayoutGroup } from 'framer-motion';
4
- import { Check, X, Edit } from 'lucide-react';
4
+ import { createPortal } from 'react-dom';
5
+ import { X, Check, Edit } from 'lucide-react';
5
6
  import * as d3 from 'd3';
6
7
 
7
8
  const ThemeContext$1 = createContext(undefined);
@@ -30,7 +31,7 @@ const ThemeProvider = ({ children, defaultTheme = 'light' }) => {
30
31
  return (jsx(ThemeContext$1.Provider, { value: { theme, toggleTheme, setTheme }, children: children }));
31
32
  };
32
33
 
33
- var styles$s = {"button":"Button-module_button__c6nkW","primary":"Button-module_primary__pMqAs","secondary":"Button-module_secondary__mBWx9","outline":"Button-module_outline__NGGGN","ghost":"Button-module_ghost__u2QBF","danger":"Button-module_danger__2ewhr","small":"Button-module_small__ZI9RX","medium":"Button-module_medium__Wnf9t","large":"Button-module_large__cQCpA","fullWidth":"Button-module_fullWidth__N8vYg","loading":"Button-module_loading__hcSI4","spinner":"Button-module_spinner__HtM96","spin":"Button-module_spin__jblrj","iconLeft":"Button-module_iconLeft__Fpz-y","iconRight":"Button-module_iconRight__kTfjS"};
34
+ var styles$u = {"button":"Button-module_button__c6nkW","primary":"Button-module_primary__pMqAs","secondary":"Button-module_secondary__mBWx9","outline":"Button-module_outline__NGGGN","ghost":"Button-module_ghost__u2QBF","danger":"Button-module_danger__2ewhr","small":"Button-module_small__ZI9RX","medium":"Button-module_medium__Wnf9t","large":"Button-module_large__cQCpA","fullWidth":"Button-module_fullWidth__N8vYg","loading":"Button-module_loading__hcSI4","spinner":"Button-module_spinner__HtM96","spin":"Button-module_spin__jblrj","iconLeft":"Button-module_iconLeft__Fpz-y","iconRight":"Button-module_iconRight__kTfjS"};
34
35
 
35
36
  const SOUND_PACKS = {
36
37
  digital: {
@@ -595,11 +596,11 @@ function useComponentSound(config, options) {
595
596
  const Button = ({ variant = 'primary', size = 'medium', fullWidth = false, loading = false, iconLeft, iconRight, children, className = '', disabled, motionProps, soundConfig, onClick, onMouseEnter, onFocus, ...rest }) => {
596
597
  const { handlers } = useComponentSound(soundConfig);
597
598
  const buttonClasses = [
598
- styles$s.button,
599
- styles$s[variant],
600
- styles$s[size],
601
- fullWidth && styles$s.fullWidth,
602
- loading && styles$s.loading,
599
+ styles$u.button,
600
+ styles$u[variant],
601
+ styles$u[size],
602
+ fullWidth && styles$u.fullWidth,
603
+ loading && styles$u.loading,
603
604
  className
604
605
  ].filter(Boolean).join(' ');
605
606
  const handleClick = (e) => {
@@ -610,10 +611,10 @@ const Button = ({ variant = 'primary', size = 'medium', fullWidth = false, loadi
610
611
  handlers.onMouseEnter?.();
611
612
  onMouseEnter?.(e);
612
613
  };
613
- return (jsxs(motion.button, { className: buttonClasses, disabled: disabled || loading, whileHover: { scale: disabled || loading ? 1 : 1.02 }, whileTap: { scale: disabled || loading ? 1 : 0.98 }, transition: { type: "spring", stiffness: 400, damping: 17 }, onClick: handleClick, onMouseEnter: handleMouseEnter, onFocus: onFocus, ...motionProps, ...rest, children: [loading && jsx("span", { className: styles$s.spinner }), iconLeft && jsx("span", { className: styles$s.iconLeft, children: iconLeft }), children, iconRight && jsx("span", { className: styles$s.iconRight, children: iconRight })] }));
614
+ return (jsxs(motion.button, { className: buttonClasses, disabled: disabled || loading, whileHover: { scale: disabled || loading ? 1 : 1.02 }, whileTap: { scale: disabled || loading ? 1 : 0.98 }, transition: { type: "spring", stiffness: 400, damping: 17 }, onClick: handleClick, onMouseEnter: handleMouseEnter, onFocus: onFocus, ...motionProps, ...rest, children: [loading && jsx("span", { className: styles$u.spinner }), iconLeft && jsx("span", { className: styles$u.iconLeft, children: iconLeft }), children, iconRight && jsx("span", { className: styles$u.iconRight, children: iconRight })] }));
614
615
  };
615
616
 
616
- var styles$r = {"card":"Card-module_card__r2DB2","hoverable":"Card-module_hoverable__X3OpS","elevated":"Card-module_elevated__hGV6-","outlined":"Card-module_outlined__ngRag","flat":"Card-module_flat__xy-xt","glass":"Card-module_glass__Sv-Vs","imageContainer":"Card-module_imageContainer__L4ma6","image":"Card-module_image__bQBt6","header":"Card-module_header__0dtj3","headerContent":"Card-module_headerContent__W7-jD","expandButton":"Card-module_expandButton__I7f49","expandIcon":"Card-module_expandIcon__Lu-OY","expandableContent":"Card-module_expandableContent__BFgO5","expandable":"Card-module_expandable__GMXzo","body":"Card-module_body__K7eL3","footer":"Card-module_footer__L5wO-","title":"Card-module_title__pW9g8","subtitle":"Card-module_subtitle__gejH4","clickable":"Card-module_clickable__Y6fm8","padding":"Card-module_padding__wtyDo","noPadding":"Card-module_noPadding__r5Qq0","loading":"Card-module_loading__S4Wng","loadingShimmer":"Card-module_loadingShimmer__Q1Osr","loadingPulse":"Card-module_loadingPulse__bXQmC"};
617
+ var styles$t = {"card":"Card-module_card__r2DB2","hoverable":"Card-module_hoverable__X3OpS","elevated":"Card-module_elevated__hGV6-","outlined":"Card-module_outlined__ngRag","flat":"Card-module_flat__xy-xt","glass":"Card-module_glass__Sv-Vs","imageContainer":"Card-module_imageContainer__L4ma6","image":"Card-module_image__bQBt6","header":"Card-module_header__0dtj3","headerContent":"Card-module_headerContent__W7-jD","expandButton":"Card-module_expandButton__I7f49","expandIcon":"Card-module_expandIcon__Lu-OY","expandableContent":"Card-module_expandableContent__BFgO5","expandable":"Card-module_expandable__GMXzo","body":"Card-module_body__K7eL3","footer":"Card-module_footer__L5wO-","title":"Card-module_title__pW9g8","subtitle":"Card-module_subtitle__gejH4","clickable":"Card-module_clickable__Y6fm8","padding":"Card-module_padding__wtyDo","noPadding":"Card-module_noPadding__r5Qq0","loading":"Card-module_loading__S4Wng","loadingShimmer":"Card-module_loadingShimmer__Q1Osr","loadingPulse":"Card-module_loadingPulse__bXQmC"};
617
618
 
618
619
  /**
619
620
  * Card Component
@@ -678,27 +679,131 @@ const Card = ({ variant = 'elevated', hoverable = false, clickable = false, padd
678
679
  onExpandChange?.(newExpanded);
679
680
  };
680
681
  const cardClasses = [
681
- styles$r.card,
682
- styles$r[variant],
683
- hoverable && styles$r.hoverable,
684
- clickable && styles$r.clickable,
685
- !padding && styles$r.noPadding,
686
- expandable && styles$r.expandable,
682
+ styles$t.card,
683
+ styles$t[variant],
684
+ hoverable && styles$t.hoverable,
685
+ clickable && styles$t.clickable,
686
+ !padding && styles$t.noPadding,
687
+ expandable && styles$t.expandable,
687
688
  className
688
689
  ].filter(Boolean).join(' ');
689
690
  const renderHeader = () => {
690
691
  if (header) {
691
- return (jsxs("div", { className: styles$r.header, children: [jsx("div", { className: styles$r.headerContent, children: header }), expandable && (jsx("button", { className: styles$r.expandButton, onClick: handleToggleExpand, "aria-label": isExpanded ? 'Collapse card' : 'Expand card', children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: styles$r.expandIcon, style: { transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)' }, children: jsx("path", { d: "M6 9l6 6 6-6" }) }) }))] }));
692
+ return (jsxs("div", { className: styles$t.header, children: [jsx("div", { className: styles$t.headerContent, children: header }), expandable && (jsx("button", { className: styles$t.expandButton, onClick: handleToggleExpand, "aria-label": isExpanded ? 'Collapse card' : 'Expand card', children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: styles$t.expandIcon, style: { transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)' }, children: jsx("path", { d: "M6 9l6 6 6-6" }) }) }))] }));
692
693
  }
693
694
  if (title || subtitle) {
694
- return (jsxs("div", { className: styles$r.header, children: [jsxs("div", { className: styles$r.headerContent, children: [title && jsx("h3", { className: styles$r.title, children: title }), subtitle && jsx("p", { className: styles$r.subtitle, children: subtitle })] }), expandable && (jsx("button", { className: styles$r.expandButton, onClick: handleToggleExpand, "aria-label": isExpanded ? 'Collapse card' : 'Expand card', children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: styles$r.expandIcon, style: { transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)' }, children: jsx("path", { d: "M6 9l6 6 6-6" }) }) }))] }));
695
+ return (jsxs("div", { className: styles$t.header, children: [jsxs("div", { className: styles$t.headerContent, children: [title && jsx("h3", { className: styles$t.title, children: title }), subtitle && jsx("p", { className: styles$t.subtitle, children: subtitle })] }), expandable && (jsx("button", { className: styles$t.expandButton, onClick: handleToggleExpand, "aria-label": isExpanded ? 'Collapse card' : 'Expand card', children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: styles$t.expandIcon, style: { transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)' }, children: jsx("path", { d: "M6 9l6 6 6-6" }) }) }))] }));
695
696
  }
696
697
  return null;
697
698
  };
698
- const cardContent = (jsxs(Fragment, { children: [image && (jsx("div", { className: styles$r.imageContainer, children: jsx("img", { src: image, alt: imageAlt, className: styles$r.image }) })), renderHeader(), jsx(AnimatePresence, { initial: false, children: (!expandable || isExpanded) && (jsxs(motion.div, { initial: expandable ? { height: 0, opacity: 0 } : undefined, animate: expandable ? { height: 'auto', opacity: 1 } : undefined, exit: expandable ? { height: 0, opacity: 0 } : undefined, transition: { duration: 0.3, ease: "easeInOut" }, className: styles$r.expandableContent, children: [children && (jsx("div", { className: padding ? styles$r.body : undefined, children: children })), footer && jsx("div", { className: styles$r.footer, children: footer })] }, "content")) })] }));
699
+ const cardContent = (jsxs(Fragment, { children: [image && (jsx("div", { className: styles$t.imageContainer, children: jsx("img", { src: image, alt: imageAlt, className: styles$t.image }) })), renderHeader(), jsx(AnimatePresence, { initial: false, children: (!expandable || isExpanded) && (jsxs(motion.div, { initial: expandable ? { height: 0, opacity: 0 } : undefined, animate: expandable ? { height: 'auto', opacity: 1 } : undefined, exit: expandable ? { height: 0, opacity: 0 } : undefined, transition: { duration: 0.3, ease: "easeInOut" }, className: styles$t.expandableContent, children: [children && (jsx("div", { className: padding ? styles$t.body : undefined, children: children })), footer && jsx("div", { className: styles$t.footer, children: footer })] }, "content")) })] }));
699
700
  return (jsx(motion.div, { className: cardClasses, onClick: clickable ? onClick : undefined, whileHover: hoverable ? { y: -4 } : undefined, transition: { type: "spring", stiffness: 400, damping: 17 }, ...motionProps, ...rest, children: cardContent }));
700
701
  };
701
702
 
703
+ var styles$s = {"wrapper":"EmptyState-module_wrapper__h3Y2E","compact":"EmptyState-module_compact__HdYu1","default":"EmptyState-module_default__ACAbn","large":"EmptyState-module_large__H3ELo","icon":"EmptyState-module_icon__i7-3M","title":"EmptyState-module_title__xCyfH","message":"EmptyState-module_message__Bc5xK","action":"EmptyState-module_action__UpnvZ"};
704
+
705
+ /**
706
+ * EmptyState Component
707
+ *
708
+ * @component
709
+ * @description
710
+ * A composable placeholder for empty lists, empty sidebars, and
711
+ * "nothing to show" panels. Renders an icon, optional title, message,
712
+ * and optional action CTA in a vertically-centered stack. Pure layout —
713
+ * no fetching, no state.
714
+ *
715
+ * @example
716
+ * // Minimal: just a message
717
+ * <EmptyState message="No documents yet." />
718
+ *
719
+ * @example
720
+ * // With an icon
721
+ * import { FileText } from 'lucide-react';
722
+ * <EmptyState
723
+ * icon={<FileText size={32} />}
724
+ * message="Select a document from the sidebar to start editing."
725
+ * />
726
+ *
727
+ * @example
728
+ * // Full: icon + title + message + action
729
+ * <EmptyState
730
+ * icon={<FolderKanban size={40} />}
731
+ * title="No projects"
732
+ * message="Create your first project to get started."
733
+ * action={<Button onClick={handleCreate}>New project</Button>}
734
+ * />
735
+ *
736
+ * @param {EmptyStateProps} props - The props for the EmptyState component
737
+ * @returns {JSX.Element} The rendered EmptyState component
738
+ */
739
+ const EmptyState = ({ icon, title, message, action, size = 'default', }) => {
740
+ const wrapperClass = [styles$s.wrapper, styles$s[size]].filter(Boolean).join(' ');
741
+ return (jsxs("div", { className: wrapperClass, children: [icon && jsx("div", { className: styles$s.icon, children: icon }), title && jsx("h3", { className: styles$s.title, children: title }), jsx("p", { className: styles$s.message, children: message }), action && jsx("div", { className: styles$s.action, children: action })] }));
742
+ };
743
+
744
+ var styles$r = {"backdrop":"Modal-module_backdrop__yOx-a","dialog":"Modal-module_dialog__yEXTq","dialogCompact":"Modal-module_dialogCompact__z1Wxp","dialogWide":"Modal-module_dialogWide__9PTXK","header":"Modal-module_header__NHHKd","title":"Modal-module_title__i3R0x","headerActions":"Modal-module_headerActions__g28UN","closeButton":"Modal-module_closeButton__siC-1","body":"Modal-module_body__U7jvM","bodyFlush":"Modal-module_bodyFlush__wtk3q"};
745
+
746
+ /**
747
+ * Modal Component
748
+ *
749
+ * @component
750
+ * @description
751
+ * An accessible overlay dialog with a backdrop, title header, optional
752
+ * header actions, and a flexible body. Renders into a React portal on
753
+ * `document.body`, animates in/out with Framer Motion, closes on Escape
754
+ * key or backdrop click, and clicks inside the dialog don't propagate
755
+ * to the backdrop.
756
+ *
757
+ * @example
758
+ * // Basic usage
759
+ * <Modal open={isOpen} title="Confirm" onClose={() => setIsOpen(false)}>
760
+ * <p>Are you sure?</p>
761
+ * </Modal>
762
+ *
763
+ * @example
764
+ * // With header actions and wide size
765
+ * <Modal
766
+ * open={isOpen}
767
+ * title="Edit profile"
768
+ * size="wide"
769
+ * actions={<button onClick={save}>Save</button>}
770
+ * onClose={handleClose}
771
+ * >
772
+ * <ProfileForm />
773
+ * </Modal>
774
+ *
775
+ * @example
776
+ * // Flush body (no padding) for editors and full-bleed content
777
+ * <Modal open={isOpen} title="Document" padding={false} onClose={handleClose}>
778
+ * <DocEditor content={content} onSave={save} />
779
+ * </Modal>
780
+ *
781
+ * @param {ModalProps} props - The props for the Modal component
782
+ * @returns {React.ReactPortal | null} A portal rendering the modal, or null on the server
783
+ */
784
+ const Modal = ({ open, title, onClose, children, size = 'default', actions, padding = true, }) => {
785
+ useEffect(() => {
786
+ if (!open)
787
+ return;
788
+ const onKey = (e) => {
789
+ if (e.key === 'Escape')
790
+ onClose();
791
+ };
792
+ document.addEventListener('keydown', onKey);
793
+ return () => document.removeEventListener('keydown', onKey);
794
+ }, [open, onClose]);
795
+ if (typeof document === 'undefined')
796
+ return null;
797
+ const dialogClass = [
798
+ styles$r.dialog,
799
+ size === 'compact' && styles$r.dialogCompact,
800
+ size === 'wide' && styles$r.dialogWide,
801
+ ]
802
+ .filter(Boolean)
803
+ .join(' ');
804
+ return createPortal(jsx(AnimatePresence, { children: open && (jsx(motion.div, { className: styles$r.backdrop, initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, onClick: onClose, "aria-hidden": "true", children: jsxs(motion.div, { className: dialogClass, role: "dialog", "aria-modal": "true", "aria-label": title, initial: { opacity: 0, scale: 0.96, y: 8 }, animate: { opacity: 1, scale: 1, y: 0 }, exit: { opacity: 0, scale: 0.96, y: 8 }, transition: { duration: 0.15, ease: 'easeOut' }, onClick: (e) => e.stopPropagation(), children: [jsxs("div", { className: styles$r.header, children: [jsx("span", { className: styles$r.title, children: title }), actions && jsx("div", { className: styles$r.headerActions, children: actions }), jsx("button", { className: styles$r.closeButton, onClick: onClose, "aria-label": "Close modal", type: "button", children: jsx(X, { size: 16 }) })] }), jsx("div", { className: padding ? styles$r.body : styles$r.bodyFlush, children: children })] }) })) }), document.body);
805
+ };
806
+
702
807
  var styles$q = {"checkboxLabel":"Checkbox-module_checkboxLabel__4tBVg","checkbox":"Checkbox-module_checkbox__BbJul","checkboxText":"Checkbox-module_checkboxText__oJsc9"};
703
808
 
704
809
  /**
@@ -5128,5 +5233,5 @@ function formatValue(value) {
5128
5233
  // Memoized Table component to prevent unnecessary re-renders
5129
5234
  const Table = memo(TableComponent);
5130
5235
 
5131
- export { ArrayInput, BooleansHeatmap, Button, Calendar, Card, Checkbox, DateInput, EditFAB, ImageSlideshow, LoadingSpinner, MoodChart, Navbar, NumberStepper, PieChart, QuantifiableHabitsChart, SearchBar, SearchableDropdown, SelectInput, SleepChart, Slider, SunburstChart, Table, Tabs, TextArea, TextInput, ThemeProvider, ThemeSwitcher, TimeInput, Toggle, ToggleButton, soundManager, useComponentSound, useSound, useTheme$1 as useTheme };
5236
+ export { ArrayInput, BooleansHeatmap, Button, Calendar, Card, Checkbox, DateInput, EditFAB, EmptyState, ImageSlideshow, LoadingSpinner, Modal, MoodChart, Navbar, NumberStepper, PieChart, QuantifiableHabitsChart, SearchBar, SearchableDropdown, SelectInput, SleepChart, Slider, SunburstChart, Table, Tabs, TextArea, TextInput, ThemeProvider, ThemeSwitcher, TimeInput, Toggle, ToggleButton, soundManager, useComponentSound, useSound, useTheme$1 as useTheme };
5132
5237
  //# sourceMappingURL=index.esm.js.map