@mohasinac/appkit 2.7.34 → 2.7.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/server/features/auctions/og.d.ts +1 -0
- package/dist/_internal/server/features/auctions/og.js +2 -1
- package/dist/_internal/server/features/blog/og.d.ts +1 -0
- package/dist/_internal/server/features/blog/og.js +3 -2
- package/dist/_internal/server/features/brands/og.d.ts +1 -0
- package/dist/_internal/server/features/brands/og.js +2 -1
- package/dist/_internal/server/features/bundles/og.d.ts +1 -0
- package/dist/_internal/server/features/bundles/og.js +2 -1
- package/dist/_internal/server/features/categories/og.d.ts +1 -0
- package/dist/_internal/server/features/categories/og.js +2 -1
- package/dist/_internal/server/features/checkout/actions.js +13 -5
- package/dist/_internal/server/features/classified/og.d.ts +2 -0
- package/dist/_internal/server/features/classified/og.js +2 -1
- package/dist/_internal/server/features/digital-code/og.d.ts +2 -0
- package/dist/_internal/server/features/digital-code/og.js +2 -1
- package/dist/_internal/server/features/events/og.d.ts +1 -0
- package/dist/_internal/server/features/events/og.js +3 -1
- package/dist/_internal/server/features/live/og.d.ts +2 -0
- package/dist/_internal/server/features/live/og.js +2 -1
- package/dist/_internal/server/features/pre-orders/og.d.ts +1 -0
- package/dist/_internal/server/features/pre-orders/og.js +2 -1
- package/dist/_internal/server/features/products/og.d.ts +3 -1
- package/dist/_internal/server/features/products/og.js +2 -1
- package/dist/_internal/server/features/seo/index.d.ts +1 -1
- package/dist/_internal/server/features/seo/index.js +1 -1
- package/dist/_internal/server/features/seo/og.d.ts +8 -1
- package/dist/_internal/server/features/seo/og.js +28 -8
- package/dist/_internal/server/features/stores/og.d.ts +1 -0
- package/dist/_internal/server/features/stores/og.js +3 -2
- package/dist/_internal/server/features/sublisting-categories/og.d.ts +1 -0
- package/dist/_internal/server/features/sublisting-categories/og.js +2 -1
- package/dist/_internal/server/jobs/core/onScamReportCreate.js +18 -14
- package/dist/_internal/server/jobs/core/onScamReportRejected.js +10 -7
- package/dist/_internal/server/jobs/core/onScamReportVerified.js +10 -7
- package/dist/_internal/shared/actions/action-registry.js +48 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.js +3 -0
- package/dist/constants/api-endpoints.d.ts +9 -0
- package/dist/constants/api-endpoints.js +3 -0
- package/dist/features/admin/components/AdminBundlesView.js +20 -2
- package/dist/features/admin/components/AdminPayoutsView.js +2 -1
- package/dist/features/admin/components/AdminProductsView.js +13 -0
- package/dist/features/admin/components/AdminStoresView.js +43 -5
- package/dist/features/admin/components/AdminUsersView.js +53 -6
- package/dist/features/admin/schemas/firestore.d.ts +1 -1
- package/dist/features/blog/components/BlogPostForm.js +1 -0
- package/dist/features/categories/components/CategoryForm.js +1 -0
- package/dist/features/homepage/components/FeaturedAuctionsSection.d.ts +4 -1
- package/dist/features/homepage/components/FeaturedAuctionsSection.js +2 -2
- package/dist/features/homepage/components/FeaturedPreOrdersSection.d.ts +4 -1
- package/dist/features/homepage/components/FeaturedPreOrdersSection.js +2 -2
- package/dist/features/homepage/components/FeaturedProductsSection.d.ts +3 -6
- package/dist/features/homepage/components/FeaturedProductsSection.js +3 -47
- package/dist/features/homepage/components/SectionCarousel.js +1 -8
- package/dist/features/homepage/lib/section-renderer.js +3 -3
- package/dist/seed/homepage-sections-seed-data.js +10 -10
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/DateInput.d.ts +27 -0
- package/dist/ui/components/DateInput.js +11 -0
- package/dist/ui/components/FormField.d.ts +2 -1
- package/dist/ui/components/FormField.js +7 -4
- package/dist/ui/components/FormField.style.css +13 -0
- package/dist/ui/components/HorizontalScroller.js +25 -30
- package/dist/ui/components/OtpInput.d.ts +14 -0
- package/dist/ui/components/OtpInput.js +55 -0
- package/dist/ui/components/OtpInput.style.css +13 -0
- package/dist/ui/components/Select.style.css +23 -6
- package/dist/ui/components/Textarea.style.css +31 -4
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface DateInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "onChange"> {
|
|
3
|
+
label?: React.ReactNode;
|
|
4
|
+
error?: React.ReactNode;
|
|
5
|
+
helperText?: React.ReactNode;
|
|
6
|
+
value?: string;
|
|
7
|
+
onChange?: (value: string) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const DateInput: React.ForwardRefExoticComponent<DateInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
10
|
+
export interface DateRangeInputProps {
|
|
11
|
+
label?: React.ReactNode;
|
|
12
|
+
startLabel?: string;
|
|
13
|
+
endLabel?: string;
|
|
14
|
+
startValue?: string;
|
|
15
|
+
endValue?: string;
|
|
16
|
+
onStartChange?: (value: string) => void;
|
|
17
|
+
onEndChange?: (value: string) => void;
|
|
18
|
+
error?: React.ReactNode;
|
|
19
|
+
helperText?: React.ReactNode;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
required?: boolean;
|
|
22
|
+
startMin?: string;
|
|
23
|
+
startMax?: string;
|
|
24
|
+
endMin?: string;
|
|
25
|
+
endMax?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function DateRangeInput({ label, startLabel, endLabel, startValue, endValue, onStartChange, onEndChange, error, helperText, disabled, required, startMin, startMax, endMin, endMax, }: DateRangeInputProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Label, Text } from "./Typography";
|
|
4
|
+
export const DateInput = React.forwardRef(function DateInput({ label, error, helperText, value, onChange, disabled, required, id, className = "", ...props }, ref) {
|
|
5
|
+
const generatedId = React.useId();
|
|
6
|
+
const inputId = id ?? generatedId;
|
|
7
|
+
return (_jsxs("div", { className: "w-full", children: [label && (_jsx(Label, { htmlFor: inputId, required: required, className: "appkit-form-field__label mb-1.5", children: label })), _jsx("input", { ...props, ref: ref, id: inputId, type: "date", value: value, disabled: disabled, required: required, "aria-invalid": error ? "true" : undefined, onChange: (e) => onChange?.(e.target.value), className: ["appkit-input", error ? "appkit-input--error" : "", className].filter(Boolean).join(" ") }), error ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", className: "mt-1.5", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "mt-1.5", children: helperText })) : null] }));
|
|
8
|
+
});
|
|
9
|
+
export function DateRangeInput({ label, startLabel = "From", endLabel = "To", startValue, endValue, onStartChange, onEndChange, error, helperText, disabled, required, startMin, startMax, endMin, endMax, }) {
|
|
10
|
+
return (_jsxs("div", { className: "w-full", children: [label && (_jsx(Text, { size: "sm", className: "appkit-form-field__label mb-1.5 block font-medium", children: label })), _jsxs("div", { className: "flex items-end gap-2", children: [_jsx(DateInput, { label: startLabel, value: startValue, onChange: onStartChange, disabled: disabled, required: required, min: startMin, max: startMax ?? endValue }), _jsx("span", { className: "mb-2.5 shrink-0 text-sm text-[var(--appkit-color-text-muted)]", children: "\u2013" }), _jsx(DateInput, { label: endLabel, value: endValue, onChange: onEndChange, disabled: disabled, min: endMin ?? startValue, max: endMax })] }), error ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", className: "mt-1.5", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "mt-1.5", children: helperText })) : null] }));
|
|
11
|
+
}
|
|
@@ -2,6 +2,7 @@ import { type SelectOption } from "./Select";
|
|
|
2
2
|
export interface FormFieldProps {
|
|
3
3
|
label?: string;
|
|
4
4
|
name: string;
|
|
5
|
+
card?: boolean;
|
|
5
6
|
type?: "text" | "email" | "password" | "tel" | "number" | "datetime-local" | "textarea" | "select" | "image" | "media";
|
|
6
7
|
value?: string;
|
|
7
8
|
onChange?: (value: string) => void;
|
|
@@ -22,5 +23,5 @@ export interface FormFieldProps {
|
|
|
22
23
|
accept?: string;
|
|
23
24
|
maxSizeMB?: number;
|
|
24
25
|
}
|
|
25
|
-
export declare function FormField({ label, name, type, value, onChange, onBlur, error, touched, placeholder, required, disabled, autoComplete, rows, hint, helpText, options, onUpload, captureSource, captureMode, accept, maxSizeMB, }: FormFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function FormField({ label, name, card, type, value, onChange, onBlur, error, touched, placeholder, required, disabled, autoComplete, rows, hint, helpText, options, onUpload, captureSource, captureMode, accept, maxSizeMB, }: FormFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
26
27
|
export default FormField;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { Input } from "./Input";
|
|
3
4
|
import { Select } from "./Select";
|
|
4
5
|
import { Textarea } from "./Textarea";
|
|
5
6
|
import { Label, Text, Span } from "./Typography";
|
|
6
7
|
import { ImageUpload, MediaUploadField } from "../../features/media";
|
|
7
|
-
|
|
8
|
+
const CARD_CLASS = "appkit-form-field appkit-form-field--card";
|
|
9
|
+
const BASE_CLASS = "appkit-form-field";
|
|
10
|
+
export function FormField({ label, name, card = false, type = "text", value = "", onChange, onBlur, error, touched, placeholder, required = false, disabled = false, autoComplete, rows, hint, helpText, options = [], onUpload, captureSource, captureMode, accept, maxSizeMB, }) {
|
|
8
11
|
const showError = error
|
|
9
12
|
? touched != null
|
|
10
13
|
? touched && !!error
|
|
@@ -14,11 +17,11 @@ export function FormField({ label, name, type = "text", value = "", onChange, on
|
|
|
14
17
|
const errorId = `${inputId}-error`;
|
|
15
18
|
const describedBy = showError ? errorId : undefined;
|
|
16
19
|
if (type === "image" && onUpload) {
|
|
17
|
-
return (_jsxs("div", { className:
|
|
20
|
+
return (_jsxs("div", { className: card ? CARD_CLASS : BASE_CLASS, "data-section": "formfield-div-505", children: [_jsx(ImageUpload, { currentImage: value || undefined, onUpload: onUpload, onChange: (url) => onChange?.(url), label: label ? `${label}${required ? " *" : ""}` : undefined, helperText: hint ?? helpText, captureSource: captureSource ?? "file-only", accept: accept, maxSizeMB: maxSizeMB }), showError ? (_jsx(Text, { id: errorId, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error })) : null] }));
|
|
18
21
|
}
|
|
19
22
|
if (type === "media" && onUpload) {
|
|
20
|
-
return (_jsxs("div", { className:
|
|
23
|
+
return (_jsxs("div", { className: card ? CARD_CLASS : BASE_CLASS, "data-section": "formfield-div-506", children: [_jsx(MediaUploadField, { label: `${label || name}${required ? " *" : ""}`, value: value, onChange: (url) => onChange?.(url), onUpload: onUpload, disabled: disabled, helperText: hint ?? helpText, captureSource: captureSource ?? "file-only", captureMode: captureMode ?? "both", accept: accept, maxSizeMB: maxSizeMB }), showError ? (_jsx(Text, { id: errorId, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error })) : null] }));
|
|
21
24
|
}
|
|
22
|
-
return (_jsxs("div", { className:
|
|
25
|
+
return (_jsxs("div", { className: card ? CARD_CLASS : BASE_CLASS, "data-section": "formfield-div-507", children: [label ? (_jsxs(Label, { htmlFor: inputId, className: "appkit-form-field__label", children: [label, required ? (_jsx(Span, { className: "appkit-form-field__required", children: "*" })) : null] })) : null, type === "select" ? (_jsx(Select, { id: inputId, name: name, value: value, onChange: (event) => onChange?.(event.target.value), onBlur: onBlur, disabled: disabled, options: options, "aria-required": required || undefined, "aria-invalid": showError || undefined, "aria-describedby": describedBy })) : type === "textarea" ? (_jsx(Textarea, { id: inputId, name: name, value: value, onChange: (event) => onChange?.(event.target.value), onBlur: onBlur, placeholder: placeholder, disabled: disabled, rows: rows, "aria-required": required || undefined, "aria-invalid": showError || undefined, "aria-describedby": describedBy })) : (_jsx(Input, { id: inputId, name: name, type: type, value: value, onChange: (event) => onChange?.(event.target.value), onBlur: onBlur, placeholder: placeholder, disabled: disabled, autoComplete: autoComplete, "aria-required": required || undefined, "aria-invalid": showError || undefined, "aria-describedby": describedBy })), !showError && (hint || helpText) ? (_jsx(Text, { size: "sm", variant: "secondary", className: "appkit-form-field__hint", children: hint ?? helpText })) : null, showError ? (_jsx(Text, { id: errorId, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error })) : null] }));
|
|
23
26
|
}
|
|
24
27
|
export default FormField;
|
|
@@ -28,3 +28,16 @@
|
|
|
28
28
|
.appkit-form-field__error {
|
|
29
29
|
margin-top: 0.375rem;
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
/* Card variant — adds a contained panel treatment around label + field */
|
|
33
|
+
.appkit-form-field--card {
|
|
34
|
+
background: var(--appkit-color-zinc-50, #fafafa);
|
|
35
|
+
border: 1px solid var(--appkit-color-zinc-200, #e4e4e7);
|
|
36
|
+
border-radius: 0.5rem;
|
|
37
|
+
padding: 1rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.dark .appkit-form-field--card {
|
|
41
|
+
background: rgba(30, 41, 59, 0.4);
|
|
42
|
+
border-color: rgba(71, 85, 105, 0.6);
|
|
43
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useRef, useState, useEffect, useCallback,
|
|
3
|
+
import { useRef, useState, useEffect, useCallback, } from "react";
|
|
4
4
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
5
5
|
const BREAKPOINTS = [
|
|
6
6
|
["2xl", 1536],
|
|
@@ -23,6 +23,7 @@ function resolvePerView(perView, containerWidth) {
|
|
|
23
23
|
}
|
|
24
24
|
export function HorizontalScroller({ children, className = "", gap = 16, snapToItems, showArrows, arrowSize = "md", showScrollbar, showFadeEdges, scrollContainerRef: externalRef, onScroll, items, renderItem, keyExtractor, perView, rows = 1, autoScroll, autoScrollInterval = 3500, minItemWidth, pauseOnHover = false, itemClassName = "", loop = false, }) {
|
|
25
25
|
const [itemWidth, setItemWidth] = useState(undefined);
|
|
26
|
+
const [colCount, setColCount] = useState(typeof perView === "number" ? perView : 3);
|
|
26
27
|
const [isPaused, setIsPaused] = useState(false);
|
|
27
28
|
const [atStart, setAtStart] = useState(true);
|
|
28
29
|
const [atEnd, setAtEnd] = useState(true);
|
|
@@ -144,6 +145,7 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
|
|
|
144
145
|
const w = entry.contentRect.width;
|
|
145
146
|
const count = resolvePerView(perView, w);
|
|
146
147
|
if (count > 0) {
|
|
148
|
+
setColCount(count);
|
|
147
149
|
setItemWidth((w - (count - 1) * gap) / count);
|
|
148
150
|
}
|
|
149
151
|
updateExtents();
|
|
@@ -156,35 +158,28 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
|
|
|
156
158
|
updateExtents();
|
|
157
159
|
}, [updateExtents, itemWidth, itemCount]);
|
|
158
160
|
const content = itemsMode ? (rows > 1 ? (
|
|
159
|
-
// Grid mode: group items into slides
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
.join(" "), style: minItemWidth ? { minWidth: minItemWidth } : undefined, children: renderItem(item, i) }, keyExtractor ? keyExtractor(item, i) : i));
|
|
182
|
-
slides[slideIndex] = cloneElement(slideElement, {
|
|
183
|
-
children: newChildren,
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
return slides;
|
|
187
|
-
}, [])) : loop && itemCount > 0 ? (
|
|
161
|
+
// Grid mode: group items into slides of (rows × colCount) cards
|
|
162
|
+
(() => {
|
|
163
|
+
const cols = colCount > 0 ? colCount : 3;
|
|
164
|
+
const cardsPerSlide = rows * cols;
|
|
165
|
+
const slideCount = Math.ceil(normalizedItems.length / cardsPerSlide);
|
|
166
|
+
return Array.from({ length: slideCount }, (_, slideIndex) => {
|
|
167
|
+
const slideItems = normalizedItems.slice(slideIndex * cardsPerSlide, (slideIndex + 1) * cardsPerSlide);
|
|
168
|
+
return (_jsx("div", { className: "appkit-hscroller__slide", style: {
|
|
169
|
+
display: "grid",
|
|
170
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
171
|
+
gap: `${gap}px`,
|
|
172
|
+
width: "100%",
|
|
173
|
+
flexShrink: 0,
|
|
174
|
+
}, children: slideItems.map((item, idx) => (_jsx("div", { className: [
|
|
175
|
+
"appkit-hscroller__item",
|
|
176
|
+
snapToItems ? "appkit-hscroller__item--snap" : "",
|
|
177
|
+
itemClassName,
|
|
178
|
+
]
|
|
179
|
+
.filter(Boolean)
|
|
180
|
+
.join(" "), style: minItemWidth ? { minWidth: minItemWidth } : undefined, children: renderItem(item, slideIndex * cardsPerSlide + idx) }, keyExtractor ? keyExtractor(item, slideIndex * cardsPerSlide + idx) : slideIndex * cardsPerSlide + idx))) }, `slide-${slideIndex}`));
|
|
181
|
+
});
|
|
182
|
+
})()) : loop && itemCount > 0 ? (
|
|
188
183
|
// Circular loop: fixed (itemCount + 2×loopCloneCount) DOM slots.
|
|
189
184
|
// Each slot maps to a real item via modulo — no array cloning, no list growth.
|
|
190
185
|
// Left slots [0 .. loopCloneCount-1] → tail of real list (left buffer)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface OtpInputProps {
|
|
3
|
+
length?: number;
|
|
4
|
+
value: string;
|
|
5
|
+
onChange: (value: string) => void;
|
|
6
|
+
label?: React.ReactNode;
|
|
7
|
+
error?: React.ReactNode;
|
|
8
|
+
helperText?: React.ReactNode;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
autoFocus?: boolean;
|
|
11
|
+
inputMode?: "numeric" | "text";
|
|
12
|
+
id?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function OtpInput({ length, value, onChange, label, error, helperText, disabled, autoFocus, inputMode, id, }: OtpInputProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef, useId } from "react";
|
|
4
|
+
import { Label, Text } from "./Typography";
|
|
5
|
+
export function OtpInput({ length = 6, value, onChange, label, error, helperText, disabled = false, autoFocus = false, inputMode = "numeric", id, }) {
|
|
6
|
+
const generatedId = useId();
|
|
7
|
+
const inputId = id ?? generatedId;
|
|
8
|
+
const inputsRef = useRef([]);
|
|
9
|
+
const digits = value.split("").concat(Array(length).fill("")).slice(0, length);
|
|
10
|
+
function focusAt(index) {
|
|
11
|
+
inputsRef.current[index]?.focus();
|
|
12
|
+
}
|
|
13
|
+
function handleChange(index, char) {
|
|
14
|
+
const sanitized = inputMode === "numeric" ? char.replace(/\D/g, "") : char;
|
|
15
|
+
const next = digits.slice();
|
|
16
|
+
next[index] = sanitized.slice(-1);
|
|
17
|
+
onChange(next.join(""));
|
|
18
|
+
if (sanitized && index < length - 1)
|
|
19
|
+
focusAt(index + 1);
|
|
20
|
+
}
|
|
21
|
+
function handleKeyDown(index, e) {
|
|
22
|
+
if (e.key === "ArrowLeft" && index > 0) {
|
|
23
|
+
focusAt(index - 1);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (e.key === "ArrowRight" && index < length - 1) {
|
|
27
|
+
focusAt(index + 1);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (e.key !== "Backspace")
|
|
31
|
+
return;
|
|
32
|
+
if (digits[index]) {
|
|
33
|
+
const next = digits.slice();
|
|
34
|
+
next[index] = "";
|
|
35
|
+
onChange(next.join(""));
|
|
36
|
+
}
|
|
37
|
+
else if (index > 0) {
|
|
38
|
+
focusAt(index - 1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function handlePaste(e) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
const pasted = e.clipboardData.getData("text");
|
|
44
|
+
const sanitized = inputMode === "numeric" ? pasted.replace(/\D/g, "") : pasted;
|
|
45
|
+
const next = sanitized.slice(0, length).split("").concat(Array(length).fill("")).slice(0, length);
|
|
46
|
+
onChange(next.join(""));
|
|
47
|
+
const lastFilled = Math.min(sanitized.length, length - 1);
|
|
48
|
+
focusAt(lastFilled);
|
|
49
|
+
}
|
|
50
|
+
return (_jsxs("div", { className: "w-full", children: [label && (_jsx(Label, { htmlFor: `${inputId}-0`, className: "appkit-form-field__label mb-2", children: label })), _jsx("div", { className: "flex gap-2", role: "group", "aria-labelledby": label ? `${inputId}-label` : undefined, children: digits.map((digit, i) => (_jsx("input", { ref: (el) => { inputsRef.current[i] = el; }, id: i === 0 ? `${inputId}-0` : undefined, type: "text", inputMode: inputMode, maxLength: 1, value: digit, disabled: disabled, autoFocus: autoFocus && i === 0, autoComplete: "one-time-code", "aria-label": `Digit ${i + 1} of ${length}`, className: [
|
|
51
|
+
"appkit-input",
|
|
52
|
+
"appkit-otp-input",
|
|
53
|
+
error ? "appkit-input--error" : "",
|
|
54
|
+
].filter(Boolean).join(" "), onChange: (e) => handleChange(i, e.target.value), onKeyDown: (e) => handleKeyDown(i, e), onPaste: handlePaste, onFocus: (e) => e.target.select() }, i))) }), error ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", className: "mt-1.5", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "mt-1.5", children: helperText })) : null] }));
|
|
55
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
.appkit-otp-input {
|
|
2
|
+
width: 2.75rem;
|
|
3
|
+
height: 2.75rem;
|
|
4
|
+
flex-shrink: 0;
|
|
5
|
+
padding: 0;
|
|
6
|
+
text-align: center;
|
|
7
|
+
font-size: 1.25rem;
|
|
8
|
+
font-weight: 600;
|
|
9
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
10
|
+
letter-spacing: 0;
|
|
11
|
+
border-radius: 0.5rem;
|
|
12
|
+
caret-color: transparent;
|
|
13
|
+
}
|
|
@@ -23,36 +23,53 @@
|
|
|
23
23
|
color: var(--appkit-color-text);
|
|
24
24
|
padding: 0.5rem 0.75rem;
|
|
25
25
|
font-size: 0.875rem;
|
|
26
|
-
transition: border-color 0.
|
|
26
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.dark .appkit-select__trigger {
|
|
30
30
|
border-color: var(--appkit-color-border);
|
|
31
|
-
background: rgba(30, 41, 59, 0.
|
|
31
|
+
background: rgba(30, 41, 59, 0.7);
|
|
32
32
|
color: var(--appkit-color-text);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
.appkit-select__trigger--enabled:hover:not(:focus-visible) {
|
|
36
|
+
border-color: var(--appkit-color-zinc-400);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.dark .appkit-select__trigger--enabled:hover:not(:focus-visible) {
|
|
40
|
+
border-color: var(--appkit-color-slate-500);
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
.appkit-select__trigger--default:focus-visible {
|
|
44
|
+
outline: none;
|
|
36
45
|
border-color: var(--appkit-color-secondary);
|
|
37
|
-
box-shadow: 0 0 0
|
|
46
|
+
box-shadow: 0 0 0 3px rgba(101, 196, 8, 0.25), inset 0 0 0 1px rgba(101, 196, 8, 0.15);
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
.dark .appkit-select__trigger--default:focus-visible {
|
|
41
50
|
border-color: var(--appkit-color-primary);
|
|
42
|
-
box-shadow: 0 0 0
|
|
51
|
+
box-shadow: 0 0 0 3px rgba(233, 30, 140, 0.25), inset 0 0 0 1px rgba(233, 30, 140, 0.15);
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
.appkit-select__trigger--error {
|
|
46
55
|
border-color: var(--appkit-color-error);
|
|
56
|
+
background: rgba(254, 202, 202, 0.06);
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
.appkit-select__trigger--error:focus-visible {
|
|
50
|
-
|
|
60
|
+
outline: none;
|
|
61
|
+
border-color: var(--appkit-color-error);
|
|
62
|
+
box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.25), inset 0 0 0 1px rgba(248, 113, 113, 0.15);
|
|
51
63
|
}
|
|
52
64
|
|
|
53
65
|
.appkit-select__trigger--disabled {
|
|
54
|
-
opacity: 0.
|
|
66
|
+
opacity: 0.55;
|
|
55
67
|
cursor: not-allowed;
|
|
68
|
+
background: var(--appkit-color-bg);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.dark .appkit-select__trigger--disabled {
|
|
72
|
+
background: rgba(30, 41, 59, 0.4);
|
|
56
73
|
}
|
|
57
74
|
|
|
58
75
|
.appkit-select__trigger--enabled {
|
|
@@ -9,22 +9,49 @@
|
|
|
9
9
|
line-height: var(--appkit-leading-normal);
|
|
10
10
|
min-height: 5rem;
|
|
11
11
|
resize: vertical;
|
|
12
|
-
transition: border-color 0.
|
|
12
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.appkit-textarea::placeholder {
|
|
16
|
+
color: var(--appkit-color-text-faint);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.appkit-textarea:hover:not(:disabled):not(:focus-visible) {
|
|
20
|
+
border-color: var(--appkit-color-zinc-400);
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
.appkit-textarea:focus-visible {
|
|
16
24
|
outline: none;
|
|
17
25
|
border-color: var(--appkit-color-secondary);
|
|
18
|
-
box-shadow: 0 0 0
|
|
26
|
+
box-shadow: 0 0 0 3px rgba(101, 196, 8, 0.25), inset 0 0 0 1px rgba(101, 196, 8, 0.15);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.appkit-textarea:disabled {
|
|
30
|
+
opacity: 0.55;
|
|
31
|
+
cursor: not-allowed;
|
|
32
|
+
background: var(--appkit-color-bg);
|
|
33
|
+
resize: none;
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
.dark .appkit-textarea {
|
|
22
37
|
border-color: var(--appkit-color-border);
|
|
23
|
-
background: rgba(30, 41, 59, 0.
|
|
38
|
+
background: rgba(30, 41, 59, 0.7);
|
|
24
39
|
color: var(--appkit-color-text);
|
|
25
40
|
}
|
|
26
41
|
|
|
42
|
+
.dark .appkit-textarea::placeholder {
|
|
43
|
+
color: var(--appkit-color-text-muted);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.dark .appkit-textarea:hover:not(:disabled):not(:focus-visible) {
|
|
47
|
+
border-color: var(--appkit-color-slate-500);
|
|
48
|
+
}
|
|
49
|
+
|
|
27
50
|
.dark .appkit-textarea:focus-visible {
|
|
28
51
|
border-color: var(--appkit-color-primary);
|
|
29
|
-
box-shadow: 0 0 0
|
|
52
|
+
box-shadow: 0 0 0 3px rgba(233, 30, 140, 0.25), inset 0 0 0 1px rgba(233, 30, 140, 0.15);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.dark .appkit-textarea:disabled {
|
|
56
|
+
background: rgba(30, 41, 59, 0.4);
|
|
30
57
|
}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -55,6 +55,10 @@ export type { SelectProps, SelectOption } from "./components/Select";
|
|
|
55
55
|
export { Select } from "./components/Select";
|
|
56
56
|
export type { InputProps } from "./components/Input";
|
|
57
57
|
export { Input } from "./components/Input";
|
|
58
|
+
export type { OtpInputProps } from "./components/OtpInput";
|
|
59
|
+
export { OtpInput } from "./components/OtpInput";
|
|
60
|
+
export type { DateInputProps, DateRangeInputProps } from "./components/DateInput";
|
|
61
|
+
export { DateInput, DateRangeInput } from "./components/DateInput";
|
|
58
62
|
export type { TextareaProps } from "./components/Textarea";
|
|
59
63
|
export { Textarea } from "./components/Textarea";
|
|
60
64
|
export type { SliderProps } from "./components/Slider";
|
package/dist/ui/index.js
CHANGED
|
@@ -31,6 +31,8 @@ export { BackgroundRenderer } from "./components/BackgroundRenderer";
|
|
|
31
31
|
export { ResponsiveView } from "./components/ResponsiveView";
|
|
32
32
|
export { Select } from "./components/Select";
|
|
33
33
|
export { Input } from "./components/Input";
|
|
34
|
+
export { OtpInput } from "./components/OtpInput";
|
|
35
|
+
export { DateInput, DateRangeInput } from "./components/DateInput";
|
|
34
36
|
export { Textarea } from "./components/Textarea";
|
|
35
37
|
export { Slider } from "./components/Slider";
|
|
36
38
|
export { Checkbox } from "./components/Checkbox";
|