@mohasinac/appkit 4.9.3 → 4.9.5

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.
Files changed (67) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +7 -2
  2. package/dist/_internal/client/features/layout/SidebarCollapseToggle.d.ts +5 -1
  3. package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +8 -2
  4. package/dist/_internal/client/features/layout/sidebarPositionClasses.d.ts +20 -0
  5. package/dist/_internal/client/features/layout/sidebarPositionClasses.js +20 -0
  6. package/dist/_internal/client/hand-mode/HandModeProvider.d.ts +21 -0
  7. package/dist/_internal/client/hand-mode/HandModeProvider.js +81 -0
  8. package/dist/_internal/client/hand-mode/index.d.ts +2 -0
  9. package/dist/_internal/client/hand-mode/index.js +1 -0
  10. package/dist/_internal/server/features/checkout/actions.d.ts +43 -0
  11. package/dist/_internal/server/features/checkout/actions.js +172 -81
  12. package/dist/_internal/server/features/checkout/index.d.ts +1 -1
  13. package/dist/_internal/server/features/checkout/index.js +1 -1
  14. package/dist/_internal/server/features/seo/robots.js +3 -2
  15. package/dist/_internal/server/features/seo/sitemap.js +70 -5
  16. package/dist/_internal/shared/config/schema.d.ts +1 -0
  17. package/dist/_internal/shared/listing-types/classified/config.js +1 -1
  18. package/dist/_internal/shared/listing-types/digital-code/config.js +1 -1
  19. package/dist/_internal/shared/listing-types/live/config.js +1 -1
  20. package/dist/client.d.ts +4 -0
  21. package/dist/client.js +9 -0
  22. package/dist/constants/api-endpoints.d.ts +3 -0
  23. package/dist/constants/api-endpoints.js +1 -0
  24. package/dist/features/about/components/AboutView.js +2 -2
  25. package/dist/features/about/components/DeveloperView.js +2 -2
  26. package/dist/features/about/schemas/firestore.d.ts +1 -0
  27. package/dist/features/account/components/UserSidebar.js +8 -2
  28. package/dist/features/account/hooks/useProfile.d.ts +2 -0
  29. package/dist/features/admin/components/AdminSidebar.js +8 -2
  30. package/dist/features/auth/schemas/firestore.d.ts +3 -0
  31. package/dist/features/cart/components/CartDrawer.js +4 -1
  32. package/dist/features/homepage/components/BrandsSection.js +3 -1
  33. package/dist/features/homepage/components/FeaturedBundlesSection.js +12 -1
  34. package/dist/features/homepage/components/GoogleReviewsSection.js +2 -2
  35. package/dist/features/homepage/components/HeroCarousel.js +3 -1
  36. package/dist/features/homepage/components/SectionCarousel.js +5 -2
  37. package/dist/features/homepage/components/SocialFeedSection.js +2 -2
  38. package/dist/features/homepage/components/WhatsAppCommunitySection.js +3 -1
  39. package/dist/features/layout/BackToTop.js +3 -1
  40. package/dist/features/layout/SidebarLayout.js +7 -1
  41. package/dist/features/seller/components/SellerSidebar.js +8 -2
  42. package/dist/features/shell/QuickFormDrawer.js +3 -1
  43. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +31 -0
  44. package/dist/hand-mode/index.d.ts +12 -0
  45. package/dist/hand-mode/index.js +11 -0
  46. package/dist/providers/db-firebase/base.js +17 -1
  47. package/dist/react/contexts/SessionContext.d.ts +5 -0
  48. package/dist/react/contexts/SessionContext.js +2 -0
  49. package/dist/seed/site-settings-seed-data.js +3 -2
  50. package/dist/server-entry.d.ts +1 -1
  51. package/dist/server-entry.js +1 -1
  52. package/dist/styles.css +2 -1
  53. package/dist/tailwind-utilities.css +1 -1
  54. package/dist/ui/components/Drawer.js +7 -4
  55. package/dist/ui/components/FilterDrawer.js +1 -1
  56. package/dist/ui/components/HandModeRow.d.ts +12 -0
  57. package/dist/ui/components/HandModeRow.js +18 -0
  58. package/dist/ui/components/Layout.d.ts +5 -1
  59. package/dist/ui/components/Layout.js +2 -1
  60. package/dist/ui/components/Layout.style.css +1 -0
  61. package/dist/ui/components/ListingFilterDrawer.js +3 -1
  62. package/dist/ui/components/Modal.js +3 -1
  63. package/dist/ui/components/SideDrawer.js +8 -5
  64. package/dist/ui/components/SideModal.js +7 -4
  65. package/dist/ui/index.d.ts +1 -0
  66. package/dist/ui/index.js +1 -0
  67. package/package.json +1 -1
@@ -5,6 +5,7 @@ import { createPortal } from "react-dom";
5
5
  import { X } from "lucide-react";
6
6
  import { Heading } from "./Typography";
7
7
  import { Button } from "./Button";
8
+ import { useHandMode } from "../../_internal/client/hand-mode";
8
9
  const SIDE_TRANSLATE = {
9
10
  left: {
10
11
  closed: "appkit-drawer__panel--left-closed",
@@ -28,7 +29,9 @@ const SIDE_SIZE = {
28
29
  lg: "appkit-drawer__panel--lg",
29
30
  full: "appkit-drawer__panel--full",
30
31
  };
31
- export function Drawer({ isOpen, onClose, title, children, footer, side = "right", size = "md", showCloseButton = true, className = "", }) {
32
+ export function Drawer({ isOpen, onClose, title, children, footer, side, size = "md", showCloseButton = true, className = "", }) {
33
+ const { hand } = useHandMode();
34
+ const resolvedSide = side ?? hand;
32
35
  const panelRef = useRef(null);
33
36
  const prevFocusRef = useRef(null);
34
37
  // Body scroll lock
@@ -55,8 +58,8 @@ export function Drawer({ isOpen, onClose, title, children, footer, side = "right
55
58
  if (e.key === "Escape")
56
59
  onClose();
57
60
  }, [onClose]);
58
- const { closed, open, base } = SIDE_TRANSLATE[side];
59
- const isBottom = side === "bottom";
61
+ const { closed, open, base } = SIDE_TRANSLATE[resolvedSide];
62
+ const isBottom = resolvedSide === "bottom";
60
63
  if (typeof document === "undefined")
61
64
  return null;
62
65
  return createPortal(_jsxs("div", { className: `appkit-drawer ${isOpen ? "appkit-drawer--open" : "appkit-drawer--closed"}`, onKeyDown: handleKeyDown, "data-section": "drawer-div-480", children: [_jsx("div", { className: `appkit-drawer__backdrop ${isOpen ? "appkit-drawer__backdrop--open" : "appkit-drawer__backdrop--closed"}`, "aria-hidden": "true", onClick: onClose }), _jsxs("div", { ref: panelRef, tabIndex: -1, role: "dialog", "aria-modal": "true", className: [
@@ -67,5 +70,5 @@ export function Drawer({ isOpen, onClose, title, children, footer, side = "right
67
70
  : `${SIDE_SIZE[size]} appkit-drawer__panel--full-height`,
68
71
  isOpen ? open : closed,
69
72
  className,
70
- ].join(" "), "data-section": "drawer-div-481", children: [isBottom && (_jsx("div", { className: "appkit-drawer__drag-wrap", "data-section": "drawer-div-482", children: _jsx("div", { className: "appkit-drawer__drag", "aria-hidden": "true" }) })), (title || showCloseButton) && (_jsxs("div", { className: "appkit-drawer__header", "data-section": "drawer-div-483", children: [title && (_jsx(Heading, { level: 2, className: "appkit-drawer__title", children: title })), showCloseButton && (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: "appkit-drawer__close", "aria-label": "Close", children: _jsx(X, { className: "appkit-drawer__close-icon" }) }))] })), _jsx("div", { className: "appkit-drawer__body", "data-section": "drawer-div-484", children: children }), footer && _jsx("div", { className: "appkit-drawer__footer", "data-section": "drawer-div-485", children: footer })] })] }), document.body);
73
+ ].join(" "), "data-section": "drawer-div-481", children: [isBottom && (_jsx("div", { className: "appkit-drawer__drag-wrap", "data-section": "drawer-div-482", children: _jsx("div", { className: "appkit-drawer__drag", "aria-hidden": "true" }) })), (title || showCloseButton) && (_jsxs("div", { className: hand === "left" ? "appkit-drawer__header flex-row-reverse" : "appkit-drawer__header", "data-section": "drawer-div-483", children: [title && (_jsx(Heading, { level: 2, className: "appkit-drawer__title", children: title })), showCloseButton && (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: "appkit-drawer__close", "aria-label": "Close", children: _jsx(X, { className: "appkit-drawer__close-icon" }) }))] })), _jsx("div", { className: "appkit-drawer__body", "data-section": "drawer-div-484", children: children }), footer && _jsx("div", { className: "appkit-drawer__footer", "data-section": "drawer-div-485", children: footer })] })] }), document.body);
71
74
  }
@@ -5,7 +5,7 @@ import { Button } from "./Button";
5
5
  import { Drawer } from "./Drawer";
6
6
  import { Span } from "./Typography";
7
7
  import { sectionBackgroundStyle } from "./Semantic";
8
- export function FilterDrawer({ children, title = "Filters", open, onOpen, onClose, onApply, onReset, activeCount = 0, pendingCount, triggerLabel = "Filters", triggerClassName = "", hideTrigger = false, side = "right", size = "md", background, }) {
8
+ export function FilterDrawer({ children, title = "Filters", open, onOpen, onClose, onApply, onReset, activeCount = 0, pendingCount, triggerLabel = "Filters", triggerClassName = "", hideTrigger = false, side, size = "md", background, }) {
9
9
  const [internalOpen, setInternalOpen] = React.useState(false);
10
10
  const isControlled = open !== undefined;
11
11
  const isOpen = isControlled ? open : internalOpen;
@@ -0,0 +1,12 @@
1
+ import { type RowProps } from "./Layout";
2
+ /**
3
+ * HandModeRow — a `<Row>` that mirrors (flex-row-reverse) in left-hand mode.
4
+ *
5
+ * `<Row>` itself stays a plain server-safe primitive (no hooks) so it can
6
+ * keep rendering inside async Server Components. This thin client wrapper
7
+ * exists specifically for header rows (title + "View All" CTA) that live
8
+ * inside Server Component sections and still need to flip sides with the
9
+ * hand-mode preference — render it as a child of the server component
10
+ * instead of calling `useHandMode()` in the server component itself.
11
+ */
12
+ export declare function HandModeRow(props: RowProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Row } from "./Layout";
4
+ import { useHandMode } from "../../_internal/client/hand-mode";
5
+ /**
6
+ * HandModeRow — a `<Row>` that mirrors (flex-row-reverse) in left-hand mode.
7
+ *
8
+ * `<Row>` itself stays a plain server-safe primitive (no hooks) so it can
9
+ * keep rendering inside async Server Components. This thin client wrapper
10
+ * exists specifically for header rows (title + "View All" CTA) that live
11
+ * inside Server Component sections and still need to flip sides with the
12
+ * hand-mode preference — render it as a child of the server component
13
+ * instead of calling `useHandMode()` in the server component itself.
14
+ */
15
+ export function HandModeRow(props) {
16
+ const { hand } = useHandMode();
17
+ return _jsx(Row, { reverse: hand === "left", ...props });
18
+ }
@@ -279,6 +279,10 @@ export interface RowProps extends React.HTMLAttributes<HTMLElement>, SurfaceProp
279
279
  color?: TextColorKey;
280
280
  /** Allow children to wrap onto multiple lines. */
281
281
  wrap?: boolean;
282
+ /** Reverse the visual order of children (`flex-direction: row-reverse`) without
283
+ * touching DOM/source order. Used for hand-mode-aware layouts (e.g. moving a
284
+ * close button to the opposite header edge) — replaces raw `flex-row-reverse`. */
285
+ reverse?: boolean;
282
286
  /** Zebra-stripe pattern when this Row is a repeating list element (rendered with `even:bg-*`). */
283
287
  oddEven?: "none" | "zebra";
284
288
  /**
@@ -315,7 +319,7 @@ declare const TEXT_COLOR_MAP: {
315
319
  readonly info: "appkit-color--info";
316
320
  };
317
321
  type TextColorKey = keyof typeof TEXT_COLOR_MAP;
318
- export declare function Row({ gap, centered, align, justify, textSize, textWeight, color, wrap, oddEven, divide, as, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className, children, ...props }: RowProps): React.JSX.Element;
322
+ export declare function Row({ gap, centered, align, justify, textSize, textWeight, color, wrap, reverse, oddEven, divide, as, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className, children, ...props }: RowProps): React.JSX.Element;
319
323
  /**
320
324
  * Responsive CSS grid. Use instead of `<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4" data-section="layout-div-537">`.
321
325
  *
@@ -231,10 +231,11 @@ const ROW_ODD_EVEN_MAP = {
231
231
  none: "",
232
232
  zebra: "even:bg-[var(--appkit-color-surface-input)] dark:even:bg-[var(--appkit-color-bg)]",
233
233
  };
234
- export function Row({ gap = "md", centered = false, align = "center", justify = "start", textSize, textWeight, color, wrap = false, oddEven, divide, as, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className = "", children, ...props }) {
234
+ export function Row({ gap = "md", centered = false, align = "center", justify = "start", textSize, textWeight, color, wrap = false, reverse = false, oddEven, divide, as, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className = "", children, ...props }) {
235
235
  const Tag = (as ?? "div");
236
236
  const classes = [
237
237
  "appkit-row",
238
+ reverse ? "appkit-row--reverse" : "",
238
239
  centered ? "appkit-row--centered" : align !== "center" ? ITEMS_MAP[align] : "",
239
240
  !centered && justify !== "start" ? JUSTIFY_MAP[justify] : "",
240
241
  GAP_MAP[gap],
@@ -58,6 +58,7 @@
58
58
  }
59
59
 
60
60
  .appkit-row--wrap { flex-wrap: wrap; }
61
+ .appkit-row--reverse { flex-direction: row-reverse; }
61
62
  .appkit-stack--centered,
62
63
  .appkit-row--centered {
63
64
  align-items: center;
@@ -4,9 +4,11 @@ import { X } from "lucide-react";
4
4
  import { Button } from "./Button";
5
5
  import { Span } from "./Typography";
6
6
  import { sectionBackgroundStyle } from "./Semantic";
7
+ import { useHandMode } from "../../_internal/client/hand-mode";
7
8
  const CLS_CLEAR_BTN = "text-xs text-[var(--appkit-color-text-muted)] hover:text-error text-[var(--appkit-color-text-muted)] transition-colors min-h-0 h-auto p-0";
8
9
  export function ListingFilterDrawer({ open, onClose, onApply, onClear, activeCount, children, background, }) {
10
+ const { hand } = useHandMode();
9
11
  if (!open)
10
12
  return null;
11
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: onClose }), _jsxs("div", { className: `fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-[var(--appkit-color-surface)] shadow-2xl${background?.value ? " relative overflow-hidden" : ""}`, children: [background?.value && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, className: "absolute inset-0 -z-10", style: sectionBackgroundStyle(background) }), background.overlay?.enabled && (_jsx(Span, { "aria-hidden": true, className: "absolute inset-0 -z-10", style: { backgroundColor: background.overlay.color, opacity: background.overlay.opacity } }))] })), _jsxs("div", { className: "flex items-center justify-between border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx(Span, { size: "base", weight: "semibold", className: "text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeCount > 0 && (_jsx(Button, { variant: "ghost", type: "button", onClick: onClear, className: CLS_CLEAR_BTN, children: "Clear all" })), _jsx(Button, { variant: "ghost", type: "button", onClick: onClose, "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] transition-colors min-h-0 h-auto", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: children }), _jsx("div", { className: "border-t border-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs(Button, { type: "button", onClick: onApply, className: "w-full", children: ["Apply Filters", activeCount > 0 ? ` (${activeCount})` : ""] }) })] })] }));
13
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: onClose }), _jsxs("div", { className: `fixed inset-y-0 ${hand === "left" ? "left-0" : "right-0"} z-50 flex w-80 flex-col bg-[var(--appkit-color-surface)] shadow-2xl${background?.value ? " relative overflow-hidden" : ""}`, children: [background?.value && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, className: "absolute inset-0 -z-10", style: sectionBackgroundStyle(background) }), background.overlay?.enabled && (_jsx(Span, { "aria-hidden": true, className: "absolute inset-0 -z-10", style: { backgroundColor: background.overlay.color, opacity: background.overlay.opacity } }))] })), _jsxs("div", { className: `flex items-center justify-between border-b border-[var(--appkit-color-border)] px-4 py-3.5${hand === "left" ? " flex-row-reverse" : ""}`, children: [_jsx(Span, { size: "base", weight: "semibold", className: "text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeCount > 0 && (_jsx(Button, { variant: "ghost", type: "button", onClick: onClear, className: CLS_CLEAR_BTN, children: "Clear all" })), _jsx(Button, { variant: "ghost", type: "button", onClick: onClose, "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] transition-colors min-h-0 h-auto", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: children }), _jsx("div", { className: "border-t border-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs(Button, { type: "button", onClick: onApply, className: "w-full", children: ["Apply Filters", activeCount > 0 ? ` (${activeCount})` : ""] }) })] })] }));
12
14
  }
@@ -6,6 +6,7 @@ import { motion, AnimatePresence, useReducedMotion } from "motion/react";
6
6
  import { Heading } from "./Typography";
7
7
  import { Button } from "./Button";
8
8
  import { SPRING_GENTLE } from "../../tokens/motion";
9
+ import { useHandMode } from "../../_internal/client/hand-mode";
9
10
  const SIZE_CLASSES = {
10
11
  sm: "appkit-modal__panel--sm",
11
12
  md: "appkit-modal__panel--md",
@@ -15,6 +16,7 @@ const SIZE_CLASSES = {
15
16
  };
16
17
  export function Modal({ isOpen, open, onClose, title, children, size = "md", showCloseButton = true, actions, className = "", }) {
17
18
  const visible = open ?? isOpen ?? false;
19
+ const { hand } = useHandMode();
18
20
  const titleId = React.useId();
19
21
  const panelRef = React.useRef(null);
20
22
  const previousFocusRef = React.useRef(null);
@@ -69,7 +71,7 @@ export function Modal({ isOpen, open, onClose, title, children, size = "md", sho
69
71
  }
70
72
  return createPortal(_jsx(AnimatePresence, { children: visible && (_jsxs(motion.div, { className: "appkit-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined, onKeyDown: handleKeyDown, initial: reduced ? false : { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, "data-section": "modal-div-561", children: [_jsx("div", { className: "appkit-modal__backdrop", "aria-hidden": "true", onClick: onClose }), _jsxs(motion.div, { ref: panelRef, tabIndex: -1, className: ["appkit-modal__panel", SIZE_CLASSES[size], className]
71
73
  .filter(Boolean)
72
- .join(" "), initial: reduced ? false : { opacity: 0, scale: 0.95, y: 8 }, animate: { opacity: 1, scale: 1, y: 0 }, exit: { opacity: 0, scale: 0.95, y: 8 }, transition: SPRING_GENTLE, "data-section": "modal-div-562", children: [(title || showCloseButton) && (_jsxs("div", { className: "appkit-modal__header", "data-section": "modal-div-563", children: [title ? (_jsx(Heading, { level: 2, id: titleId, className: "appkit-modal__title", children: title })) : (_jsx("span", {})), showCloseButton ? (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: "appkit-modal__close", "aria-label": "Close", children: _jsx("span", { className: "appkit-modal__close-icon", "aria-hidden": "true", children: "\u00D7" }) })) : null] })), _jsx("div", { className: "appkit-modal__body", "data-section": "modal-div-564", children: children }), actions ? _jsx(ModalFooter, { children: actions }) : null] })] })) }), document.body);
74
+ .join(" "), initial: reduced ? false : { opacity: 0, scale: 0.95, y: 8 }, animate: { opacity: 1, scale: 1, y: 0 }, exit: { opacity: 0, scale: 0.95, y: 8 }, transition: SPRING_GENTLE, "data-section": "modal-div-562", children: [(title || showCloseButton) && (_jsxs("div", { className: hand === "left" ? "appkit-modal__header flex-row-reverse" : "appkit-modal__header", "data-section": "modal-div-563", children: [title ? (_jsx(Heading, { level: 2, id: titleId, className: "appkit-modal__title", children: title })) : (_jsx("span", {})), showCloseButton ? (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: "appkit-modal__close", "aria-label": "Close", children: _jsx("span", { className: "appkit-modal__close-icon", "aria-hidden": "true", children: "\u00D7" }) })) : null] })), _jsx("div", { className: "appkit-modal__body", "data-section": "modal-div-564", children: children }), actions ? _jsx(ModalFooter, { children: actions }) : null] })] })) }), document.body);
73
75
  }
74
76
  export function ModalFooter({ children, className = "", }) {
75
77
  return (_jsx("div", { className: [
@@ -9,6 +9,7 @@ import { Heading, Span } from "./Typography";
9
9
  import { useSwipe } from "../../react";
10
10
  import { Text } from "./Typography";
11
11
  import { SPRING_SNAPPY } from "../../tokens/motion";
12
+ import { useHandMode } from "../../_internal/client/hand-mode";
12
13
  const UI_SIDE_DRAWER = {
13
14
  backdrop: "appkit-side-drawer__backdrop",
14
15
  root: "appkit-side-drawer",
@@ -65,7 +66,9 @@ function handleFocusTrap(e, first, last) {
65
66
  * </SideDrawer>
66
67
  * ```
67
68
  */
68
- export function SideDrawer({ isOpen, onClose, title, children, footer, mode = "view", isDirty = false, side = "right", }) {
69
+ export function SideDrawer({ isOpen, onClose, title, children, footer, mode = "view", isDirty = false, side, }) {
70
+ const { hand } = useHandMode();
71
+ const resolvedSide = side ?? hand;
69
72
  const drawerRef = useRef(null);
70
73
  const [showUnsavedWarning, setShowUnsavedWarning] = useState(false);
71
74
  const tActions = useTranslations("actions");
@@ -141,16 +144,16 @@ export function SideDrawer({ isOpen, onClose, title, children, footer, mode = "v
141
144
  };
142
145
  }, [isOpen]);
143
146
  useSwipe(drawerRef, {
144
- ...(side === "left"
147
+ ...(resolvedSide === "left"
145
148
  ? { onSwipeLeft: () => attemptClose() }
146
149
  : { onSwipeRight: () => attemptClose() }),
147
150
  minSwipeDistance: 60,
148
151
  });
149
152
  const reduced = useReducedMotion();
150
- const positionClass = side === "left" ? UI_SIDE_DRAWER.left : UI_SIDE_DRAWER.right;
151
- const slideX = side === "left" ? "-100%" : "100%";
153
+ const positionClass = resolvedSide === "left" ? UI_SIDE_DRAWER.left : UI_SIDE_DRAWER.right;
154
+ const slideX = resolvedSide === "left" ? "-100%" : "100%";
152
155
  return (_jsx(AnimatePresence, { children: isOpen && (_jsxs(_Fragment, { children: [_jsx(motion.div, { className: UI_SIDE_DRAWER.backdrop, onClick: attemptClose, "aria-hidden": "true", initial: reduced ? false : { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 } }), _jsxs(motion.div, { ref: drawerRef, className: [UI_SIDE_DRAWER.root, positionClass].join(" "), role: "dialog", "aria-modal": "true", "aria-labelledby": "drawer-title", initial: reduced ? false : { x: slideX }, animate: { x: 0 }, exit: { x: slideX }, transition: SPRING_SNAPPY, "data-section": "sidedrawer-div-598", children: [_jsxs(Row, { justify: "between", gap: "none", className: [
153
156
  UI_SIDE_DRAWER.header,
154
157
  UI_SIDE_DRAWER.headerMode[mode],
155
- ].join(" "), children: [_jsxs(Row, { gap: "3", className: "min-w-0", children: [_jsx(Button, { variant: "ghost", onClick: attemptClose, className: UI_SIDE_DRAWER.closeBtn, "aria-label": tActions("close"), children: _jsx("svg", { className: UI_SIDE_DRAWER.closeIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }), _jsx(Heading, { level: 3, id: "drawer-title", className: "truncate", children: title })] }), mode === "delete" && (_jsx(Span, { className: UI_SIDE_DRAWER.deleteBadge, children: tActions("delete") }))] }), _jsx("div", { className: UI_SIDE_DRAWER.content, "data-section": "sidedrawer-div-599", children: children }), footer && _jsx("div", { className: UI_SIDE_DRAWER.footer, "data-section": "sidedrawer-div-600", children: footer })] }), showUnsavedWarning && (_jsxs(_Fragment, { children: [_jsx("div", { className: UI_SIDE_DRAWER.warnBackdrop, onClick: cancelClose }), _jsxs("div", { className: UI_SIDE_DRAWER.warnDialog, "data-section": "sidedrawer-div-601", children: [_jsxs("div", { className: UI_SIDE_DRAWER.warnHeader, "data-section": "sidedrawer-div-602", children: [_jsx("div", { className: UI_SIDE_DRAWER.warnIcon, "data-section": "sidedrawer-div-603", children: _jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }) }), _jsxs("div", { "data-section": "sidedrawer-div-604", children: [_jsx(Heading, { level: 4, className: "mb-1", children: tConfirm("unsavedChangesTitle") }), _jsx(Text, { className: UI_SIDE_DRAWER.warnText, children: tConfirm("unsavedChangesDescription") })] })] }), _jsxs("div", { className: UI_SIDE_DRAWER.warnActions, "data-section": "sidedrawer-div-605", children: [_jsx(Button, { variant: "outline", onClick: cancelClose, size: "sm", children: tActions("keepEditing") }), _jsx(Button, { variant: "danger", onClick: confirmClose, size: "sm", children: tActions("discardChanges") })] })] })] }))] })) }));
158
+ ].join(" "), children: [_jsxs(Row, { gap: "3", className: "min-w-0", reverse: hand === "left", children: [_jsx(Button, { variant: "ghost", onClick: attemptClose, className: UI_SIDE_DRAWER.closeBtn, "aria-label": tActions("close"), children: _jsx("svg", { className: UI_SIDE_DRAWER.closeIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }), _jsx(Heading, { level: 3, id: "drawer-title", className: "truncate", children: title })] }), mode === "delete" && (_jsx(Span, { className: UI_SIDE_DRAWER.deleteBadge, children: tActions("delete") }))] }), _jsx("div", { className: UI_SIDE_DRAWER.content, "data-section": "sidedrawer-div-599", children: children }), footer && _jsx("div", { className: UI_SIDE_DRAWER.footer, "data-section": "sidedrawer-div-600", children: footer })] }), showUnsavedWarning && (_jsxs(_Fragment, { children: [_jsx("div", { className: UI_SIDE_DRAWER.warnBackdrop, onClick: cancelClose }), _jsxs("div", { className: UI_SIDE_DRAWER.warnDialog, "data-section": "sidedrawer-div-601", children: [_jsxs("div", { className: UI_SIDE_DRAWER.warnHeader, "data-section": "sidedrawer-div-602", children: [_jsx("div", { className: UI_SIDE_DRAWER.warnIcon, "data-section": "sidedrawer-div-603", children: _jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }) }), _jsxs("div", { "data-section": "sidedrawer-div-604", children: [_jsx(Heading, { level: 4, className: "mb-1", children: tConfirm("unsavedChangesTitle") }), _jsx(Text, { className: UI_SIDE_DRAWER.warnText, children: tConfirm("unsavedChangesDescription") })] })] }), _jsxs("div", { className: UI_SIDE_DRAWER.warnActions, "data-section": "sidedrawer-div-605", children: [_jsx(Button, { variant: "outline", onClick: cancelClose, size: "sm", children: tActions("keepEditing") }), _jsx(Button, { variant: "danger", onClick: confirmClose, size: "sm", children: tActions("discardChanges") })] })] })] }))] })) }));
156
159
  }
@@ -4,6 +4,7 @@ import { useEffect, useRef } from "react";
4
4
  import { motion, AnimatePresence, useReducedMotion } from "motion/react";
5
5
  import { Button, Div, Heading } from "..";
6
6
  import { SPRING_SNAPPY } from "../../tokens/motion";
7
+ import { useHandMode } from "../../_internal/client/hand-mode";
7
8
  /**
8
9
  * SideModal — slides in from the right (or left) on desktop (60 vw),
9
10
  * full-screen drawer on mobile (< sm breakpoint).
@@ -14,7 +15,9 @@ import { SPRING_SNAPPY } from "../../tokens/motion";
14
15
  * - backdrop click closes panel
15
16
  * - role="dialog" + aria-modal + aria-labelledby
16
17
  */
17
- export function SideModal({ isOpen, onClose, title, side = "right", children, className = "", }) {
18
+ export function SideModal({ isOpen, onClose, title, side, children, className = "", }) {
19
+ const { hand } = useHandMode();
20
+ const resolvedSide = side ?? hand;
18
21
  const panelRef = useRef(null);
19
22
  const titleId = useRef(`side-modal-title-${Math.random().toString(36).slice(2, 8)}`);
20
23
  // Escape key handler
@@ -53,12 +56,12 @@ export function SideModal({ isOpen, onClose, title, side = "right", children, cl
53
56
  };
54
57
  }, [isOpen]);
55
58
  const reduced = useReducedMotion();
56
- const slideX = side === "left" ? "-100%" : "100%";
59
+ const slideX = resolvedSide === "left" ? "-100%" : "100%";
57
60
  return (_jsx(AnimatePresence, { children: isOpen && (_jsxs(Div, { className: "appkit-side-modal", "aria-hidden": !isOpen, children: [_jsx(motion.div, { className: "appkit-side-modal__backdrop", onClick: onClose, "aria-hidden": "true", initial: reduced ? false : { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 } }), _jsxs(motion.div, { ref: panelRef, role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId.current : undefined, className: [
58
61
  "appkit-side-modal__panel",
59
- `appkit-side-modal__panel--${side}`,
62
+ `appkit-side-modal__panel--${resolvedSide}`,
60
63
  className,
61
64
  ]
62
65
  .filter(Boolean)
63
- .join(" "), initial: reduced ? false : { x: slideX }, animate: { x: 0 }, exit: { x: slideX }, transition: SPRING_SNAPPY, children: [title && (_jsxs(Div, { className: "appkit-side-modal__header", children: [_jsx(Heading, { level: 2, id: titleId.current, className: "text-base font-semibold text-[var(--appkit-color-text)] dark:text-white truncate", children: title }), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, "aria-label": "Close panel", className: "ml-2 flex-shrink-0", children: "\u2715" })] })), _jsx(Div, { className: "appkit-side-modal__body", children: children })] })] })) }));
66
+ .join(" "), initial: reduced ? false : { x: slideX }, animate: { x: 0 }, exit: { x: slideX }, transition: SPRING_SNAPPY, children: [title && (_jsxs(Div, { className: hand === "left" ? "appkit-side-modal__header flex-row-reverse" : "appkit-side-modal__header", children: [_jsx(Heading, { level: 2, id: titleId.current, className: "text-base font-semibold text-[var(--appkit-color-text)] dark:text-white truncate", children: title }), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, "aria-label": "Close panel", className: hand === "left" ? "mr-2 flex-shrink-0" : "ml-2 flex-shrink-0", children: "\u2715" })] })), _jsx(Div, { className: "appkit-side-modal__body", children: children })] })] })) }));
64
67
  }
@@ -175,6 +175,7 @@ export type { SurfaceKey, PaddingKey, RoundedKey, BorderKey, ShadowKey, SurfaceP
175
175
  export { SURFACE_MAP, PADDING_MAP, ROUNDED_MAP, BORDER_MAP, SHADOW_MAP, buildSurfaceClasses, } from "./components/surface-tokens";
176
176
  export type { GapKey, ContainerSize, GridCols, ViewPortal, ContainerProps, StackProps, RowProps, GridProps, } from "./components/Layout";
177
177
  export { Container, Stack, Row, Grid, GRID_MAP } from "./components/Layout";
178
+ export { HandModeRow } from "./components/HandModeRow";
178
179
  export { classNames, mergeTailwindClasses } from "./style.helper";
179
180
  export type { SideModalProps } from "./components/SideModal";
180
181
  export { SideModal } from "./components/SideModal";
package/dist/ui/index.js CHANGED
@@ -90,6 +90,7 @@ export { DataTable } from "./DataTable";
90
90
  export { DEFAULT_TABLE_CONFIG, DEFAULT_PAGINATION_CONFIG, DEFAULT_STICKY_CONFIG, mergeTableConfig, } from "../contracts";
91
91
  export { SURFACE_MAP, PADDING_MAP, ROUNDED_MAP, BORDER_MAP, SHADOW_MAP, buildSurfaceClasses, } from "./components/surface-tokens";
92
92
  export { Container, Stack, Row, Grid, GRID_MAP } from "./components/Layout";
93
+ export { HandModeRow } from "./components/HandModeRow";
93
94
  // --- UI Helpers ---------------------------------------------------------------
94
95
  export { classNames, mergeTailwindClasses } from "./style.helper";
95
96
  export { SideModal } from "./components/SideModal";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.9.3",
3
+ "version": "4.9.5",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"