najm-kit 2.1.28 → 2.1.31

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/index.d.ts CHANGED
@@ -2074,6 +2074,7 @@ interface ImageInputProps extends BaseProps {
2074
2074
  placeholder?: string;
2075
2075
  icon?: InputIcon;
2076
2076
  showIcon?: boolean;
2077
+ uploadIcon?: React.ReactNode;
2077
2078
  previewClassName?: string;
2078
2079
  showPreview?: boolean;
2079
2080
  previewPosition?: "top" | "bottom" | "left" | "right";
@@ -2082,6 +2083,12 @@ interface ImageInputProps extends BaseProps {
2082
2083
  defaultImage?: string;
2083
2084
  imageSize?: "sm" | "md" | "lg" | "xl";
2084
2085
  imageVersion?: string | number | null;
2086
+ title?: string;
2087
+ subtitle?: string;
2088
+ replaceTitle?: string;
2089
+ replaceSubtitle?: string;
2090
+ trigger?: "icon" | "button" | "both";
2091
+ buttonLabel?: string;
2085
2092
  }
2086
2093
  interface EmojiInputProps extends BaseProps {
2087
2094
  value: number;
@@ -2179,7 +2186,7 @@ interface NUploaderProps {
2179
2186
  }
2180
2187
  declare function NUploader({ title, subtitle, accept, multiple, disabled, items, listTitle, className, dropzoneClassName, onFilesSelected, onCancel, onRemove, }: NUploaderProps): react_jsx_runtime.JSX.Element;
2181
2188
 
2182
- declare function ImageInput({ value, onChange, previewClassName, showPreview, previewPosition, allowClear, accept, defaultImage, imageSize, imageVersion, disabled, }: ImageInputProps & {
2189
+ declare function ImageInput({ value, onChange, previewClassName, showPreview, previewPosition, allowClear, accept, defaultImage, imageSize, imageVersion, disabled, uploadIcon, title, subtitle, replaceTitle, replaceSubtitle, trigger, buttonLabel, }: ImageInputProps & {
2183
2190
  disabled?: boolean;
2184
2191
  }): react_jsx_runtime.JSX.Element;
2185
2192
 
@@ -2285,6 +2292,13 @@ interface BaseFormInputProps {
2285
2292
  onChange?: (value: any) => void;
2286
2293
  }
2287
2294
  type FormInputSpecificProps<T> = Omit<T, "value" | "onChange" | keyof BaseFormInputProps>;
2295
+ type FormImageInputProps = FormInputSpecificProps<ImageInputProps> & {
2296
+ /**
2297
+ * Optional controlled preview value.
2298
+ * React Hook Form owns the value when omitted.
2299
+ */
2300
+ value?: ImageInputProps["value"];
2301
+ };
2288
2302
  type InputTypeMap = {
2289
2303
  text: FormInputSpecificProps<TextInputProps>;
2290
2304
  number: FormInputSpecificProps<NumberInputProps>;
@@ -2298,7 +2312,7 @@ type InputTypeMap = {
2298
2312
  checkbox: FormInputSpecificProps<CheckboxInputProps>;
2299
2313
  checkboxGroup: FormInputSpecificProps<CheckboxGroupInputProps>;
2300
2314
  file: FormInputSpecificProps<FileInputProps>;
2301
- image: FormInputSpecificProps<ImageInputProps>;
2315
+ image: FormImageInputProps;
2302
2316
  date: FormInputSpecificProps<DateInputProps>;
2303
2317
  starRating: FormInputSpecificProps<StarRatingInputProps>;
2304
2318
  colorArray: FormInputSpecificProps<ColorArrayInputProps>;
package/dist/index.mjs CHANGED
@@ -1234,11 +1234,26 @@ var Button = React.forwardRef(
1234
1234
  },
1235
1235
  [autoLoading, isDisabled, onClick]
1236
1236
  );
1237
- const content = loadingText && isLoading ? loadingText : children;
1237
+ const child = asChild ? React.Children.only(children) : null;
1238
+ const childContent = asChild && React.isValidElement(child) ? child.props.children : children;
1239
+ const content = loadingText && isLoading ? loadingText : childContent;
1238
1240
  const loaderNode = isLoading ? loader : null;
1239
1241
  const leftIconNode = renderIcon(leftIcon);
1240
1242
  const rightIconNode = renderIcon(rightIcon);
1241
1243
  const showCenteredLoader = Boolean(loaderNode && loaderPosition === "center");
1244
+ const renderedContent = showCenteredLoader ? /* @__PURE__ */ jsxs(Fragment, { children: [
1245
+ /* @__PURE__ */ jsxs("span", { className: "invisible inline-flex items-center gap-2", children: [
1246
+ leftIconNode,
1247
+ content,
1248
+ rightIconNode
1249
+ ] }),
1250
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-0 inline-flex items-center justify-center", children: loaderNode })
1251
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1252
+ loaderNode && loaderPosition === "left" ? loaderNode : leftIconNode,
1253
+ content,
1254
+ loaderNode && loaderPosition === "right" ? loaderNode : rightIconNode
1255
+ ] });
1256
+ const renderedChild = asChild && React.isValidElement(child) ? React.cloneElement(child, void 0, renderedContent) : renderedContent;
1242
1257
  return /* @__PURE__ */ jsx(
1243
1258
  Comp,
1244
1259
  {
@@ -1267,18 +1282,7 @@ var Button = React.forwardRef(
1267
1282
  ),
1268
1283
  onClick: handleClick,
1269
1284
  ...props,
1270
- children: showCenteredLoader ? /* @__PURE__ */ jsxs(Fragment, { children: [
1271
- /* @__PURE__ */ jsxs("span", { className: "invisible inline-flex items-center gap-2", children: [
1272
- leftIconNode,
1273
- content,
1274
- rightIconNode
1275
- ] }),
1276
- /* @__PURE__ */ jsx("span", { className: "absolute inset-0 inline-flex items-center justify-center", children: loaderNode })
1277
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1278
- loaderNode && loaderPosition === "left" ? loaderNode : leftIconNode,
1279
- content,
1280
- loaderNode && loaderPosition === "right" ? loaderNode : rightIconNode
1281
- ] })
1285
+ children: renderedChild
1282
1286
  }
1283
1287
  );
1284
1288
  }
@@ -7459,7 +7463,14 @@ function ImageInput({
7459
7463
  defaultImage,
7460
7464
  imageSize = "md",
7461
7465
  imageVersion,
7462
- disabled = false
7466
+ disabled = false,
7467
+ uploadIcon,
7468
+ title = "Click to upload",
7469
+ subtitle,
7470
+ replaceTitle = "Replace image",
7471
+ replaceSubtitle,
7472
+ trigger = "icon",
7473
+ buttonLabel = "Upload"
7463
7474
  }) {
7464
7475
  const fileInputRef = useRef(null);
7465
7476
  const [preview, setPreview] = useState(null);
@@ -7489,6 +7500,8 @@ function ImageInput({
7489
7500
  if (fileInputRef.current) fileInputRef.current.value = "";
7490
7501
  };
7491
7502
  const effectiveSize = previewClassName || IMAGE_SIZE_MAP[imageSize];
7503
+ const isDropzone = !!previewClassName;
7504
+ const effectiveReplaceSubtitle = replaceSubtitle ?? subtitle;
7492
7505
  const renderPreview = () => /* @__PURE__ */ jsx(
7493
7506
  "div",
7494
7507
  {
@@ -7496,7 +7509,24 @@ function ImageInput({
7496
7509
  "flex relative group rounded-lg overflow-hidden border-2 border-dashed border-muted-foreground/60 hover:border-primary transition-colors",
7497
7510
  effectiveSize
7498
7511
  ),
7499
- children: preview ? /* @__PURE__ */ jsxs(Fragment, { children: [
7512
+ children: preview ? isDropzone ? /* @__PURE__ */ jsxs(Fragment, { children: [
7513
+ /* @__PURE__ */ jsx("img", { src: preview, alt: "Preview", className: "absolute inset-0 w-full h-full object-cover" }),
7514
+ /* @__PURE__ */ jsxs("div", { className: "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", children: [
7515
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: replaceTitle }),
7516
+ effectiveReplaceSubtitle ? /* @__PURE__ */ jsx("span", { className: "text-xs opacity-80", children: effectiveReplaceSubtitle }) : null,
7517
+ allowClear && /* @__PURE__ */ jsx(
7518
+ "button",
7519
+ {
7520
+ type: "button",
7521
+ onClick: handleClear,
7522
+ disabled,
7523
+ 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",
7524
+ "aria-label": "Remove image",
7525
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
7526
+ }
7527
+ )
7528
+ ] })
7529
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7500
7530
  /* @__PURE__ */ jsx("img", { src: preview, alt: "Preview", className: "w-full h-full object-cover" }),
7501
7531
  /* @__PURE__ */ jsx(
7502
7532
  "div",
@@ -7526,7 +7556,26 @@ function ImageInput({
7526
7556
  children: /* @__PURE__ */ jsx(Upload, { className: "h-8 w-8 text-white" })
7527
7557
  }
7528
7558
  )
7529
- ] }) : /* @__PURE__ */ jsxs(
7559
+ ] }) : isDropzone ? /* @__PURE__ */ jsxs(
7560
+ "div",
7561
+ {
7562
+ onClick: handleClick,
7563
+ className: "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",
7564
+ children: [
7565
+ (trigger === "icon" || trigger === "both") && /* @__PURE__ */ jsx("div", { className: "text-primary", children: uploadIcon ?? /* @__PURE__ */ jsx(Plus, { className: "h-8 w-8" }) }),
7566
+ (trigger === "button" || trigger === "both") && /* @__PURE__ */ jsx(
7567
+ "span",
7568
+ {
7569
+ role: "button",
7570
+ 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",
7571
+ children: buttonLabel
7572
+ }
7573
+ ),
7574
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: title }),
7575
+ subtitle ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: subtitle }) : null
7576
+ ]
7577
+ }
7578
+ ) : /* @__PURE__ */ jsxs(
7530
7579
  "div",
7531
7580
  {
7532
7581
  onClick: handleClick,
package/dist/json.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import * as React3 from 'react';
2
- import React3__default, { createContext, useContext, useMemo, useRef, useCallback } from 'react';
1
+ import * as React5 from 'react';
2
+ import React5__default, { createContext, useContext, useMemo, useRef, useCallback } from 'react';
3
3
  import CodeMirror from '@uiw/react-codemirror';
4
4
  import { json } from '@codemirror/lang-json';
5
5
  import { keymap, EditorView, Decoration, ViewPlugin } from '@codemirror/view';
@@ -201,8 +201,8 @@ var NIcon = ({
201
201
  height: size,
202
202
  ...style
203
203
  };
204
- if (React3__default.isValidElement(icon)) {
205
- return React3__default.cloneElement(icon, {
204
+ if (React5__default.isValidElement(icon)) {
205
+ return React5__default.cloneElement(icon, {
206
206
  className: cn(className, icon.props.className),
207
207
  onClick,
208
208
  ...rest
@@ -249,15 +249,15 @@ var NIcon = ({
249
249
  return null;
250
250
  };
251
251
  NIcon.displayName = "NIcon";
252
- React3.createContext(0);
253
- React3.createContext(null);
252
+ React5.createContext(0);
253
+ React5.createContext(null);
254
254
  var DEFAULT_APPEARANCE = {};
255
- React3.createContext(DEFAULT_APPEARANCE);
256
- var NajmDesignContext = React3.createContext({
255
+ React5.createContext(DEFAULT_APPEARANCE);
256
+ var NajmDesignContext = React5.createContext({
257
257
  components: {}
258
258
  });
259
259
  function useNajmDesign() {
260
- return React3.useContext(NajmDesignContext);
260
+ return React5.useContext(NajmDesignContext);
261
261
  }
262
262
  function useNajmComponentStyle(name) {
263
263
  const { components } = useNajmDesign();
@@ -427,7 +427,7 @@ function renderIcon(icon) {
427
427
  if (!icon) return null;
428
428
  return /* @__PURE__ */ jsx(NIcon, { "aria-hidden": "true", icon, className: "shrink-0" });
429
429
  }
430
- var Button = React3.forwardRef(
430
+ var Button = React5.forwardRef(
431
431
  ({
432
432
  className,
433
433
  variant,
@@ -459,11 +459,11 @@ var Button = React3.forwardRef(
459
459
  const effSize = size ?? recipe?.defaultSize;
460
460
  const recipeRadius = rounded === void 0 ? resolveRadiusValue(recipe?.radius) : void 0;
461
461
  const recipeStyle = recipeRadius ? { borderRadius: recipeRadius } : void 0;
462
- const [pending, setPending] = React3.useState(false);
462
+ const [pending, setPending] = React5.useState(false);
463
463
  const isLoading = loading || pending;
464
464
  const isDisabled = disabled || disabledWhileLoading && isLoading;
465
465
  const Comp = asChild ? Slot : "button";
466
- const handleClick = React3.useCallback(
466
+ const handleClick = React5.useCallback(
467
467
  (event) => {
468
468
  if (isDisabled) {
469
469
  event.preventDefault();
@@ -480,11 +480,26 @@ var Button = React3.forwardRef(
480
480
  },
481
481
  [autoLoading, isDisabled, onClick]
482
482
  );
483
- const content = loadingText && isLoading ? loadingText : children;
483
+ const child = asChild ? React5.Children.only(children) : null;
484
+ const childContent = asChild && React5.isValidElement(child) ? child.props.children : children;
485
+ const content = loadingText && isLoading ? loadingText : childContent;
484
486
  const loaderNode = isLoading ? loader : null;
485
487
  const leftIconNode = renderIcon(leftIcon);
486
488
  const rightIconNode = renderIcon(rightIcon);
487
489
  const showCenteredLoader = Boolean(loaderNode && loaderPosition === "center");
490
+ const renderedContent = showCenteredLoader ? /* @__PURE__ */ jsxs(Fragment, { children: [
491
+ /* @__PURE__ */ jsxs("span", { className: "invisible inline-flex items-center gap-2", children: [
492
+ leftIconNode,
493
+ content,
494
+ rightIconNode
495
+ ] }),
496
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-0 inline-flex items-center justify-center", children: loaderNode })
497
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
498
+ loaderNode && loaderPosition === "left" ? loaderNode : leftIconNode,
499
+ content,
500
+ loaderNode && loaderPosition === "right" ? loaderNode : rightIconNode
501
+ ] });
502
+ const renderedChild = asChild && React5.isValidElement(child) ? React5.cloneElement(child, void 0, renderedContent) : renderedContent;
488
503
  return /* @__PURE__ */ jsx(
489
504
  Comp,
490
505
  {
@@ -513,18 +528,7 @@ var Button = React3.forwardRef(
513
528
  ),
514
529
  onClick: handleClick,
515
530
  ...props,
516
- children: showCenteredLoader ? /* @__PURE__ */ jsxs(Fragment, { children: [
517
- /* @__PURE__ */ jsxs("span", { className: "invisible inline-flex items-center gap-2", children: [
518
- leftIconNode,
519
- content,
520
- rightIconNode
521
- ] }),
522
- /* @__PURE__ */ jsx("span", { className: "absolute inset-0 inline-flex items-center justify-center", children: loaderNode })
523
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
524
- loaderNode && loaderPosition === "left" ? loaderNode : leftIconNode,
525
- content,
526
- loaderNode && loaderPosition === "right" ? loaderNode : rightIconNode
527
- ] })
531
+ children: renderedChild
528
532
  }
529
533
  );
530
534
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-kit",
3
- "version": "2.1.28",
3
+ "version": "2.1.31",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Reusable React UI component package for Najm framework",