najm-kit 2.1.48 → 2.1.50
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/CHANGELOG.md +29 -0
- package/README.md +69 -1
- package/dist/index.d.ts +29 -2
- package/dist/index.mjs +299 -74
- package/dist/theme.css +56 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.1.49 - 2026-08-04
|
|
4
|
+
|
|
5
|
+
### ImageInput and AvatarInput
|
|
6
|
+
|
|
7
|
+
- Add `previewAlt`, `fallbackImage`, `fallbackAlt`, `unavailableContent`,
|
|
8
|
+
`imageClassName`, `onPreviewError`, `replaceAriaLabel`, and `clearAriaLabel`
|
|
9
|
+
to `ImageInputProps`. `AvatarInput` forwards every new prop unchanged.
|
|
10
|
+
- Preview sources resolve in this priority order: `value`, `fallbackImage`,
|
|
11
|
+
`defaultImage`. Candidate URLs are deduplicated so a failing primary URL is
|
|
12
|
+
never retried through multiple stages. When every candidate fails, the
|
|
13
|
+
broken `<img>` is unmounted and `unavailableContent` (or a neutral default)
|
|
14
|
+
is rendered in its place.
|
|
15
|
+
- `imageVersion` is appended safely to relative, absolute, queried, and
|
|
16
|
+
fragmented URLs. `data:`, `blob:`, `javascript:`, and `file:` URLs are
|
|
17
|
+
left unchanged.
|
|
18
|
+
- Expose `data-image-input-state="empty" | "preview" | "fallback" | "unavailable"`
|
|
19
|
+
on the preview container for styling, testing, and consumer diagnostics.
|
|
20
|
+
- File selection is race-safe: stale `FileReader` completions cannot replace
|
|
21
|
+
a newer value. Object URLs created by the component are tracked so
|
|
22
|
+
consumer-owned blob URLs are never revoked.
|
|
23
|
+
- Replace and clear controls are real `<button>` elements, are reachable
|
|
24
|
+
with the keyboard, and stay visible on touch and coarse-pointer devices.
|
|
25
|
+
Only on `(hover: hover) and (pointer: fine)` desktops do the controls fall
|
|
26
|
+
back to a hover/focus reveal. `focus-visible` always restores visibility.
|
|
27
|
+
- Use logical positioning (`end-*`) so the clear button works correctly in
|
|
28
|
+
RTL layouts.
|
|
29
|
+
- Re-export `ImageInputPreviewSource` and `ImageInputPreviewError` from
|
|
30
|
+
`najm-kit/components/inputs` and the package root.
|
|
31
|
+
|
|
3
32
|
## 2.1.48 - 2026-08-04
|
|
4
33
|
|
|
5
34
|
- Keep responsive card row actions visible on phone, tablet, and touch input,
|
package/README.md
CHANGED
|
@@ -141,12 +141,80 @@ import { Form, FormInput, useNForm } from 'najm-kit';
|
|
|
141
141
|
| Category | Components |
|
|
142
142
|
|----------|-----------|
|
|
143
143
|
| Actions | NButton, IconButton, toggleVariants |
|
|
144
|
-
| Forms | Input, Textarea, Label, Select, Checkbox, RadioGroup, Switch, DateInput, FileInput |
|
|
144
|
+
| Forms | Input, Textarea, Label, Select, Checkbox, RadioGroup, Switch, DateInput, FileInput, ImageInput, AvatarInput |
|
|
145
145
|
| Feedback | Alert, Badge, Progress, Spinner, Toast |
|
|
146
146
|
| Layout | Card, Sheet, Dialog, Popover, DropdownMenu, Tabs |
|
|
147
147
|
| Data | Table (NTable), StatCard, DetailList |
|
|
148
148
|
| Overlays | Command palette, Tooltip, Toast |
|
|
149
149
|
|
|
150
|
+
## ImageInput and AvatarInput
|
|
151
|
+
|
|
152
|
+
`ImageInput` and `AvatarInput` ship with a resilient preview contract so
|
|
153
|
+
consumers do not need to wrap them with application-specific preview
|
|
154
|
+
components.
|
|
155
|
+
|
|
156
|
+
Source precedence:
|
|
157
|
+
|
|
158
|
+
- When `value` is a non-empty string URL, candidates are tried in order:
|
|
159
|
+
1. `value` is the primary preview source.
|
|
160
|
+
2. If the primary source fails, `fallbackImage` is tried when supplied.
|
|
161
|
+
3. `defaultImage` is the last-resort fallback.
|
|
162
|
+
- When `value` is `null` or empty, only `defaultImage` is tracked. The
|
|
163
|
+
`fallbackImage` is intentionally not used in the empty state — a null
|
|
164
|
+
`value` is the consumer's empty-state signal, and only the configured
|
|
165
|
+
default participates in the failed-default → unavailable transition.
|
|
166
|
+
If `defaultImage` itself fails, `onPreviewError({ source: "default" })`
|
|
167
|
+
fires and the unavailable state is rendered.
|
|
168
|
+
|
|
169
|
+
Candidate URLs are deduplicated so the same failing URL is never retried
|
|
170
|
+
through multiple stages. When every candidate fails, the broken `<img>` is
|
|
171
|
+
unmounted and `unavailableContent` (or a neutral default) is rendered in its
|
|
172
|
+
place. A `data-image-input-state="empty" | "preview" | "fallback" | "unavailable"`
|
|
173
|
+
marker is exposed for styling, testing, and consumer diagnostics.
|
|
174
|
+
|
|
175
|
+
Candidate URLs are deduplicated so the same failing URL is never retried
|
|
176
|
+
through multiple stages. When every candidate fails, the broken `<img>` is
|
|
177
|
+
unmounted and `unavailableContent` (or a neutral default) is rendered in its
|
|
178
|
+
place. A `data-image-input-state="empty" | "preview" | "fallback" | "unavailable"`
|
|
179
|
+
marker is exposed for styling, testing, and consumer diagnostics.
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
import { ImageInput } from "najm-kit";
|
|
183
|
+
|
|
184
|
+
<ImageInput
|
|
185
|
+
value="https://cdn.example.com/avatar.png"
|
|
186
|
+
onChange={setAvatar}
|
|
187
|
+
previewAlt="Workspace logo"
|
|
188
|
+
fallbackImage="/assets/logo-default.png"
|
|
189
|
+
fallbackAlt="Default workspace logo"
|
|
190
|
+
unavailableContent={<span>Logo unavailable</span>}
|
|
191
|
+
imageClassName="object-contain"
|
|
192
|
+
imageVersion={cacheBustVersion}
|
|
193
|
+
replaceAriaLabel="Replace workspace logo"
|
|
194
|
+
clearAriaLabel="Remove workspace logo"
|
|
195
|
+
onPreviewError={(err) => log(err)}
|
|
196
|
+
/>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Key behaviors:
|
|
200
|
+
|
|
201
|
+
- The replace and clear controls are real `<button>` elements, are reachable
|
|
202
|
+
with the keyboard (`Enter` and `Space` activate them once), and stay
|
|
203
|
+
visible on touch and coarse-pointer devices. Only on `(hover: hover) and
|
|
204
|
+
(pointer: fine)` desktops do the controls fall back to a hover/focus
|
|
205
|
+
reveal. `focus-visible` always restores visibility.
|
|
206
|
+
- Positioning uses logical properties (`end-*`) so the clear button works
|
|
207
|
+
correctly in RTL layouts.
|
|
208
|
+
- `imageVersion` is appended safely to relative, absolute, queried, and
|
|
209
|
+
fragmented URLs. `data:`, `blob:`, `javascript:`, and `file:` URLs are
|
|
210
|
+
left unchanged.
|
|
211
|
+
- File selection is race-safe: stale `FileReader` completions cannot replace
|
|
212
|
+
a newer value, and object URLs created by the component are tracked so
|
|
213
|
+
consumer-owned blob URLs are never revoked.
|
|
214
|
+
|
|
215
|
+
`AvatarInput` forwards every preview and accessibility prop unchanged while
|
|
216
|
+
preserving its circular, size, fill, and camera-icon defaults.
|
|
217
|
+
|
|
150
218
|
## Hooks
|
|
151
219
|
|
|
152
220
|
```tsx
|
package/dist/index.d.ts
CHANGED
|
@@ -2178,6 +2178,11 @@ interface TimeInputProps extends BaseProps {
|
|
|
2178
2178
|
interface TimeZoneInputProps extends Omit<ComboboxInputProps, "items"> {
|
|
2179
2179
|
items?: SelectItemType[];
|
|
2180
2180
|
}
|
|
2181
|
+
type ImageInputPreviewSource = "value" | "fallback" | "default";
|
|
2182
|
+
interface ImageInputPreviewError {
|
|
2183
|
+
source: ImageInputPreviewSource;
|
|
2184
|
+
src: string;
|
|
2185
|
+
}
|
|
2181
2186
|
interface ImageInputProps extends BaseProps {
|
|
2182
2187
|
value: File | string | null;
|
|
2183
2188
|
onChange: (file: File | null) => void;
|
|
@@ -2192,6 +2197,8 @@ interface ImageInputProps extends BaseProps {
|
|
|
2192
2197
|
previewStyle?: CSSProperties;
|
|
2193
2198
|
/** Class applied to the empty and replace-overlay content inside the preview. */
|
|
2194
2199
|
contentClassName?: string;
|
|
2200
|
+
/** Class applied to the preview <img> element to control object-fit / sizing. */
|
|
2201
|
+
imageClassName?: string;
|
|
2195
2202
|
showPreview?: boolean;
|
|
2196
2203
|
previewPosition?: "top" | "bottom" | "left" | "right";
|
|
2197
2204
|
allowClear?: boolean;
|
|
@@ -2208,6 +2215,26 @@ interface ImageInputProps extends BaseProps {
|
|
|
2208
2215
|
trigger?: "icon" | "button" | "both";
|
|
2209
2216
|
buttonLabel?: string;
|
|
2210
2217
|
disabled?: boolean;
|
|
2218
|
+
/** Accessible name for the primary preview image. Falls back to `replaceTitle`. */
|
|
2219
|
+
previewAlt?: string;
|
|
2220
|
+
/** Accessible name for the fallback/default preview image. Falls back to `previewAlt`. */
|
|
2221
|
+
fallbackAlt?: string;
|
|
2222
|
+
/**
|
|
2223
|
+
* URL tried when the primary `value` fails to load.
|
|
2224
|
+
* The same URL is never attempted twice, so a failing primary cannot loop.
|
|
2225
|
+
*/
|
|
2226
|
+
fallbackImage?: string | null;
|
|
2227
|
+
/**
|
|
2228
|
+
* Rendered inside the preview area when every candidate URL has failed.
|
|
2229
|
+
* When omitted, a small neutral unavailable state is rendered instead.
|
|
2230
|
+
*/
|
|
2231
|
+
unavailableContent?: React.ReactNode;
|
|
2232
|
+
/** Fires once for every URL that failed to load, with its source. */
|
|
2233
|
+
onPreviewError?: (error: ImageInputPreviewError) => void;
|
|
2234
|
+
/** Accessible name for the replace (re-upload) control. */
|
|
2235
|
+
replaceAriaLabel?: string;
|
|
2236
|
+
/** Accessible name for the clear control. */
|
|
2237
|
+
clearAriaLabel?: string;
|
|
2211
2238
|
}
|
|
2212
2239
|
interface OtpInputProps extends BaseProps {
|
|
2213
2240
|
value: string;
|
|
@@ -2334,7 +2361,7 @@ interface NUploaderProps {
|
|
|
2334
2361
|
}
|
|
2335
2362
|
declare function NUploader({ title, subtitle, accept, multiple, disabled, items, listTitle, className, dropzoneClassName, onFilesSelected, onCancel, onRemove, }: NUploaderProps): react_jsx_runtime.JSX.Element;
|
|
2336
2363
|
|
|
2337
|
-
declare function ImageInput({ value, onChange, containerClassName, previewClassName, previewStyle, contentClassName, showPreview, previewPosition, allowClear, accept, defaultImage, imageSize, imageVersion, disabled, uploadIcon, title, subtitle, titleClassName, subtitleClassName, replaceTitle, replaceSubtitle, trigger, buttonLabel, }: ImageInputProps): react_jsx_runtime.JSX.Element;
|
|
2364
|
+
declare function ImageInput({ value, onChange, containerClassName, previewClassName, previewStyle, contentClassName, imageClassName, showPreview, previewPosition, allowClear, accept, defaultImage, fallbackImage, previewAlt, fallbackAlt, unavailableContent, onPreviewError, replaceAriaLabel, clearAriaLabel, imageSize, imageVersion, disabled, uploadIcon, title, subtitle, titleClassName, subtitleClassName, replaceTitle, replaceSubtitle, trigger, buttonLabel, }: ImageInputProps): react_jsx_runtime.JSX.Element;
|
|
2338
2365
|
|
|
2339
2366
|
/** A circular image picker preset for profile photos and user avatars. */
|
|
2340
2367
|
declare function AvatarInput({ imageSize, size, fill, radius, containerClassName, previewClassName, previewStyle, contentClassName, uploadIcon, title, subtitleClassName, trigger, ...props }: AvatarInputProps): react_jsx_runtime.JSX.Element;
|
|
@@ -3905,4 +3932,4 @@ interface NGridItemProps extends Omit<React$1.AllHTMLAttributes<HTMLDivElement>,
|
|
|
3905
3932
|
declare function NGrid({ as: Comp, children, cols, smCols, mdCols, lgCols, xlCols, gap, className, style, ...props }: NGridProps): react_jsx_runtime.JSX.Element;
|
|
3906
3933
|
declare function NGridItem({ children, span, smSpan, mdSpan, lgSpan, xlSpan, className, ...props }: NGridItemProps): react_jsx_runtime.JSX.Element;
|
|
3907
3934
|
|
|
3908
|
-
export { Alert, type AlertLook, type AlertOrientation, type AlertProps, type AlertSize, type AlertTone, type AlertVariant, NCard as AsyncCard, Avatar, AvatarFallback, AvatarFormInput, type AvatarFormInputProps, AvatarGroup, type AvatarGroupProps, AvatarImage, AvatarInput, type AvatarInputProps, type AvatarInputRadius, type AvatarProps$1 as AvatarProps, type AvatarShape$1 as AvatarShape, type AvatarSize, AvatarStatus, type AvatarStatusType, Badge, type BadgeColor, type BadgeIcon, type BadgeLook, type BadgeProps, type BadgeShape, type BadgeSize, type BadgeVariant, BaseInput, type BuildDefaultFileColumnsOptions, Button, type ButtonConfig, type ButtonIcon, type ButtonLoaderPosition, type ButtonProps, type ButtonRounded, type ButtonSize, type ButtonVariant, Calendar, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxGroupInput, type CheckboxGroupInputProps, CheckboxInput, type CheckboxInputProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorArrayInput, type ColorArrayInputProps, type ColorFormat, ColorPickerInput, type ColorPickerInputProps, Combobox, ComboboxInput, type ComboboxInputProps, type ComboboxOption, type ComboboxProps, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, type ContextMenuItem, DEFAULT_THEME_FILE_NAME, DateInput, type DateInputProps, type DeleteDialogOptions, Dialog, type DialogActionMode, type DialogApi, DialogClose, type DialogConfig, DialogContent, type DialogContentProps, DialogDescription, DialogFooter, DialogHeader, type DialogHeight, DialogOverlay, type DialogPadding, DialogPortal, type DialogRenderContext, type DialogRenderer, type DialogSize, type DialogStore, DialogTitle, DialogTrigger, type DialogVariant, type DialogWidth, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DynamicArray, type DynamicArrayProps, EmojiInput, type EmojiInputProps, type FileBrowserMode, FileImportButton, FileInput, type FileInputProps, type FileNode, Form, FormControl, FormDescription, FormField, FormInput, type FormInputBackground, type FormInputProps, FormItem, FormLabel, FormMessage, type FormProps, type FormSlotClassNames, type FormVariant, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, ImageInput, type ImageInputProps, Indicator, type IndicatorHorizontal, type IndicatorOverlay, type IndicatorPosition, type IndicatorProps, type IndicatorResponsivePosition, type IndicatorSize, type IndicatorVertical, Input, type InputIcon, Label, LangInput, type LangInputProps, type LinkComponentType, MultiSelectInput, type MultiSelectInputProps, NAJM_COMPONENT_NAMES, NAlert, type NAppCommandItem, NAppShell, type NAppShellAction, type NAppShellClassNames, type NAppShellProps, type NAppShellUser, NCard as NAsyncCard, type CardClassNames as NAsyncCardClassNames, type CardProps as NAsyncCardProps, NAvatar, type NAvatarClassNames, type NAvatarProps, type AvatarShape as NAvatarShape, NBadge, type NBadgeLook, type NBadgeProps, type NBulkAction, type NBulkActionButton, type NBulkActionSelect, NBulkActionsBar, type NBulkActionsBarProps, NButton, type NButtonProps, NCard, NCardAction, type CardClassNames as NCardClassNames, type NCardDensity, NCardFooter, NCardInfo, type NCardInfoProps, NCardMedia, type NCardMediaAspect, type NCardMediaPlacement, type NCardMediaProps, type NCardMediaSize, type NCardMediaVariant, type CardProps as NCardProps, NCardSection, type NCardSectionProps, type NCardSectionSurface, NCommandPalette, type NCommandPaletteProps, NConfirmDialog, type NConfirmDialogProps, NContextMenu, type NContextMenuItem, type NContextMenuProps, NDataCardShell, type NDataCardShellActions, type NDataCardShellProps, NDeleteDialog, NDeleteDialogContent, type NDeleteDialogContentProps, type NDeleteDialogProps, NDetailCard, type NDetailCardClassNames, type NDetailCardProps, NDetailItem, type NDetailItemProps, NDetailList, type NDetailListItem, type NDetailListProps, NDialog, type NDialogActionProps, NDialogDescription, type NDialogDescriptionProps, type NDialogDirectProps, NDialogHeader, type NDialogHeaderProps, NDialogPrimaryButton, type NDialogProps, NDialogSecondaryButton, NDonutCard, type NDonutCardClassNames, type NDonutCardItem, type NDonutCardLayout, type NDonutCardLegendMarker, type NDonutCardProps, type NDonutCardVariant, type NEditorTab, NEditorTabs, type NEditorTabsProps, NEmptyState, type NEmptyStateProps, NErrorBoundary, NErrorState, type NErrorStateProps, NFileBrowser, type NFileBrowserCardProps, type NFileBrowserProps, type NFileBrowserRenderThumbProps, NFileTypeIcon, type NFileTypeIconProps, NFilterBar, NFolderIcon, type NFolderIconProps, NForm, NFormSectionHeader, type NFormSectionHeaderProps, NGrid, type NGridCols, NGridItem, type NGridItemProps, type NGridProps, type NGridSpan, NIcon, type NIconProps, type NIconSource, NIndicator, NInspectorSheet, NLoadingState, type NLoadingStateProps, NMultiDialog, type NMultiDialogProps, NNavbar, NPageHeader, NPageHeaderActions, type NPageHeaderBreakpoint, NPageHeaderCompactActions, NPageHeaderFilters, type NPageHeaderProps, NPageHeaderTop, NPageLayout, type NPageLayoutProps, NPortalScopeProvider, NProgress, type NProgressProps, NRowActions, NSection, NSectionHeader, type NSectionHeaderActionsProps, type NSectionHeaderContentProps, type NSectionHeaderProps, type NSectionHeaderSubtitleProps, type NSectionHeaderTitleProps, NSectionInfo, type NSectionInfoProps, type NSectionProps, NSectionWithInfo, type NSectionWithInfoItem, type NSectionWithInfoProps, NSheet, type NSheetClassNames, type NSheetProps, NSidebar, NSidebarContent, type NSidebarContentProps, NSidebarFooter, type NSidebarFooterProps, NSidebarHeader, type NSidebarHeaderProps, NSidebarItem, NSidebarLogo, type NSidebarLogoProps, NSidebarMobile, type NSidebarMobileProps, NSidebarSection, type NSidebarSectionProps, NSkeleton, NSkeletonCalendar, NSkeletonChart, NSkeletonDonut, NSkeletonEventList, NSkeletonWidget, NSkeletonWidgets, NSlider, type NSliderProps, NSmartPasteDialog, type NSmartPasteDialogProps, NSpinner, type NSpinnerProps, NStatCard, type NStatCardClassNames, type NStatCardProps, NStatCardSkeleton, type NStatCardVariant, Swap as NSwap, type NSwapProps, NTable, type NTableCardPagination, NTableCardRoot, type NTableCardRootProps, NTableCards, type NTableClassNames, type NTableColumnBreakpoint, type NTableColumnDef, type NTableColumnMeta, NTableContent, NTableHeader, type NTableLoadMorePagination, NTableLoadingSkeleton, type NTableMenu, type NTableMenuProp, NTablePagination, type NTableProps, NTableRowSkeleton, NTableSkeleton, type NTableState, NTabs, type NTabsClassNames, type NTabsColor, type NTabsItem, type NTabsProps, type NTabsStyles, NThemeCustomizer, type NThemeCustomizerFontOption, type NThemeCustomizerLabels, type NThemeCustomizerProps, type NThemeCustomizerTab, NUploader, type NUploaderItem, type NUploaderItemStatus, type NUploaderProps, NViewBody, NViewToggle, type NajmAccent, type NajmAppearance, type NajmBorderSide, type NajmComponentName, type NajmComponentRadius, type NajmComponentStyleConfig, type NajmComponentThemeConfig, type NajmDensity, type NajmDesignConfig, NajmDesignProvider, type NajmDesignProviderProps, type NajmLayoutConfig, type NajmMode, type NajmPreset, type NajmResponsiveBreakpoint, type NajmResponsiveValue, NajmScroll, type NajmScrollProps, type NajmSlotStyle, type NajmThemeConfig, NajmThemeProvider, type NajmThemeProviderProps, type NajmThemeTokens, type NajmTypographyConfig, type NajmVariantStyle, NativeSelect, type NativeSelectOption, type NativeSelectProps, type NavItem, type NavItemGroup, NumberInput, type NumberInputProps, OtpInput, type OtpInputProps, PasswordInput, type PasswordInputProps, PhoneInput, type PhoneInputProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PrefixProvider, Progress, type ProgressColor, type ProgressLabelPosition, type ProgressProps, type ProgressSize, type PushDialogOptions, RADIUS_VALUE_MAP, RadioGroup, RadioGroupInput, type RadioGroupInputProps, RadioGroupItem, type RenderSlot, RepeatingFields, type RepeatingFieldsProps, ScrollArea, type ScrollAreaProps, SearchField, SearchField as SearchInput, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectInput, type SelectInputProps, SelectItem, type SelectItemType$1 as SelectItemDataType, type SelectItemType, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, type SidebarItemProps, type SidebarProps, type SidebarWidth, type SidebarWidths, SimpleTooltip, type SimpleTooltipProps, NSkeleton as Skeleton, Slider, SliderInput, type SliderInputProps, type SliderOrientation, type SliderProps, type SliderSize, type SliderVariant, type SmartPastePreview, type SpinnerVariant, StarRatingInput, type StarRatingInputProps, StatusPill, type StatusPillProps, type StatusPillTone, type StepConfig, StepIndicator, type StepMeta, StepsHeader, StepsProgress, type StorageMenuAction, type StorageSortOption, type StorageTarget, Swap, type SwapEffect, SwapIndeterminate, SwapOff, SwapOn, type SwapProps, type SwapSize, type SwapState, Switch, type SwitchColor, SwitchInput, type SwitchInputProps, type SwitchSize, TAB_COLORS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableHeaderColor, TableRow, type TableState, type TableStore, TableStoreContext, Tabs, TabsContent, TabsList, type TabsListProps, type TabsOrientation, type TabsProps, TabsTrigger, type TabsTriggerProps, type TabsVariant, TextAreaInput, type TextAreaInputProps, TextInput, type TextInputProps, Textarea, TimeInput, type TimeInputProps, TimeZoneInput, type TimeZoneInputProps, Toaster, Toggle, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UseContextMenuResult, type UseNFormOptions, type UseStorageContextMenuOptions, type UseStorageContextMenuResult, type UserMenuAction, VariantProvider, type WizardClassNames, WizardForm, type WizardFormProps, alertVariants, avatarVariants, badgeColorVariants, badgeVariants, buildDefaultFileColumns, buttonVariants, cn, composePreset, createDialogStore, createTableStore, defineNajmDesignConfig, defineNajmThemeConfig, detectFormat, dialogVariants, filterResponsiveColumns, formatColor, formatFileBytes, formatFileRelative, getIconColorProps, getNextEditorTabValue, hiddenBelowClasses, indicatorVariants, inputBorderClasses, normalizeThemeFileName, parseColor, parseNajmDesignConfig, parseNajmThemeConfig, parseThemeFile, resolveHiddenBelowClass, resolvePreset, resolveRadiusValue, resolveSlot, resolveVariantAlias, sidebarBorderClasses, sliderVariants, stringifyNajmDesignConfig, stringifyNajmThemeConfig, stringifyThemeFile, surfaceBorderClasses, swapVariants, toPickerHex, toggleVariants, tooltipContentVariants, truncateByCharacters, useClickOutside, useContextMenu, useDebouncedValue, useDelayedLoading, useDialog, useDialogStore, useDynamicPageSize, useFormField, useFormSubmission, useInfiniteScroll, useKeyboard, useLocalStorageState, useNForm, useNPortalScope, useNajmAppearance, useNajmComponentStyle, useNajmDesign, useNajmThemeMode, usePrefix, useSelection, useStepNavigation, useStorageContextMenu, useStoreSync, useTable, useTableKeyboard, useTableStore, useVariant, useVariantPreset };
|
|
3935
|
+
export { Alert, type AlertLook, type AlertOrientation, type AlertProps, type AlertSize, type AlertTone, type AlertVariant, NCard as AsyncCard, Avatar, AvatarFallback, AvatarFormInput, type AvatarFormInputProps, AvatarGroup, type AvatarGroupProps, AvatarImage, AvatarInput, type AvatarInputProps, type AvatarInputRadius, type AvatarProps$1 as AvatarProps, type AvatarShape$1 as AvatarShape, type AvatarSize, AvatarStatus, type AvatarStatusType, Badge, type BadgeColor, type BadgeIcon, type BadgeLook, type BadgeProps, type BadgeShape, type BadgeSize, type BadgeVariant, BaseInput, type BuildDefaultFileColumnsOptions, Button, type ButtonConfig, type ButtonIcon, type ButtonLoaderPosition, type ButtonProps, type ButtonRounded, type ButtonSize, type ButtonVariant, Calendar, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxGroupInput, type CheckboxGroupInputProps, CheckboxInput, type CheckboxInputProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorArrayInput, type ColorArrayInputProps, type ColorFormat, ColorPickerInput, type ColorPickerInputProps, Combobox, ComboboxInput, type ComboboxInputProps, type ComboboxOption, type ComboboxProps, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, type ContextMenuItem, DEFAULT_THEME_FILE_NAME, DateInput, type DateInputProps, type DeleteDialogOptions, Dialog, type DialogActionMode, type DialogApi, DialogClose, type DialogConfig, DialogContent, type DialogContentProps, DialogDescription, DialogFooter, DialogHeader, type DialogHeight, DialogOverlay, type DialogPadding, DialogPortal, type DialogRenderContext, type DialogRenderer, type DialogSize, type DialogStore, DialogTitle, DialogTrigger, type DialogVariant, type DialogWidth, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DynamicArray, type DynamicArrayProps, EmojiInput, type EmojiInputProps, type FileBrowserMode, FileImportButton, FileInput, type FileInputProps, type FileNode, Form, FormControl, FormDescription, FormField, FormInput, type FormInputBackground, type FormInputProps, FormItem, FormLabel, FormMessage, type FormProps, type FormSlotClassNames, type FormVariant, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonVariant, ImageInput, type ImageInputPreviewError, type ImageInputPreviewSource, type ImageInputProps, Indicator, type IndicatorHorizontal, type IndicatorOverlay, type IndicatorPosition, type IndicatorProps, type IndicatorResponsivePosition, type IndicatorSize, type IndicatorVertical, Input, type InputIcon, Label, LangInput, type LangInputProps, type LinkComponentType, MultiSelectInput, type MultiSelectInputProps, NAJM_COMPONENT_NAMES, NAlert, type NAppCommandItem, NAppShell, type NAppShellAction, type NAppShellClassNames, type NAppShellProps, type NAppShellUser, NCard as NAsyncCard, type CardClassNames as NAsyncCardClassNames, type CardProps as NAsyncCardProps, NAvatar, type NAvatarClassNames, type NAvatarProps, type AvatarShape as NAvatarShape, NBadge, type NBadgeLook, type NBadgeProps, type NBulkAction, type NBulkActionButton, type NBulkActionSelect, NBulkActionsBar, type NBulkActionsBarProps, NButton, type NButtonProps, NCard, NCardAction, type CardClassNames as NCardClassNames, type NCardDensity, NCardFooter, NCardInfo, type NCardInfoProps, NCardMedia, type NCardMediaAspect, type NCardMediaPlacement, type NCardMediaProps, type NCardMediaSize, type NCardMediaVariant, type CardProps as NCardProps, NCardSection, type NCardSectionProps, type NCardSectionSurface, NCommandPalette, type NCommandPaletteProps, NConfirmDialog, type NConfirmDialogProps, NContextMenu, type NContextMenuItem, type NContextMenuProps, NDataCardShell, type NDataCardShellActions, type NDataCardShellProps, NDeleteDialog, NDeleteDialogContent, type NDeleteDialogContentProps, type NDeleteDialogProps, NDetailCard, type NDetailCardClassNames, type NDetailCardProps, NDetailItem, type NDetailItemProps, NDetailList, type NDetailListItem, type NDetailListProps, NDialog, type NDialogActionProps, NDialogDescription, type NDialogDescriptionProps, type NDialogDirectProps, NDialogHeader, type NDialogHeaderProps, NDialogPrimaryButton, type NDialogProps, NDialogSecondaryButton, NDonutCard, type NDonutCardClassNames, type NDonutCardItem, type NDonutCardLayout, type NDonutCardLegendMarker, type NDonutCardProps, type NDonutCardVariant, type NEditorTab, NEditorTabs, type NEditorTabsProps, NEmptyState, type NEmptyStateProps, NErrorBoundary, NErrorState, type NErrorStateProps, NFileBrowser, type NFileBrowserCardProps, type NFileBrowserProps, type NFileBrowserRenderThumbProps, NFileTypeIcon, type NFileTypeIconProps, NFilterBar, NFolderIcon, type NFolderIconProps, NForm, NFormSectionHeader, type NFormSectionHeaderProps, NGrid, type NGridCols, NGridItem, type NGridItemProps, type NGridProps, type NGridSpan, NIcon, type NIconProps, type NIconSource, NIndicator, NInspectorSheet, NLoadingState, type NLoadingStateProps, NMultiDialog, type NMultiDialogProps, NNavbar, NPageHeader, NPageHeaderActions, type NPageHeaderBreakpoint, NPageHeaderCompactActions, NPageHeaderFilters, type NPageHeaderProps, NPageHeaderTop, NPageLayout, type NPageLayoutProps, NPortalScopeProvider, NProgress, type NProgressProps, NRowActions, NSection, NSectionHeader, type NSectionHeaderActionsProps, type NSectionHeaderContentProps, type NSectionHeaderProps, type NSectionHeaderSubtitleProps, type NSectionHeaderTitleProps, NSectionInfo, type NSectionInfoProps, type NSectionProps, NSectionWithInfo, type NSectionWithInfoItem, type NSectionWithInfoProps, NSheet, type NSheetClassNames, type NSheetProps, NSidebar, NSidebarContent, type NSidebarContentProps, NSidebarFooter, type NSidebarFooterProps, NSidebarHeader, type NSidebarHeaderProps, NSidebarItem, NSidebarLogo, type NSidebarLogoProps, NSidebarMobile, type NSidebarMobileProps, NSidebarSection, type NSidebarSectionProps, NSkeleton, NSkeletonCalendar, NSkeletonChart, NSkeletonDonut, NSkeletonEventList, NSkeletonWidget, NSkeletonWidgets, NSlider, type NSliderProps, NSmartPasteDialog, type NSmartPasteDialogProps, NSpinner, type NSpinnerProps, NStatCard, type NStatCardClassNames, type NStatCardProps, NStatCardSkeleton, type NStatCardVariant, Swap as NSwap, type NSwapProps, NTable, type NTableCardPagination, NTableCardRoot, type NTableCardRootProps, NTableCards, type NTableClassNames, type NTableColumnBreakpoint, type NTableColumnDef, type NTableColumnMeta, NTableContent, NTableHeader, type NTableLoadMorePagination, NTableLoadingSkeleton, type NTableMenu, type NTableMenuProp, NTablePagination, type NTableProps, NTableRowSkeleton, NTableSkeleton, type NTableState, NTabs, type NTabsClassNames, type NTabsColor, type NTabsItem, type NTabsProps, type NTabsStyles, NThemeCustomizer, type NThemeCustomizerFontOption, type NThemeCustomizerLabels, type NThemeCustomizerProps, type NThemeCustomizerTab, NUploader, type NUploaderItem, type NUploaderItemStatus, type NUploaderProps, NViewBody, NViewToggle, type NajmAccent, type NajmAppearance, type NajmBorderSide, type NajmComponentName, type NajmComponentRadius, type NajmComponentStyleConfig, type NajmComponentThemeConfig, type NajmDensity, type NajmDesignConfig, NajmDesignProvider, type NajmDesignProviderProps, type NajmLayoutConfig, type NajmMode, type NajmPreset, type NajmResponsiveBreakpoint, type NajmResponsiveValue, NajmScroll, type NajmScrollProps, type NajmSlotStyle, type NajmThemeConfig, NajmThemeProvider, type NajmThemeProviderProps, type NajmThemeTokens, type NajmTypographyConfig, type NajmVariantStyle, NativeSelect, type NativeSelectOption, type NativeSelectProps, type NavItem, type NavItemGroup, NumberInput, type NumberInputProps, OtpInput, type OtpInputProps, PasswordInput, type PasswordInputProps, PhoneInput, type PhoneInputProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PrefixProvider, Progress, type ProgressColor, type ProgressLabelPosition, type ProgressProps, type ProgressSize, type PushDialogOptions, RADIUS_VALUE_MAP, RadioGroup, RadioGroupInput, type RadioGroupInputProps, RadioGroupItem, type RenderSlot, RepeatingFields, type RepeatingFieldsProps, ScrollArea, type ScrollAreaProps, SearchField, SearchField as SearchInput, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectInput, type SelectInputProps, SelectItem, type SelectItemType$1 as SelectItemDataType, type SelectItemType, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, type SidebarItemProps, type SidebarProps, type SidebarWidth, type SidebarWidths, SimpleTooltip, type SimpleTooltipProps, NSkeleton as Skeleton, Slider, SliderInput, type SliderInputProps, type SliderOrientation, type SliderProps, type SliderSize, type SliderVariant, type SmartPastePreview, type SpinnerVariant, StarRatingInput, type StarRatingInputProps, StatusPill, type StatusPillProps, type StatusPillTone, type StepConfig, StepIndicator, type StepMeta, StepsHeader, StepsProgress, type StorageMenuAction, type StorageSortOption, type StorageTarget, Swap, type SwapEffect, SwapIndeterminate, SwapOff, SwapOn, type SwapProps, type SwapSize, type SwapState, Switch, type SwitchColor, SwitchInput, type SwitchInputProps, type SwitchSize, TAB_COLORS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableHeaderColor, TableRow, type TableState, type TableStore, TableStoreContext, Tabs, TabsContent, TabsList, type TabsListProps, type TabsOrientation, type TabsProps, TabsTrigger, type TabsTriggerProps, type TabsVariant, TextAreaInput, type TextAreaInputProps, TextInput, type TextInputProps, Textarea, TimeInput, type TimeInputProps, TimeZoneInput, type TimeZoneInputProps, Toaster, Toggle, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UseContextMenuResult, type UseNFormOptions, type UseStorageContextMenuOptions, type UseStorageContextMenuResult, type UserMenuAction, VariantProvider, type WizardClassNames, WizardForm, type WizardFormProps, alertVariants, avatarVariants, badgeColorVariants, badgeVariants, buildDefaultFileColumns, buttonVariants, cn, composePreset, createDialogStore, createTableStore, defineNajmDesignConfig, defineNajmThemeConfig, detectFormat, dialogVariants, filterResponsiveColumns, formatColor, formatFileBytes, formatFileRelative, getIconColorProps, getNextEditorTabValue, hiddenBelowClasses, indicatorVariants, inputBorderClasses, normalizeThemeFileName, parseColor, parseNajmDesignConfig, parseNajmThemeConfig, parseThemeFile, resolveHiddenBelowClass, resolvePreset, resolveRadiusValue, resolveSlot, resolveVariantAlias, sidebarBorderClasses, sliderVariants, stringifyNajmDesignConfig, stringifyNajmThemeConfig, stringifyThemeFile, surfaceBorderClasses, swapVariants, toPickerHex, toggleVariants, tooltipContentVariants, truncateByCharacters, useClickOutside, useContextMenu, useDebouncedValue, useDelayedLoading, useDialog, useDialogStore, useDynamicPageSize, useFormField, useFormSubmission, useInfiniteScroll, useKeyboard, useLocalStorageState, useNForm, useNPortalScope, useNajmAppearance, useNajmComponentStyle, useNajmDesign, useNajmThemeMode, usePrefix, useSelection, useStepNavigation, useStorageContextMenu, useStoreSync, useTable, useTableKeyboard, useTableStore, useVariant, useVariantPreset };
|
package/dist/index.mjs
CHANGED
|
@@ -7508,6 +7508,14 @@ function mediaRootClasses(layout) {
|
|
|
7508
7508
|
}
|
|
7509
7509
|
return "grid grid-cols-[auto_minmax(0,1fr)] gap-3 p-3 sm:flex sm:flex-col sm:p-4";
|
|
7510
7510
|
}
|
|
7511
|
+
function CardMediaBody({
|
|
7512
|
+
children,
|
|
7513
|
+
className,
|
|
7514
|
+
grouped
|
|
7515
|
+
}) {
|
|
7516
|
+
if (!grouped) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
7517
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "card-body", className, children });
|
|
7518
|
+
}
|
|
7511
7519
|
function NCard({
|
|
7512
7520
|
children,
|
|
7513
7521
|
title,
|
|
@@ -7624,11 +7632,11 @@ function NCard({
|
|
|
7624
7632
|
}
|
|
7625
7633
|
) : null,
|
|
7626
7634
|
/* @__PURE__ */ jsxs(
|
|
7627
|
-
|
|
7635
|
+
CardMediaBody,
|
|
7628
7636
|
{
|
|
7629
|
-
|
|
7637
|
+
grouped: compactSideBody,
|
|
7630
7638
|
className: cn(
|
|
7631
|
-
|
|
7639
|
+
"col-start-2 row-start-1 flex min-w-0 flex-col gap-2 self-start",
|
|
7632
7640
|
(responsiveAvatarBody || responsiveImageBody) && "sm:contents"
|
|
7633
7641
|
),
|
|
7634
7642
|
children: [
|
|
@@ -9850,12 +9858,74 @@ function UploaderRow({ item, onCancel, onRemove }) {
|
|
|
9850
9858
|
}
|
|
9851
9859
|
);
|
|
9852
9860
|
}
|
|
9861
|
+
|
|
9862
|
+
// src/components/inputs/imagePreview.ts
|
|
9863
|
+
var NON_APPENDABLE_PREFIXES = ["data:", "blob:", "javascript:", "file:"];
|
|
9864
|
+
function isNonAppendable(src) {
|
|
9865
|
+
const lower = src.toLowerCase();
|
|
9866
|
+
return NON_APPENDABLE_PREFIXES.some((prefix) => lower.startsWith(prefix));
|
|
9867
|
+
}
|
|
9868
|
+
function isMeaningful(src) {
|
|
9869
|
+
return typeof src === "string" && src.length > 0;
|
|
9870
|
+
}
|
|
9871
|
+
function appendImageVersion(src, version) {
|
|
9872
|
+
if (!isMeaningful(src)) return src;
|
|
9873
|
+
if (version == null || version === "") return src;
|
|
9874
|
+
if (isNonAppendable(src)) return src;
|
|
9875
|
+
const fragmentIndex = src.indexOf("#");
|
|
9876
|
+
const beforeFragment = fragmentIndex === -1 ? src : src.slice(0, fragmentIndex);
|
|
9877
|
+
const fragment = fragmentIndex === -1 ? "" : src.slice(fragmentIndex);
|
|
9878
|
+
const queryIndex = beforeFragment.indexOf("?");
|
|
9879
|
+
const base = queryIndex === -1 ? beforeFragment : beforeFragment.slice(0, queryIndex);
|
|
9880
|
+
const existingQuery = queryIndex === -1 ? "" : beforeFragment.slice(queryIndex);
|
|
9881
|
+
const separator = existingQuery ? "&" : "?";
|
|
9882
|
+
const versionString = `${separator}v=${encodeURIComponent(String(version))}`;
|
|
9883
|
+
if (!existingQuery && !fragment) {
|
|
9884
|
+
return `${base}${versionString}`;
|
|
9885
|
+
}
|
|
9886
|
+
if (!existingQuery) {
|
|
9887
|
+
return `${base}${versionString}${fragment}`;
|
|
9888
|
+
}
|
|
9889
|
+
if (!fragment) {
|
|
9890
|
+
return `${base}${existingQuery}${versionString}`;
|
|
9891
|
+
}
|
|
9892
|
+
return `${base}${existingQuery}${versionString}${fragment}`;
|
|
9893
|
+
}
|
|
9894
|
+
function appendVersionToCandidate(src, version) {
|
|
9895
|
+
if (version == null || version === "") return src;
|
|
9896
|
+
if (isNonAppendable(src)) return src;
|
|
9897
|
+
return appendImageVersion(src, version);
|
|
9898
|
+
}
|
|
9899
|
+
function buildPreviewCandidates(options) {
|
|
9900
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9901
|
+
const result = [];
|
|
9902
|
+
const push = (src, source) => {
|
|
9903
|
+
if (!isMeaningful(src)) return;
|
|
9904
|
+
if (seen.has(src)) return;
|
|
9905
|
+
seen.add(src);
|
|
9906
|
+
result.push({ src, source });
|
|
9907
|
+
};
|
|
9908
|
+
push(options.value ?? null, "value");
|
|
9909
|
+
push(options.fallback ?? null, "fallback");
|
|
9910
|
+
push(options.defaultImage ?? null, "default");
|
|
9911
|
+
const version = options.imageVersion;
|
|
9912
|
+
if (version == null || version === "") return result;
|
|
9913
|
+
return result.map((candidate) => ({
|
|
9914
|
+
src: appendVersionToCandidate(candidate.src, version),
|
|
9915
|
+
source: candidate.source
|
|
9916
|
+
}));
|
|
9917
|
+
}
|
|
9918
|
+
function candidatesKey(candidates) {
|
|
9919
|
+
return candidates.map((candidate) => `${candidate.source}:${candidate.src}`).join("|");
|
|
9920
|
+
}
|
|
9853
9921
|
var IMAGE_SIZE_MAP = {
|
|
9854
9922
|
sm: "w-16 h-16",
|
|
9855
9923
|
md: "w-24 h-24",
|
|
9856
9924
|
lg: "w-32 h-32",
|
|
9857
9925
|
xl: "w-40 h-40"
|
|
9858
9926
|
};
|
|
9927
|
+
var CONTROL_VISIBILITY = "nimage-input-control";
|
|
9928
|
+
var COMPACT_OVERLAY_VISIBILITY = "nimage-input-compact-overlay";
|
|
9859
9929
|
function ImageInput({
|
|
9860
9930
|
value,
|
|
9861
9931
|
onChange,
|
|
@@ -9863,11 +9933,19 @@ function ImageInput({
|
|
|
9863
9933
|
previewClassName,
|
|
9864
9934
|
previewStyle,
|
|
9865
9935
|
contentClassName,
|
|
9936
|
+
imageClassName,
|
|
9866
9937
|
showPreview = true,
|
|
9867
9938
|
previewPosition = "top",
|
|
9868
9939
|
allowClear = true,
|
|
9869
9940
|
accept = "image/*",
|
|
9870
9941
|
defaultImage,
|
|
9942
|
+
fallbackImage,
|
|
9943
|
+
previewAlt,
|
|
9944
|
+
fallbackAlt,
|
|
9945
|
+
unavailableContent,
|
|
9946
|
+
onPreviewError,
|
|
9947
|
+
replaceAriaLabel,
|
|
9948
|
+
clearAriaLabel,
|
|
9871
9949
|
imageSize = "md",
|
|
9872
9950
|
imageVersion,
|
|
9873
9951
|
disabled = false,
|
|
@@ -9882,117 +9960,242 @@ function ImageInput({
|
|
|
9882
9960
|
buttonLabel = "Upload"
|
|
9883
9961
|
}) {
|
|
9884
9962
|
const fileInputRef = useRef(null);
|
|
9885
|
-
const [
|
|
9963
|
+
const [localFilePreview, setLocalFilePreview] = useState(null);
|
|
9964
|
+
const [failedSources, setFailedSources] = useState(() => /* @__PURE__ */ new Set());
|
|
9965
|
+
const readerTokenRef = useRef(0);
|
|
9966
|
+
const previewCandidates = useMemo(() => {
|
|
9967
|
+
if (value instanceof File) return [];
|
|
9968
|
+
if (typeof value === "string" && value) {
|
|
9969
|
+
return buildPreviewCandidates({
|
|
9970
|
+
value,
|
|
9971
|
+
fallback: fallbackImage ?? null,
|
|
9972
|
+
defaultImage: defaultImage ?? null,
|
|
9973
|
+
imageVersion
|
|
9974
|
+
});
|
|
9975
|
+
}
|
|
9976
|
+
return buildPreviewCandidates({
|
|
9977
|
+
value: null,
|
|
9978
|
+
fallback: null,
|
|
9979
|
+
defaultImage: defaultImage ?? null,
|
|
9980
|
+
imageVersion
|
|
9981
|
+
});
|
|
9982
|
+
}, [value, fallbackImage, defaultImage, imageVersion]);
|
|
9983
|
+
const candidateKeyValue = useMemo(
|
|
9984
|
+
() => candidatesKey(previewCandidates),
|
|
9985
|
+
[previewCandidates]
|
|
9986
|
+
);
|
|
9987
|
+
const [trackedKey, setTrackedKey] = useState(candidateKeyValue);
|
|
9988
|
+
if (trackedKey !== candidateKeyValue) {
|
|
9989
|
+
setTrackedKey(candidateKeyValue);
|
|
9990
|
+
setFailedSources(/* @__PURE__ */ new Set());
|
|
9991
|
+
}
|
|
9886
9992
|
useEffect(() => {
|
|
9887
|
-
if (value instanceof File) {
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
9993
|
+
if (!(value instanceof File)) {
|
|
9994
|
+
setLocalFilePreview(null);
|
|
9995
|
+
return void 0;
|
|
9996
|
+
}
|
|
9997
|
+
const token = ++readerTokenRef.current;
|
|
9998
|
+
const reader = new FileReader();
|
|
9999
|
+
reader.onloadend = () => {
|
|
10000
|
+
if (token !== readerTokenRef.current) return;
|
|
10001
|
+
if (typeof reader.result === "string") {
|
|
10002
|
+
setLocalFilePreview(reader.result);
|
|
10003
|
+
}
|
|
10004
|
+
};
|
|
10005
|
+
reader.onerror = () => {
|
|
10006
|
+
if (token !== readerTokenRef.current) return;
|
|
10007
|
+
setLocalFilePreview(null);
|
|
10008
|
+
};
|
|
10009
|
+
reader.readAsDataURL(value);
|
|
10010
|
+
return () => {
|
|
10011
|
+
if (token === readerTokenRef.current) {
|
|
10012
|
+
readerTokenRef.current = token - 1;
|
|
10013
|
+
}
|
|
10014
|
+
};
|
|
10015
|
+
}, [value]);
|
|
10016
|
+
const activeCandidate = useMemo(() => {
|
|
10017
|
+
if (value instanceof File) return null;
|
|
10018
|
+
for (const candidate of previewCandidates) {
|
|
10019
|
+
if (!failedSources.has(candidate.src)) return candidate;
|
|
9896
10020
|
}
|
|
9897
|
-
|
|
10021
|
+
return null;
|
|
10022
|
+
}, [previewCandidates, failedSources, value]);
|
|
10023
|
+
const handleCandidateError = (candidate) => {
|
|
10024
|
+
setFailedSources((prev) => {
|
|
10025
|
+
if (prev.has(candidate.src)) return prev;
|
|
10026
|
+
const next = new Set(prev);
|
|
10027
|
+
next.add(candidate.src);
|
|
10028
|
+
return next;
|
|
10029
|
+
});
|
|
10030
|
+
onPreviewError?.({ source: candidate.source, src: candidate.src });
|
|
10031
|
+
};
|
|
9898
10032
|
const handleClick = () => {
|
|
9899
|
-
if (
|
|
10033
|
+
if (disabled) return;
|
|
10034
|
+
fileInputRef.current?.click();
|
|
9900
10035
|
};
|
|
9901
10036
|
const handleChange = (e) => {
|
|
9902
10037
|
const file = e.target.files?.[0] || null;
|
|
9903
10038
|
if (file) onChange(file);
|
|
10039
|
+
e.target.value = "";
|
|
9904
10040
|
};
|
|
9905
10041
|
const handleClear = (e) => {
|
|
9906
10042
|
e.stopPropagation();
|
|
10043
|
+
if (disabled) return;
|
|
9907
10044
|
onChange(null);
|
|
9908
|
-
|
|
10045
|
+
setLocalFilePreview(null);
|
|
9909
10046
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
9910
10047
|
};
|
|
10048
|
+
const handleImgError = () => {
|
|
10049
|
+
if (value instanceof File) return;
|
|
10050
|
+
if (activeCandidate) handleCandidateError(activeCandidate);
|
|
10051
|
+
};
|
|
9911
10052
|
const effectiveSize = previewClassName || IMAGE_SIZE_MAP[imageSize];
|
|
9912
10053
|
const isDropzone = !!previewClassName;
|
|
9913
10054
|
const effectiveReplaceSubtitle = replaceSubtitle ?? subtitle;
|
|
10055
|
+
const primaryAlt = previewAlt ?? replaceTitle ?? "Preview";
|
|
10056
|
+
const secondaryAlt = fallbackAlt ?? previewAlt ?? replaceTitle ?? "Preview";
|
|
10057
|
+
const replaceAccessibleName = replaceAriaLabel ?? replaceTitle;
|
|
10058
|
+
const clearAccessibleName = clearAriaLabel ?? "Remove image";
|
|
10059
|
+
let dataState = "empty";
|
|
10060
|
+
let previewSrc = null;
|
|
10061
|
+
let previewAltText = primaryAlt;
|
|
10062
|
+
if (value instanceof File) {
|
|
10063
|
+
dataState = localFilePreview ? "preview" : "empty";
|
|
10064
|
+
previewSrc = localFilePreview;
|
|
10065
|
+
previewAltText = primaryAlt;
|
|
10066
|
+
} else if (activeCandidate) {
|
|
10067
|
+
dataState = activeCandidate.source === "value" ? "preview" : "fallback";
|
|
10068
|
+
previewSrc = activeCandidate.src;
|
|
10069
|
+
previewAltText = activeCandidate.source === "value" ? primaryAlt : secondaryAlt;
|
|
10070
|
+
} else if (previewCandidates.length > 0) {
|
|
10071
|
+
dataState = "unavailable";
|
|
10072
|
+
}
|
|
10073
|
+
const renderUnavailableContent = () => unavailableContent ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10074
|
+
/* @__PURE__ */ jsx(Image, { className: "h-10 w-10 text-muted-foreground/50", "aria-hidden": true }),
|
|
10075
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Image unavailable" })
|
|
10076
|
+
] });
|
|
10077
|
+
const renderClearButton = (compact) => {
|
|
10078
|
+
if (!allowClear) return null;
|
|
10079
|
+
return /* @__PURE__ */ jsx(
|
|
10080
|
+
"button",
|
|
10081
|
+
{
|
|
10082
|
+
type: "button",
|
|
10083
|
+
onClick: handleClear,
|
|
10084
|
+
disabled,
|
|
10085
|
+
"aria-label": clearAccessibleName,
|
|
10086
|
+
className: cn(
|
|
10087
|
+
"absolute top-2 end-2 z-10 flex items-center justify-center rounded-full bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
10088
|
+
compact ? "p-1.5" : "h-6 w-6",
|
|
10089
|
+
CONTROL_VISIBILITY
|
|
10090
|
+
),
|
|
10091
|
+
children: /* @__PURE__ */ jsx(X, { className: compact ? "h-4 w-4" : "h-3.5 w-3.5" })
|
|
10092
|
+
}
|
|
10093
|
+
);
|
|
10094
|
+
};
|
|
9914
10095
|
const renderPreview = () => /* @__PURE__ */ jsx(
|
|
9915
10096
|
"div",
|
|
9916
10097
|
{
|
|
9917
10098
|
style: previewStyle,
|
|
10099
|
+
"data-image-input-state": dataState,
|
|
9918
10100
|
className: cn(
|
|
9919
|
-
"flex relative
|
|
10101
|
+
"group/image flex relative rounded-lg overflow-hidden border-2 border-dashed border-muted-foreground/60 hover:border-primary transition-colors",
|
|
9920
10102
|
effectiveSize
|
|
9921
10103
|
),
|
|
9922
|
-
children:
|
|
9923
|
-
/* @__PURE__ */ jsx("img", { src: preview, alt: "Preview", className: "absolute inset-0 w-full h-full object-cover" }),
|
|
9924
|
-
/* @__PURE__ */ jsxs("div", { className: cn("relative w-full h-full flex flex-col items-center justify-center gap-1.5 bg-black/40 text-white px-6 py-8 text-center", contentClassName), children: [
|
|
9925
|
-
/* @__PURE__ */ jsx("span", { className: cn("text-sm font-medium", titleClassName), children: replaceTitle }),
|
|
9926
|
-
effectiveReplaceSubtitle ? /* @__PURE__ */ jsx("span", { className: cn("text-xs opacity-80", subtitleClassName), children: effectiveReplaceSubtitle }) : null,
|
|
9927
|
-
allowClear && /* @__PURE__ */ jsx(
|
|
9928
|
-
"button",
|
|
9929
|
-
{
|
|
9930
|
-
type: "button",
|
|
9931
|
-
onClick: handleClear,
|
|
9932
|
-
disabled,
|
|
9933
|
-
className: "absolute top-2 right-2 flex h-6 w-6 items-center justify-center rounded-full bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
9934
|
-
"aria-label": "Remove image",
|
|
9935
|
-
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
9936
|
-
}
|
|
9937
|
-
)
|
|
9938
|
-
] })
|
|
9939
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9940
|
-
/* @__PURE__ */ jsx("img", { src: preview, alt: "Preview", className: "w-full h-full object-cover" }),
|
|
10104
|
+
children: previewSrc ? isDropzone ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9941
10105
|
/* @__PURE__ */ jsx(
|
|
9942
|
-
"
|
|
10106
|
+
"img",
|
|
9943
10107
|
{
|
|
9944
|
-
|
|
9945
|
-
|
|
9946
|
-
|
|
10108
|
+
src: previewSrc,
|
|
10109
|
+
alt: previewAltText,
|
|
10110
|
+
onError: handleImgError,
|
|
10111
|
+
className: cn(
|
|
10112
|
+
"absolute inset-0 w-full h-full object-cover",
|
|
10113
|
+
imageClassName
|
|
10114
|
+
)
|
|
9947
10115
|
}
|
|
9948
10116
|
),
|
|
9949
|
-
|
|
10117
|
+
/* @__PURE__ */ jsxs(
|
|
9950
10118
|
"button",
|
|
9951
10119
|
{
|
|
9952
10120
|
type: "button",
|
|
9953
|
-
onClick:
|
|
10121
|
+
onClick: handleClick,
|
|
9954
10122
|
disabled,
|
|
9955
|
-
|
|
9956
|
-
|
|
10123
|
+
"aria-label": replaceAccessibleName,
|
|
10124
|
+
className: cn(
|
|
10125
|
+
"relative w-full h-full flex flex-col items-center justify-center gap-1.5 bg-black/40 text-white px-6 py-8 text-center cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
10126
|
+
contentClassName
|
|
10127
|
+
),
|
|
10128
|
+
children: [
|
|
10129
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-sm font-medium", titleClassName), children: replaceTitle }),
|
|
10130
|
+
effectiveReplaceSubtitle ? /* @__PURE__ */ jsx("span", { className: cn("text-xs opacity-80", subtitleClassName), children: effectiveReplaceSubtitle }) : null
|
|
10131
|
+
]
|
|
9957
10132
|
}
|
|
9958
|
-
)
|
|
9959
|
-
|
|
9960
|
-
|
|
10133
|
+
),
|
|
10134
|
+
renderClearButton(false)
|
|
10135
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9961
10136
|
/* @__PURE__ */ jsx(
|
|
9962
|
-
"
|
|
10137
|
+
"img",
|
|
10138
|
+
{
|
|
10139
|
+
src: previewSrc,
|
|
10140
|
+
alt: previewAltText,
|
|
10141
|
+
onError: handleImgError,
|
|
10142
|
+
className: cn("w-full h-full", imageClassName ?? "object-cover")
|
|
10143
|
+
}
|
|
10144
|
+
),
|
|
10145
|
+
/* @__PURE__ */ jsx(
|
|
10146
|
+
"button",
|
|
9963
10147
|
{
|
|
10148
|
+
type: "button",
|
|
9964
10149
|
onClick: handleClick,
|
|
9965
|
-
|
|
9966
|
-
|
|
10150
|
+
disabled,
|
|
10151
|
+
"aria-label": replaceAccessibleName,
|
|
10152
|
+
className: cn(
|
|
10153
|
+
"absolute inset-0 bg-black/50 flex items-center justify-center cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
10154
|
+
COMPACT_OVERLAY_VISIBILITY
|
|
10155
|
+
),
|
|
10156
|
+
children: /* @__PURE__ */ jsx(Upload, { className: "h-8 w-8 text-white", "aria-hidden": true })
|
|
9967
10157
|
}
|
|
9968
|
-
)
|
|
9969
|
-
|
|
10158
|
+
),
|
|
10159
|
+
renderClearButton(true)
|
|
10160
|
+
] }) : dataState === "unavailable" ? /* @__PURE__ */ jsx(
|
|
9970
10161
|
"div",
|
|
9971
10162
|
{
|
|
10163
|
+
"data-image-input-unavailable": true,
|
|
10164
|
+
className: cn(
|
|
10165
|
+
"w-full h-full flex flex-col items-center justify-center gap-2 bg-muted/30 text-muted-foreground px-6 py-8 text-center",
|
|
10166
|
+
contentClassName
|
|
10167
|
+
),
|
|
10168
|
+
children: renderUnavailableContent()
|
|
10169
|
+
}
|
|
10170
|
+
) : isDropzone ? /* @__PURE__ */ jsxs(
|
|
10171
|
+
"button",
|
|
10172
|
+
{
|
|
10173
|
+
type: "button",
|
|
9972
10174
|
onClick: handleClick,
|
|
9973
|
-
|
|
10175
|
+
disabled,
|
|
10176
|
+
"aria-label": replaceAriaLabel ?? title,
|
|
10177
|
+
className: cn(
|
|
10178
|
+
"w-full h-full flex flex-col items-center justify-center gap-2 cursor-pointer bg-muted/30 hover:bg-muted/50 transition-colors px-6 py-8 text-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
10179
|
+
contentClassName
|
|
10180
|
+
),
|
|
9974
10181
|
children: [
|
|
9975
10182
|
(trigger === "icon" || trigger === "both") && /* @__PURE__ */ jsx("div", { className: "text-primary", children: uploadIcon ?? /* @__PURE__ */ jsx(Plus, { className: "h-8 w-8" }) }),
|
|
9976
|
-
(trigger === "button" || trigger === "both") && /* @__PURE__ */ jsx(
|
|
9977
|
-
"span",
|
|
9978
|
-
{
|
|
9979
|
-
role: "button",
|
|
9980
|
-
className: "inline-flex items-center justify-center rounded-md border border-input bg-background px-3 py-1.5 text-xs font-medium text-foreground shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
9981
|
-
children: buttonLabel
|
|
9982
|
-
}
|
|
9983
|
-
),
|
|
10183
|
+
(trigger === "button" || trigger === "both") && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center justify-center rounded-md border border-input bg-background px-3 py-1.5 text-xs font-medium text-foreground shadow-sm hover:bg-accent hover:text-accent-foreground", children: buttonLabel }),
|
|
9984
10184
|
/* @__PURE__ */ jsx("span", { className: cn("text-sm font-medium text-foreground", titleClassName), children: title }),
|
|
9985
10185
|
subtitle ? /* @__PURE__ */ jsx("span", { className: cn("text-xs text-muted-foreground", subtitleClassName), children: subtitle }) : null
|
|
9986
10186
|
]
|
|
9987
10187
|
}
|
|
9988
10188
|
) : /* @__PURE__ */ jsxs(
|
|
9989
|
-
"
|
|
10189
|
+
"button",
|
|
9990
10190
|
{
|
|
10191
|
+
type: "button",
|
|
9991
10192
|
onClick: handleClick,
|
|
9992
|
-
|
|
10193
|
+
disabled,
|
|
10194
|
+
"aria-label": replaceAriaLabel ?? title,
|
|
10195
|
+
className: "w-full h-full flex flex-col items-center justify-center cursor-pointer bg-muted/30 hover:bg-muted/50 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
9993
10196
|
children: [
|
|
9994
|
-
/* @__PURE__ */ jsx(Image, { className: "h-12 w-12 text-muted-foreground/50 mb-2" }),
|
|
9995
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground text-center px-2", children:
|
|
10197
|
+
/* @__PURE__ */ jsx(Image, { className: "h-12 w-12 text-muted-foreground/50 mb-2", "aria-hidden": true }),
|
|
10198
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground text-center px-2", children: title })
|
|
9996
10199
|
]
|
|
9997
10200
|
}
|
|
9998
10201
|
)
|
|
@@ -10006,20 +10209,42 @@ function ImageInput({
|
|
|
10006
10209
|
onChange: handleChange,
|
|
10007
10210
|
className: "hidden",
|
|
10008
10211
|
accept,
|
|
10009
|
-
disabled
|
|
10212
|
+
disabled,
|
|
10213
|
+
"aria-hidden": true,
|
|
10214
|
+
tabIndex: -1
|
|
10010
10215
|
}
|
|
10011
10216
|
);
|
|
10012
10217
|
if (!showPreview) return renderFileInput();
|
|
10013
10218
|
if (previewPosition === "left" || previewPosition === "right") {
|
|
10014
|
-
return /* @__PURE__ */ jsxs(
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10219
|
+
return /* @__PURE__ */ jsxs(
|
|
10220
|
+
"div",
|
|
10221
|
+
{
|
|
10222
|
+
className: cn(
|
|
10223
|
+
"flex items-center gap-4",
|
|
10224
|
+
previewPosition === "right" && "flex-row-reverse",
|
|
10225
|
+
containerClassName
|
|
10226
|
+
),
|
|
10227
|
+
children: [
|
|
10228
|
+
renderPreview(),
|
|
10229
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: renderFileInput() })
|
|
10230
|
+
]
|
|
10231
|
+
}
|
|
10232
|
+
);
|
|
10018
10233
|
}
|
|
10019
|
-
return /* @__PURE__ */ jsxs(
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10234
|
+
return /* @__PURE__ */ jsxs(
|
|
10235
|
+
"div",
|
|
10236
|
+
{
|
|
10237
|
+
className: cn(
|
|
10238
|
+
"flex flex-col gap-3",
|
|
10239
|
+
previewPosition === "bottom" && "flex-col-reverse",
|
|
10240
|
+
containerClassName
|
|
10241
|
+
),
|
|
10242
|
+
children: [
|
|
10243
|
+
renderPreview(),
|
|
10244
|
+
renderFileInput()
|
|
10245
|
+
]
|
|
10246
|
+
}
|
|
10247
|
+
);
|
|
10023
10248
|
}
|
|
10024
10249
|
var AVATAR_SIZE_MAP = {
|
|
10025
10250
|
sm: "size-16",
|
package/dist/theme.css
CHANGED
|
@@ -311,23 +311,62 @@ input:autofill {
|
|
|
311
311
|
display: none;
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
-
/* Responsive table actions stay discoverable on touch and tablet layouts.
|
|
315
|
-
Fine-pointer desktops may keep the quieter hover/focus reveal treatment. */
|
|
316
|
-
.ntable-card-action {
|
|
317
|
-
opacity: 1;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
@media (min-width: 64rem) and (hover: hover) and (pointer: fine) {
|
|
321
|
-
.ntable-card-action {
|
|
322
|
-
opacity: 0;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
.group:hover > .ntable-card-action,
|
|
326
|
-
.group:focus-within > .ntable-card-action,
|
|
327
|
-
.ntable-card-action:focus,
|
|
328
|
-
.ntable-card-action:focus-within {
|
|
329
|
-
opacity: 1;
|
|
330
|
-
}
|
|
314
|
+
/* Responsive table actions stay discoverable on touch and tablet layouts.
|
|
315
|
+
Fine-pointer desktops may keep the quieter hover/focus reveal treatment. */
|
|
316
|
+
.ntable-card-action {
|
|
317
|
+
opacity: 1;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
@media (min-width: 64rem) and (hover: hover) and (pointer: fine) {
|
|
321
|
+
.ntable-card-action {
|
|
322
|
+
opacity: 0;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.group:hover > .ntable-card-action,
|
|
326
|
+
.group:focus-within > .ntable-card-action,
|
|
327
|
+
.ntable-card-action:focus,
|
|
328
|
+
.ntable-card-action:focus-within {
|
|
329
|
+
opacity: 1;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* ImageInput controls (clear button and replace overlay) stay visible on touch
|
|
334
|
+
and coarse-pointer devices. Only on fine-pointer desktops do they fall back to
|
|
335
|
+
a hover/focus reveal. Keyboard focus always restores visibility. The rule
|
|
336
|
+
below ships in dist/theme.css via scripts/build-css.mjs so the JSX only needs
|
|
337
|
+
to reference the static class names. */
|
|
338
|
+
.nimage-input-control {
|
|
339
|
+
opacity: 1;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
@media (hover: hover) and (pointer: fine) {
|
|
343
|
+
.nimage-input-control {
|
|
344
|
+
opacity: 0;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.group\/image:hover .nimage-input-control,
|
|
348
|
+
.group\/image:focus-within .nimage-input-control,
|
|
349
|
+
.nimage-input-control:focus,
|
|
350
|
+
.nimage-input-control:focus-visible {
|
|
351
|
+
opacity: 1;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.nimage-input-compact-overlay {
|
|
356
|
+
opacity: 1;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@media (hover: hover) and (pointer: fine) {
|
|
360
|
+
.nimage-input-compact-overlay {
|
|
361
|
+
opacity: 0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.group\/image:hover .nimage-input-compact-overlay,
|
|
365
|
+
.group\/image:focus-within .nimage-input-compact-overlay,
|
|
366
|
+
.nimage-input-compact-overlay:focus,
|
|
367
|
+
.nimage-input-compact-overlay:focus-visible {
|
|
368
|
+
opacity: 1;
|
|
369
|
+
}
|
|
331
370
|
}
|
|
332
371
|
|
|
333
372
|
/* OverlayScrollbars theme used by the <NajmScroll> component — a thin,
|