@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.
Files changed (74) hide show
  1. package/dist/_internal/server/features/auctions/og.d.ts +1 -0
  2. package/dist/_internal/server/features/auctions/og.js +2 -1
  3. package/dist/_internal/server/features/blog/og.d.ts +1 -0
  4. package/dist/_internal/server/features/blog/og.js +3 -2
  5. package/dist/_internal/server/features/brands/og.d.ts +1 -0
  6. package/dist/_internal/server/features/brands/og.js +2 -1
  7. package/dist/_internal/server/features/bundles/og.d.ts +1 -0
  8. package/dist/_internal/server/features/bundles/og.js +2 -1
  9. package/dist/_internal/server/features/categories/og.d.ts +1 -0
  10. package/dist/_internal/server/features/categories/og.js +2 -1
  11. package/dist/_internal/server/features/checkout/actions.js +13 -5
  12. package/dist/_internal/server/features/classified/og.d.ts +2 -0
  13. package/dist/_internal/server/features/classified/og.js +2 -1
  14. package/dist/_internal/server/features/digital-code/og.d.ts +2 -0
  15. package/dist/_internal/server/features/digital-code/og.js +2 -1
  16. package/dist/_internal/server/features/events/og.d.ts +1 -0
  17. package/dist/_internal/server/features/events/og.js +3 -1
  18. package/dist/_internal/server/features/live/og.d.ts +2 -0
  19. package/dist/_internal/server/features/live/og.js +2 -1
  20. package/dist/_internal/server/features/pre-orders/og.d.ts +1 -0
  21. package/dist/_internal/server/features/pre-orders/og.js +2 -1
  22. package/dist/_internal/server/features/products/og.d.ts +3 -1
  23. package/dist/_internal/server/features/products/og.js +2 -1
  24. package/dist/_internal/server/features/seo/index.d.ts +1 -1
  25. package/dist/_internal/server/features/seo/index.js +1 -1
  26. package/dist/_internal/server/features/seo/og.d.ts +8 -1
  27. package/dist/_internal/server/features/seo/og.js +28 -8
  28. package/dist/_internal/server/features/stores/og.d.ts +1 -0
  29. package/dist/_internal/server/features/stores/og.js +3 -2
  30. package/dist/_internal/server/features/sublisting-categories/og.d.ts +1 -0
  31. package/dist/_internal/server/features/sublisting-categories/og.js +2 -1
  32. package/dist/_internal/server/jobs/core/onScamReportCreate.js +18 -14
  33. package/dist/_internal/server/jobs/core/onScamReportRejected.js +10 -7
  34. package/dist/_internal/server/jobs/core/onScamReportVerified.js +10 -7
  35. package/dist/_internal/shared/actions/action-registry.js +48 -0
  36. package/dist/client.d.ts +6 -0
  37. package/dist/client.js +3 -0
  38. package/dist/constants/api-endpoints.d.ts +9 -0
  39. package/dist/constants/api-endpoints.js +3 -0
  40. package/dist/features/admin/components/AdminBundlesView.js +20 -2
  41. package/dist/features/admin/components/AdminPayoutsView.js +2 -1
  42. package/dist/features/admin/components/AdminProductsView.js +13 -0
  43. package/dist/features/admin/components/AdminStoresView.js +43 -5
  44. package/dist/features/admin/components/AdminUsersView.js +53 -6
  45. package/dist/features/admin/schemas/firestore.d.ts +1 -1
  46. package/dist/features/blog/components/BlogPostForm.js +1 -0
  47. package/dist/features/categories/components/CategoryForm.js +1 -0
  48. package/dist/features/homepage/components/FeaturedAuctionsSection.d.ts +4 -1
  49. package/dist/features/homepage/components/FeaturedAuctionsSection.js +2 -2
  50. package/dist/features/homepage/components/FeaturedPreOrdersSection.d.ts +4 -1
  51. package/dist/features/homepage/components/FeaturedPreOrdersSection.js +2 -2
  52. package/dist/features/homepage/components/FeaturedProductsSection.d.ts +3 -6
  53. package/dist/features/homepage/components/FeaturedProductsSection.js +3 -47
  54. package/dist/features/homepage/components/SectionCarousel.js +1 -8
  55. package/dist/features/homepage/lib/section-renderer.js +3 -3
  56. package/dist/seed/homepage-sections-seed-data.js +10 -10
  57. package/dist/server.d.ts +1 -1
  58. package/dist/server.js +1 -1
  59. package/dist/tailwind-utilities.css +1 -1
  60. package/dist/ui/components/DateInput.d.ts +27 -0
  61. package/dist/ui/components/DateInput.js +11 -0
  62. package/dist/ui/components/FormField.d.ts +2 -1
  63. package/dist/ui/components/FormField.js +7 -4
  64. package/dist/ui/components/FormField.style.css +13 -0
  65. package/dist/ui/components/HorizontalScroller.js +25 -30
  66. package/dist/ui/components/OtpInput.d.ts +14 -0
  67. package/dist/ui/components/OtpInput.js +55 -0
  68. package/dist/ui/components/OtpInput.style.css +13 -0
  69. package/dist/ui/components/Select.style.css +23 -6
  70. package/dist/ui/components/Textarea.style.css +31 -4
  71. package/dist/ui/components/index.style.css +1 -0
  72. package/dist/ui/index.d.ts +4 -0
  73. package/dist/ui/index.js +2 -0
  74. 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
- export function FormField({ label, name, type = "text", value = "", onChange, onBlur, error, touched, placeholder, required = false, disabled = false, autoComplete, rows, hint, helpText, options = [], onUpload, captureSource, captureMode, accept, maxSizeMB, }) {
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: "appkit-form-field", "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] }));
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: "appkit-form-field", "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] }));
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: "appkit-form-field", "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] }));
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, cloneElement, } from "react";
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 with rows x cols
160
- normalizedItems.reduce((slides, item, i) => {
161
- const cardsPerSlide = 6;
162
- const slideIndex = Math.floor(i / cardsPerSlide);
163
- const itemInSlide = i % cardsPerSlide;
164
- const rowIndex = Math.floor(itemInSlide / 3);
165
- const colIndex = itemInSlide % 3;
166
- if (!slides[slideIndex]) {
167
- slides[slideIndex] = (_jsx("div", { className: "appkit-hscroller__slide grid grid-cols-1 sm:grid-cols-3 gap-4 place-items-center sm:place-items-start", style: { gap: `${gap}px`, width: "100%", flexShrink: 0 }, children: Array.from({ length: 6 }, (_, idx) => (_jsx("div", { className: "appkit-hscroller__item-placeholder w-full sm:w-auto" }, `placeholder-${idx}`))) }, `slide-${slideIndex}`));
168
- }
169
- const slideElement = slides[slideIndex];
170
- const gridIndex = rowIndex * 3 + colIndex;
171
- const childrenArr = slideElement.props.children ?? [];
172
- if (childrenArr[gridIndex]) {
173
- const newChildren = [...childrenArr];
174
- newChildren[gridIndex] = (_jsx("div", { className: [
175
- "appkit-hscroller__item",
176
- snapToItems ? "appkit-hscroller__item--snap" : "",
177
- itemClassName,
178
- "w-full sm:w-auto",
179
- ]
180
- .filter(Boolean)
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.2s ease, box-shadow 0.2s ease;
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.6);
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 2px rgba(101, 196, 8, 0.2);
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 2px rgba(233, 30, 140, 0.2);
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
- box-shadow: 0 0 0 2px rgba(248, 113, 113, 0.2);
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.5;
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.2s ease, box-shadow 0.2s ease;
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 2px rgba(101, 196, 8, 0.2);
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.6);
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 2px rgba(233, 30, 140, 0.2);
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
  }
@@ -1,6 +1,7 @@
1
1
  @import "./Typography.style.css";
2
2
  @import "./Button.style.css";
3
3
  @import "./Input.style.css";
4
+ @import "./OtpInput.style.css";
4
5
  @import "./Textarea.style.css";
5
6
  @import "./Select.style.css";
6
7
  @import "./Accordion.style.css";
@@ -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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "2.7.34",
3
+ "version": "2.7.36",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"