@algorithm-shift/design-system 1.2.11 → 1.2.12

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 (89) hide show
  1. package/package.json +2 -1
  2. package/src/components/Basic/Image/Image.stories.tsx +13 -0
  3. package/src/components/Basic/Image/Image.tsx +52 -0
  4. package/src/components/Basic/Shape/Shape.tsx +16 -0
  5. package/src/components/Basic/Typography/Typography.stories.tsx +18 -0
  6. package/src/components/Basic/Typography/Typography.tsx +31 -0
  7. package/src/components/DataDisplay/Table/Table.stories.tsx +35 -0
  8. package/src/components/DataDisplay/Table/Table.tsx +15 -0
  9. package/src/components/DataDisplay/index.ts +1 -0
  10. package/src/components/Global/SelectDropdown.tsx +61 -0
  11. package/src/components/Global/TinyMceEditor.tsx +81 -0
  12. package/src/components/Inputs/Checkbox/Checkbox.tsx +18 -0
  13. package/src/components/Inputs/DatePicker/DatePicker.tsx +81 -0
  14. package/src/components/Inputs/DateRange/DateRange.tsx +64 -0
  15. package/src/components/Inputs/Dropdown/Dropdown.tsx +62 -0
  16. package/src/components/Inputs/EmailInput/EmailInput.tsx +64 -0
  17. package/src/components/Inputs/FileInput/FileInput.tsx +24 -0
  18. package/src/components/Inputs/MultiCheckbox/MultiCheckbox.tsx +24 -0
  19. package/src/components/Inputs/NumberInput/NumberInput.tsx +66 -0
  20. package/src/components/Inputs/PasswordInput/PasswordInput.tsx +66 -0
  21. package/src/components/Inputs/PhoneInput/PhoneInput.tsx +58 -0
  22. package/src/components/Inputs/RadioInput/RadioInput.tsx +24 -0
  23. package/src/components/Inputs/RichText/RichText.tsx +10 -0
  24. package/src/components/Inputs/SearchInput/SearchInput.tsx +64 -0
  25. package/src/components/Inputs/SwitchToggle/SwitchToggle.tsx +22 -0
  26. package/src/components/Inputs/TextInput/TextInput.tsx +59 -0
  27. package/src/components/Inputs/Textarea/Textarea.tsx +58 -0
  28. package/src/components/Inputs/UrlInput/UrlInput.tsx +66 -0
  29. package/src/components/Layout/Flex.tsx +13 -0
  30. package/src/components/Layout/Grid.tsx +13 -0
  31. package/src/components/Navigation/Logo/Logo.stories.tsx +25 -0
  32. package/src/components/Navigation/Logo/Logo.tsx +33 -0
  33. package/src/components/Navigation/Notification/Notification.stories.tsx +20 -0
  34. package/src/components/Navigation/Notification/Notification.tsx +20 -0
  35. package/src/components/Navigation/Profile/Profile.stories.tsx +20 -0
  36. package/src/components/Navigation/Profile/Profile.tsx +25 -0
  37. package/src/components/Navigation/Spacer/Spacer.stories.tsx +18 -0
  38. package/src/components/Navigation/Spacer/Spacer.tsx +11 -0
  39. package/src/components/Navigation/Stages/Stages.stories.tsx +24 -0
  40. package/src/components/Navigation/Stages/Stages.tsx +49 -0
  41. package/src/components/Navigation/Tabs/Tabs.stories.tsx +38 -0
  42. package/src/components/Navigation/Tabs/Tabs.tsx +72 -0
  43. package/src/components/Navigation/index.ts +6 -0
  44. package/src/components/index.ts +27 -0
  45. package/src/components/ui/button.stories.tsx +24 -0
  46. package/src/components/ui/button.tsx +59 -0
  47. package/src/components/ui/calendar.tsx +211 -0
  48. package/src/components/ui/checkbox.tsx +30 -0
  49. package/src/components/ui/data-table.tsx +138 -0
  50. package/src/components/ui/dropdown-menu.tsx +258 -0
  51. package/src/components/ui/input.tsx +21 -0
  52. package/src/components/ui/label.tsx +22 -0
  53. package/src/components/ui/popover.tsx +46 -0
  54. package/src/components/ui/radio-group.tsx +43 -0
  55. package/src/components/ui/select.tsx +183 -0
  56. package/src/components/ui/switch.tsx +29 -0
  57. package/src/components/ui/table.tsx +121 -0
  58. package/src/components/ui/textarea.tsx +18 -0
  59. package/src/global.css +6 -0
  60. package/src/index.css +119 -0
  61. package/src/index.ts +4 -0
  62. package/src/lib/utils.ts +6 -0
  63. package/src/stories/Button.stories.ts +54 -0
  64. package/src/stories/Button.tsx +35 -0
  65. package/src/stories/Configure.mdx +364 -0
  66. package/src/stories/Header.stories.ts +34 -0
  67. package/src/stories/Header.tsx +54 -0
  68. package/src/stories/Page.stories.ts +33 -0
  69. package/src/stories/Page.tsx +73 -0
  70. package/src/stories/assets/accessibility.png +0 -0
  71. package/src/stories/assets/accessibility.svg +1 -0
  72. package/src/stories/assets/addon-library.png +0 -0
  73. package/src/stories/assets/assets.png +0 -0
  74. package/src/stories/assets/avif-test-image.avif +0 -0
  75. package/src/stories/assets/context.png +0 -0
  76. package/src/stories/assets/discord.svg +1 -0
  77. package/src/stories/assets/docs.png +0 -0
  78. package/src/stories/assets/figma-plugin.png +0 -0
  79. package/src/stories/assets/github.svg +1 -0
  80. package/src/stories/assets/share.png +0 -0
  81. package/src/stories/assets/styling.png +0 -0
  82. package/src/stories/assets/testing.png +0 -0
  83. package/src/stories/assets/theming.png +0 -0
  84. package/src/stories/assets/tutorials.svg +1 -0
  85. package/src/stories/assets/youtube.svg +1 -0
  86. package/src/stories/button.css +30 -0
  87. package/src/stories/header.css +32 -0
  88. package/src/stories/page.css +68 -0
  89. package/src/types/global.d.ts +85 -0
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@algorithm-shift/design-system",
3
- "version": "1.2.11",
3
+ "version": "1.2.12",
4
4
  "description": "A React + Tailwind CSS design system",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
9
  "dist",
10
+ "src",
10
11
  "public"
11
12
  ],
12
13
  "scripts": {
@@ -0,0 +1,13 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+
3
+ import Image from './Image';
4
+
5
+ const meta = {
6
+ component: Image,
7
+ } satisfies Meta<typeof Image>;
8
+
9
+ export default meta;
10
+
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {};
@@ -0,0 +1,52 @@
1
+ import { cn } from '@/lib/utils';
2
+ import { ImageProps } from '@/types/global';
3
+
4
+ const ImageControl = ({
5
+ className,
6
+ style,
7
+ imageUrl,
8
+ imageUrlExternal,
9
+ altText = 'Preview',
10
+ }: ImageProps) => {
11
+
12
+ const imageClass = cn(
13
+ 'h-[200px] w-[200px] border-1',
14
+ 'border-2 border-dashed border-gray-400 flex items-center justify-center cursor-pointer hover:border-blue-500 transition',
15
+ className
16
+ );
17
+
18
+ const defaultImgClass = cn(
19
+ 'w-full h-full',
20
+ className
21
+ );
22
+
23
+ let src: string;
24
+ let extraProps: Record<string, any>;
25
+
26
+ if (imageUrlExternal) {
27
+ src = imageUrlExternal;
28
+ extraProps = {
29
+ className: defaultImgClass,
30
+ };
31
+ } else if (imageUrl) {
32
+ src = imageUrl;
33
+ extraProps = {
34
+ className: defaultImgClass,
35
+ };
36
+ } else {
37
+ src = './image-placeholder.png';
38
+ extraProps = {
39
+ width: 50,
40
+ height: 50,
41
+ className: 'opacity-50',
42
+ };
43
+ }
44
+
45
+ return (
46
+ <div className={imageClass} style={style}>
47
+ <img src={src} alt={altText} {...extraProps} />
48
+ </div>
49
+ );
50
+ };
51
+
52
+ export default ImageControl;
@@ -0,0 +1,16 @@
1
+ import { ElementProps } from '@/types/global';
2
+
3
+ const Shape = ({
4
+ children,
5
+ className,
6
+ style
7
+ }: ElementProps) => {
8
+
9
+ return (
10
+ <div className={className} style={style}>
11
+ {children}
12
+ </div>
13
+ );
14
+ };
15
+
16
+ export default Shape;
@@ -0,0 +1,18 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+
3
+ import Typography from './Typography';
4
+
5
+ const meta = {
6
+ component: Typography,
7
+ } satisfies Meta<typeof Typography>;
8
+
9
+ export default meta;
10
+
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {
14
+ args: {
15
+ tagName: 'p',
16
+ textContent: 'This is a sample typography component.',
17
+ },
18
+ };
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+
3
+ import { cn } from '@/lib/utils';
4
+ import { TypographyProps } from '@/types/global';
5
+
6
+ const Typography = ({
7
+ className,
8
+ style,
9
+ tagName,
10
+ textContent,
11
+ }: TypographyProps) => {
12
+
13
+ const Tag = tagName || 'h1';
14
+
15
+ return React.createElement(
16
+ Tag,
17
+ {
18
+ style,
19
+ className: cn(className, 'pointer-events-auto'),
20
+ },
21
+ [
22
+ React.createElement('span', {
23
+ key: 'html',
24
+ className: 'pointer-events-none',
25
+ dangerouslySetInnerHTML: { __html: textContent },
26
+ })
27
+ ]
28
+ );
29
+ };
30
+
31
+ export default Typography;
@@ -0,0 +1,35 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+
3
+ import Table from './Table';
4
+
5
+ const meta = {
6
+ component: Table,
7
+ tags: ['autodocs'],
8
+ } satisfies Meta<typeof Table>;
9
+
10
+ export default meta;
11
+
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ export const Default: Story = {
15
+ args: {
16
+ columns: [
17
+ { accessorKey: 'id', header: 'ID' },
18
+ { accessorKey: 'name', header: 'Name' },
19
+ { accessorKey: 'age', header: 'Age' },
20
+ ],
21
+ data: [
22
+ { id: 1, name: 'John Doe', age: 28 },
23
+ { id: 2, name: 'Jane Smith', age: 34 },
24
+ { id: 3, name: 'Sam Johnson', age: 45 },
25
+ ],
26
+ rowActions: [
27
+ {
28
+ header: 'Edit'
29
+ },
30
+ {
31
+ header: 'Delete'
32
+ },
33
+ ],
34
+ },
35
+ };
@@ -0,0 +1,15 @@
1
+
2
+ import { DataTable } from '@/components/ui/data-table';
3
+ import { TableProps } from '@/types/global';
4
+
5
+ const Table = ({ columns, data, rowActions }: TableProps) => {
6
+ const rawColumns = Array.isArray(columns) ? columns : [];
7
+ const rawData = Array.isArray(data) ? data : [];
8
+ const rawRowActions = Array.isArray(rowActions) ? rowActions : [];
9
+
10
+ return (
11
+ <DataTable columns={rawColumns} data={rawData} rowActions={rawRowActions} />
12
+ );
13
+ };
14
+
15
+ export default Table;
@@ -0,0 +1 @@
1
+ export { default as Table } from './Table/Table'
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Select,
5
+ SelectContent,
6
+ SelectItem,
7
+ SelectTrigger,
8
+ SelectValue,
9
+ } from '@/components/ui/select';
10
+
11
+ interface Option {
12
+ label: string;
13
+ value: string;
14
+ }
15
+
16
+ interface SelectDropdownProps {
17
+ options: Option[];
18
+ placeholder?: string;
19
+ value?: string;
20
+ onChange?: (value: string) => void;
21
+ className?: string;
22
+ style?: React.CSSProperties;
23
+ id?: string;
24
+ autoComplete?: string;
25
+ disabled?: boolean;
26
+ readOnly?: boolean;
27
+ required?: boolean;
28
+ pattern?: string;
29
+ }
30
+
31
+ export default function SelectDropdown({
32
+ options,
33
+ placeholder = 'Select an option',
34
+ value,
35
+ onChange,
36
+ className,
37
+ id,
38
+ disabled,
39
+ readOnly,
40
+ style
41
+ }: SelectDropdownProps) {
42
+ return (
43
+ <Select value={value} onValueChange={onChange} disabled={disabled}>
44
+ <SelectTrigger
45
+ id={id}
46
+ className={className}
47
+ style={style ?? {}}
48
+ aria-readonly={readOnly}
49
+ >
50
+ <SelectValue placeholder={placeholder} />
51
+ </SelectTrigger>
52
+ <SelectContent>
53
+ {options.map((opt) => (
54
+ <SelectItem key={opt.value} value={opt.value}>
55
+ {opt.label}
56
+ </SelectItem>
57
+ ))}
58
+ </SelectContent>
59
+ </Select>
60
+ );
61
+ }
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+
3
+ import { useMemo, useRef } from 'react';
4
+
5
+ import { Editor } from '@tinymce/tinymce-react';
6
+
7
+ export default function MyEditor({
8
+ value,
9
+ onChange,
10
+ isDefault
11
+ }: {
12
+ value?: string;
13
+ onChange?: (value: string) => void;
14
+ isDefault?: boolean;
15
+ }) {
16
+ const editorRef = useRef<any>(null);
17
+
18
+ function stripOuterP(html: string): string {
19
+ const trimmedHtml = html.trim();
20
+ if (!trimmedHtml) return '';
21
+ const div = document.createElement('div');
22
+ div.innerHTML = trimmedHtml;
23
+ const firstChild = div.firstElementChild;
24
+ if (div.childElementCount === 1 && firstChild?.tagName === 'P') {
25
+ return firstChild.innerHTML;
26
+ }
27
+ return trimmedHtml;
28
+ }
29
+
30
+ const isDefaultToolbar = useMemo(() => {
31
+ let toolbar = 'undo redo | formatselect | bold italic forecolor';
32
+ if (isDefault) {
33
+ toolbar = 'undo redo | blocks | ' +
34
+ 'bold italic forecolor | alignleft aligncenter ' +
35
+ 'alignright alignjustify | bullist numlist outdent indent | ' +
36
+ 'removeformat | help';
37
+ }
38
+ return toolbar;
39
+ }, [isDefault]);
40
+
41
+ return (
42
+ <Editor
43
+ apiKey={process.env.NEXT_PUBLIC_TINYMCE_API_KEY}
44
+ tinymceScriptSrc={process.env.NEXT_PUBLIC_TINYMCE_SCRIPT_SRC}
45
+ onInit={(_evt, editor) => (editorRef.current = editor)}
46
+ value={value}
47
+ licenseKey='gpl'
48
+ init={{
49
+ height: 300,
50
+ menubar: false,
51
+ forced_root_block: false,
52
+ plugins: [
53
+ 'advlist',
54
+ 'autolink',
55
+ 'lists',
56
+ 'link',
57
+ 'image',
58
+ 'charmap',
59
+ 'preview',
60
+ 'anchor',
61
+ 'searchreplace',
62
+ 'visualblocks',
63
+ 'code',
64
+ 'fullscreen',
65
+ 'insertdatetime',
66
+ 'media',
67
+ 'table',
68
+ 'help',
69
+ 'wordcount',
70
+ ],
71
+ toolbar: isDefaultToolbar,
72
+ content_style: `
73
+ body {
74
+ outline: none;
75
+ }
76
+ `,
77
+ }}
78
+ onEditorChange={(content) => onChange?.(stripOuterP(content))}
79
+ />
80
+ );
81
+ }
@@ -0,0 +1,18 @@
1
+ import { Checkbox } from '@/components/ui/checkbox';
2
+ import { Label } from '@/components/ui/label';
3
+ import { CheckboxInputProps } from '@/types/global';
4
+
5
+ const CheckboxInput = ({ className, style, ...props }: CheckboxInputProps) => {
6
+ const text = props.text ? props.text : 'Subscribe';
7
+
8
+ return (
9
+ <div className={className} style={style}>
10
+ <div className="flex items-center space-x-2">
11
+ <Checkbox id="newsletter" />
12
+ <Label htmlFor="newsletter">{text}</Label>
13
+ </div>
14
+ </div>
15
+ );
16
+ }
17
+
18
+ export { CheckboxInput };
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+ import React from 'react';
3
+
4
+ import { Calendar } from 'lucide-react';
5
+
6
+ import { Input } from '@/components/ui/input';
7
+ import { DatePickerInputProps } from '@/types/global';
8
+
9
+ export default function DatePicker({ className, style, ...props }: DatePickerInputProps) {
10
+ const placeholder = props.placeholder ?? 'Placeholder text';
11
+
12
+ // Controls (now option objects)
13
+ const minimumDate = props.minimumDate ?? 'none';
14
+ const customMinimumDate = props.customMinimumDate ?? '';
15
+
16
+ const maximumDate = props.maximumDate ?? 'none';
17
+ const customMaximumDate = props.customMaximumDate ?? '';
18
+
19
+ const errorMessage = props.errorMessage ?? 'Required';
20
+ const isRequired = props.isRequired ?? false;
21
+ const isEditable = props.isEditable ?? true;
22
+ const isDisabled = props.isDisabled ?? false;
23
+ const isReadonly = props.isReadonly ?? false;
24
+ const isAutocomplete = props.isAutocomplete ?? false;
25
+
26
+ const [value, setValue] = React.useState('');
27
+ const [error, setError] = React.useState<string | null>(null);
28
+
29
+ const resolveDate = (option: any, customOption?: any): string | undefined => {
30
+ if (!option) return undefined;
31
+
32
+ switch (option) {
33
+ case 'today':
34
+ return new Date().toISOString().split('T')[0];
35
+ case 'custom':
36
+ return customOption ?? undefined;
37
+ case 'none':
38
+ default:
39
+ return undefined;
40
+ }
41
+ };
42
+
43
+ const minDate = resolveDate(minimumDate, customMinimumDate);
44
+ const maxDate = resolveDate(maximumDate, customMaximumDate);
45
+
46
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
47
+ const val = e.target.value;
48
+ setValue(val);
49
+
50
+ if (isRequired && val.trim() === '') {
51
+ setError(errorMessage);
52
+ } else {
53
+ setError(null);
54
+ }
55
+ };
56
+
57
+ return (
58
+ <>
59
+ <div className="flex justify-start items-center relative">
60
+ <Calendar className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[#BDBDBD]" />
61
+ <Input
62
+ type="date"
63
+ id="date"
64
+ autoComplete={isAutocomplete ? 'on' : 'off'}
65
+ onChange={handleChange}
66
+ disabled={isDisabled || !isEditable}
67
+ value={value}
68
+ readOnly={isReadonly}
69
+ required={isRequired}
70
+ placeholder={placeholder}
71
+ min={minDate}
72
+ max={maxDate}
73
+ className={className}
74
+ style={style}
75
+ />
76
+ </div>
77
+
78
+ {error && <p className="mt-1 text-xs text-red-500">{error}</p>}
79
+ </>
80
+ );
81
+ }
@@ -0,0 +1,64 @@
1
+
2
+ 'use client';
3
+ import * as React from 'react';
4
+
5
+ import { addDays, format } from 'date-fns';
6
+ import { DateRange as DateRangeProps } from 'react-day-picker';
7
+
8
+ import { Button } from '@/components/ui/button';
9
+ import { Calendar } from '@/components/ui/calendar';
10
+ import {
11
+ Popover,
12
+ PopoverContent,
13
+ PopoverTrigger,
14
+ } from '@/components/ui/popover';
15
+ import { cn } from '@/lib/utils';
16
+ import { DateRangeInputProps } from '@/types/global';
17
+
18
+ const DateRange = ({ className, style }: DateRangeInputProps) => {
19
+ const [date, setDate] = React.useState<DateRangeProps | undefined>({
20
+ from: new Date(),
21
+ to: addDays(new Date(), 7),
22
+ });
23
+
24
+ return (
25
+ <div className={className} style={style}>
26
+ <Popover>
27
+ <PopoverTrigger asChild>
28
+ <Button
29
+ id="date"
30
+ variant={'outline'}
31
+ className={cn(
32
+ 'w-[300px] justify-start text-left font-normal text-[11px]',
33
+ !date && 'text-muted-foreground'
34
+ )}
35
+ >
36
+ {date?.from ? (
37
+ date.to ? (
38
+ <>
39
+ {format(date.from, 'LLL dd, y')} -{' '}
40
+ {format(date.to, 'LLL dd, y')}
41
+ </>
42
+ ) : (
43
+ format(date.from, 'LLL dd, y')
44
+ )
45
+ ) : (
46
+ <span>Pick a date range</span>
47
+ )}
48
+ </Button>
49
+ </PopoverTrigger>
50
+ <PopoverContent className="w-auto p-0" align="start">
51
+ <Calendar
52
+ mode="range"
53
+ defaultMonth={date?.from}
54
+ selected={date}
55
+ onSelect={setDate}
56
+ numberOfMonths={2}
57
+ />
58
+ </PopoverContent>
59
+ </Popover>
60
+ </div>
61
+ );
62
+ }
63
+
64
+ export default DateRange;
@@ -0,0 +1,62 @@
1
+ import * as React from 'react';
2
+
3
+ import SelectDropdown from '@/components/Global/SelectDropdown';
4
+ import { SelectDropdownInputProps } from '@/types/global';
5
+
6
+ const Dropdown = ({ className, style, ...props }: SelectDropdownInputProps) => {
7
+ const text = Array.isArray(props.text) ? props.text : [props.text ?? 'Default'];
8
+ const placeholder = props.placeholder ? props.placeholder : 'Placeholder text';
9
+ const formatList = text.map((item: any) => ({
10
+ label: item || 'value1',
11
+ value: item
12
+ }));
13
+
14
+ // Controls
15
+ const regexPattern = props.regexPattern ?? '';
16
+ const errorMessage = props.errorMessage ?? 'Required';
17
+ const isRequired = props.isRequired ?? false;
18
+ const isEditable = props.isEditable ?? true;
19
+ const isDisabled = props.isDisabled ?? false;
20
+ const isReadonly = props.isReadonly ?? false;
21
+ const isAutocomplete = props.isAutocomplete ?? false;
22
+
23
+ const [value, setValue] = React.useState('');
24
+ const [error, setError] = React.useState<string | null>(null);
25
+
26
+ const handleChange = (val: string) => {
27
+ setValue(val);
28
+ if (isRequired && val.trim() === '') {
29
+ setError(errorMessage);
30
+ } else if (regexPattern && !new RegExp(regexPattern).test(val)) {
31
+ setError(errorMessage);
32
+ } else {
33
+ setError(null);
34
+ }
35
+ };
36
+
37
+ return (
38
+ <>
39
+ <div className="flex gap-3 flex-col">
40
+ <SelectDropdown
41
+ options={formatList}
42
+ placeholder={placeholder}
43
+ id={'select-field'}
44
+ value={value}
45
+ className={className}
46
+ style={style}
47
+ autoComplete={isAutocomplete ? 'on' : 'off'}
48
+ onChange={handleChange}
49
+ disabled={isDisabled || !isEditable}
50
+ readOnly={isReadonly}
51
+ required={isRequired}
52
+ pattern={regexPattern || undefined}
53
+ />
54
+ </div>
55
+ {error && (
56
+ <p className="mt-1 text-xs text-red-500">{error}</p>
57
+ )}
58
+ </>
59
+ );
60
+ }
61
+
62
+ export { Dropdown };
@@ -0,0 +1,64 @@
1
+ import * as React from 'react';
2
+
3
+ import { Mail } from 'lucide-react';
4
+
5
+ import { Input } from '@/components/ui/input';
6
+ import { EmailInputProps } from '@/types/global';
7
+
8
+ const EmailInput = ({ className, style, ...props }: EmailInputProps) => {
9
+ const placeholder = props.placeholder ?? 'Placeholder text';
10
+
11
+ // Controls
12
+ const regexPattern = props.regexPattern ?? '';
13
+ const errorMessage = props.errorMessage ?? 'Required';
14
+ const noOfCharacters = props.noOfCharacters ?? 100;
15
+ const isRequired = props.isRequired ?? false;
16
+ const isEditable = props.isEditable ?? true;
17
+ const isDisabled = props.isDisabled ?? false;
18
+ const isReadonly = props.isReadonly ?? false;
19
+ const isAutocomplete = props.isAutocomplete ?? false;
20
+
21
+ const [value, setValue] = React.useState('');
22
+ const [error, setError] = React.useState<string | null>(null);
23
+
24
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
25
+ const val = e.target.value;
26
+ if (val.length > noOfCharacters) return;
27
+ setValue(val);
28
+ if (isRequired && val.trim() === '') {
29
+ setError(errorMessage);
30
+ } else if (regexPattern && !new RegExp(regexPattern).test(val)) {
31
+ setError(errorMessage);
32
+ } else {
33
+ setError(null);
34
+ }
35
+ };
36
+
37
+ return (
38
+ <>
39
+ <div className="flex justify-start items-center relative">
40
+ <Mail className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[#BDBDBD]" />
41
+ <Input
42
+ type="email"
43
+ value={value}
44
+ className={className}
45
+ style={style}
46
+ autoComplete={isAutocomplete ? 'on' : 'off'}
47
+ placeholder={placeholder}
48
+ onChange={handleChange}
49
+ disabled={isDisabled || !isEditable}
50
+ readOnly={isReadonly}
51
+ required={isRequired}
52
+ maxLength={noOfCharacters}
53
+ pattern={regexPattern || undefined}
54
+ />
55
+ </div>
56
+
57
+ {error && (
58
+ <p className="mt-1 text-xs text-red-500">{error}</p>
59
+ )}
60
+ </>
61
+ );
62
+ }
63
+
64
+ export { EmailInput };
@@ -0,0 +1,24 @@
1
+ import { Input } from '@/components/ui/input';
2
+ import { FileInputProps } from '@/types/global';
3
+
4
+ const FileInput = ({ className, style, ...props }: FileInputProps) => {
5
+ const placeholder = props.placeholder ?? 'Placeholder text';
6
+
7
+ return (
8
+ <div className="d-flex items-center relative align-middle">
9
+ <div className="bg-[#E9E9E9] absolute px-10 text-center top-1/2 h-full justify-center items-center flex w-10 -translate-y-1/2 text-[#383838] font-[500] text-[11px]">
10
+ Browse
11
+ </div>
12
+ <Input
13
+ type="file"
14
+ id="file"
15
+ autoComplete="off"
16
+ placeholder={placeholder}
17
+ className={className}
18
+ style={style}
19
+ />
20
+ </div>
21
+ );
22
+ }
23
+
24
+ export { FileInput };
@@ -0,0 +1,24 @@
1
+ import { Checkbox } from '@/components/ui/checkbox';
2
+ import { Label } from '@/components/ui/label';
3
+ import { MultiCheckboxInputProps } from '@/types/global';
4
+
5
+ const MultiCheckbox = ({ className, style, ...props }: MultiCheckboxInputProps) => {
6
+ const text = Array.isArray(props.text)
7
+ ? props.text
8
+ : [props.text ?? 'Subscribe'];
9
+
10
+ return (
11
+ <div className={className} style={style}>
12
+ <div className="flex gap-3 flex-col">
13
+ {text?.map((item: string, index: number) => (
14
+ <div className="flex items-center space-x-2" key={index}>
15
+ <Checkbox id={`newsletter-${index}`} />
16
+ <Label htmlFor={`newsletter-${index}`}>{item}</Label>
17
+ </div>
18
+ ))}
19
+ </div>
20
+ </div>
21
+ );
22
+ }
23
+
24
+ export { MultiCheckbox };