@mohasinac/appkit 4.9.4 → 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.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +7 -2
- package/dist/_internal/client/features/layout/SidebarCollapseToggle.d.ts +5 -1
- package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +8 -2
- package/dist/_internal/client/features/layout/sidebarPositionClasses.d.ts +20 -0
- package/dist/_internal/client/features/layout/sidebarPositionClasses.js +20 -0
- package/dist/_internal/client/hand-mode/HandModeProvider.d.ts +21 -0
- package/dist/_internal/client/hand-mode/HandModeProvider.js +81 -0
- package/dist/_internal/client/hand-mode/index.d.ts +2 -0
- package/dist/_internal/client/hand-mode/index.js +1 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +43 -0
- package/dist/_internal/server/features/checkout/actions.js +172 -81
- package/dist/_internal/server/features/checkout/index.d.ts +1 -1
- package/dist/_internal/server/features/checkout/index.js +1 -1
- package/dist/_internal/server/features/seo/robots.js +3 -2
- package/dist/_internal/server/features/seo/sitemap.js +70 -5
- package/dist/_internal/shared/config/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/config.js +1 -1
- package/dist/_internal/shared/listing-types/digital-code/config.js +1 -1
- package/dist/_internal/shared/listing-types/live/config.js +1 -1
- package/dist/client.d.ts +4 -0
- package/dist/client.js +9 -0
- package/dist/constants/api-endpoints.d.ts +3 -0
- package/dist/constants/api-endpoints.js +1 -0
- package/dist/features/about/components/AboutView.js +2 -2
- package/dist/features/about/components/DeveloperView.js +2 -2
- package/dist/features/about/schemas/firestore.d.ts +1 -0
- package/dist/features/account/components/UserSidebar.js +8 -2
- package/dist/features/account/hooks/useProfile.d.ts +2 -0
- package/dist/features/admin/components/AdminSidebar.js +8 -2
- package/dist/features/auth/schemas/firestore.d.ts +3 -0
- package/dist/features/cart/components/CartDrawer.js +4 -1
- package/dist/features/homepage/components/BrandsSection.js +3 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +12 -1
- package/dist/features/homepage/components/GoogleReviewsSection.js +2 -2
- package/dist/features/homepage/components/HeroCarousel.js +3 -1
- package/dist/features/homepage/components/SectionCarousel.js +5 -2
- package/dist/features/homepage/components/SocialFeedSection.js +2 -2
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +3 -1
- package/dist/features/layout/BackToTop.js +3 -1
- package/dist/features/layout/SidebarLayout.js +7 -1
- package/dist/features/seller/components/SellerSidebar.js +8 -2
- package/dist/features/shell/QuickFormDrawer.js +3 -1
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +31 -0
- package/dist/hand-mode/index.d.ts +12 -0
- package/dist/hand-mode/index.js +11 -0
- package/dist/react/contexts/SessionContext.d.ts +5 -0
- package/dist/react/contexts/SessionContext.js +2 -0
- package/dist/seed/site-settings-seed-data.js +3 -2
- package/dist/server-entry.d.ts +1 -1
- package/dist/server-entry.js +1 -1
- package/dist/styles.css +2 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Drawer.js +7 -4
- package/dist/ui/components/FilterDrawer.js +1 -1
- package/dist/ui/components/HandModeRow.d.ts +12 -0
- package/dist/ui/components/HandModeRow.js +18 -0
- package/dist/ui/components/Layout.d.ts +5 -1
- package/dist/ui/components/Layout.js +2 -1
- package/dist/ui/components/Layout.style.css +1 -0
- package/dist/ui/components/ListingFilterDrawer.js +3 -1
- package/dist/ui/components/Modal.js +3 -1
- package/dist/ui/components/SideDrawer.js +8 -5
- package/dist/ui/components/SideModal.js +7 -4
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +1 -0
- 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
|
|
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[
|
|
59
|
-
const isBottom =
|
|
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
|
|
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],
|
|
@@ -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:
|
|
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
|
|
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
|
-
...(
|
|
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 =
|
|
151
|
-
const slideX =
|
|
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
|
|
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 =
|
|
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--${
|
|
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
|
}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -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";
|