shru-design-system 0.5.1 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -156,8 +156,10 @@ declare const textAsElements: readonly ["p", "span", "div", "code"];
156
156
  interface TextProps extends React$1.HTMLAttributes<HTMLParagraphElement> {
157
157
  variant?: typeof textVariants[number];
158
158
  as?: typeof textAsElements[number];
159
+ leftIcon?: React$1.ReactNode;
160
+ rightIcon?: React$1.ReactNode;
159
161
  }
160
- declare function Text({ className, variant, as: Component, ...props }: TextProps): react_jsx_runtime.JSX.Element;
162
+ declare function Text({ className, variant, as: Component, leftIcon, rightIcon, children, ...props }: TextProps): react_jsx_runtime.JSX.Element;
161
163
 
162
164
  declare function InputOTP({ className, containerClassName, ...props }: React$1.ComponentProps<typeof OTPInput> & {
163
165
  containerClassName?: string;
@@ -185,7 +187,7 @@ interface ImageProps extends React$1.ImgHTMLAttributes<HTMLImageElement> {
185
187
  * For Next.js optimization, use next/image directly in your Next.js app.
186
188
  * This component is library-compatible and works in any React environment.
187
189
  */
188
- declare function Image({ className, fallback, alt, src, width, height, loading, ...props }: ImageProps): react_jsx_runtime.JSX.Element;
190
+ declare function Image({ className, fallback, alt, src, width, height, loading, ...props }: ImageProps): react_jsx_runtime.JSX.Element | null;
189
191
 
190
192
  interface UploadProps extends React$1.InputHTMLAttributes<HTMLInputElement> {
191
193
  onUpload?: (files: FileList | null) => void;
@@ -595,8 +597,9 @@ interface StatusTextProps {
595
597
  variant?: "caption" | "body" | "heading" | "badge";
596
598
  formatText?: (text: string, count?: number, label?: string) => string;
597
599
  className?: string;
600
+ as?: "div" | "span" | "p";
598
601
  }
599
- declare function StatusText({ text, status, count, label, variant, formatText, className, }: StatusTextProps): react_jsx_runtime.JSX.Element;
602
+ declare function StatusText({ text, status, count, label, variant, formatText, className, as, }: StatusTextProps): react_jsx_runtime.JSX.Element;
600
603
 
601
604
  interface StepperProps {
602
605
  steps: Array<{
@@ -680,13 +683,14 @@ declare function SearchInput({ placeholder, value: valueProp, onChange, onSearch
680
683
  interface ConfirmModalProps {
681
684
  open?: boolean;
682
685
  onOpenChange?: (open: boolean) => void;
683
- triggerLabel?: string;
686
+ triggerLabel?: string | React$1.ReactNode;
684
687
  triggerProps?: React$1.ComponentProps<typeof Button>;
685
688
  stopPropagation?: boolean;
686
689
  icon?: React$1.ReactNode;
687
690
  text?: string;
688
691
  title: string;
689
692
  description?: string;
693
+ message?: string;
690
694
  onConfirm: () => void | Promise<void>;
691
695
  confirmLabel?: string;
692
696
  cancelLabel?: string;
@@ -695,7 +699,7 @@ interface ConfirmModalProps {
695
699
  error?: string;
696
700
  showModal?: boolean;
697
701
  }
698
- declare function ConfirmModal({ open: openProp, onOpenChange, triggerLabel, triggerProps, stopPropagation, icon, text, title, description, onConfirm, confirmLabel, cancelLabel, variant, loading, error, showModal, ...props }: ConfirmModalProps): react_jsx_runtime.JSX.Element;
702
+ declare function ConfirmModal({ open: openProp, onOpenChange, triggerLabel, triggerProps, stopPropagation, icon, text, title, description, message, onConfirm, confirmLabel, cancelLabel, variant, loading, error, showModal, ...props }: ConfirmModalProps): react_jsx_runtime.JSX.Element;
699
703
 
700
704
  interface CopyButtonProps extends React$1.ComponentProps<typeof Button> {
701
705
  text?: string;
@@ -748,10 +752,12 @@ interface FormFieldConfig {
748
752
  interface FormModalProps {
749
753
  open?: boolean;
750
754
  onOpenChange?: (open: boolean) => void;
751
- triggerLabel?: string;
755
+ triggerLabel?: string | React$1.ReactNode;
752
756
  triggerProps?: React$1.ComponentProps<typeof Button>;
753
757
  icon?: React$1.ReactNode;
754
758
  title: string;
759
+ description?: string;
760
+ message?: string;
755
761
  variant?: "create" | "edit";
756
762
  itemType?: string;
757
763
  onSubmit: (data: Record<string, any>) => void | Promise<void>;
@@ -766,10 +772,10 @@ interface FormModalProps {
766
772
  beforeFields?: React$1.ReactNode;
767
773
  afterFields?: React$1.ReactNode;
768
774
  }
769
- declare function FormModal({ open: openProp, onOpenChange, triggerLabel, triggerProps, icon, title, variant, itemType, onSubmit, submitLabel, submittingLabel, cancelLabel, loading: loadingProp, isSubmitDisabled, onCreated, fields, children, beforeFields, afterFields, ...props }: FormModalProps): react_jsx_runtime.JSX.Element;
775
+ declare function FormModal({ open: openProp, onOpenChange, triggerLabel, triggerProps, icon, title, description, message, variant, itemType, onSubmit, submitLabel, submittingLabel, cancelLabel, loading: loadingProp, isSubmitDisabled, onCreated, fields, children, beforeFields, afterFields, ...props }: FormModalProps): react_jsx_runtime.JSX.Element;
770
776
 
771
777
  interface TriggerModalProps extends Omit<React$1.ComponentProps<typeof Modal>, "children"> {
772
- triggerLabel?: string;
778
+ triggerLabel?: string | React$1.ReactNode;
773
779
  trigger?: React$1.ReactNode;
774
780
  triggerProps?: React$1.ComponentProps<typeof Button>;
775
781
  stopPropagation?: boolean;
@@ -780,8 +786,9 @@ interface TriggerModalProps extends Omit<React$1.ComponentProps<typeof Modal>, "
780
786
  footer?: React$1.ReactNode;
781
787
  showCloseButton?: boolean;
782
788
  className?: string;
789
+ message?: string;
783
790
  }
784
- declare function TriggerModal({ open, onOpenChange, triggerLabel, trigger, triggerProps, stopPropagation, icon, title, description, children, footer, showCloseButton, className, ...props }: TriggerModalProps): react_jsx_runtime.JSX.Element;
791
+ declare function TriggerModal({ open, onOpenChange, triggerLabel, trigger, triggerProps, stopPropagation, icon, title, description, children, footer, showCloseButton, className, message, ...props }: TriggerModalProps): react_jsx_runtime.JSX.Element;
785
792
 
786
793
  interface HistoryControlButtonsProps {
787
794
  canUndo?: boolean;
package/dist/index.d.ts CHANGED
@@ -156,8 +156,10 @@ declare const textAsElements: readonly ["p", "span", "div", "code"];
156
156
  interface TextProps extends React$1.HTMLAttributes<HTMLParagraphElement> {
157
157
  variant?: typeof textVariants[number];
158
158
  as?: typeof textAsElements[number];
159
+ leftIcon?: React$1.ReactNode;
160
+ rightIcon?: React$1.ReactNode;
159
161
  }
160
- declare function Text({ className, variant, as: Component, ...props }: TextProps): react_jsx_runtime.JSX.Element;
162
+ declare function Text({ className, variant, as: Component, leftIcon, rightIcon, children, ...props }: TextProps): react_jsx_runtime.JSX.Element;
161
163
 
162
164
  declare function InputOTP({ className, containerClassName, ...props }: React$1.ComponentProps<typeof OTPInput> & {
163
165
  containerClassName?: string;
@@ -185,7 +187,7 @@ interface ImageProps extends React$1.ImgHTMLAttributes<HTMLImageElement> {
185
187
  * For Next.js optimization, use next/image directly in your Next.js app.
186
188
  * This component is library-compatible and works in any React environment.
187
189
  */
188
- declare function Image({ className, fallback, alt, src, width, height, loading, ...props }: ImageProps): react_jsx_runtime.JSX.Element;
190
+ declare function Image({ className, fallback, alt, src, width, height, loading, ...props }: ImageProps): react_jsx_runtime.JSX.Element | null;
189
191
 
190
192
  interface UploadProps extends React$1.InputHTMLAttributes<HTMLInputElement> {
191
193
  onUpload?: (files: FileList | null) => void;
@@ -595,8 +597,9 @@ interface StatusTextProps {
595
597
  variant?: "caption" | "body" | "heading" | "badge";
596
598
  formatText?: (text: string, count?: number, label?: string) => string;
597
599
  className?: string;
600
+ as?: "div" | "span" | "p";
598
601
  }
599
- declare function StatusText({ text, status, count, label, variant, formatText, className, }: StatusTextProps): react_jsx_runtime.JSX.Element;
602
+ declare function StatusText({ text, status, count, label, variant, formatText, className, as, }: StatusTextProps): react_jsx_runtime.JSX.Element;
600
603
 
601
604
  interface StepperProps {
602
605
  steps: Array<{
@@ -680,13 +683,14 @@ declare function SearchInput({ placeholder, value: valueProp, onChange, onSearch
680
683
  interface ConfirmModalProps {
681
684
  open?: boolean;
682
685
  onOpenChange?: (open: boolean) => void;
683
- triggerLabel?: string;
686
+ triggerLabel?: string | React$1.ReactNode;
684
687
  triggerProps?: React$1.ComponentProps<typeof Button>;
685
688
  stopPropagation?: boolean;
686
689
  icon?: React$1.ReactNode;
687
690
  text?: string;
688
691
  title: string;
689
692
  description?: string;
693
+ message?: string;
690
694
  onConfirm: () => void | Promise<void>;
691
695
  confirmLabel?: string;
692
696
  cancelLabel?: string;
@@ -695,7 +699,7 @@ interface ConfirmModalProps {
695
699
  error?: string;
696
700
  showModal?: boolean;
697
701
  }
698
- declare function ConfirmModal({ open: openProp, onOpenChange, triggerLabel, triggerProps, stopPropagation, icon, text, title, description, onConfirm, confirmLabel, cancelLabel, variant, loading, error, showModal, ...props }: ConfirmModalProps): react_jsx_runtime.JSX.Element;
702
+ declare function ConfirmModal({ open: openProp, onOpenChange, triggerLabel, triggerProps, stopPropagation, icon, text, title, description, message, onConfirm, confirmLabel, cancelLabel, variant, loading, error, showModal, ...props }: ConfirmModalProps): react_jsx_runtime.JSX.Element;
699
703
 
700
704
  interface CopyButtonProps extends React$1.ComponentProps<typeof Button> {
701
705
  text?: string;
@@ -748,10 +752,12 @@ interface FormFieldConfig {
748
752
  interface FormModalProps {
749
753
  open?: boolean;
750
754
  onOpenChange?: (open: boolean) => void;
751
- triggerLabel?: string;
755
+ triggerLabel?: string | React$1.ReactNode;
752
756
  triggerProps?: React$1.ComponentProps<typeof Button>;
753
757
  icon?: React$1.ReactNode;
754
758
  title: string;
759
+ description?: string;
760
+ message?: string;
755
761
  variant?: "create" | "edit";
756
762
  itemType?: string;
757
763
  onSubmit: (data: Record<string, any>) => void | Promise<void>;
@@ -766,10 +772,10 @@ interface FormModalProps {
766
772
  beforeFields?: React$1.ReactNode;
767
773
  afterFields?: React$1.ReactNode;
768
774
  }
769
- declare function FormModal({ open: openProp, onOpenChange, triggerLabel, triggerProps, icon, title, variant, itemType, onSubmit, submitLabel, submittingLabel, cancelLabel, loading: loadingProp, isSubmitDisabled, onCreated, fields, children, beforeFields, afterFields, ...props }: FormModalProps): react_jsx_runtime.JSX.Element;
775
+ declare function FormModal({ open: openProp, onOpenChange, triggerLabel, triggerProps, icon, title, description, message, variant, itemType, onSubmit, submitLabel, submittingLabel, cancelLabel, loading: loadingProp, isSubmitDisabled, onCreated, fields, children, beforeFields, afterFields, ...props }: FormModalProps): react_jsx_runtime.JSX.Element;
770
776
 
771
777
  interface TriggerModalProps extends Omit<React$1.ComponentProps<typeof Modal>, "children"> {
772
- triggerLabel?: string;
778
+ triggerLabel?: string | React$1.ReactNode;
773
779
  trigger?: React$1.ReactNode;
774
780
  triggerProps?: React$1.ComponentProps<typeof Button>;
775
781
  stopPropagation?: boolean;
@@ -780,8 +786,9 @@ interface TriggerModalProps extends Omit<React$1.ComponentProps<typeof Modal>, "
780
786
  footer?: React$1.ReactNode;
781
787
  showCloseButton?: boolean;
782
788
  className?: string;
789
+ message?: string;
783
790
  }
784
- declare function TriggerModal({ open, onOpenChange, triggerLabel, trigger, triggerProps, stopPropagation, icon, title, description, children, footer, showCloseButton, className, ...props }: TriggerModalProps): react_jsx_runtime.JSX.Element;
791
+ declare function TriggerModal({ open, onOpenChange, triggerLabel, trigger, triggerProps, stopPropagation, icon, title, description, children, footer, showCloseButton, className, message, ...props }: TriggerModalProps): react_jsx_runtime.JSX.Element;
785
792
 
786
793
  interface HistoryControlButtonsProps {
787
794
  canUndo?: boolean;
package/dist/index.js CHANGED
@@ -733,9 +733,13 @@ function Text({
733
733
  className,
734
734
  variant = "default",
735
735
  as: Component2 = "p",
736
+ leftIcon,
737
+ rightIcon,
738
+ children,
736
739
  ...props
737
740
  }) {
738
- return /* @__PURE__ */ jsxRuntime.jsx(
741
+ const hasIcons = leftIcon || rightIcon;
742
+ return /* @__PURE__ */ jsxRuntime.jsxs(
739
743
  Component2,
740
744
  {
741
745
  "data-slot": "text",
@@ -744,9 +748,15 @@ function Text({
744
748
  variant === "muted" && "text-muted-foreground",
745
749
  variant === "small" && "text-sm",
746
750
  variant === "large" && "text-lg",
751
+ hasIcons && "flex items-center gap-2",
747
752
  className
748
753
  ),
749
- ...props
754
+ ...props,
755
+ children: [
756
+ leftIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: leftIcon }),
757
+ children,
758
+ rightIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: rightIcon })
759
+ ]
750
760
  }
751
761
  );
752
762
  }
@@ -841,6 +851,12 @@ function Image2({
841
851
  ...props
842
852
  }) {
843
853
  const [hasError, setHasError] = React15__namespace.useState(false);
854
+ if (!src || src.trim() === "") {
855
+ if (fallback) {
856
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
857
+ }
858
+ return null;
859
+ }
844
860
  if (hasError && fallback) {
845
861
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
846
862
  }
@@ -1081,10 +1097,10 @@ function CardAction({ className, ...props }) {
1081
1097
  }
1082
1098
  function CardContent({ className, size, ...props }) {
1083
1099
  const sizePadding = {
1084
- xs: "py-2.5 px-3",
1085
- sm: "py-3 px-4",
1086
- md: "py-4 px-6",
1087
- lg: "py-5 px-8"
1100
+ xs: "py-1.5 px-2",
1101
+ sm: "py-2 px-3",
1102
+ md: "py-3 px-4",
1103
+ lg: "py-4 px-6"
1088
1104
  };
1089
1105
  const effectiveSize = size || "md";
1090
1106
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1111,10 +1127,10 @@ function CardFooter({
1111
1127
  ...props
1112
1128
  }) {
1113
1129
  const sizePadding = {
1114
- xs: "py-2.5 px-3 [.border-t]:pt-2.5",
1115
- sm: "py-3 px-4 [.border-t]:pt-3",
1116
- md: "py-4 px-6 [.border-t]:pt-4",
1117
- lg: "py-5 px-8 [.border-t]:pt-5"
1130
+ xs: "gap-0.5 py-1.5 px-2",
1131
+ sm: "gap-1 py-2 px-3",
1132
+ md: "gap-2 py-3 px-4",
1133
+ lg: "gap-3 py-4 px-6"
1118
1134
  };
1119
1135
  const effectiveSize = size || "md";
1120
1136
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -4219,7 +4235,8 @@ function StatusText({
4219
4235
  label,
4220
4236
  variant = "body",
4221
4237
  formatText,
4222
- className
4238
+ className,
4239
+ as = "div"
4223
4240
  }) {
4224
4241
  const displayText = React15__namespace.useMemo(() => {
4225
4242
  let baseText = "";
@@ -4264,7 +4281,7 @@ function StatusText({
4264
4281
  return /* @__PURE__ */ jsxRuntime.jsxs(
4265
4282
  Text,
4266
4283
  {
4267
- as: "div",
4284
+ as,
4268
4285
  "data-slot": "status-text",
4269
4286
  className: cn(
4270
4287
  "flex items-center gap-2",
@@ -4379,7 +4396,7 @@ function InlineEdit({
4379
4396
  setIsEditing(false);
4380
4397
  };
4381
4398
  if (isEditing) {
4382
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "inline-edit", className: cn("inline-flex items-center", className), children: /* @__PURE__ */ jsxRuntime.jsx(
4399
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "inline-edit", className: cn("inline-flex items-center m-1", className), children: /* @__PURE__ */ jsxRuntime.jsx(
4383
4400
  TextInput,
4384
4401
  {
4385
4402
  value,
@@ -4399,7 +4416,7 @@ function InlineEdit({
4399
4416
  {
4400
4417
  "data-slot": "inline-edit-display",
4401
4418
  className: cn(
4402
- "inline-flex items-center h-9 cursor-pointer hover:underline px-3 py-2",
4419
+ "inline-flex items-center h-9 cursor-pointer hover:underline py-2 m-1",
4403
4420
  "border border-transparent rounded-md",
4404
4421
  className
4405
4422
  ),
@@ -4755,14 +4772,15 @@ function TriggerModal({
4755
4772
  footer,
4756
4773
  showCloseButton = true,
4757
4774
  className,
4775
+ message,
4758
4776
  ...props
4759
4777
  }) {
4760
4778
  return /* @__PURE__ */ jsxRuntime.jsxs(Modal, { open, onOpenChange, ...props, children: [
4761
- (triggerLabel || trigger) && /* @__PURE__ */ jsxRuntime.jsx(ModalTrigger, { asChild: true, children: trigger || /* @__PURE__ */ jsxRuntime.jsxs(Button, { ...triggerProps, stopPropagation, children: [
4779
+ (triggerLabel || trigger || icon) && /* @__PURE__ */ jsxRuntime.jsx(ModalTrigger, { asChild: true, children: trigger || /* @__PURE__ */ jsxRuntime.jsxs(Button, { ...triggerProps, stopPropagation, children: [
4762
4780
  icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-2", children: icon }),
4763
- triggerLabel
4781
+ triggerLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: triggerLabel })
4764
4782
  ] }) }),
4765
- /* @__PURE__ */ jsxRuntime.jsx(
4783
+ /* @__PURE__ */ jsxRuntime.jsxs(
4766
4784
  ModalContent,
4767
4785
  {
4768
4786
  "data-slot": "trigger-modal",
@@ -4774,7 +4792,10 @@ function TriggerModal({
4774
4792
  description && /* @__PURE__ */ jsxRuntime.jsx(ModalDescription, { children: description })
4775
4793
  ] }),
4776
4794
  footer: footer ? /* @__PURE__ */ jsxRuntime.jsx(ModalFooter, { children: footer }) : void 0,
4777
- children
4795
+ children: [
4796
+ message && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4", children: message }),
4797
+ children
4798
+ ]
4778
4799
  }
4779
4800
  )
4780
4801
  ] });
@@ -4789,6 +4810,7 @@ function ConfirmModal({
4789
4810
  text,
4790
4811
  title,
4791
4812
  description,
4813
+ message,
4792
4814
  onConfirm,
4793
4815
  confirmLabel,
4794
4816
  cancelLabel = "Cancel",
@@ -4872,7 +4894,8 @@ function ConfirmModal({
4872
4894
  icon,
4873
4895
  stopPropagation,
4874
4896
  title,
4875
- description: description || text,
4897
+ description,
4898
+ message: message || text,
4876
4899
  footer,
4877
4900
  showCloseButton: false,
4878
4901
  className: "data-slot-confirm-modal",
@@ -4920,6 +4943,8 @@ function FormModal({
4920
4943
  triggerProps,
4921
4944
  icon,
4922
4945
  title,
4946
+ description,
4947
+ message,
4923
4948
  variant,
4924
4949
  itemType,
4925
4950
  onSubmit,
@@ -4965,6 +4990,25 @@ function FormModal({
4965
4990
  setFormData(initialData);
4966
4991
  }
4967
4992
  }, [fields]);
4993
+ const fieldOptions = React15__namespace.useMemo(() => {
4994
+ if (!fields) return {};
4995
+ const optionsMap = {};
4996
+ fields.forEach((field) => {
4997
+ if (field.type === "select" && field.options) {
4998
+ if (Array.isArray(field.options)) {
4999
+ optionsMap[field.name] = field.options;
5000
+ } else if (typeof field.options === "function") {
5001
+ try {
5002
+ optionsMap[field.name] = field.options(formData);
5003
+ } catch (e) {
5004
+ console.error(`Error resolving options for field ${field.name}:`, e);
5005
+ optionsMap[field.name] = [];
5006
+ }
5007
+ }
5008
+ }
5009
+ });
5010
+ return optionsMap;
5011
+ }, [fields, formData]);
4968
5012
  const handleChange = (name, value, field) => {
4969
5013
  const newFormData = { ...formData, [name]: value };
4970
5014
  setFormData(newFormData);
@@ -5103,19 +5147,8 @@ function FormModal({
5103
5147
  error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", role: "alert", children: error })
5104
5148
  ] }, field.name);
5105
5149
  case "select":
5106
- const resolvedOptions = React15__namespace.useMemo(() => {
5107
- if (!field.options) return [];
5108
- if (Array.isArray(field.options)) return field.options;
5109
- if (typeof field.options === "function") {
5110
- try {
5111
- return field.options(formData);
5112
- } catch (e) {
5113
- console.error(`Error resolving options for field ${field.name}:`, e);
5114
- return [];
5115
- }
5116
- }
5117
- return [];
5118
- }, [field.options, formData]);
5150
+ const resolvedOptions = fieldOptions[field.name] || [];
5151
+ const optionsKey = resolvedOptions.map((o) => o.value).join(",");
5119
5152
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
5120
5153
  field.label && /* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: field.name, className: error && "text-destructive", children: [
5121
5154
  field.label,
@@ -5129,9 +5162,10 @@ function FormModal({
5129
5162
  onValueChange: (val) => handleChange(field.name, val, field),
5130
5163
  children: [
5131
5164
  /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: field.name, className: error && "border-destructive", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: resolvedPlaceholder || "Select..." }) }),
5132
- /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: resolvedOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
5165
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: resolvedOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: option.value, children: option.label }, option.value)) }, `select-content-${field.name}-${optionsKey}`)
5133
5166
  ]
5134
- }
5167
+ },
5168
+ `select-${field.name}-${optionsKey}`
5135
5169
  ),
5136
5170
  resolvedHelpText && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: resolvedHelpText }),
5137
5171
  error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", role: "alert", children: error })
@@ -5224,6 +5258,7 @@ function FormModal({
5224
5258
  /* @__PURE__ */ jsxRuntime.jsx(
5225
5259
  Button,
5226
5260
  {
5261
+ className: "ml-2",
5227
5262
  type: "submit",
5228
5263
  disabled: loading || (typeof isSubmitDisabled === "function" ? isSubmitDisabled(formData) : isSubmitDisabled ?? false),
5229
5264
  children: loading ? getSubmitLabel() : getSubmitLabel()
@@ -5240,6 +5275,8 @@ function FormModal({
5240
5275
  icon,
5241
5276
  stopPropagation: true,
5242
5277
  title,
5278
+ description,
5279
+ message,
5243
5280
  showCloseButton: false,
5244
5281
  footer,
5245
5282
  className: "data-slot-form-modal",
@@ -5844,30 +5881,30 @@ function CollapsiblePanel({
5844
5881
  const getTriggerIcon = () => {
5845
5882
  if (direction === "horizontal") {
5846
5883
  if (position === "left") {
5847
- return isOpen ? lucideReact.ChevronRight : lucideReact.ChevronLeft;
5848
- } else {
5849
5884
  return isOpen ? lucideReact.ChevronLeft : lucideReact.ChevronRight;
5885
+ } else {
5886
+ return isOpen ? lucideReact.ChevronRight : lucideReact.ChevronLeft;
5850
5887
  }
5851
5888
  } else {
5852
5889
  if (position === "top") {
5853
- return isOpen ? lucideReact.ChevronDown : lucideReact.ChevronUp;
5854
- } else {
5855
5890
  return isOpen ? lucideReact.ChevronUp : lucideReact.ChevronDown;
5891
+ } else {
5892
+ return isOpen ? lucideReact.ChevronDown : lucideReact.ChevronUp;
5856
5893
  }
5857
5894
  }
5858
5895
  };
5859
5896
  const getTriggerPosition = () => {
5860
5897
  if (direction === "horizontal") {
5861
5898
  if (position === "left") {
5862
- return isOpen ? "absolute -left-3 top-1/2 -translate-y-1/2" : "absolute -left-6 top-1/2 -translate-y-1/2";
5863
- } else {
5864
5899
  return isOpen ? "absolute -right-3 top-1/2 -translate-y-1/2" : "absolute -right-6 top-1/2 -translate-y-1/2";
5900
+ } else {
5901
+ return isOpen ? "absolute -left-3 top-1/2 -translate-y-1/2" : "absolute -left-6 top-1/2 -translate-y-1/2";
5865
5902
  }
5866
5903
  } else {
5867
5904
  if (position === "top") {
5868
- return isOpen ? "absolute -top-3 left-1/2 -translate-x-1/2" : "absolute -top-6 left-1/2 -translate-x-1/2";
5869
- } else {
5870
5905
  return isOpen ? "absolute -bottom-3 left-1/2 -translate-x-1/2" : "absolute -bottom-6 left-1/2 -translate-x-1/2";
5906
+ } else {
5907
+ return isOpen ? "absolute -top-3 left-1/2 -translate-x-1/2" : "absolute -top-6 left-1/2 -translate-x-1/2";
5871
5908
  }
5872
5909
  }
5873
5910
  };
package/dist/index.mjs CHANGED
@@ -8,7 +8,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import * as LabelPrimitive from '@radix-ui/react-label';
9
9
  import * as SeparatorPrimitive from '@radix-ui/react-separator';
10
10
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
11
- import { CheckIcon, CircleIcon, Loader2Icon, MinusIcon, XIcon, ChevronDownIcon, ChevronUpIcon, ChevronRight, MoreHorizontal, ChevronRightIcon, ChevronLeftIcon, MoreHorizontalIcon, SearchIcon, ArrowLeft, ArrowRight, OctagonXIcon, TriangleAlertIcon, InfoIcon, CircleCheckIcon, AlertCircleIcon, XCircleIcon, CheckCircleIcon, CopyIcon, UndoIcon, RedoIcon, RotateCcwIcon, GripVerticalIcon, PanelLeftIcon, ChevronLeft, ChevronDown, ChevronUp } from 'lucide-react';
11
+ import { CheckIcon, CircleIcon, Loader2Icon, MinusIcon, XIcon, ChevronDownIcon, ChevronUpIcon, ChevronRight, MoreHorizontal, ChevronRightIcon, ChevronLeftIcon, MoreHorizontalIcon, SearchIcon, ArrowLeft, ArrowRight, OctagonXIcon, TriangleAlertIcon, InfoIcon, CircleCheckIcon, AlertCircleIcon, XCircleIcon, CheckCircleIcon, CopyIcon, UndoIcon, RedoIcon, RotateCcwIcon, GripVerticalIcon, PanelLeftIcon, ChevronLeft, ChevronUp, ChevronDown } from 'lucide-react';
12
12
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
13
13
  import * as ProgressPrimitive from '@radix-ui/react-progress';
14
14
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
@@ -682,9 +682,13 @@ function Text({
682
682
  className,
683
683
  variant = "default",
684
684
  as: Component2 = "p",
685
+ leftIcon,
686
+ rightIcon,
687
+ children,
685
688
  ...props
686
689
  }) {
687
- return /* @__PURE__ */ jsx(
690
+ const hasIcons = leftIcon || rightIcon;
691
+ return /* @__PURE__ */ jsxs(
688
692
  Component2,
689
693
  {
690
694
  "data-slot": "text",
@@ -693,9 +697,15 @@ function Text({
693
697
  variant === "muted" && "text-muted-foreground",
694
698
  variant === "small" && "text-sm",
695
699
  variant === "large" && "text-lg",
700
+ hasIcons && "flex items-center gap-2",
696
701
  className
697
702
  ),
698
- ...props
703
+ ...props,
704
+ children: [
705
+ leftIcon && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: leftIcon }),
706
+ children,
707
+ rightIcon && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: rightIcon })
708
+ ]
699
709
  }
700
710
  );
701
711
  }
@@ -790,6 +800,12 @@ function Image2({
790
800
  ...props
791
801
  }) {
792
802
  const [hasError, setHasError] = React15.useState(false);
803
+ if (!src || src.trim() === "") {
804
+ if (fallback) {
805
+ return /* @__PURE__ */ jsx(Fragment, { children: fallback });
806
+ }
807
+ return null;
808
+ }
793
809
  if (hasError && fallback) {
794
810
  return /* @__PURE__ */ jsx(Fragment, { children: fallback });
795
811
  }
@@ -1030,10 +1046,10 @@ function CardAction({ className, ...props }) {
1030
1046
  }
1031
1047
  function CardContent({ className, size, ...props }) {
1032
1048
  const sizePadding = {
1033
- xs: "py-2.5 px-3",
1034
- sm: "py-3 px-4",
1035
- md: "py-4 px-6",
1036
- lg: "py-5 px-8"
1049
+ xs: "py-1.5 px-2",
1050
+ sm: "py-2 px-3",
1051
+ md: "py-3 px-4",
1052
+ lg: "py-4 px-6"
1037
1053
  };
1038
1054
  const effectiveSize = size || "md";
1039
1055
  return /* @__PURE__ */ jsx(
@@ -1060,10 +1076,10 @@ function CardFooter({
1060
1076
  ...props
1061
1077
  }) {
1062
1078
  const sizePadding = {
1063
- xs: "py-2.5 px-3 [.border-t]:pt-2.5",
1064
- sm: "py-3 px-4 [.border-t]:pt-3",
1065
- md: "py-4 px-6 [.border-t]:pt-4",
1066
- lg: "py-5 px-8 [.border-t]:pt-5"
1079
+ xs: "gap-0.5 py-1.5 px-2",
1080
+ sm: "gap-1 py-2 px-3",
1081
+ md: "gap-2 py-3 px-4",
1082
+ lg: "gap-3 py-4 px-6"
1067
1083
  };
1068
1084
  const effectiveSize = size || "md";
1069
1085
  return /* @__PURE__ */ jsx(
@@ -4168,7 +4184,8 @@ function StatusText({
4168
4184
  label,
4169
4185
  variant = "body",
4170
4186
  formatText,
4171
- className
4187
+ className,
4188
+ as = "div"
4172
4189
  }) {
4173
4190
  const displayText = React15.useMemo(() => {
4174
4191
  let baseText = "";
@@ -4213,7 +4230,7 @@ function StatusText({
4213
4230
  return /* @__PURE__ */ jsxs(
4214
4231
  Text,
4215
4232
  {
4216
- as: "div",
4233
+ as,
4217
4234
  "data-slot": "status-text",
4218
4235
  className: cn(
4219
4236
  "flex items-center gap-2",
@@ -4328,7 +4345,7 @@ function InlineEdit({
4328
4345
  setIsEditing(false);
4329
4346
  };
4330
4347
  if (isEditing) {
4331
- return /* @__PURE__ */ jsx("div", { "data-slot": "inline-edit", className: cn("inline-flex items-center", className), children: /* @__PURE__ */ jsx(
4348
+ return /* @__PURE__ */ jsx("div", { "data-slot": "inline-edit", className: cn("inline-flex items-center m-1", className), children: /* @__PURE__ */ jsx(
4332
4349
  TextInput,
4333
4350
  {
4334
4351
  value,
@@ -4348,7 +4365,7 @@ function InlineEdit({
4348
4365
  {
4349
4366
  "data-slot": "inline-edit-display",
4350
4367
  className: cn(
4351
- "inline-flex items-center h-9 cursor-pointer hover:underline px-3 py-2",
4368
+ "inline-flex items-center h-9 cursor-pointer hover:underline py-2 m-1",
4352
4369
  "border border-transparent rounded-md",
4353
4370
  className
4354
4371
  ),
@@ -4704,14 +4721,15 @@ function TriggerModal({
4704
4721
  footer,
4705
4722
  showCloseButton = true,
4706
4723
  className,
4724
+ message,
4707
4725
  ...props
4708
4726
  }) {
4709
4727
  return /* @__PURE__ */ jsxs(Modal, { open, onOpenChange, ...props, children: [
4710
- (triggerLabel || trigger) && /* @__PURE__ */ jsx(ModalTrigger, { asChild: true, children: trigger || /* @__PURE__ */ jsxs(Button, { ...triggerProps, stopPropagation, children: [
4728
+ (triggerLabel || trigger || icon) && /* @__PURE__ */ jsx(ModalTrigger, { asChild: true, children: trigger || /* @__PURE__ */ jsxs(Button, { ...triggerProps, stopPropagation, children: [
4711
4729
  icon && /* @__PURE__ */ jsx("span", { className: "mr-2", children: icon }),
4712
- triggerLabel
4730
+ triggerLabel && /* @__PURE__ */ jsx("span", { children: triggerLabel })
4713
4731
  ] }) }),
4714
- /* @__PURE__ */ jsx(
4732
+ /* @__PURE__ */ jsxs(
4715
4733
  ModalContent,
4716
4734
  {
4717
4735
  "data-slot": "trigger-modal",
@@ -4723,7 +4741,10 @@ function TriggerModal({
4723
4741
  description && /* @__PURE__ */ jsx(ModalDescription, { children: description })
4724
4742
  ] }),
4725
4743
  footer: footer ? /* @__PURE__ */ jsx(ModalFooter, { children: footer }) : void 0,
4726
- children
4744
+ children: [
4745
+ message && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4", children: message }),
4746
+ children
4747
+ ]
4727
4748
  }
4728
4749
  )
4729
4750
  ] });
@@ -4738,6 +4759,7 @@ function ConfirmModal({
4738
4759
  text,
4739
4760
  title,
4740
4761
  description,
4762
+ message,
4741
4763
  onConfirm,
4742
4764
  confirmLabel,
4743
4765
  cancelLabel = "Cancel",
@@ -4821,7 +4843,8 @@ function ConfirmModal({
4821
4843
  icon,
4822
4844
  stopPropagation,
4823
4845
  title,
4824
- description: description || text,
4846
+ description,
4847
+ message: message || text,
4825
4848
  footer,
4826
4849
  showCloseButton: false,
4827
4850
  className: "data-slot-confirm-modal",
@@ -4869,6 +4892,8 @@ function FormModal({
4869
4892
  triggerProps,
4870
4893
  icon,
4871
4894
  title,
4895
+ description,
4896
+ message,
4872
4897
  variant,
4873
4898
  itemType,
4874
4899
  onSubmit,
@@ -4914,6 +4939,25 @@ function FormModal({
4914
4939
  setFormData(initialData);
4915
4940
  }
4916
4941
  }, [fields]);
4942
+ const fieldOptions = React15.useMemo(() => {
4943
+ if (!fields) return {};
4944
+ const optionsMap = {};
4945
+ fields.forEach((field) => {
4946
+ if (field.type === "select" && field.options) {
4947
+ if (Array.isArray(field.options)) {
4948
+ optionsMap[field.name] = field.options;
4949
+ } else if (typeof field.options === "function") {
4950
+ try {
4951
+ optionsMap[field.name] = field.options(formData);
4952
+ } catch (e) {
4953
+ console.error(`Error resolving options for field ${field.name}:`, e);
4954
+ optionsMap[field.name] = [];
4955
+ }
4956
+ }
4957
+ }
4958
+ });
4959
+ return optionsMap;
4960
+ }, [fields, formData]);
4917
4961
  const handleChange = (name, value, field) => {
4918
4962
  const newFormData = { ...formData, [name]: value };
4919
4963
  setFormData(newFormData);
@@ -5052,19 +5096,8 @@ function FormModal({
5052
5096
  error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", role: "alert", children: error })
5053
5097
  ] }, field.name);
5054
5098
  case "select":
5055
- const resolvedOptions = React15.useMemo(() => {
5056
- if (!field.options) return [];
5057
- if (Array.isArray(field.options)) return field.options;
5058
- if (typeof field.options === "function") {
5059
- try {
5060
- return field.options(formData);
5061
- } catch (e) {
5062
- console.error(`Error resolving options for field ${field.name}:`, e);
5063
- return [];
5064
- }
5065
- }
5066
- return [];
5067
- }, [field.options, formData]);
5099
+ const resolvedOptions = fieldOptions[field.name] || [];
5100
+ const optionsKey = resolvedOptions.map((o) => o.value).join(",");
5068
5101
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
5069
5102
  field.label && /* @__PURE__ */ jsxs(Label, { htmlFor: field.name, className: error && "text-destructive", children: [
5070
5103
  field.label,
@@ -5078,9 +5111,10 @@ function FormModal({
5078
5111
  onValueChange: (val) => handleChange(field.name, val, field),
5079
5112
  children: [
5080
5113
  /* @__PURE__ */ jsx(SelectTrigger, { id: field.name, className: error && "border-destructive", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: resolvedPlaceholder || "Select..." }) }),
5081
- /* @__PURE__ */ jsx(SelectContent, { children: resolvedOptions.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
5114
+ /* @__PURE__ */ jsx(SelectContent, { children: resolvedOptions.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) }, `select-content-${field.name}-${optionsKey}`)
5082
5115
  ]
5083
- }
5116
+ },
5117
+ `select-${field.name}-${optionsKey}`
5084
5118
  ),
5085
5119
  resolvedHelpText && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: resolvedHelpText }),
5086
5120
  error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", role: "alert", children: error })
@@ -5173,6 +5207,7 @@ function FormModal({
5173
5207
  /* @__PURE__ */ jsx(
5174
5208
  Button,
5175
5209
  {
5210
+ className: "ml-2",
5176
5211
  type: "submit",
5177
5212
  disabled: loading || (typeof isSubmitDisabled === "function" ? isSubmitDisabled(formData) : isSubmitDisabled ?? false),
5178
5213
  children: loading ? getSubmitLabel() : getSubmitLabel()
@@ -5189,6 +5224,8 @@ function FormModal({
5189
5224
  icon,
5190
5225
  stopPropagation: true,
5191
5226
  title,
5227
+ description,
5228
+ message,
5192
5229
  showCloseButton: false,
5193
5230
  footer,
5194
5231
  className: "data-slot-form-modal",
@@ -5793,30 +5830,30 @@ function CollapsiblePanel({
5793
5830
  const getTriggerIcon = () => {
5794
5831
  if (direction === "horizontal") {
5795
5832
  if (position === "left") {
5796
- return isOpen ? ChevronRight : ChevronLeft;
5797
- } else {
5798
5833
  return isOpen ? ChevronLeft : ChevronRight;
5834
+ } else {
5835
+ return isOpen ? ChevronRight : ChevronLeft;
5799
5836
  }
5800
5837
  } else {
5801
5838
  if (position === "top") {
5802
- return isOpen ? ChevronDown : ChevronUp;
5803
- } else {
5804
5839
  return isOpen ? ChevronUp : ChevronDown;
5840
+ } else {
5841
+ return isOpen ? ChevronDown : ChevronUp;
5805
5842
  }
5806
5843
  }
5807
5844
  };
5808
5845
  const getTriggerPosition = () => {
5809
5846
  if (direction === "horizontal") {
5810
5847
  if (position === "left") {
5811
- return isOpen ? "absolute -left-3 top-1/2 -translate-y-1/2" : "absolute -left-6 top-1/2 -translate-y-1/2";
5812
- } else {
5813
5848
  return isOpen ? "absolute -right-3 top-1/2 -translate-y-1/2" : "absolute -right-6 top-1/2 -translate-y-1/2";
5849
+ } else {
5850
+ return isOpen ? "absolute -left-3 top-1/2 -translate-y-1/2" : "absolute -left-6 top-1/2 -translate-y-1/2";
5814
5851
  }
5815
5852
  } else {
5816
5853
  if (position === "top") {
5817
- return isOpen ? "absolute -top-3 left-1/2 -translate-x-1/2" : "absolute -top-6 left-1/2 -translate-x-1/2";
5818
- } else {
5819
5854
  return isOpen ? "absolute -bottom-3 left-1/2 -translate-x-1/2" : "absolute -bottom-6 left-1/2 -translate-x-1/2";
5855
+ } else {
5856
+ return isOpen ? "absolute -top-3 left-1/2 -translate-x-1/2" : "absolute -top-6 left-1/2 -translate-x-1/2";
5820
5857
  }
5821
5858
  }
5822
5859
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shru-design-system",
3
- "version": "0.5.1",
3
+ "version": "0.5.4",
4
4
  "description": "A React component library with atoms and molecules built on Radix UI and Tailwind CSS",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",