@rovula/ui 0.0.68 → 0.0.70

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 (39) hide show
  1. package/dist/cjs/bundle.css +67 -0
  2. package/dist/cjs/bundle.js +3 -3
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
  5. package/dist/cjs/types/components/FocusedScrollView/FocusedScrollView.d.ts +12 -0
  6. package/dist/cjs/types/components/FocusedScrollView/FocusedScrollView.stories.d.ts +7 -0
  7. package/dist/cjs/types/components/InputFilter/InputFilter.stories.d.ts +2 -0
  8. package/dist/cjs/types/components/Search/Search.stories.d.ts +2 -0
  9. package/dist/cjs/types/components/TextInput/TextInput.d.ts +4 -0
  10. package/dist/cjs/types/components/TextInput/TextInput.stories.d.ts +11 -0
  11. package/dist/cjs/types/components/TextInput/TextInput.styles.d.ts +1 -0
  12. package/dist/cjs/types/index.d.ts +1 -0
  13. package/dist/components/FocusedScrollView/FocusedScrollView.js +67 -0
  14. package/dist/components/FocusedScrollView/FocusedScrollView.stories.js +33 -0
  15. package/dist/components/TextInput/TextInput.js +66 -14
  16. package/dist/components/TextInput/TextInput.stories.js +15 -0
  17. package/dist/components/TextInput/TextInput.styles.js +116 -7
  18. package/dist/esm/bundle.css +67 -0
  19. package/dist/esm/bundle.js +3 -3
  20. package/dist/esm/bundle.js.map +1 -1
  21. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
  22. package/dist/esm/types/components/FocusedScrollView/FocusedScrollView.d.ts +12 -0
  23. package/dist/esm/types/components/FocusedScrollView/FocusedScrollView.stories.d.ts +7 -0
  24. package/dist/esm/types/components/InputFilter/InputFilter.stories.d.ts +2 -0
  25. package/dist/esm/types/components/Search/Search.stories.d.ts +2 -0
  26. package/dist/esm/types/components/TextInput/TextInput.d.ts +4 -0
  27. package/dist/esm/types/components/TextInput/TextInput.stories.d.ts +11 -0
  28. package/dist/esm/types/components/TextInput/TextInput.styles.d.ts +1 -0
  29. package/dist/esm/types/index.d.ts +1 -0
  30. package/dist/index.d.ts +17 -2
  31. package/dist/index.js +1 -0
  32. package/dist/src/theme/global.css +86 -0
  33. package/package.json +1 -1
  34. package/src/components/FocusedScrollView/FocusedScrollView.stories.tsx +114 -0
  35. package/src/components/FocusedScrollView/FocusedScrollView.tsx +112 -0
  36. package/src/components/TextInput/TextInput.stories.tsx +83 -0
  37. package/src/components/TextInput/TextInput.styles.ts +117 -7
  38. package/src/components/TextInput/TextInput.tsx +115 -21
  39. package/src/index.ts +1 -0
@@ -2,6 +2,7 @@ import React, { useRef } 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";
5
+ import Icon from "../Icon/Icon";
5
6
 
6
7
  // More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction
7
8
  const meta = {
@@ -124,3 +125,85 @@ export const FuctionInput = {
124
125
  );
125
126
  },
126
127
  } satisfies StoryObj;
128
+
129
+ export const CustomIcon = {
130
+ args: {
131
+ label: "Placeholder Text",
132
+ // value: "",
133
+ disabled: true,
134
+ },
135
+ render: (args) => {
136
+ console.log("args ", args);
137
+ const props: typeof args = {
138
+ ...args,
139
+ };
140
+ return (
141
+ <div className="flex flex-col gap-4 w-full">
142
+ <div>
143
+ <h4>Icon mode: solid:</h4>
144
+ </div>
145
+ <div className="flex flex-row gap-4 w-full">
146
+ <TextInput
147
+ id="1"
148
+ size="lg"
149
+ {...args}
150
+ startIcon={<CalendarIcon className="size-full" fill="inherit" />}
151
+ endIcon={<CalendarIcon className="size-full" fill="inherit" />}
152
+ />
153
+ <TextInput
154
+ id="2"
155
+ size="md"
156
+ {...args}
157
+ startIcon={<CalendarIcon className="size-full" fill="inherit" />}
158
+ endIcon={<CalendarIcon className="size-full" fill="inherit" />}
159
+ />
160
+ <TextInput
161
+ id="3"
162
+ size="sm"
163
+ {...args}
164
+ startIcon={<CalendarIcon className="size-full" fill="inherit" />}
165
+ endIcon={<CalendarIcon className="size-full" fill="inherit" />}
166
+ />
167
+ </div>
168
+ <div>
169
+ <h4>Icon mode: flat:</h4>
170
+ </div>
171
+ <div className="flex flex-row gap-4 w-full">
172
+ <TextInput
173
+ id="1"
174
+ size="lg"
175
+ {...args}
176
+ iconMode="flat"
177
+ startIcon={<CalendarIcon className="size-full" fill="inherit" />}
178
+ endIcon={<CalendarIcon className="size-full" fill="inherit" />}
179
+ />
180
+ <TextInput
181
+ id="2"
182
+ size="md"
183
+ {...args}
184
+ iconMode="flat"
185
+ startIcon={<CalendarIcon className="size-full" fill="inherit" />}
186
+ endIcon={<CalendarIcon className="size-full" fill="inherit" />}
187
+ />
188
+ <TextInput
189
+ id="3"
190
+ size="sm"
191
+ {...args}
192
+ iconMode="flat"
193
+ startIcon={<CalendarIcon className="size-full" fill="inherit" />}
194
+ endIcon={<CalendarIcon className="size-full" fill="inherit" />}
195
+ />
196
+ </div>
197
+ <TextInput
198
+ id="1"
199
+ size="lg"
200
+ {...args}
201
+ iconMode="flat"
202
+ startIcon={<Icon name="magnifying-glass" fill="inherit" />}
203
+ // <MagnifyingGlassIcon className="size-full" fill="inherit" />
204
+ hasClearIcon
205
+ />
206
+ </div>
207
+ );
208
+ },
209
+ } satisfies StoryObj;
@@ -156,6 +156,9 @@ export const labelVariant = cva(
156
156
  hasSearchIcon: {
157
157
  false: "",
158
158
  },
159
+ hasLeftSectionIcon: {
160
+ false: "",
161
+ },
159
162
  isFloatingLabel: {
160
163
  false: "hidden peer-placeholder-shown:block peer-focus:bg-transparent",
161
164
  },
@@ -268,12 +271,70 @@ export const labelVariant = cva(
268
271
  "left-11 peer-placeholder-shown:top-4 peer-placeholder-shown:typography-subtitile1",
269
272
  ],
270
273
  },
274
+
275
+ // -------- hasLeftSectionIcon ------
276
+ {
277
+ isFloatingLabel: true,
278
+ hasLeftSectionIcon: true,
279
+ size: "sm",
280
+ className: [
281
+ "left-[38px] -top-1.5 typography-label2 bg-input-label-bg",
282
+ "peer-placeholder-shown:top-2 peer-placeholder-shown:typography-small1 peer-placeholder-shown:bg-transparent",
283
+ "peer-focus:-top-1.5 peer-focus:typography-label2",
284
+ ],
285
+ },
286
+ {
287
+ isFloatingLabel: true,
288
+ hasLeftSectionIcon: true,
289
+ size: "md",
290
+ className: [
291
+ "left-[46px] -top-1.5 typography-label1 bg-input-label-bg",
292
+ "peer-placeholder-shown:top-2 peer-placeholder-shown:typography-subtitile4 peer-placeholder-shown:bg-transparent",
293
+ "peer-focus:-top-1.5 peer-focus:typography-label1",
294
+ ],
295
+ },
296
+ {
297
+ isFloatingLabel: true,
298
+ hasLeftSectionIcon: true,
299
+ size: "lg",
300
+ className: [
301
+ "left-[72px] -top-1.5 typography-label1 bg-input-label-bg",
302
+ "peer-placeholder-shown:top-4 peer-placeholder-shown:typography-subtitile1 peer-placeholder-shown:bg-transparent",
303
+ "peer-focus:-top-1.5 peer-focus:typography-label1",
304
+ ],
305
+ },
306
+ // floating = false and has search icon
307
+ {
308
+ isFloatingLabel: false,
309
+ hasLeftSectionIcon: true,
310
+ size: "sm",
311
+ className: [
312
+ "left-[38px] peer-placeholder-shown:top-2 peer-placeholder-shown:typography-small1",
313
+ ],
314
+ },
315
+ {
316
+ isFloatingLabel: false,
317
+ hasLeftSectionIcon: true,
318
+ size: "md",
319
+ className: [
320
+ "left-[46px] peer-placeholder-shown:top-2 peer-placeholder-shown:typography-subtitile4",
321
+ ],
322
+ },
323
+ {
324
+ isFloatingLabel: false,
325
+ hasLeftSectionIcon: true,
326
+ size: "lg",
327
+ className: [
328
+ "left-[72px] peer-placeholder-shown:top-4 peer-placeholder-shown:typography-subtitile1",
329
+ ],
330
+ },
271
331
  ],
272
332
  defaultVariants: {
273
333
  size: "md",
274
334
  disabled: false,
275
335
  error: false,
276
336
  hasSearchIcon: false,
337
+ hasLeftSectionIcon: false,
277
338
  isFloatingLabel: true,
278
339
  },
279
340
  }
@@ -360,7 +421,6 @@ export const sectionIconWrapperVariant = cva(
360
421
  [
361
422
  "cursor-pointer",
362
423
  "absolute items-center justify-center flex",
363
- "border-l border-l-input-default-stroke peer-hover:border-l-input-active-stroke peer-focus:border-l-input-active-stroke peer-disabled:border-l-input-disable-stroke",
364
424
  "fill-input-default-text peer-hover:fill-input-filled-text peer-focus:fill-input-filled-text peer-disabled:fill-input-disable-stroke",
365
425
  ],
366
426
  {
@@ -371,18 +431,68 @@ export const sectionIconWrapperVariant = cva(
371
431
  lg: "p-3 size-14",
372
432
  },
373
433
  rounded: {
374
- none: "rounded-r-none",
375
- normal: "rounded-r-xl",
376
- full: "rounded-r-full",
434
+ none: "",
435
+ normal: "",
436
+ full: "",
377
437
  },
378
438
  error: {
379
- true: "border-l-input-error",
439
+ false: "",
380
440
  },
381
441
  position: {
382
- start: "inset-y-0 left-0 ",
383
- end: "inset-y-0 right-0 ",
442
+ start: [
443
+ "inset-y-0 left-0 ",
444
+ "border-r border-r-input-default-stroke peer-hover:border-r-input-active-stroke peer-focus:border-r-input-active-stroke peer-disabled:border-r-input-disable-stroke",
445
+ ],
446
+ end: [
447
+ "inset-y-0 right-0 ",
448
+ "border-l border-l-input-default-stroke peer-hover:border-l-input-active-stroke peer-focus:border-l-input-active-stroke peer-disabled:border-l-input-disable-stroke",
449
+ ],
384
450
  },
385
451
  },
452
+ compoundVariants: [
453
+ // --- start rounded ---
454
+ {
455
+ position: "start",
456
+ rounded: "none",
457
+ className: "rounded-l-none",
458
+ },
459
+ {
460
+ position: "start",
461
+ rounded: "normal",
462
+ className: "rounded-l-xl",
463
+ },
464
+ {
465
+ position: "start",
466
+ rounded: "full",
467
+ className: "rounded-l-full",
468
+ },
469
+ {
470
+ position: "end",
471
+ rounded: "none",
472
+ className: "rounded-r-none",
473
+ },
474
+ {
475
+ position: "end",
476
+ rounded: "normal",
477
+ className: "rounded-r-xl",
478
+ },
479
+ {
480
+ position: "end",
481
+ rounded: "full",
482
+ className: "rounded-r-full",
483
+ },
484
+ // --- Error start ---
485
+ {
486
+ position: "start",
487
+ error: true,
488
+ className: "border-r-input-error",
489
+ },
490
+ {
491
+ position: "end",
492
+ error: true,
493
+ className: "border-l-input-error",
494
+ },
495
+ ],
386
496
  defaultVariants: {
387
497
  size: "md",
388
498
  rounded: "normal",
@@ -4,6 +4,7 @@ import React, {
4
4
  forwardRef,
5
5
  useCallback,
6
6
  useImperativeHandle,
7
+ useMemo,
7
8
  useRef,
8
9
  } from "react";
9
10
  import {
@@ -28,6 +29,7 @@ export type InputProps = {
28
29
  size?: "sm" | "md" | "lg";
29
30
  rounded?: "none" | "normal" | "full";
30
31
  variant?: "flat" | "outline" | "underline";
32
+ iconMode?: "flat" | "solid";
31
33
  type?: React.HTMLInputTypeAttribute;
32
34
  helperText?: string;
33
35
  errorMessage?: string;
@@ -45,6 +47,7 @@ export type InputProps = {
45
47
  labelClassName?: string;
46
48
  onClickStartIcon?: () => void;
47
49
  onClickEndIcon?: () => void;
50
+ renderStartIcon?: () => ReactNode;
48
51
  renderEndIcon?: () => ReactNode;
49
52
  } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
50
53
 
@@ -57,6 +60,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
57
60
  rounded = "normal",
58
61
  variant = "outline",
59
62
  type = "text",
63
+ iconMode = "solid",
60
64
  helperText,
61
65
  errorMessage,
62
66
  fullwidth = true,
@@ -70,7 +74,9 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
70
74
  startIcon,
71
75
  endIcon,
72
76
  labelClassName,
77
+ onClickStartIcon,
73
78
  onClickEndIcon,
79
+ renderStartIcon,
74
80
  renderEndIcon,
75
81
  ...props
76
82
  },
@@ -78,8 +84,9 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
78
84
  ) => {
79
85
  const inputRef = useRef<HTMLInputElement>(null);
80
86
  const _id = id || `${type}-${label}-input`;
81
- const hasLeftSectionIcon = !!startIcon;
82
- const hasRightSectionIcon = !!endIcon;
87
+ const hasLeftSectionIcon = !!startIcon || !!renderStartIcon;
88
+ const hasRightSectionIcon = !!endIcon || !!renderEndIcon;
89
+
83
90
  const inputClassname = inputVariant({
84
91
  size,
85
92
  rounded,
@@ -87,27 +94,40 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
87
94
  fullwidth,
88
95
  disabled,
89
96
  error,
90
- hasSearchIcon,
91
- hasClearIcon: hasRightSectionIcon ? false : hasClearIcon,
92
- leftSectionIcon: hasLeftSectionIcon,
93
- rightSectionIcon: hasRightSectionIcon,
97
+ hasSearchIcon:
98
+ (iconMode === "flat" && hasLeftSectionIcon) || hasSearchIcon,
99
+ hasClearIcon:
100
+ (iconMode === "flat" && hasRightSectionIcon) || hasClearIcon,
101
+ leftSectionIcon: iconMode === "solid" ? hasLeftSectionIcon : false,
102
+ rightSectionIcon: iconMode === "solid" ? hasRightSectionIcon : false,
94
103
  });
95
104
  const labelClassname = labelVariant({
96
105
  size,
97
106
  disabled,
98
107
  error,
99
- hasSearchIcon,
108
+ hasSearchIcon:
109
+ (iconMode === "flat" && hasLeftSectionIcon) || hasSearchIcon,
110
+ hasLeftSectionIcon: iconMode === "solid" ? hasLeftSectionIcon : false,
100
111
  isFloatingLabel,
101
112
  });
102
113
  const helperTextClassname = helperTextVariant({ size, error, disabled });
103
114
  const iconWrapperClassname = iconWrapperVariant({ size });
104
115
  const iconSearchWrapperClassname = iconSearchWrapperVariant({ size });
105
116
  const iconClassname = iconVariant({ size });
106
- // TODO startIcon
117
+
118
+ // TODO wait for clearify aboutm start,end, search and clearIcon with iconMode
119
+
120
+ const startIconWrapperClassname = sectionIconWrapperVariant({
121
+ size,
122
+ rounded,
123
+ error,
124
+ position: "start",
125
+ });
107
126
  const endIconWrapperClassname = sectionIconWrapperVariant({
108
127
  size,
109
128
  rounded,
110
129
  error,
130
+ position: "end",
111
131
  });
112
132
 
113
133
  useImperativeHandle(ref, () => inputRef?.current as HTMLInputElement);
@@ -123,7 +143,15 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
123
143
  }
124
144
  }, [props.onChange]);
125
145
 
126
- // TODO startIcon
146
+ const handleOnClickLeftSectionIcon = useCallback(() => {
147
+ if (disabled) return;
148
+
149
+ onClickStartIcon?.();
150
+ if (inputRef.current) {
151
+ inputRef.current.focus();
152
+ }
153
+ }, [disabled, onClickStartIcon]);
154
+
127
155
  const handleOnClickRightSectionIcon = useCallback(() => {
128
156
  if (disabled) return;
129
157
 
@@ -133,10 +161,84 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
133
161
  }
134
162
  }, [disabled, onClickEndIcon]);
135
163
 
164
+ const startIconElement = useMemo(() => {
165
+ if (!hasLeftSectionIcon) return;
166
+
167
+ if (renderStartIcon) return renderStartIcon();
168
+
169
+ if (iconMode === "flat") {
170
+ return (
171
+ <div className={iconSearchWrapperClassname}>
172
+ <div
173
+ className={iconClassname}
174
+ onClick={handleOnClickLeftSectionIcon}
175
+ >
176
+ {startIcon}
177
+ </div>
178
+ </div>
179
+ );
180
+ }
181
+
182
+ return (
183
+ <div
184
+ className={startIconWrapperClassname}
185
+ onClick={handleOnClickLeftSectionIcon}
186
+ >
187
+ {startIcon}
188
+ </div>
189
+ );
190
+ }, [
191
+ hasLeftSectionIcon,
192
+ startIcon,
193
+ iconMode,
194
+ iconSearchWrapperClassname,
195
+ startIconWrapperClassname,
196
+ iconClassname,
197
+ renderStartIcon,
198
+ handleOnClickLeftSectionIcon,
199
+ ]);
200
+
201
+ const endIconElement = useMemo(() => {
202
+ if (!hasRightSectionIcon) return;
203
+
204
+ if (renderEndIcon) return renderEndIcon();
205
+
206
+ if (iconMode === "flat") {
207
+ return (
208
+ <div className={cn(iconWrapperClassname, "flex")}>
209
+ <div
210
+ className={iconClassname}
211
+ onClick={handleOnClickRightSectionIcon}
212
+ >
213
+ {endIcon}
214
+ </div>
215
+ </div>
216
+ );
217
+ }
218
+
219
+ return (
220
+ <div
221
+ className={endIconWrapperClassname}
222
+ onClick={handleOnClickRightSectionIcon}
223
+ >
224
+ {endIcon}
225
+ </div>
226
+ );
227
+ }, [
228
+ hasRightSectionIcon,
229
+ endIcon,
230
+ iconMode,
231
+ iconSearchWrapperClassname,
232
+ endIconWrapperClassname,
233
+ iconClassname,
234
+ renderEndIcon,
235
+ handleOnClickRightSectionIcon,
236
+ ]);
237
+
136
238
  return (
137
239
  <div className={`inline-flex flex-col ${fullwidth ? "w-full" : ""}`}>
138
240
  <div className="relative">
139
- {hasSearchIcon && (
241
+ {hasSearchIcon && !hasLeftSectionIcon && (
140
242
  <div className={iconSearchWrapperClassname}>
141
243
  <MagnifyingGlassIcon className={iconClassname} />
142
244
  </div>
@@ -150,6 +252,8 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
150
252
  disabled={disabled}
151
253
  className={cn(inputClassname, props.className)}
152
254
  />
255
+ {startIconElement}
256
+
153
257
  {hasClearIcon && !hasRightSectionIcon && (
154
258
  <div
155
259
  className={iconWrapperClassname}
@@ -165,17 +269,7 @@ export const TextInput = forwardRef<HTMLInputElement, InputProps>(
165
269
  />
166
270
  </div>
167
271
  )}
168
- {hasRightSectionIcon &&
169
- (renderEndIcon ? (
170
- renderEndIcon()
171
- ) : (
172
- <div
173
- className={endIconWrapperClassname}
174
- onClick={handleOnClickRightSectionIcon}
175
- >
176
- {endIcon}
177
- </div>
178
- ))}
272
+ {endIconElement}
179
273
 
180
274
  <label htmlFor={_id} className={cn(labelClassname, labelClassName)}>
181
275
  {label}{" "}
package/src/index.ts CHANGED
@@ -40,6 +40,7 @@ export * from "./components/Toast/Toast";
40
40
  export * from "./components/Toast/Toaster";
41
41
  export * from "./components/Toast/useToast";
42
42
  export * from "./components/Tree";
43
+ export * from "./components/FocusedScrollView/FocusedScrollView";
43
44
 
44
45
  // Export component types
45
46
  export type { ButtonProps } from "./components/Button/Button";