@mohasinac/appkit 3.5.2 → 3.5.4
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/features/homepage/components/CustomCardsSection.js +1 -1
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/seller/components/CategoryInlineSelect.d.ts +1 -1
- package/dist/features/seller/components/CategoryInlineSelect.js +1 -1
- package/dist/react/hooks/useBulkSelection.js +12 -3
- package/dist/react/hooks/useGesture.js +48 -4
- package/dist/react/hooks/useLongPress.d.ts +1 -0
- package/dist/react/hooks/useLongPress.js +5 -0
- package/dist/react/hooks/usePullToRefresh.js +12 -1
- package/dist/react/hooks/useRealtimeEvent.js +18 -1
- package/dist/react/hooks/useSwipe.js +14 -0
- package/dist/seed/addresses-seed-data.js +8 -99
- package/dist/seed/categories-seed-data.js +103 -2082
- package/dist/seed/homepage-sections-seed-data.js +9 -9
- package/dist/seed/products-art-seed-data.d.ts +1 -10
- package/dist/seed/products-art-seed-data.js +13 -122
- package/dist/seed/products-auctions-seed-data.js +39 -747
- package/dist/seed/products-classifieds-seed-data.d.ts +1 -9
- package/dist/seed/products-classifieds-seed-data.js +13 -195
- package/dist/seed/products-digital-codes-seed-data.d.ts +1 -9
- package/dist/seed/products-digital-codes-seed-data.js +13 -202
- package/dist/seed/products-live-items-seed-data.d.ts +1 -12
- package/dist/seed/products-live-items-seed-data.js +13 -188
- package/dist/seed/products-preorders-seed-data.js +18 -149
- package/dist/seed/products-prize-draws-seed-data.d.ts +1 -16
- package/dist/seed/products-prize-draws-seed-data.js +13 -385
- package/dist/seed/products-standard-seed-data.js +152 -1331
- package/dist/seed/products-stickers-seed-data.d.ts +1 -10
- package/dist/seed/products-stickers-seed-data.js +13 -122
- package/dist/seed/site-settings-seed-data.js +106 -108
- package/dist/seed/store-addresses-seed-data.js +14 -46
- package/dist/seed/stores-seed-data.js +13 -205
- package/dist/styles.css +19 -18
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/BaseListingCard.d.ts +2 -1
- package/dist/ui/components/BaseListingCard.js +4 -5
- package/dist/ui/components/BulkActionBar.style.css +4 -4
- package/dist/ui/components/Button.js +52 -22
- package/dist/ui/components/Button.style.css +14 -13
- package/dist/ui/components/DateInput.js +1 -0
- package/dist/ui/components/DynamicBgDiv.js +8 -6
- package/dist/ui/components/FormField.js +1 -1
- package/dist/ui/components/HorizontalScroller.js +35 -16
- package/dist/ui/components/Iframe.d.ts +8 -1
- package/dist/ui/components/Layout.d.ts +4 -4
- package/dist/ui/components/Layout.js +8 -8
- package/dist/ui/components/ListingToolbar.js +4 -4
- package/dist/ui/components/Motion.js +4 -1
- package/dist/ui/components/OtpInput.js +1 -1
- package/dist/ui/components/PaginatedSelect.js +29 -3
- package/dist/ui/components/Pagination.js +10 -5
- package/dist/ui/components/RichTextEditor.js +64 -3
- package/dist/ui/components/Select.js +1 -0
- package/dist/ui/components/Semantic.d.ts +10 -10
- package/dist/ui/components/Semantic.js +25 -25
- package/dist/ui/components/SideDrawer.js +13 -11
- package/dist/ui/components/SideModal.js +9 -7
- package/dist/ui/components/SlottedListingView.d.ts +13 -2
- package/dist/ui/components/StickyToolbar.js +9 -5
- package/dist/ui/components/TagInput.js +15 -2
- package/dist/ui/components/Textarea.js +1 -0
- package/dist/ui/components/Toggle.js +8 -1
- package/dist/ui/components/UnsavedChangesModal.js +12 -1
- package/dist/ui/forms/ColorPickerField.js +7 -0
- package/dist/ui/forms/FieldCheckbox.js +1 -1
- package/dist/utils/id-generators.d.ts +7 -4
- package/dist/utils/id-generators.js +53 -31
- package/dist/utils/number.formatter.js +19 -6
- package/dist/utils/string.formatter.js +4 -1
- package/package.json +1 -1
|
@@ -6,12 +6,17 @@ function getPageNumbers(currentPage, totalPages, maxVisible) {
|
|
|
6
6
|
return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
7
7
|
}
|
|
8
8
|
const pages = [];
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
// Split unevenly (floor/ceil) around (maxVisible - 1) rather than using a
|
|
10
|
+
// single halved value on both sides — `Math.floor(maxVisible / 2)` on
|
|
11
|
+
// both sides produces a window of `2*halfVisible + 1`, which is one page
|
|
12
|
+
// too many whenever maxVisible is even.
|
|
13
|
+
const before = Math.floor((maxVisible - 1) / 2);
|
|
14
|
+
const after = Math.ceil((maxVisible - 1) / 2);
|
|
15
|
+
let startPage = Math.max(1, currentPage - before);
|
|
16
|
+
let endPage = Math.min(totalPages, currentPage + after);
|
|
17
|
+
if (currentPage <= before)
|
|
13
18
|
endPage = maxVisible;
|
|
14
|
-
if (currentPage >= totalPages -
|
|
19
|
+
if (currentPage >= totalPages - after)
|
|
15
20
|
startPage = totalPages - maxVisible + 1;
|
|
16
21
|
if (startPage > 1) {
|
|
17
22
|
pages.push(1);
|
|
@@ -1,12 +1,69 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useMemo, useRef } from "react";
|
|
4
|
+
// Matches exactly what this editor's own toolbar (bold/italic/underline/
|
|
5
|
+
// lists/link/clear-formatting) and typing can legitimately produce.
|
|
6
|
+
// Anything else — <script>, <img onerror=...>, <iframe>, <svg>, <style>,
|
|
7
|
+
// <form>, event-handler attributes — can only have arrived via paste, and is
|
|
8
|
+
// dropped entirely rather than risk it either executing live in the editor
|
|
9
|
+
// or being persisted and re-rendered elsewhere as stored XSS.
|
|
10
|
+
const ALLOWED_TAGS = new Set([
|
|
11
|
+
"B", "STRONG", "I", "EM", "U", "S", "STRIKE",
|
|
12
|
+
"UL", "OL", "LI", "A", "BR", "DIV", "SPAN", "P",
|
|
13
|
+
]);
|
|
14
|
+
function isSafeHref(href) {
|
|
15
|
+
const trimmed = href.trim();
|
|
16
|
+
// Same rule as string.formatter.ts's applyMark: allow http(s)/mailto, a
|
|
17
|
+
// single leading "/" (same-site relative path, NOT "//host" protocol-
|
|
18
|
+
// relative), or "#" — reject everything else including javascript:.
|
|
19
|
+
return /^(https?:\/\/|mailto:|\/(?!\/)|#)/i.test(trimmed);
|
|
20
|
+
}
|
|
21
|
+
function sanitizeRichTextHtml(html) {
|
|
22
|
+
if (typeof document === "undefined" || !html)
|
|
23
|
+
return "";
|
|
24
|
+
const template = document.createElement("template");
|
|
25
|
+
template.innerHTML = html;
|
|
26
|
+
const walk = (root) => {
|
|
27
|
+
Array.from(root.childNodes).forEach((child) => {
|
|
28
|
+
if (child.nodeType === Node.ELEMENT_NODE) {
|
|
29
|
+
const el = child;
|
|
30
|
+
if (!ALLOWED_TAGS.has(el.tagName)) {
|
|
31
|
+
root.removeChild(el);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
Array.from(el.attributes).forEach((attr) => {
|
|
35
|
+
if (el.tagName === "A" && attr.name.toLowerCase() === "href") {
|
|
36
|
+
if (!isSafeHref(attr.value))
|
|
37
|
+
el.setAttribute("href", "#");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
el.removeAttribute(attr.name);
|
|
41
|
+
});
|
|
42
|
+
walk(el);
|
|
43
|
+
}
|
|
44
|
+
else if (child.nodeType !== Node.TEXT_NODE) {
|
|
45
|
+
root.removeChild(child);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
walk(template.content);
|
|
50
|
+
return template.innerHTML;
|
|
51
|
+
}
|
|
4
52
|
export function RichTextEditor({ value, onChange, disabled = false, className = "", minHeightClassName = "min-h-[180px]", placeholder = "Write formatted content...", }) {
|
|
5
53
|
const editorRef = useRef(null);
|
|
6
54
|
const emitChange = () => {
|
|
7
55
|
if (!editorRef.current)
|
|
8
56
|
return;
|
|
9
|
-
|
|
57
|
+
const raw = editorRef.current.innerHTML;
|
|
58
|
+
const sanitized = sanitizeRichTextHtml(raw);
|
|
59
|
+
// Only touch the live DOM (which would reset caret position) when
|
|
60
|
+
// sanitization actually changed something — a no-op for every normal
|
|
61
|
+
// keystroke/toolbar action, since those only ever produce allowlisted
|
|
62
|
+
// markup; only a malicious paste triggers this branch.
|
|
63
|
+
if (sanitized !== raw) {
|
|
64
|
+
editorRef.current.innerHTML = sanitized;
|
|
65
|
+
}
|
|
66
|
+
onChange(sanitized);
|
|
10
67
|
};
|
|
11
68
|
const exec = (command, commandValue) => {
|
|
12
69
|
if (disabled || typeof document === "undefined")
|
|
@@ -51,8 +108,12 @@ export function RichTextEditor({ value, onChange, disabled = false, className =
|
|
|
51
108
|
const editor = editorRef.current;
|
|
52
109
|
if (!editor)
|
|
53
110
|
return;
|
|
54
|
-
|
|
55
|
-
|
|
111
|
+
// Sanitize on every incoming `value` too — this is the stored-XSS path:
|
|
112
|
+
// previously-saved content (e.g. loaded from Firestore) must not be
|
|
113
|
+
// trusted just because it was already persisted.
|
|
114
|
+
const safeValue = sanitizeRichTextHtml(value);
|
|
115
|
+
if (editor.innerHTML !== safeValue) {
|
|
116
|
+
editor.innerHTML = safeValue;
|
|
56
117
|
}
|
|
57
118
|
}, [value]);
|
|
58
119
|
return (_jsxs("div", { className: `appkit-rich-text-editor rounded-lg border border-zinc-200 bg-[var(--appkit-color-surface)] border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${className}`, "data-section": "richtexteditor-div-582", children: [_jsx("div", { className: "appkit-rich-text-editor__toolbar flex flex-wrap gap-1 border-b border-zinc-200 p-2 border-[var(--appkit-color-border)]", "data-section": "richtexteditor-div-583", children: toolbarActions.map((action) => (_jsx("button", { type: "button", title: action.title, onClick: action.run, disabled: disabled, className: "appkit-rich-text-editor__btn rounded px-2 py-1 text-xs font-medium text-zinc-700 transition hover:bg-zinc-50 disabled:cursor-not-allowed disabled:opacity-50 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)]", children: action.label }, action.title))) }), _jsx("div", { ref: editorRef, role: "textbox", "aria-label": "Rich text editor", contentEditable: !disabled, suppressContentEditableWarning: true, "data-placeholder": placeholder, onInput: emitChange, onBlur: emitChange, className: `prose prose-sm max-w-none p-3 focus:outline-none dark:prose-invert ${minHeightClassName} ${disabled ? "cursor-not-allowed opacity-70" : ""}` })] }));
|
|
@@ -87,7 +87,7 @@ export interface ArticleProps extends React.HTMLAttributes<HTMLElement>, Surface
|
|
|
87
87
|
color?: SemanticColor;
|
|
88
88
|
children?: React.ReactNode;
|
|
89
89
|
}
|
|
90
|
-
export declare function Article({ className, surface, padding, rounded, border, shadow, color, children, ...props }: ArticleProps): React.JSX.Element;
|
|
90
|
+
export declare function Article({ className, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, color, children, ...props }: ArticleProps): React.JSX.Element;
|
|
91
91
|
/**
|
|
92
92
|
* Semantic `<main>` element.
|
|
93
93
|
* Wraps the primary content of the document. Should appear only once per page.
|
|
@@ -96,7 +96,7 @@ export interface MainProps extends React.HTMLAttributes<HTMLElement>, SurfacePro
|
|
|
96
96
|
color?: SemanticColor;
|
|
97
97
|
children: React.ReactNode;
|
|
98
98
|
}
|
|
99
|
-
export declare function Main({ className, surface, padding, paddingX, paddingY, rounded, border, shadow, color, children, ...props }: MainProps): React.JSX.Element;
|
|
99
|
+
export declare function Main({ className, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, color, children, ...props }: MainProps): React.JSX.Element;
|
|
100
100
|
/**
|
|
101
101
|
* Semantic `<aside>` element.
|
|
102
102
|
* Use for supplementary content tangentially related to the main content:
|
|
@@ -134,7 +134,7 @@ export interface NavProps extends React.HTMLAttributes<HTMLElement>, SurfaceProp
|
|
|
134
134
|
textSize?: "xs" | "sm" | "base" | "lg";
|
|
135
135
|
children: React.ReactNode;
|
|
136
136
|
}
|
|
137
|
-
export declare function Nav({ surface, padding, rounded, border, shadow, spacing, gap, layout, color, textSize, className, children, ...props }: NavProps): React.JSX.Element;
|
|
137
|
+
export declare function Nav({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, spacing, gap, layout, color, textSize, className, children, ...props }: NavProps): React.JSX.Element;
|
|
138
138
|
/**
|
|
139
139
|
* Semantic `<header>` element for block-level component headers.
|
|
140
140
|
* Use inside `Section`, `Article`, or card bodies — NOT as the page-level header.
|
|
@@ -151,7 +151,7 @@ export declare function Nav({ surface, padding, rounded, border, shadow, spacing
|
|
|
151
151
|
export interface BlockHeaderProps extends React.HTMLAttributes<HTMLElement>, SurfaceProps {
|
|
152
152
|
children: React.ReactNode;
|
|
153
153
|
}
|
|
154
|
-
export declare function BlockHeader({ className, surface, padding, rounded, border, shadow, children, ...props }: BlockHeaderProps): React.JSX.Element;
|
|
154
|
+
export declare function BlockHeader({ className, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }: BlockHeaderProps): React.JSX.Element;
|
|
155
155
|
/**
|
|
156
156
|
* Semantic `<footer>` element for block-level component footers.
|
|
157
157
|
* Use inside `Section`, `Article`, or card bodies — NOT as the page-level footer.
|
|
@@ -159,7 +159,7 @@ export declare function BlockHeader({ className, surface, padding, rounded, bord
|
|
|
159
159
|
export interface BlockFooterProps extends React.HTMLAttributes<HTMLElement>, SurfaceProps {
|
|
160
160
|
children: React.ReactNode;
|
|
161
161
|
}
|
|
162
|
-
export declare function BlockFooter({ className, surface, padding, rounded, border, shadow, children, ...props }: BlockFooterProps): React.JSX.Element;
|
|
162
|
+
export declare function BlockFooter({ className, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }: BlockFooterProps): React.JSX.Element;
|
|
163
163
|
/**
|
|
164
164
|
* Semantic `<ul>` (unordered list) element.
|
|
165
165
|
*
|
|
@@ -266,7 +266,7 @@ export interface TableProps extends Omit<React.TableHTMLAttributes<HTMLTableElem
|
|
|
266
266
|
stickyHeader?: boolean;
|
|
267
267
|
children: React.ReactNode;
|
|
268
268
|
}
|
|
269
|
-
export declare function Table({ variant, size, stickyHeader, className, surface, padding, rounded, border, shadow, children, ...props }: TableProps): React.JSX.Element;
|
|
269
|
+
export declare function Table({ variant, size, stickyHeader, className, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }: TableProps): React.JSX.Element;
|
|
270
270
|
type TheadSurface = "none" | "default" | "muted" | "subtle";
|
|
271
271
|
export interface TheadProps extends React.HTMLAttributes<HTMLTableSectionElement> {
|
|
272
272
|
/** Background tone for the header row. */
|
|
@@ -347,16 +347,16 @@ export declare function Code({ color, weight, size, padding, rounded, surface, c
|
|
|
347
347
|
export interface PreProps extends React.HTMLAttributes<HTMLPreElement>, SurfaceProps {
|
|
348
348
|
children: React.ReactNode;
|
|
349
349
|
}
|
|
350
|
-
export declare function Pre({ className, surface, padding, rounded, border, shadow, children, ...props }: PreProps): React.JSX.Element;
|
|
350
|
+
export declare function Pre({ className, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }: PreProps): React.JSX.Element;
|
|
351
351
|
export interface BlockquoteProps extends React.BlockquoteHTMLAttributes<HTMLQuoteElement>, SurfaceProps {
|
|
352
352
|
color?: "default" | "primary" | "info" | "warning";
|
|
353
353
|
children: React.ReactNode;
|
|
354
354
|
}
|
|
355
|
-
export declare function Blockquote({ color, className, surface, padding, paddingX, paddingY, rounded, border, shadow, children, ...props }: BlockquoteProps): React.JSX.Element;
|
|
355
|
+
export declare function Blockquote({ color, className, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }: BlockquoteProps): React.JSX.Element;
|
|
356
356
|
export interface FigureProps extends React.HTMLAttributes<HTMLElement>, SurfaceProps {
|
|
357
357
|
children: React.ReactNode;
|
|
358
358
|
}
|
|
359
|
-
export declare function Figure({ className, surface, padding, rounded, border, shadow, children, ...props }: FigureProps): React.JSX.Element;
|
|
359
|
+
export declare function Figure({ className, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }: FigureProps): React.JSX.Element;
|
|
360
360
|
export interface FigcaptionProps extends React.HTMLAttributes<HTMLElement> {
|
|
361
361
|
children: React.ReactNode;
|
|
362
362
|
}
|
|
@@ -372,7 +372,7 @@ export interface DlProps extends React.HTMLAttributes<HTMLDListElement>, Surface
|
|
|
372
372
|
divide?: boolean | "default" | "subtle";
|
|
373
373
|
children: React.ReactNode;
|
|
374
374
|
}
|
|
375
|
-
export declare function Dl({ variant, divide, className, surface, padding, rounded, border, shadow, children, ...props }: DlProps): React.JSX.Element;
|
|
375
|
+
export declare function Dl({ variant, divide, className, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }: DlProps): React.JSX.Element;
|
|
376
376
|
type DtDdColor = "default" | "primary" | "muted" | "faint";
|
|
377
377
|
type DtDdWeight = "normal" | "medium" | "semibold" | "bold";
|
|
378
378
|
export interface DtProps extends React.HTMLAttributes<HTMLElement> {
|
|
@@ -59,9 +59,9 @@ export function sectionBackgroundStyle(bg) {
|
|
|
59
59
|
return {};
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
export const Section = React.forwardRef(({ tone = "plain", background, color, layout, gap, align, justify, className = "", surface, padding, paddingX, paddingY, rounded, border, shadow, children, ...props }, ref) => (_jsxs("section", { className: [
|
|
62
|
+
export const Section = React.forwardRef(({ tone = "plain", background, color, layout, gap, align, justify, className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }, ref) => (_jsxs("section", { className: [
|
|
63
63
|
SECTION_TONE_MAP[tone],
|
|
64
|
-
buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, border, shadow }),
|
|
64
|
+
buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }),
|
|
65
65
|
color ? SEMANTIC_COLOR_MAP[color] : "",
|
|
66
66
|
layout ? SECTION_LAYOUT_MAP[layout] : "",
|
|
67
67
|
gap ? SECTION_GAP_MAP[gap] : "",
|
|
@@ -73,13 +73,13 @@ export const Section = React.forwardRef(({ tone = "plain", background, color, la
|
|
|
73
73
|
.filter(Boolean)
|
|
74
74
|
.join(" "), ref: ref, ...props, 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 } }))] })), children] })));
|
|
75
75
|
Section.displayName = "Section";
|
|
76
|
-
export function Article({ className = "", surface, padding, rounded, border, shadow, color, children, ...props }) {
|
|
77
|
-
return (_jsx("article", { className: [buildSurfaceClasses({ surface, padding, rounded, border, shadow }), color ? SEMANTIC_COLOR_MAP[color] : "", className].filter(Boolean).join(" "), ...props, children: children }));
|
|
76
|
+
export function Article({ className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, color, children, ...props }) {
|
|
77
|
+
return (_jsx("article", { className: [buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), color ? SEMANTIC_COLOR_MAP[color] : "", className].filter(Boolean).join(" "), ...props, children: children }));
|
|
78
78
|
}
|
|
79
|
-
export function Main({ className = "", surface, padding, paddingX, paddingY, rounded, border, shadow, color, children, ...props }) {
|
|
80
|
-
return (_jsx("main", { className: [buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, border, shadow }), color ? SEMANTIC_COLOR_MAP[color] : "", className].filter(Boolean).join(" "), ...props, children: children }));
|
|
79
|
+
export function Main({ className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, color, children, ...props }) {
|
|
80
|
+
return (_jsx("main", { className: [buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), color ? SEMANTIC_COLOR_MAP[color] : "", className].filter(Boolean).join(" "), ...props, children: children }));
|
|
81
81
|
}
|
|
82
|
-
export const Aside = React.forwardRef(({ className = "", surface, padding, rounded, border, shadow, color, children, ...props }, ref) => (_jsx("aside", { className: [buildSurfaceClasses({ surface, padding, rounded, border, shadow }), color ? SEMANTIC_COLOR_MAP[color] : "", className].filter(Boolean).join(" "), ref: ref, ...props, children: children })));
|
|
82
|
+
export const Aside = React.forwardRef(({ className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, color, children, ...props }, ref) => (_jsx("aside", { className: [buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), color ? SEMANTIC_COLOR_MAP[color] : "", className].filter(Boolean).join(" "), ref: ref, ...props, children: children })));
|
|
83
83
|
Aside.displayName = "Aside";
|
|
84
84
|
const NAV_TEXT_SIZE_MAP = {
|
|
85
85
|
xs: "text-xs",
|
|
@@ -97,8 +97,8 @@ const NAV_SPACING_MAP = {
|
|
|
97
97
|
};
|
|
98
98
|
const NAV_GAP_MAP = {
|
|
99
99
|
none: "",
|
|
100
|
-
"2xs": "gap-1
|
|
101
|
-
xs: "gap-1",
|
|
100
|
+
"2xs": "gap-1",
|
|
101
|
+
xs: "gap-1.5",
|
|
102
102
|
sm: "gap-2",
|
|
103
103
|
md: "gap-3",
|
|
104
104
|
lg: "gap-4",
|
|
@@ -114,9 +114,9 @@ const NAV_COLOR_MAP = {
|
|
|
114
114
|
muted: "appkit-color--muted",
|
|
115
115
|
faint: "appkit-color--faint",
|
|
116
116
|
};
|
|
117
|
-
export function Nav({ surface, padding, rounded, border, shadow, spacing, gap, layout, color, textSize, className = "", children, ...props }) {
|
|
117
|
+
export function Nav({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, spacing, gap, layout, color, textSize, className = "", children, ...props }) {
|
|
118
118
|
return (_jsx("nav", { className: [
|
|
119
|
-
buildSurfaceClasses({ surface, padding, rounded, border, shadow }),
|
|
119
|
+
buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }),
|
|
120
120
|
spacing ? NAV_SPACING_MAP[spacing] : "",
|
|
121
121
|
gap ? NAV_GAP_MAP[gap] : "",
|
|
122
122
|
layout ? NAV_LAYOUT_MAP[layout] : "",
|
|
@@ -125,11 +125,11 @@ export function Nav({ surface, padding, rounded, border, shadow, spacing, gap, l
|
|
|
125
125
|
className,
|
|
126
126
|
].filter(Boolean).join(" "), ...props, children: children }));
|
|
127
127
|
}
|
|
128
|
-
export function BlockHeader({ className = "", surface, padding, rounded, border, shadow, children, ...props }) {
|
|
129
|
-
return (_jsx("header", { className: [buildSurfaceClasses({ surface, padding, rounded, border, shadow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
128
|
+
export function BlockHeader({ className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }) {
|
|
129
|
+
return (_jsx("header", { className: [buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
130
130
|
}
|
|
131
|
-
export function BlockFooter({ className = "", surface, padding, rounded, border, shadow, children, ...props }) {
|
|
132
|
-
return (_jsx("footer", { className: [buildSurfaceClasses({ surface, padding, rounded, border, shadow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
131
|
+
export function BlockFooter({ className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }) {
|
|
132
|
+
return (_jsx("footer", { className: [buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
133
133
|
}
|
|
134
134
|
const LIST_MARKER_MAP = {
|
|
135
135
|
disc: "appkit-list--marker-disc",
|
|
@@ -263,8 +263,8 @@ const TABLE_SIZE_MAP = {
|
|
|
263
263
|
md: "appkit-table--md",
|
|
264
264
|
lg: "appkit-table--lg",
|
|
265
265
|
};
|
|
266
|
-
export function Table({ variant = "default", size = "md", stickyHeader = false, className = "", surface, padding, rounded, border, shadow, children, ...props }) {
|
|
267
|
-
return (_jsx("table", { className: ["appkit-table", TABLE_VARIANT_MAP[variant], TABLE_SIZE_MAP[size], stickyHeader ? "appkit-table--sticky-header" : "", buildSurfaceClasses({ surface, padding, rounded, border, shadow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
266
|
+
export function Table({ variant = "default", size = "md", stickyHeader = false, className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }) {
|
|
267
|
+
return (_jsx("table", { className: ["appkit-table", TABLE_VARIANT_MAP[variant], TABLE_SIZE_MAP[size], stickyHeader ? "appkit-table--sticky-header" : "", buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
268
268
|
}
|
|
269
269
|
const THEAD_SURFACE_MAP = {
|
|
270
270
|
none: "",
|
|
@@ -444,8 +444,8 @@ export function Code({ color = "default", weight, size, padding, rounded, surfac
|
|
|
444
444
|
className,
|
|
445
445
|
].filter(Boolean).join(" "), ...props, children: children }));
|
|
446
446
|
}
|
|
447
|
-
export function Pre({ className = "", surface, padding, rounded, border, shadow, children, ...props }) {
|
|
448
|
-
return (_jsx("pre", { className: ["appkit-pre", buildSurfaceClasses({ surface, padding, rounded, border, shadow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
447
|
+
export function Pre({ className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }) {
|
|
448
|
+
return (_jsx("pre", { className: ["appkit-pre", buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
449
449
|
}
|
|
450
450
|
const BLOCKQUOTE_COLOR_MAP = {
|
|
451
451
|
default: "appkit-blockquote--default",
|
|
@@ -453,22 +453,22 @@ const BLOCKQUOTE_COLOR_MAP = {
|
|
|
453
453
|
info: "appkit-blockquote--info",
|
|
454
454
|
warning: "appkit-blockquote--warning",
|
|
455
455
|
};
|
|
456
|
-
export function Blockquote({ color = "default", className = "", surface, padding, paddingX, paddingY, rounded, border, shadow, children, ...props }) {
|
|
457
|
-
return (_jsx("blockquote", { className: ["appkit-blockquote", BLOCKQUOTE_COLOR_MAP[color], buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, border, shadow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
456
|
+
export function Blockquote({ color = "default", className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }) {
|
|
457
|
+
return (_jsx("blockquote", { className: ["appkit-blockquote", BLOCKQUOTE_COLOR_MAP[color], buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
458
458
|
}
|
|
459
|
-
export function Figure({ className = "", surface, padding, rounded, border, shadow, children, ...props }) {
|
|
460
|
-
return (_jsx("figure", { className: ["appkit-figure", buildSurfaceClasses({ surface, padding, rounded, border, shadow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
459
|
+
export function Figure({ className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }) {
|
|
460
|
+
return (_jsx("figure", { className: ["appkit-figure", buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
461
461
|
}
|
|
462
462
|
export function Figcaption({ className = "", children, ...props }) {
|
|
463
463
|
return (_jsx("figcaption", { className: ["appkit-figcaption", className].filter(Boolean).join(" "), ...props, children: children }));
|
|
464
464
|
}
|
|
465
|
-
export function Dl({ variant = "stacked", divide, className = "", surface, padding, rounded, border, shadow, children, ...props }) {
|
|
465
|
+
export function Dl({ variant = "stacked", divide, className = "", surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, children, ...props }) {
|
|
466
466
|
const divideCls = divide
|
|
467
467
|
? divide === "subtle"
|
|
468
468
|
? "appkit-stack--divide-subtle"
|
|
469
469
|
: "appkit-stack--divide"
|
|
470
470
|
: "";
|
|
471
|
-
return (_jsx("dl", { className: ["appkit-dl", variant === "inline" ? "appkit-dl--inline" : "appkit-dl--stacked", divideCls, buildSurfaceClasses({ surface, padding, rounded, border, shadow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
471
|
+
return (_jsx("dl", { className: ["appkit-dl", variant === "inline" ? "appkit-dl--inline" : "appkit-dl--stacked", divideCls, buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }), className].filter(Boolean).join(" "), ...props, children: children }));
|
|
472
472
|
}
|
|
473
473
|
const DT_DD_COLOR_MAP = {
|
|
474
474
|
default: "",
|
|
@@ -124,18 +124,20 @@ export function SideDrawer({ isOpen, onClose, title, children, footer, mode = "v
|
|
|
124
124
|
}, [isOpen]);
|
|
125
125
|
// Prevent body scroll when drawer is open
|
|
126
126
|
useEffect(() => {
|
|
127
|
-
if (isOpen)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
127
|
+
if (!isOpen)
|
|
128
|
+
return;
|
|
129
|
+
// Capture and restore the PRIOR values rather than hard-coding "" —
|
|
130
|
+
// otherwise closing this drawer while another overlay (Modal,
|
|
131
|
+
// SideModal, a second SideDrawer) is still open underneath wipes out
|
|
132
|
+
// that overlay's own scroll lock too.
|
|
133
|
+
const previousOverflow = document.body.style.overflow;
|
|
134
|
+
const previousPaddingRight = document.body.style.paddingRight;
|
|
135
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
136
|
+
document.body.style.overflow = "hidden";
|
|
137
|
+
document.body.style.paddingRight = `${scrollbarWidth}px`;
|
|
136
138
|
return () => {
|
|
137
|
-
document.body.style.overflow =
|
|
138
|
-
document.body.style.paddingRight =
|
|
139
|
+
document.body.style.overflow = previousOverflow;
|
|
140
|
+
document.body.style.paddingRight = previousPaddingRight;
|
|
139
141
|
};
|
|
140
142
|
}, [isOpen]);
|
|
141
143
|
useSwipe(drawerRef, {
|
|
@@ -40,14 +40,16 @@ export function SideModal({ isOpen, onClose, title, side = "right", children, cl
|
|
|
40
40
|
}, [isOpen]);
|
|
41
41
|
// Prevent body scroll while open
|
|
42
42
|
useEffect(() => {
|
|
43
|
-
if (isOpen)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
if (!isOpen)
|
|
44
|
+
return;
|
|
45
|
+
// Capture and restore the PRIOR value rather than hard-coding "" —
|
|
46
|
+
// otherwise closing this modal while another overlay is still open
|
|
47
|
+
// underneath (Modal, SideDrawer, a second SideModal) wipes out that
|
|
48
|
+
// overlay's own scroll lock too.
|
|
49
|
+
const previousOverflow = document.body.style.overflow;
|
|
50
|
+
document.body.style.overflow = "hidden";
|
|
49
51
|
return () => {
|
|
50
|
-
document.body.style.overflow =
|
|
52
|
+
document.body.style.overflow = previousOverflow;
|
|
51
53
|
};
|
|
52
54
|
}, [isOpen]);
|
|
53
55
|
const reduced = useReducedMotion();
|
|
@@ -46,11 +46,22 @@ export interface SlottedListingViewProps {
|
|
|
46
46
|
* - `public`: no managed state by default
|
|
47
47
|
*/
|
|
48
48
|
portal?: ViewPortal;
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Currently unused — `renderSearch` is always invoked when provided,
|
|
51
|
+
* regardless of this flag. Kept as a no-op accepted prop because real
|
|
52
|
+
* consumers (CategoryProductsView, StoreProductsView, FAQPageContent, …)
|
|
53
|
+
* already pass their own self-contained `renderSearch` closures that
|
|
54
|
+
* manage search state externally and would lose their search UI if this
|
|
55
|
+
* ever started gating the render call. Do not wire this up to gate
|
|
56
|
+
* `renderSearch` without auditing every consumer's `renderSearch` first.
|
|
57
|
+
*/
|
|
50
58
|
manageSearch?: boolean;
|
|
51
59
|
/** Enable managed selection state (default driven by portal, otherwise false). */
|
|
52
60
|
manageSelection?: boolean;
|
|
53
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* Currently unused — see `manageSearch` above; `renderSort` is always
|
|
63
|
+
* invoked when provided, regardless of this flag, for the same reason.
|
|
64
|
+
*/
|
|
54
65
|
manageSort?: boolean;
|
|
55
66
|
/** Wrap search + sort slots in a flex row. */
|
|
56
67
|
inlineToolbar?: boolean;
|
|
@@ -12,21 +12,25 @@ const PADDING_CLS = {
|
|
|
12
12
|
lg: "px-4 py-3",
|
|
13
13
|
toolbar: "px-3 py-1.5",
|
|
14
14
|
};
|
|
15
|
-
function
|
|
15
|
+
function resolveOffsetClass(offset) {
|
|
16
16
|
if (offset === "header") {
|
|
17
17
|
return "top-[var(--header-height,0px)]";
|
|
18
18
|
}
|
|
19
19
|
if (offset === "header+nav") {
|
|
20
20
|
return "top-[calc(var(--header-height,0px)+var(--appkit-navbar-height,2.5rem))]";
|
|
21
21
|
}
|
|
22
|
-
// Numeric
|
|
23
|
-
|
|
22
|
+
// Numeric offsets are applied via inline style instead (see below) —
|
|
23
|
+
// Tailwind's static scanner can never see a dynamically-interpolated
|
|
24
|
+
// `top-[${offset}px]` arbitrary value, so no CSS rule would ever be
|
|
25
|
+
// generated for it in the compiled stylesheet.
|
|
26
|
+
return "";
|
|
24
27
|
}
|
|
25
28
|
export function StickyToolbar({ children, offset = "header", tone = "translucent", border = true, padding = "toolbar", z = "above-content", role, }) {
|
|
26
|
-
const offsetCls =
|
|
29
|
+
const offsetCls = resolveOffsetClass(offset);
|
|
30
|
+
const offsetStyle = typeof offset === "number" ? { top: `${offset}px` } : undefined;
|
|
27
31
|
const borderCls = border ? "border-b border-[var(--appkit-color-border)]" : "";
|
|
28
32
|
const zCls = z === "above-content" ? "z-10" : "z-[5]";
|
|
29
|
-
return (_jsx("div", { role: role,
|
|
33
|
+
return (_jsx("div", { role: role, style: offsetStyle,
|
|
30
34
|
// for the translucent sticky-toolbar pattern. The header offset is
|
|
31
35
|
// sourced from --header-height (set by AppLayoutShell at runtime).
|
|
32
36
|
className: `sticky ${offsetCls} ${zCls} ${TONE_CLS[tone]} ${borderCls} ${PADDING_CLS[padding]}`, children: children }));
|
|
@@ -5,8 +5,9 @@ import { Label, Span } from "./Typography";
|
|
|
5
5
|
export function TagInput({ value, onChange, disabled = false, label, placeholder = "Add a tag...", className = "", helperText = "Press Enter or comma to add a tag", }) {
|
|
6
6
|
const [draft, setDraft] = useState("");
|
|
7
7
|
const inputRef = useRef(null);
|
|
8
|
+
const normalizeTag = (raw) => raw.trim().replace(/,+$/, "").trim();
|
|
8
9
|
const addTag = (raw) => {
|
|
9
|
-
const tag = raw
|
|
10
|
+
const tag = normalizeTag(raw);
|
|
10
11
|
if (!tag || value.includes(tag)) {
|
|
11
12
|
setDraft("");
|
|
12
13
|
return;
|
|
@@ -32,7 +33,19 @@ export function TagInput({ value, onChange, disabled = false, label, placeholder
|
|
|
32
33
|
if (inputValue.includes(",")) {
|
|
33
34
|
const parts = inputValue.split(",");
|
|
34
35
|
const last = parts.pop() ?? "";
|
|
35
|
-
|
|
36
|
+
// Accumulate locally instead of calling addTag() in a loop — addTag
|
|
37
|
+
// always computes `[...value, tag]` off the current render's `value`
|
|
38
|
+
// prop, so multiple synchronous calls in the same paste each started
|
|
39
|
+
// from the SAME stale array and every onChange but the last one got
|
|
40
|
+
// silently overwritten (only the final comma-segment ever survived).
|
|
41
|
+
let next = value;
|
|
42
|
+
for (const part of parts) {
|
|
43
|
+
const tag = normalizeTag(part);
|
|
44
|
+
if (tag && !next.includes(tag))
|
|
45
|
+
next = [...next, tag];
|
|
46
|
+
}
|
|
47
|
+
if (next !== value)
|
|
48
|
+
onChange(next);
|
|
36
49
|
setDraft(last);
|
|
37
50
|
return;
|
|
38
51
|
}
|
|
@@ -43,5 +43,12 @@ export function Toggle({ checked: checkedProp, defaultChecked = false, onChange,
|
|
|
43
43
|
.join(" "), children: _jsx(Span, { "aria-hidden": "true", className: [
|
|
44
44
|
UI_TOGGLE.thumb,
|
|
45
45
|
checked ? UI_TOGGLE.thumbOn : UI_TOGGLE.thumbOff,
|
|
46
|
-
].join(" ") }) }), label && (
|
|
46
|
+
].join(" ") }) }), label && (
|
|
47
|
+
// No onClick here — `htmlFor` on a `<label>` already makes the
|
|
48
|
+
// browser forward a click on the label to the labelable `<button>`
|
|
49
|
+
// above natively. Also attaching handleToggle here fired it TWICE
|
|
50
|
+
// per click (once from this handler, once from the native
|
|
51
|
+
// label→button forwarding), both reading the same stale `checked`
|
|
52
|
+
// closure, so onChange(true) could fire twice for one click.
|
|
53
|
+
_jsx(Label, { id: `${toggleId}-label`, htmlFor: toggleId, className: disabled ? "cursor-not-allowed opacity-60" : "cursor-pointer", children: label }))] }));
|
|
47
54
|
}
|
|
@@ -18,7 +18,18 @@ export function UnsavedChangesModal({ labels = {} }) {
|
|
|
18
18
|
useEffect(() => {
|
|
19
19
|
const subscription = eventBus.on(UNSAVED_CHANGES_EVENT, (...args) => {
|
|
20
20
|
const resolveFn = args[0];
|
|
21
|
-
setResolve(() =>
|
|
21
|
+
setResolve((prevResolve) => {
|
|
22
|
+
// A second UNSAVED_CHANGES_EVENT arrived before the user answered
|
|
23
|
+
// the first — resolve that abandoned promise with `false` instead
|
|
24
|
+
// of silently overwriting it and leaving it pending forever.
|
|
25
|
+
prevResolve?.(false);
|
|
26
|
+
// Returning `resolveFn` directly (not `() => resolveFn`) is
|
|
27
|
+
// correct here — the double-wrap trick is only needed when
|
|
28
|
+
// passing setState a bare function *value* (to stop React from
|
|
29
|
+
// treating it as an updater); inside an updater callback the
|
|
30
|
+
// return value already becomes the next state as-is.
|
|
31
|
+
return resolveFn;
|
|
32
|
+
});
|
|
22
33
|
setIsOpen(true);
|
|
23
34
|
});
|
|
24
35
|
return () => subscription.unsubscribe();
|
|
@@ -31,6 +31,13 @@ export function ColorPickerField({ name, label, hint, required, swatchWidth = "f
|
|
|
31
31
|
}
|
|
32
32
|
function handleChange(e) {
|
|
33
33
|
onChange?.(e.target.value);
|
|
34
|
+
// FieldInput/FieldTextarea/FieldSelect/FieldCheckbox all clear the
|
|
35
|
+
// FormShell error for this field on change — this was the only Field*
|
|
36
|
+
// wrapper that omitted it, so a picked (valid) color never cleared a
|
|
37
|
+
// previously-set error until something else called clearErrors/
|
|
38
|
+
// setFieldError.
|
|
39
|
+
if (showError)
|
|
40
|
+
ctx?.clearFieldError(name);
|
|
34
41
|
}
|
|
35
42
|
return (_jsxs(Span, { layout: "flex", gap: "sm", className: "flex-col", children: [label != null && (_jsx(Label, { htmlFor: inputId, required: required, children: label })), _jsx("input", { id: inputId, type: "color", name: name, className: `h-10 ${SWATCH_WIDTH_MAP[swatchWidth]} rounded border border-[var(--appkit-color-border)] cursor-pointer ${className ?? ""}`, "aria-invalid": showError ? true : undefined, "aria-describedby": showError ? errorId : undefined, onBlur: handleBlur, onChange: handleChange, ...inputProps }), hint != null && !showError && (_jsx(Text, { size: "xs", color: "muted", children: hint })), showError && (_jsx(Text, { id: errorId, role: "alert", size: "xs", color: "error", children: error }))] }));
|
|
36
43
|
}
|
|
@@ -21,5 +21,5 @@ export function FieldCheckbox({ name, label, hint, checked, onChange, onBlur, di
|
|
|
21
21
|
ctx?.setFieldTouched(name);
|
|
22
22
|
onBlur?.();
|
|
23
23
|
}
|
|
24
|
-
return (_jsxs("div", { className: ["appkit-form-field", className].filter(Boolean).join(" "), children: [_jsx(Checkbox, { id: inputId, name: name, checked: checked, onChange: handleChange, onBlur: handleBlur, label: label, disabled: disabled,
|
|
24
|
+
return (_jsxs("div", { className: ["appkit-form-field", className].filter(Boolean).join(" "), children: [_jsx(Checkbox, { id: inputId, name: name, checked: checked, onChange: handleChange, onBlur: handleBlur, label: label, disabled: disabled, "aria-describedby": showError ? errorId : undefined }), !showError && hint && (_jsx(Text, { size: "sm", variant: "secondary", className: "appkit-form-field__hint", children: hint })), showError && (_jsx(Text, { id: errorId, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error }))] }));
|
|
25
25
|
}
|
|
@@ -270,10 +270,13 @@ export type MediaFilenameContext = ({
|
|
|
270
270
|
export declare function generateMediaFilename(ctx: MediaFilenameContext): string;
|
|
271
271
|
export declare function validateMediaFilename(filename: string): boolean;
|
|
272
272
|
/**
|
|
273
|
-
* Derives the `MediaFilenameContext["type"]` a filename was
|
|
274
|
-
* (e.g. "catalogue-image") by matching against the same
|
|
275
|
-
* {@link validateMediaFilename} uses
|
|
276
|
-
*
|
|
273
|
+
* Derives the `MediaFilenameContext["type"]` family a filename was
|
|
274
|
+
* generated under (e.g. "catalogue-image") by matching against the same
|
|
275
|
+
* shape patterns {@link validateMediaFilename} uses. Sibling context types
|
|
276
|
+
* that share one generator (e.g. blog-cover/blog-content-image/
|
|
277
|
+
* blog-additional-image all produce the same shape as blog-image) resolve
|
|
278
|
+
* to that shared family name — the dispatcher itself can't distinguish them
|
|
279
|
+
* from the filename alone, since it never encoded the sub-type into the name.
|
|
277
280
|
* Used at /api/media/finalize time to stamp `MediaAssetDocument.contextType`
|
|
278
281
|
* without changing the finalize request contract.
|
|
279
282
|
*/
|