@neoptocom/neopto-ui 1.5.2 → 1.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.cjs CHANGED
@@ -264,6 +264,39 @@ function Card({
264
264
  }
265
265
  );
266
266
  }
267
+ var sizeMap = {
268
+ sm: 16,
269
+ md: 24,
270
+ lg: 36
271
+ };
272
+ function Icon({
273
+ name,
274
+ className = "",
275
+ title,
276
+ size = "md",
277
+ fill = 0
278
+ }) {
279
+ const fontSize = sizeMap[size] ?? sizeMap.md;
280
+ const hasColorClass = /\b(?:text-|fill-|stroke-)\S+/.test(className);
281
+ const style = {
282
+ fontVariationSettings: `'FILL' ${fill}, 'wght' 300, 'GRAD' 0, 'opsz' ${fontSize}`,
283
+ fontSize,
284
+ lineHeight: 1,
285
+ display: "inline-block",
286
+ verticalAlign: "middle",
287
+ ...hasColorClass ? {} : { color: "inherit" }
288
+ };
289
+ return /* @__PURE__ */ jsxRuntime.jsx(
290
+ "span",
291
+ {
292
+ className: `material-symbols-rounded rounded ${className}`,
293
+ style,
294
+ "aria-hidden": title ? void 0 : true,
295
+ title,
296
+ children: name
297
+ }
298
+ );
299
+ }
267
300
  var Input = React3__namespace.forwardRef(
268
301
  ({
269
302
  className,
@@ -273,14 +306,17 @@ var Input = React3__namespace.forwardRef(
273
306
  fieldsetProps,
274
307
  legendProps,
275
308
  error = false,
309
+ icon,
276
310
  ...props
277
311
  }, ref) => {
278
312
  const isInlineVariant = variant === "inline";
279
313
  const shouldUseInlineStyles = isInlineVariant || Boolean(label);
280
314
  const isError = error && !disabled;
315
+ const hasIcon = Boolean(icon);
281
316
  const inputClasses = [
282
317
  "w-full bg-transparent outline-none transition-colors",
283
- shouldUseInlineStyles ? "h-9" : "h-12 px-4 rounded-full",
318
+ shouldUseInlineStyles ? "h-9" : "h-12 rounded-full",
319
+ shouldUseInlineStyles ? hasIcon ? "pr-8" : "" : hasIcon ? "px-4 pr-10" : "px-4",
284
320
  "font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]"
285
321
  ];
286
322
  if (!shouldUseInlineStyles) {
@@ -307,6 +343,12 @@ var Input = React3__namespace.forwardRef(
307
343
  const inputClassName = inputClasses.join(" ");
308
344
  const inputElement = /* @__PURE__ */ jsxRuntime.jsx("input", { ref, disabled, className: inputClassName, ...props });
309
345
  if (!label) {
346
+ if (hasIcon) {
347
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
348
+ inputElement,
349
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, className: "text-[var(--muted-fg)] opacity-50" }) })
350
+ ] });
351
+ }
310
352
  return inputElement;
311
353
  }
312
354
  const { className: fieldsetClassNameProp = "", ...restFieldsetProps } = fieldsetProps ?? {};
@@ -338,7 +380,10 @@ var Input = React3__namespace.forwardRef(
338
380
  children: label
339
381
  }
340
382
  ),
341
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex pl-5 pr-3 pb-1 h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full", children: inputElement }) })
383
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex pl-5 pr-3 pb-1 h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full relative", children: [
384
+ inputElement,
385
+ hasIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-1 top-1/2 -translate-y-1/2 pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, className: "text-[var(--muted-fg)] opacity-50" }) })
386
+ ] }) })
342
387
  ]
343
388
  }
344
389
  );
@@ -625,39 +670,6 @@ function Skeleton({ className = "", rounded = "md", ...props }) {
625
670
  }
626
671
  );
627
672
  }
628
- var sizeMap = {
629
- sm: 16,
630
- md: 24,
631
- lg: 36
632
- };
633
- function Icon({
634
- name,
635
- className = "",
636
- title,
637
- size = "md",
638
- fill = 0
639
- }) {
640
- const fontSize = sizeMap[size] ?? sizeMap.md;
641
- const hasColorClass = /\b(?:text-|fill-|stroke-)\S+/.test(className);
642
- const style = {
643
- fontVariationSettings: `'FILL' ${fill}, 'wght' 300, 'GRAD' 0, 'opsz' ${fontSize}`,
644
- fontSize,
645
- lineHeight: 1,
646
- display: "inline-block",
647
- verticalAlign: "middle",
648
- ...hasColorClass ? {} : { color: "inherit" }
649
- };
650
- return /* @__PURE__ */ jsxRuntime.jsx(
651
- "span",
652
- {
653
- className: `material-symbols-rounded rounded ${className}`,
654
- style,
655
- "aria-hidden": title ? void 0 : true,
656
- title,
657
- children: name
658
- }
659
- );
660
- }
661
673
  function getIconButtonClasses(variant = "ghost", size = "md", className) {
662
674
  const base = "cursor-pointer flex items-center justify-center rounded-full flex-shrink-0 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-cyan-500/40 disabled:cursor-not-allowed disabled:opacity-50";
663
675
  const variants = {
@@ -831,7 +843,7 @@ function Autocomplete({
831
843
  "fieldset",
832
844
  {
833
845
  className: [
834
- "w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-16",
846
+ "w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-13",
835
847
  "border-[var(--border)] focus-within:border-[var(--color-brand)]",
836
848
  disabled ? "opacity-60 cursor-not-allowed" : ""
837
849
  ].join(" "),
@@ -885,6 +897,7 @@ function Autocomplete({
885
897
  onClick: selectedOption && !open ? handleClear : () => setOpen((s) => !s),
886
898
  disabled,
887
899
  "aria-label": selectedOption && !open ? "Clear" : open ? "Collapse" : "Expand",
900
+ className: "absolute right-0 top-[-30%]",
888
901
  iconClassName: [
889
902
  "transition-transform duration-300 text-[var(--muted-fg)]",
890
903
  open ? "rotate-180 text-[var(--color-brand)]" : ""
package/dist/index.d.cts CHANGED
@@ -70,6 +70,8 @@ type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & {
70
70
  legendProps?: React.HTMLAttributes<HTMLLegendElement>;
71
71
  /** Flag to visually mark the input as errored */
72
72
  error?: boolean;
73
+ /** Optional icon name to display on the inner right of the input */
74
+ icon?: string;
73
75
  };
74
76
  declare const Input: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & {
75
77
  /** Input visual variant */
@@ -82,6 +84,8 @@ declare const Input: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttribu
82
84
  legendProps?: React.HTMLAttributes<HTMLLegendElement>;
83
85
  /** Flag to visually mark the input as errored */
84
86
  error?: boolean;
87
+ /** Optional icon name to display on the inner right of the input */
88
+ icon?: string;
85
89
  } & React.RefAttributes<HTMLInputElement>>;
86
90
 
87
91
  type TextareaProps = Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> & {
package/dist/index.d.ts CHANGED
@@ -70,6 +70,8 @@ type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & {
70
70
  legendProps?: React.HTMLAttributes<HTMLLegendElement>;
71
71
  /** Flag to visually mark the input as errored */
72
72
  error?: boolean;
73
+ /** Optional icon name to display on the inner right of the input */
74
+ icon?: string;
73
75
  };
74
76
  declare const Input: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & {
75
77
  /** Input visual variant */
@@ -82,6 +84,8 @@ declare const Input: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttribu
82
84
  legendProps?: React.HTMLAttributes<HTMLLegendElement>;
83
85
  /** Flag to visually mark the input as errored */
84
86
  error?: boolean;
87
+ /** Optional icon name to display on the inner right of the input */
88
+ icon?: string;
85
89
  } & React.RefAttributes<HTMLInputElement>>;
86
90
 
87
91
  type TextareaProps = Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> & {
package/dist/index.js CHANGED
@@ -243,6 +243,39 @@ function Card({
243
243
  }
244
244
  );
245
245
  }
246
+ var sizeMap = {
247
+ sm: 16,
248
+ md: 24,
249
+ lg: 36
250
+ };
251
+ function Icon({
252
+ name,
253
+ className = "",
254
+ title,
255
+ size = "md",
256
+ fill = 0
257
+ }) {
258
+ const fontSize = sizeMap[size] ?? sizeMap.md;
259
+ const hasColorClass = /\b(?:text-|fill-|stroke-)\S+/.test(className);
260
+ const style = {
261
+ fontVariationSettings: `'FILL' ${fill}, 'wght' 300, 'GRAD' 0, 'opsz' ${fontSize}`,
262
+ fontSize,
263
+ lineHeight: 1,
264
+ display: "inline-block",
265
+ verticalAlign: "middle",
266
+ ...hasColorClass ? {} : { color: "inherit" }
267
+ };
268
+ return /* @__PURE__ */ jsx(
269
+ "span",
270
+ {
271
+ className: `material-symbols-rounded rounded ${className}`,
272
+ style,
273
+ "aria-hidden": title ? void 0 : true,
274
+ title,
275
+ children: name
276
+ }
277
+ );
278
+ }
246
279
  var Input = React3.forwardRef(
247
280
  ({
248
281
  className,
@@ -252,14 +285,17 @@ var Input = React3.forwardRef(
252
285
  fieldsetProps,
253
286
  legendProps,
254
287
  error = false,
288
+ icon,
255
289
  ...props
256
290
  }, ref) => {
257
291
  const isInlineVariant = variant === "inline";
258
292
  const shouldUseInlineStyles = isInlineVariant || Boolean(label);
259
293
  const isError = error && !disabled;
294
+ const hasIcon = Boolean(icon);
260
295
  const inputClasses = [
261
296
  "w-full bg-transparent outline-none transition-colors",
262
- shouldUseInlineStyles ? "h-9" : "h-12 px-4 rounded-full",
297
+ shouldUseInlineStyles ? "h-9" : "h-12 rounded-full",
298
+ shouldUseInlineStyles ? hasIcon ? "pr-8" : "" : hasIcon ? "px-4 pr-10" : "px-4",
263
299
  "font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]"
264
300
  ];
265
301
  if (!shouldUseInlineStyles) {
@@ -286,6 +322,12 @@ var Input = React3.forwardRef(
286
322
  const inputClassName = inputClasses.join(" ");
287
323
  const inputElement = /* @__PURE__ */ jsx("input", { ref, disabled, className: inputClassName, ...props });
288
324
  if (!label) {
325
+ if (hasIcon) {
326
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
327
+ inputElement,
328
+ /* @__PURE__ */ jsx("div", { className: "absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none", children: /* @__PURE__ */ jsx(Icon, { name: icon, className: "text-[var(--muted-fg)] opacity-50" }) })
329
+ ] });
330
+ }
289
331
  return inputElement;
290
332
  }
291
333
  const { className: fieldsetClassNameProp = "", ...restFieldsetProps } = fieldsetProps ?? {};
@@ -317,7 +359,10 @@ var Input = React3.forwardRef(
317
359
  children: label
318
360
  }
319
361
  ),
320
- /* @__PURE__ */ jsx("div", { className: "relative flex pl-5 pr-3 pb-1 h-full", children: /* @__PURE__ */ jsx("div", { className: "flex w-full", children: inputElement }) })
362
+ /* @__PURE__ */ jsx("div", { className: "relative flex pl-5 pr-3 pb-1 h-full", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full relative", children: [
363
+ inputElement,
364
+ hasIcon && /* @__PURE__ */ jsx("div", { className: "absolute right-1 top-1/2 -translate-y-1/2 pointer-events-none", children: /* @__PURE__ */ jsx(Icon, { name: icon, className: "text-[var(--muted-fg)] opacity-50" }) })
365
+ ] }) })
321
366
  ]
322
367
  }
323
368
  );
@@ -604,39 +649,6 @@ function Skeleton({ className = "", rounded = "md", ...props }) {
604
649
  }
605
650
  );
606
651
  }
607
- var sizeMap = {
608
- sm: 16,
609
- md: 24,
610
- lg: 36
611
- };
612
- function Icon({
613
- name,
614
- className = "",
615
- title,
616
- size = "md",
617
- fill = 0
618
- }) {
619
- const fontSize = sizeMap[size] ?? sizeMap.md;
620
- const hasColorClass = /\b(?:text-|fill-|stroke-)\S+/.test(className);
621
- const style = {
622
- fontVariationSettings: `'FILL' ${fill}, 'wght' 300, 'GRAD' 0, 'opsz' ${fontSize}`,
623
- fontSize,
624
- lineHeight: 1,
625
- display: "inline-block",
626
- verticalAlign: "middle",
627
- ...hasColorClass ? {} : { color: "inherit" }
628
- };
629
- return /* @__PURE__ */ jsx(
630
- "span",
631
- {
632
- className: `material-symbols-rounded rounded ${className}`,
633
- style,
634
- "aria-hidden": title ? void 0 : true,
635
- title,
636
- children: name
637
- }
638
- );
639
- }
640
652
  function getIconButtonClasses(variant = "ghost", size = "md", className) {
641
653
  const base = "cursor-pointer flex items-center justify-center rounded-full flex-shrink-0 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-cyan-500/40 disabled:cursor-not-allowed disabled:opacity-50";
642
654
  const variants = {
@@ -810,7 +822,7 @@ function Autocomplete({
810
822
  "fieldset",
811
823
  {
812
824
  className: [
813
- "w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-16",
825
+ "w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-13",
814
826
  "border-[var(--border)] focus-within:border-[var(--color-brand)]",
815
827
  disabled ? "opacity-60 cursor-not-allowed" : ""
816
828
  ].join(" "),
@@ -864,6 +876,7 @@ function Autocomplete({
864
876
  onClick: selectedOption && !open ? handleClear : () => setOpen((s) => !s),
865
877
  disabled,
866
878
  "aria-label": selectedOption && !open ? "Clear" : open ? "Collapse" : "Expand",
879
+ className: "absolute right-0 top-[-30%]",
867
880
  iconClassName: [
868
881
  "transition-transform duration-300 text-[var(--muted-fg)]",
869
882
  open ? "rotate-180 text-[var(--color-brand)]" : ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neoptocom/neopto-ui",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "private": false,
5
5
  "description": "A modern React component library built with Tailwind CSS v4 and TypeScript. Features dark mode, design tokens, and comprehensive Storybook documentation. Requires Tailwind v4+.",
6
6
  "keywords": [
@@ -167,7 +167,7 @@ export default function Autocomplete({
167
167
  >
168
168
  <fieldset
169
169
  className={[
170
- "w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-16",
170
+ "w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-13",
171
171
  "border-[var(--border)] focus-within:border-[var(--color-brand)]",
172
172
  disabled ? "opacity-60 cursor-not-allowed" : ""
173
173
  ].join(" ")}
@@ -222,6 +222,7 @@ export default function Autocomplete({
222
222
  }
223
223
  disabled={disabled}
224
224
  aria-label={selectedOption && !open ? "Clear" : open ? "Collapse" : "Expand"}
225
+ className="absolute right-0 top-[-30%]"
225
226
  iconClassName={[
226
227
  "transition-transform duration-300 text-[var(--muted-fg)]",
227
228
  open ? "rotate-180 text-[var(--color-brand)]" : ""
@@ -59,3 +59,6 @@ page load. The interactive example demonstrates an in-app documents flow.
59
59
 
60
60
 
61
61
 
62
+
63
+
64
+
@@ -77,3 +77,6 @@ export const InteractiveNavigation: Story = {
77
77
 
78
78
 
79
79
 
80
+
81
+
82
+
@@ -55,3 +55,6 @@ Use variants to communicate hierarchy:
55
55
 
56
56
 
57
57
 
58
+
59
+
60
+
@@ -109,3 +109,6 @@ export const FullWidthCallToAction: Story = {
109
109
 
110
110
 
111
111
 
112
+
113
+
114
+
@@ -55,3 +55,6 @@ attaching click handlers to the card root.
55
55
 
56
56
 
57
57
 
58
+
59
+
60
+
@@ -1,4 +1,5 @@
1
1
  import * as React from "react";
2
+ import Icon from "./Icon";
2
3
 
3
4
  export type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> & {
4
5
  /** Input visual variant */
@@ -11,6 +12,8 @@ export type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "size
11
12
  legendProps?: React.HTMLAttributes<HTMLLegendElement>;
12
13
  /** Flag to visually mark the input as errored */
13
14
  error?: boolean;
15
+ /** Optional icon name to display on the inner right of the input */
16
+ icon?: string;
14
17
  };
15
18
 
16
19
  export const Input = React.forwardRef<HTMLInputElement, InputProps>(
@@ -23,6 +26,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
23
26
  fieldsetProps,
24
27
  legendProps,
25
28
  error = false,
29
+ icon,
26
30
  ...props
27
31
  },
28
32
  ref
@@ -30,10 +34,14 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
30
34
  const isInlineVariant = variant === "inline";
31
35
  const shouldUseInlineStyles = isInlineVariant || Boolean(label);
32
36
  const isError = error && !disabled;
37
+ const hasIcon = Boolean(icon);
33
38
 
34
39
  const inputClasses: string[] = [
35
40
  "w-full bg-transparent outline-none transition-colors",
36
- shouldUseInlineStyles ? "h-9" : "h-12 px-4 rounded-full",
41
+ shouldUseInlineStyles ? "h-9" : "h-12 rounded-full",
42
+ shouldUseInlineStyles
43
+ ? (hasIcon ? "pr-8" : "")
44
+ : (hasIcon ? "px-4 pr-10" : "px-4"),
37
45
  "font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]"
38
46
  ];
39
47
 
@@ -67,7 +75,18 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
67
75
  <input ref={ref} disabled={disabled} className={inputClassName} {...props} />
68
76
  );
69
77
 
78
+ // Standalone input (no label)
70
79
  if (!label) {
80
+ if (hasIcon) {
81
+ return (
82
+ <div className="relative">
83
+ {inputElement}
84
+ <div className="absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none">
85
+ <Icon name={icon!} className="text-[var(--muted-fg)] opacity-50" />
86
+ </div>
87
+ </div>
88
+ );
89
+ }
71
90
  return inputElement;
72
91
  }
73
92
 
@@ -113,7 +132,14 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
113
132
  {label}
114
133
  </legend>
115
134
  <div className="relative flex pl-5 pr-3 pb-1 h-full">
116
- <div className="flex w-full">{inputElement}</div>
135
+ <div className="flex w-full relative">
136
+ {inputElement}
137
+ {hasIcon && (
138
+ <div className="absolute right-1 top-1/2 -translate-y-1/2 pointer-events-none">
139
+ <Icon name={icon!} className="text-[var(--muted-fg)] opacity-50" />
140
+ </div>
141
+ )}
142
+ </div>
117
143
  </div>
118
144
  </fieldset>
119
145
  );
@@ -73,3 +73,14 @@ export const Error: Story = {
73
73
  </div>
74
74
  )
75
75
  };
76
+
77
+ export const WithIcon: Story = {
78
+ render: () => (
79
+ <div className="flex flex-col gap-4 w-96">
80
+ <Input variant="inline" icon="search" placeholder="Search..." />
81
+ <Input icon="email" type="email" placeholder="Email" />
82
+ <Input label="Username" icon="person" placeholder="johndoe" />
83
+ <Input label="Password" icon="lock" type="password" placeholder="Required field" error />
84
+ </div>
85
+ )
86
+ };