@rovula/ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/cjs/bundle.css +129 -0
  2. package/dist/cjs/bundle.js +9255 -3
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +1 -0
  5. package/dist/cjs/types/components/Footer/Footer.d.ts +21 -0
  6. package/dist/cjs/types/components/Footer/Footer.stories.d.ts +45 -0
  7. package/dist/cjs/types/components/Footer/index.d.ts +2 -0
  8. package/dist/cjs/types/components/Icon/Icon.d.ts +1 -1
  9. package/dist/cjs/types/components/Icon/Icon.stories.d.ts +9 -1
  10. package/dist/cjs/types/components/InputFilter/InputFilter.stories.d.ts +1 -0
  11. package/dist/cjs/types/components/MaskedTextInput/MaskedTextInput.d.ts +1 -0
  12. package/dist/cjs/types/components/MaskedTextInput/MaskedTextInput.stories.d.ts +2 -0
  13. package/dist/cjs/types/components/Navbar/Navbar.d.ts +5 -0
  14. package/dist/cjs/types/components/Navbar/Navbar.stories.d.ts +14 -0
  15. package/dist/cjs/types/components/PasswordInput/PasswordInput.d.ts +19 -0
  16. package/dist/cjs/types/components/PasswordInput/PasswordInput.stories.d.ts +396 -0
  17. package/dist/cjs/types/components/PasswordInput/index.d.ts +2 -0
  18. package/dist/cjs/types/components/Search/Search.stories.d.ts +1 -0
  19. package/dist/cjs/types/components/TextInput/TextInput.d.ts +2 -0
  20. package/dist/cjs/types/components/TextInput/TextInput.stories.d.ts +10 -0
  21. package/dist/cjs/types/components/TextInput/TextInput.styles.d.ts +15 -0
  22. package/dist/cjs/types/icons/index.d.ts +1 -0
  23. package/dist/cjs/types/icons/lucideIconNames.d.ts +9 -0
  24. package/dist/cjs/types/index.d.ts +7 -1
  25. package/dist/cjs/types/utils/colors.d.ts +330 -0
  26. package/dist/components/Footer/Footer.js +11 -0
  27. package/dist/components/Footer/Footer.stories.js +34 -0
  28. package/dist/components/Footer/index.js +2 -0
  29. package/dist/components/Icon/Icon.js +28 -11
  30. package/dist/components/Icon/Icon.stories.js +39 -0
  31. package/dist/components/Navbar/Navbar.js +18 -4
  32. package/dist/components/Navbar/Navbar.stories.js +16 -9
  33. package/dist/components/PasswordInput/PasswordInput.js +36 -0
  34. package/dist/components/PasswordInput/PasswordInput.stories.js +67 -0
  35. package/dist/components/PasswordInput/index.js +1 -0
  36. package/dist/components/TextArea/TextArea.styles.js +1 -1
  37. package/dist/components/TextInput/TextInput.js +33 -24
  38. package/dist/components/TextInput/TextInput.stories.js +14 -2
  39. package/dist/components/TextInput/TextInput.styles.js +25 -10
  40. package/dist/esm/bundle.css +129 -0
  41. package/dist/esm/bundle.js +9255 -3
  42. package/dist/esm/bundle.js.map +1 -1
  43. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +1 -0
  44. package/dist/esm/types/components/Footer/Footer.d.ts +21 -0
  45. package/dist/esm/types/components/Footer/Footer.stories.d.ts +45 -0
  46. package/dist/esm/types/components/Footer/index.d.ts +2 -0
  47. package/dist/esm/types/components/Icon/Icon.d.ts +1 -1
  48. package/dist/esm/types/components/Icon/Icon.stories.d.ts +9 -1
  49. package/dist/esm/types/components/InputFilter/InputFilter.stories.d.ts +1 -0
  50. package/dist/esm/types/components/MaskedTextInput/MaskedTextInput.d.ts +1 -0
  51. package/dist/esm/types/components/MaskedTextInput/MaskedTextInput.stories.d.ts +2 -0
  52. package/dist/esm/types/components/Navbar/Navbar.d.ts +5 -0
  53. package/dist/esm/types/components/Navbar/Navbar.stories.d.ts +14 -0
  54. package/dist/esm/types/components/PasswordInput/PasswordInput.d.ts +19 -0
  55. package/dist/esm/types/components/PasswordInput/PasswordInput.stories.d.ts +396 -0
  56. package/dist/esm/types/components/PasswordInput/index.d.ts +2 -0
  57. package/dist/esm/types/components/Search/Search.stories.d.ts +1 -0
  58. package/dist/esm/types/components/TextInput/TextInput.d.ts +2 -0
  59. package/dist/esm/types/components/TextInput/TextInput.stories.d.ts +10 -0
  60. package/dist/esm/types/components/TextInput/TextInput.styles.d.ts +15 -0
  61. package/dist/esm/types/icons/index.d.ts +1 -0
  62. package/dist/esm/types/icons/lucideIconNames.d.ts +9 -0
  63. package/dist/esm/types/index.d.ts +7 -1
  64. package/dist/esm/types/utils/colors.d.ts +330 -0
  65. package/dist/icons/index.js +1 -0
  66. package/dist/icons/lucideIconNames.js +12 -0
  67. package/dist/index.d.ts +389 -2
  68. package/dist/index.js +4 -0
  69. package/dist/src/theme/global.css +200 -24
  70. package/dist/utils/colors.js +369 -0
  71. package/package.json +2 -1
  72. package/src/components/Footer/Footer.stories.tsx +119 -0
  73. package/src/components/Footer/Footer.tsx +122 -0
  74. package/src/components/Footer/index.ts +3 -0
  75. package/src/components/Icon/Icon.stories.tsx +89 -0
  76. package/src/components/Icon/Icon.tsx +44 -23
  77. package/src/components/Navbar/Navbar.stories.tsx +109 -55
  78. package/src/components/Navbar/Navbar.tsx +41 -3
  79. package/src/components/PasswordInput/PasswordInput.stories.tsx +111 -0
  80. package/src/components/PasswordInput/PasswordInput.tsx +50 -0
  81. package/src/components/PasswordInput/index.ts +2 -0
  82. package/src/components/TextArea/TextArea.styles.ts +1 -1
  83. package/src/components/TextInput/TextInput.stories.tsx +60 -2
  84. package/src/components/TextInput/TextInput.styles.ts +36 -19
  85. package/src/components/TextInput/TextInput.tsx +83 -55
  86. package/src/icons/index.ts +1 -0
  87. package/src/icons/lucideIconNames.ts +14 -0
  88. package/src/index.ts +15 -1
  89. package/src/theme/themes/skyller/typography.css +24 -24
  90. package/src/theme/tokens/baseline.css +1 -0
  91. package/src/theme/tokens/components/footer.css +9 -0
  92. package/src/theme/tokens/components/navbar.css +2 -1
  93. package/src/types/lucide-react.d.ts +5 -0
  94. package/src/utils/colors.ts +383 -0
@@ -1,4 +1,4 @@
1
- import React, { useRef } from "react";
1
+ import React, { useRef, useState } from "react";
2
2
  import type { Meta, StoryObj } from "@storybook/react";
3
3
  import TextInput from "./TextInput";
4
4
  import { CalendarIcon } from "@heroicons/react/16/solid";
@@ -15,7 +15,7 @@ const meta = {
15
15
  },
16
16
  decorators: [
17
17
  (Story) => (
18
- <div className="p-5 flex w-full bg-[rgb(var(--base-bg-2))] ">
18
+ <div className="p-5 flex h-full w-full bg-[var(--base-color-popup)] ">
19
19
  <Story />
20
20
  </div>
21
21
  ),
@@ -207,3 +207,61 @@ export const CustomIcon = {
207
207
  );
208
208
  },
209
209
  } satisfies StoryObj;
210
+
211
+ const KeepFooterSpaceDemo = () => {
212
+ const [hasError, setHasError] = useState(false);
213
+ return (
214
+ <div className="flex flex-col gap-8 w-full max-w-md">
215
+ <p className="text-sm text-text-grey-dark">
216
+ Use <code>keepFooterSpace</code> to always reserve space for the
217
+ footer/helper area, preventing layout shift when an error or helper text
218
+ is shown or hidden.
219
+ </p>
220
+ <div className="flex flex-col gap-4">
221
+ <label className="flex items-center gap-2 cursor-pointer">
222
+ <input
223
+ type="checkbox"
224
+ checked={hasError}
225
+ onChange={(e) => setHasError(e.target.checked)}
226
+ />
227
+ Show error message
228
+ </label>
229
+ <div className="flex flex-col">
230
+ <div>
231
+ <h4 className="text-sm font-medium mb-2 text-text-grey-dark">
232
+ With keepFooterSpace (layout stays stable)
233
+ </h4>
234
+ <TextInput
235
+ id="with-keep"
236
+ label="Email"
237
+ keepFooterSpace
238
+ error={hasError}
239
+ errorMessage={hasError ? "Please enter a valid email address" : undefined}
240
+ />
241
+ </div>
242
+ <div>
243
+ <h4 className="text-sm font-medium mb-2 text-text-grey-dark">
244
+ Without keepFooterSpace (layout shifts when error appears)
245
+ </h4>
246
+ <TextInput
247
+ id="no-keep"
248
+ label="Email"
249
+ error={hasError}
250
+ errorMessage={hasError ? "Please enter a valid email address" : undefined}
251
+ />
252
+ </div>
253
+ <div></div>
254
+ </div>
255
+ </div>
256
+ </div>
257
+ );
258
+ };
259
+
260
+ export const KeepFooterSpace = {
261
+ args: {
262
+ label: "Email",
263
+ fullwidth: true,
264
+ keepFooterSpace: true,
265
+ },
266
+ render: () => <KeepFooterSpaceDemo />,
267
+ } satisfies StoryObj;
@@ -39,7 +39,7 @@ export const inputVariant = cva(
39
39
  ],
40
40
  },
41
41
  error: {
42
- true: "ring-input-error focus:ring-input-error",
42
+ true: "ring-input-error hover:ring-input-error focus:ring-input-error",
43
43
  },
44
44
  hasClearIcon: {
45
45
  true: "",
@@ -132,13 +132,13 @@ export const inputVariant = cva(
132
132
  leftSectionIcon: false, // TODO function style
133
133
  rightSectionIcon: false,
134
134
  },
135
- }
135
+ },
136
136
  );
137
137
 
138
138
  export const labelVariant = cva(
139
139
  [
140
140
  "absolute block duration-450 transition-all px-[2px] text-input-default-text",
141
- "peer-focus:text-input-filled-text peer-focus:bg-input-label-bg", // TODO bg
141
+ "peer-focus:text-input-default-text peer-focus:bg-input-label-bg", // TODO bg
142
142
  ],
143
143
  {
144
144
  variants: {
@@ -337,11 +337,11 @@ export const labelVariant = cva(
337
337
  hasLeftSectionIcon: false,
338
338
  isFloatingLabel: true,
339
339
  },
340
- }
340
+ },
341
341
  );
342
342
 
343
343
  export const helperTextVariant = cva(
344
- ["text-small1 flex flex-row items-center gap-1"],
344
+ ["typography-small1 flex flex-row items-center gap-1"],
345
345
  {
346
346
  variants: {
347
347
  size: {
@@ -362,12 +362,22 @@ export const helperTextVariant = cva(
362
362
  disabled: false,
363
363
  error: false,
364
364
  },
365
- }
365
+ },
366
366
  );
367
367
 
368
- export const iconWrapperVariant = cva(
368
+ const iconInteractiveColorStateClasses = [
369
+ "fill-input-default-text",
370
+ "peer-hover:fill-input-filled-text peer-focus:fill-input-filled-text peer-active:fill-input-filled-text",
371
+ "peer-disabled:fill-input-disable-stroke",
372
+ "[&_svg]:text-input-default-text",
373
+ "peer-hover:[&_svg]:text-input-filled-text peer-focus:[&_svg]:text-input-filled-text peer-active:[&_svg]:text-input-filled-text",
374
+ "peer-disabled:[&_svg]:text-input-disable-stroke",
375
+ ];
376
+
377
+ export const inlineEndIconWrapperVariant = cva(
369
378
  [
370
379
  "absolute inset-y-0 right-0 items-center justify-center hidden peer-focus:flex",
380
+ ...iconInteractiveColorStateClasses,
371
381
  ],
372
382
  {
373
383
  variants: {
@@ -380,11 +390,14 @@ export const iconWrapperVariant = cva(
380
390
  defaultVariants: {
381
391
  size: "md",
382
392
  },
383
- }
393
+ },
384
394
  );
385
395
 
386
- export const iconSearchWrapperVariant = cva(
387
- ["absolute inset-y-0 left-0 items-center justify-center flex"],
396
+ export const inlineStartIconWrapperVariant = cva(
397
+ [
398
+ "absolute inset-y-0 left-0 items-center justify-center flex",
399
+ ...iconInteractiveColorStateClasses,
400
+ ],
388
401
  {
389
402
  variants: {
390
403
  size: {
@@ -396,13 +409,11 @@ export const iconSearchWrapperVariant = cva(
396
409
  defaultVariants: {
397
410
  size: "md",
398
411
  },
399
- }
412
+ },
400
413
  );
401
414
 
402
- export const iconVariant = cva(
403
- [
404
- "cursor-pointer z-50 fill-input-active-stroke hover:fill-input-default-text",
405
- ],
415
+ export const iconActionVariant = cva(
416
+ ["cursor-pointer z-50"],
406
417
  {
407
418
  variants: {
408
419
  size: {
@@ -414,14 +425,14 @@ export const iconVariant = cva(
414
425
  defaultVariants: {
415
426
  size: "md",
416
427
  },
417
- }
428
+ },
418
429
  );
419
430
 
420
- export const sectionIconWrapperVariant = cva(
431
+ export const segmentedIconWrapperVariant = cva(
421
432
  [
422
433
  "cursor-pointer",
423
434
  "absolute items-center justify-center flex",
424
- "fill-input-default-text peer-hover:fill-input-filled-text peer-focus:fill-input-filled-text peer-disabled:fill-input-disable-stroke",
435
+ ...iconInteractiveColorStateClasses,
425
436
  ],
426
437
  {
427
438
  variants: {
@@ -499,5 +510,11 @@ export const sectionIconWrapperVariant = cva(
499
510
  error: false,
500
511
  position: "end",
501
512
  },
502
- }
513
+ },
503
514
  );
515
+
516
+ // Backward-compatible aliases (can be removed in a future major version)
517
+ export const iconWrapperVariant = inlineEndIconWrapperVariant;
518
+ export const iconSearchWrapperVariant = inlineStartIconWrapperVariant;
519
+ export const iconVariant = iconActionVariant;
520
+ export const sectionIconWrapperVariant = segmentedIconWrapperVariant;
@@ -1,5 +1,4 @@
1
1
  import React, {
2
- FC,
3
2
  ReactNode,
4
3
  forwardRef,
5
4
  useCallback,
@@ -9,18 +8,18 @@ import React, {
9
8
  } from "react";
10
9
  import {
11
10
  helperTextVariant,
12
- iconSearchWrapperVariant,
13
- iconVariant,
14
- iconWrapperVariant,
11
+ iconActionVariant,
12
+ inlineEndIconWrapperVariant,
13
+ inlineStartIconWrapperVariant,
15
14
  inputVariant,
16
15
  labelVariant,
17
- sectionIconWrapperVariant,
16
+ segmentedIconWrapperVariant,
18
17
  } from "./TextInput.styles";
19
18
  import {
20
- XCircleIcon,
21
- ExclamationCircleIcon,
22
- MagnifyingGlassIcon,
23
- } from "@heroicons/react/16/solid";
19
+ CircleAlert,
20
+ CircleX,
21
+ Search,
22
+ } from "lucide-react";
24
23
  import { cn } from "@/utils/cn";
25
24
 
26
25
  export type InputProps = {
@@ -39,6 +38,7 @@ export type InputProps = {
39
38
  required?: boolean;
40
39
  isFloatingLabel?: boolean;
41
40
  keepCloseIconOnValue?: boolean;
41
+ keepFooterSpace?: boolean;
42
42
  hasClearIcon?: boolean;
43
43
  hasSearchIcon?: boolean;
44
44
  startIcon?: ReactNode;
@@ -76,6 +76,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
76
76
  required = true,
77
77
  isFloatingLabel = true,
78
78
  keepCloseIconOnValue = false,
79
+ keepFooterSpace = true,
79
80
  hasClearIcon = true,
80
81
  hasSearchIcon = false,
81
82
  startIcon,
@@ -118,20 +119,26 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
118
119
  hasLeftSectionIcon: iconMode === "solid" ? hasLeftSectionIcon : false,
119
120
  isFloatingLabel,
120
121
  });
121
- const helperTextClassname = helperTextVariant({ size, error, disabled });
122
- const iconWrapperClassname = iconWrapperVariant({ size });
123
- const iconSearchWrapperClassname = iconSearchWrapperVariant({ size });
124
- const iconClassname = iconVariant({ size });
122
+ const helperTextClassname = helperTextVariant({
123
+ size,
124
+ error,
125
+ disabled,
126
+ });
127
+ const inlineEndIconWrapperClassname = inlineEndIconWrapperVariant({ size });
128
+ const inlineStartIconWrapperClassname = inlineStartIconWrapperVariant({
129
+ size,
130
+ });
131
+ const iconActionClassname = iconActionVariant({ size });
125
132
 
126
133
  // TODO wait for clearify aboutm start,end, search and clearIcon with iconMode
127
134
 
128
- const startIconWrapperClassname = sectionIconWrapperVariant({
135
+ const startSegmentIconWrapperClassname = segmentedIconWrapperVariant({
129
136
  size,
130
137
  rounded,
131
138
  error,
132
139
  position: "start",
133
140
  });
134
- const endIconWrapperClassname = sectionIconWrapperVariant({
141
+ const endSegmentIconWrapperClassname = segmentedIconWrapperVariant({
135
142
  size,
136
143
  rounded,
137
144
  error,
@@ -176,7 +183,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
176
183
  return (
177
184
  <div
178
185
  className={cn(
179
- iconSearchWrapperClassname,
186
+ inlineStartIconWrapperClassname,
180
187
  "flex",
181
188
  classes?.iconSearchWrapper
182
189
  )}
@@ -190,12 +197,12 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
190
197
  return (
191
198
  <div
192
199
  className={cn(
193
- iconSearchWrapperClassname,
200
+ inlineStartIconWrapperClassname,
194
201
  classes?.iconSearchWrapper
195
202
  )}
196
203
  >
197
204
  <div
198
- className={cn(iconClassname, classes?.icon)}
205
+ className={cn(iconActionClassname, classes?.icon)}
199
206
  onClick={handleOnClickLeftSectionIcon}
200
207
  >
201
208
  {startIcon}
@@ -206,7 +213,10 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
206
213
 
207
214
  return (
208
215
  <div
209
- className={cn(startIconWrapperClassname, classes?.startIconWrapper)}
216
+ className={cn(
217
+ startSegmentIconWrapperClassname,
218
+ classes?.startIconWrapper
219
+ )}
210
220
  onClick={handleOnClickLeftSectionIcon}
211
221
  >
212
222
  {startIcon}
@@ -216,9 +226,9 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
216
226
  hasLeftSectionIcon,
217
227
  startIcon,
218
228
  iconMode,
219
- iconSearchWrapperClassname,
220
- startIconWrapperClassname,
221
- iconClassname,
229
+ inlineStartIconWrapperClassname,
230
+ startSegmentIconWrapperClassname,
231
+ iconActionClassname,
222
232
  renderStartIcon,
223
233
  handleOnClickLeftSectionIcon,
224
234
  ]);
@@ -229,7 +239,11 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
229
239
  if (renderEndIcon) {
230
240
  return (
231
241
  <div
232
- className={cn(iconWrapperClassname, "flex", classes?.iconWrapper)}
242
+ className={cn(
243
+ inlineEndIconWrapperClassname,
244
+ "flex",
245
+ classes?.iconWrapper
246
+ )}
233
247
  >
234
248
  {renderEndIcon()}
235
249
  </div>
@@ -239,10 +253,14 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
239
253
  if (iconMode === "flat") {
240
254
  return (
241
255
  <div
242
- className={cn(iconWrapperClassname, "flex", classes?.iconWrapper)}
256
+ className={cn(
257
+ inlineEndIconWrapperClassname,
258
+ "flex",
259
+ classes?.iconWrapper
260
+ )}
243
261
  >
244
262
  <div
245
- className={cn(iconClassname, classes?.icon)}
263
+ className={cn(iconActionClassname, classes?.icon)}
246
264
  onClick={handleOnClickRightSectionIcon}
247
265
  >
248
266
  {endIcon}
@@ -253,7 +271,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
253
271
 
254
272
  return (
255
273
  <div
256
- className={cn(endIconWrapperClassname, classes?.endIconWrapper)}
274
+ className={cn(endSegmentIconWrapperClassname, classes?.endIconWrapper)}
257
275
  onClick={handleOnClickRightSectionIcon}
258
276
  >
259
277
  {endIcon}
@@ -263,9 +281,9 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
263
281
  hasRightSectionIcon,
264
282
  endIcon,
265
283
  iconMode,
266
- iconSearchWrapperClassname,
267
- endIconWrapperClassname,
268
- iconClassname,
284
+ inlineEndIconWrapperClassname,
285
+ endSegmentIconWrapperClassname,
286
+ iconActionClassname,
269
287
  renderEndIcon,
270
288
  handleOnClickRightSectionIcon,
271
289
  ]);
@@ -273,18 +291,6 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
273
291
  return (
274
292
  <div className={`inline-flex flex-col ${fullwidth ? "w-full" : ""}`}>
275
293
  <div className="relative">
276
- {hasSearchIcon && !hasLeftSectionIcon && (
277
- <div
278
- className={cn(
279
- iconSearchWrapperClassname,
280
- classes?.iconSearchWrapper
281
- )}
282
- >
283
- <MagnifyingGlassIcon
284
- className={cn(iconClassname, classes?.icon)}
285
- />
286
- </div>
287
- )}
288
294
  <input
289
295
  {...props}
290
296
  placeholder=" "
@@ -294,19 +300,33 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
294
300
  disabled={disabled}
295
301
  className={cn(inputClassname, props.className)}
296
302
  />
303
+ {hasSearchIcon && !hasLeftSectionIcon && (
304
+ <div
305
+ className={cn(
306
+ inlineStartIconWrapperClassname,
307
+ classes?.iconSearchWrapper
308
+ )}
309
+ >
310
+ <Search className={cn(iconActionClassname, classes?.icon)} />
311
+ </div>
312
+ )}
297
313
  {startIconElement}
298
314
 
299
315
  {hasClearIcon && !hasRightSectionIcon && (
300
316
  <div
301
- className={cn(iconWrapperClassname, classes?.iconWrapper)}
317
+ className={cn(inlineEndIconWrapperClassname, classes?.iconWrapper)}
302
318
  style={{
303
319
  display:
304
320
  keepCloseIconOnValue && props.value ? "flex" : undefined,
305
321
  }}
306
322
  >
307
- <XCircleIcon
308
- type="button"
309
- className={cn(iconClassname, classes?.icon)}
323
+ <CircleX
324
+ className={cn(
325
+ iconActionClassname,
326
+ // 'fill-none stroke-current',
327
+ // "fill-none stroke-input-default-text hover:stroke-input-filled-text active:stroke-input-filled-text",
328
+ classes?.icon
329
+ )}
310
330
  onMouseDown={handleClearInput}
311
331
  />
312
332
  </div>
@@ -317,7 +337,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
317
337
  {label}{" "}
318
338
  {required && (
319
339
  <span
320
- className={cn("text-error", {
340
+ className={cn("text-input-error", {
321
341
  "text-input-disable-text": disabled,
322
342
  })}
323
343
  >
@@ -326,16 +346,24 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
326
346
  )}
327
347
  </label>
328
348
  </div>
329
- {(errorMessage || helperText) && (
349
+ {(errorMessage || helperText || keepFooterSpace) && (
330
350
  <span className={helperTextClassname}>
331
- <span className="h-full">
332
- <ExclamationCircleIcon
333
- width={16}
334
- height={16}
335
- className={error ? "fill-error" : ""}
336
- />
337
- </span>
338
- {errorMessage || helperText}
351
+ {(errorMessage || helperText) && (
352
+ <span className="h-full shrink-0">
353
+ <CircleAlert
354
+ width={14}
355
+ height={14}
356
+ className={cn(
357
+ "fill-none",
358
+ error ? "stroke-input-error" : "stroke-input-filled-text"
359
+ )}
360
+ />
361
+ </span>
362
+ )}
363
+ {keepFooterSpace && !error && !helperText && (
364
+ <span className="block min-h-[14px]" aria-hidden />
365
+ )}
366
+ {(error ? errorMessage : helperText) || ''}
339
367
  </span>
340
368
  )}
341
369
  </div>
@@ -1 +1,2 @@
1
1
  export * from "./iconRegistry";
2
+ export { getLucideIconNames } from "./lucideIconNames";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Get all available Lucide icon names (kebab-case).
3
+ * Use for autocomplete, validation, or browsing.
4
+ *
5
+ * @example
6
+ * const names = await getLucideIconNames();
7
+ * names.includes("eye-closed"); // true
8
+ */
9
+ export async function getLucideIconNames(): Promise<string[]> {
10
+ const { default: dynamicIconImports } = await import(
11
+ "lucide-react/dynamicIconImports"
12
+ );
13
+ return Object.keys(dynamicIconImports);
14
+ }
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ import "./icons/iconConfig";
6
6
  export { default as Button } from "./components/Button/Button";
7
7
  export { default as TextInput } from "./components/TextInput/TextInput";
8
8
  export { default as MaskedTextInput } from "./components/MaskedTextInput";
9
+ export { default as PasswordInput } from "./components/PasswordInput";
9
10
  export { NumberInput } from "./components/NumberInput/NumberInput";
10
11
  export { default as TextArea } from "./components/TextArea/TextArea";
11
12
  export { default as Text } from "./components/Text/Text";
@@ -15,6 +16,7 @@ export { Checkbox } from "./components/Checkbox/Checkbox";
15
16
  export { Label } from "./components/Label/Label";
16
17
  export { Input } from "./components/Input/Input";
17
18
  export { Navbar } from "./components/Navbar";
19
+ export { Footer } from "./components/Footer";
18
20
  export { default as ActionButton } from "./components/ActionButton/ActionButton";
19
21
  export { Avatar, AvatarGroup } from "./components/Avatar";
20
22
  export { Collapsible } from "./components/Collapsible";
@@ -50,6 +52,7 @@ export * from "./components/RadioGroup/RadioGroup";
50
52
  // Export component types
51
53
  export type { ButtonProps } from "./components/Button/Button";
52
54
  export type { InputProps } from "./components/TextInput/TextInput";
55
+ export type { PasswordInputProps } from "./components/PasswordInput/PasswordInput";
53
56
  export type {
54
57
  MaskedTextInputProps,
55
58
  MaskRule,
@@ -57,7 +60,8 @@ export type {
57
60
  export type { NumberInputProps } from "./components/NumberInput/NumberInput";
58
61
  export type { TextAreaProps } from "./components/TextArea/TextArea";
59
62
  export type { DropdownProps, Options } from "./components/Dropdown/Dropdown";
60
- export type { NavbarProps } from "./components/Navbar/Navbar";
63
+ export type { NavbarProps, NavbarVariant } from "./components/Navbar/Navbar";
64
+ export type { FooterProps, FooterVariant } from "./components/Footer/Footer";
61
65
  export type { AvatarProps } from "./components/Avatar/Avatar";
62
66
  export type { AvatarGroupProps } from "./components/Avatar/AvatarGroup";
63
67
 
@@ -75,6 +79,16 @@ export * from "./hooks";
75
79
 
76
80
  export { cn } from "./utils/cn";
77
81
 
82
+ export {
83
+ srgbToHex,
84
+ getThemeColor,
85
+ getThemeColors,
86
+ THEME_COLOR_KEYS,
87
+ type ThemeColorKey,
88
+ } from "./utils/colors";
89
+
90
+ export { getLucideIconNames } from "./icons/lucideIconNames";
91
+
78
92
  // const mainPreset = require("./theme/main-preset");
79
93
 
80
94
  // export { mainPreset };
@@ -3,28 +3,28 @@
3
3
  /* Typography */
4
4
  /* ------------------------------------------------------------------ */
5
5
 
6
- --h1-family: "Poppins";
7
- --h2-family: "Poppins";
8
- --h3-family: "Poppins";
9
- --h4-family: "Poppins";
10
- --h5-family: "Poppins";
11
- --h6-family: "Poppins";
12
- --subtitle2-family: "Poppins";
13
- --subtitle3-family: "Poppins";
14
- --subtitle4-family: "Poppins";
15
- --subtitle5-family: "Poppins";
16
- --subtitle6-family: "Poppins";
17
- --body1-family: "Poppins";
18
- --body2-family: "Poppins";
19
- --body3-family: "Poppins";
20
- --body4-family: "Poppins";
21
- --small1-family: "Poppins";
22
- --small2-family: "Poppins";
23
- --small3-family: "Poppins";
24
- --small4-family: "Poppins";
25
- --small5-family: "Poppins";
26
- --label-label1-family: "Poppins";
27
- --label-label2-family: "Poppins";
28
- --button-button-l-family: "Poppins";
29
- --button-button-ms-family: "Poppins";
6
+ --h1-family: "Montserrat";
7
+ --h2-family: "Montserrat";
8
+ --h3-family: "Montserrat";
9
+ --h4-family: "Montserrat";
10
+ --h5-family: "Montserrat";
11
+ --h6-family: "Montserrat";
12
+ --subtitle2-family: "Montserrat";
13
+ --subtitle3-family: "Montserrat";
14
+ --subtitle4-family: "Montserrat";
15
+ --subtitle5-family: "Montserrat";
16
+ --subtitle6-family: "Montserrat";
17
+ --body1-family: "Montserrat";
18
+ --body2-family: "Montserrat";
19
+ --body3-family: "Montserrat";
20
+ --body4-family: "Montserrat";
21
+ --small1-family: "Montserrat";
22
+ --small2-family: "Montserrat";
23
+ --small3-family: "Montserrat";
24
+ --small4-family: "Montserrat";
25
+ --small5-family: "Montserrat";
26
+ --label-label1-family: "Montserrat";
27
+ --label-label2-family: "Montserrat";
28
+ --button-button-l-family: "Montserrat";
29
+ --button-button-ms-family: "Montserrat";
30
30
  }
@@ -8,5 +8,6 @@
8
8
  @import url(components/action-button.css);
9
9
  @import url(components/loading.css);
10
10
  @import url(components/navbar.css);
11
+ @import url(components/footer.css);
11
12
  @import url(components/dropdown-menu.css);
12
13
  @import url(components/switch.css);
@@ -0,0 +1,9 @@
1
+ :root {
2
+ /* Footer */
3
+ --footer-height: 58px;
4
+ --footer-height-simple: 48px;
5
+ --footer-bg-color: var(--state-color-primary-default);
6
+ --footer-text-color: var(--primary-foreground);
7
+ --footer-border-color: var(--primary-foreground);
8
+ --footer-gap: 16px;
9
+ }
@@ -1,8 +1,9 @@
1
1
  :root {
2
- /* Navbar */
2
+ /* Navbar */
3
3
  --navbar-height: 58px;
4
4
  --navbar-bg-color: var(--primary-default);
5
5
  --navbar-text-color: var(--primary-foreground);
6
6
  --navbar-border-color: var(--primary-foreground);
7
7
  --navbar-gap: 16px;
8
+ --navbar-shadow-scrolled: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
8
9
  }
@@ -0,0 +1,5 @@
1
+ declare module "lucide-react/dynamicIconImports" {
2
+ type IconLoader = () => Promise<{ default: React.ComponentType<any> }>;
3
+ const dynamicIconImports: Record<string, IconLoader>;
4
+ export { dynamicIconImports as default };
5
+ }