@dktunited-techoff/techoff-suite-ui 1.5.7 → 1.6.1

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 (50) hide show
  1. package/esm/components/TsCheckbox/TsCheckbox.css +0 -3
  2. package/esm/components/TsDropdown/TsDropdown/TsDropdown.css +92 -0
  3. package/esm/components/TsDropdown/TsDropdown/TsDropdown.d.ts +4 -0
  4. package/esm/components/TsDropdown/TsDropdown/TsDropdown.js +73 -0
  5. package/esm/components/TsDropdown/TsDropdown/TsDropdown.js.map +1 -0
  6. package/esm/components/TsDropdown/TsDropdown/TsDropdown.tsx +131 -0
  7. package/esm/components/TsDropdown/TsDropdown/TsDropdown.types.d.ts +13 -0
  8. package/esm/components/TsDropdown/TsDropdown/TsDropdown.types.js +2 -0
  9. package/esm/components/TsDropdown/TsDropdown/TsDropdown.types.js.map +1 -0
  10. package/esm/components/TsDropdown/TsDropdown/TsDropdown.types.ts +13 -0
  11. package/esm/components/TsDropdown/TsDropdown/__stories__/TsDropdown.stories.mdx +68 -0
  12. package/esm/components/TsDropdown/TsDropdown/__stories__/fruits.d.ts +4 -0
  13. package/esm/components/TsDropdown/TsDropdown/__stories__/fruits.js +48 -0
  14. package/esm/components/TsDropdown/TsDropdown/__stories__/fruits.js.map +1 -0
  15. package/esm/components/TsDropdown/TsDropdown/__stories__/fruits.ts +47 -0
  16. package/esm/components/TsInput/TsInput/TsInput.css +1 -0
  17. package/esm/components/TsInput/TsInput/__stories__/TsInput.stories.mdx +6 -1
  18. package/esm/index.d.ts +1 -0
  19. package/esm/index.js +1 -0
  20. package/esm/index.js.map +1 -1
  21. package/lib/components/TsCheckbox/TsCheckbox.css +0 -3
  22. package/lib/components/TsDropdown/TsDropdown/TsDropdown.css +92 -0
  23. package/lib/components/TsDropdown/TsDropdown/TsDropdown.d.ts +4 -0
  24. package/lib/components/TsDropdown/TsDropdown/TsDropdown.js +77 -0
  25. package/lib/components/TsDropdown/TsDropdown/TsDropdown.js.map +1 -0
  26. package/lib/components/TsDropdown/TsDropdown/TsDropdown.tsx +131 -0
  27. package/lib/components/TsDropdown/TsDropdown/TsDropdown.types.d.ts +13 -0
  28. package/lib/components/TsDropdown/TsDropdown/TsDropdown.types.js +3 -0
  29. package/lib/components/TsDropdown/TsDropdown/TsDropdown.types.js.map +1 -0
  30. package/lib/components/TsDropdown/TsDropdown/TsDropdown.types.ts +13 -0
  31. package/lib/components/TsDropdown/TsDropdown/__stories__/TsDropdown.stories.mdx +68 -0
  32. package/lib/components/TsDropdown/TsDropdown/__stories__/fruits.d.ts +4 -0
  33. package/lib/components/TsDropdown/TsDropdown/__stories__/fruits.js +51 -0
  34. package/lib/components/TsDropdown/TsDropdown/__stories__/fruits.js.map +1 -0
  35. package/lib/components/TsDropdown/TsDropdown/__stories__/fruits.ts +47 -0
  36. package/lib/components/TsInput/TsInput/TsInput.css +1 -0
  37. package/lib/components/TsInput/TsInput/__stories__/TsInput.stories.mdx +6 -1
  38. package/lib/index.d.ts +1 -0
  39. package/lib/index.js +1 -0
  40. package/lib/index.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/components/TsCheckbox/TsCheckbox.css +0 -3
  43. package/src/components/TsDropdown/TsDropdown/TsDropdown.css +92 -0
  44. package/src/components/TsDropdown/TsDropdown/TsDropdown.tsx +131 -0
  45. package/src/components/TsDropdown/TsDropdown/TsDropdown.types.ts +13 -0
  46. package/src/components/TsDropdown/TsDropdown/__stories__/TsDropdown.stories.mdx +68 -0
  47. package/src/components/TsDropdown/TsDropdown/__stories__/fruits.ts +47 -0
  48. package/src/components/TsInput/TsInput/TsInput.css +1 -0
  49. package/src/components/TsInput/TsInput/__stories__/TsInput.stories.mdx +6 -1
  50. package/src/index.ts +1 -0
@@ -0,0 +1,92 @@
1
+ .ts-dropdown-container {
2
+ position: relative;
3
+ width: 100%;
4
+ }
5
+ .ts-dropdown-input {
6
+ display: flex;
7
+ align-items: center;
8
+ width: 100%;
9
+ height: 32px;
10
+ padding-left: 10px;
11
+ border: 1px solid #949494;
12
+ cursor: pointer;
13
+ }
14
+ .ts-dropdown-input--disabled {
15
+ background: #fafafa;
16
+ cursor: not-allowed;
17
+ }
18
+ .ts-dropdown-input--placeholder {
19
+ flex: 1;
20
+ color: #7a7a7a;
21
+ font-weight: 400;
22
+ }
23
+ .ts-dropdown-input--value {
24
+ flex: 1;
25
+ font-weight: 600;
26
+ }
27
+ .ts-dropdown-input--chevron {
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ width: 32px;
32
+ height: 32px;
33
+ }
34
+ /* ####### */
35
+ /* LABEL */
36
+ .ts-dropdown-label {
37
+ padding-bottom: 6px;
38
+ }
39
+ /* ###### */
40
+ /* MENU */
41
+ .ts-dropdown-menu-container {
42
+ position: absolute;
43
+ top: calc(100%+8px);
44
+ display: flex;
45
+ flex-direction: column;
46
+ min-width: 100%;
47
+ max-height: 250px;
48
+ padding: 12px;
49
+ box-shadow: 0px 8px 9px 0px #1010101a;
50
+ background: #ffffff;
51
+ overflow: hidden;
52
+ z-index: 10;
53
+ }
54
+ .ts-dropdown-search {
55
+ padding-bottom: 12px;
56
+ margin-bottom: 12px;
57
+ border-bottom: 1px solid #d9dde1;
58
+ }
59
+ .ts-dropdown-menu {
60
+ overflow: auto;
61
+ }
62
+ .ts-dropdown-menu-loader {
63
+ display: flex;
64
+ justify-content: center;
65
+ }
66
+ .ts-dropdown-menu-error-text {
67
+ display: flex;
68
+ justify-content: center;
69
+ color: #7a7a7a;
70
+ font-style: italic;
71
+ }
72
+ .ts-dropdown-menu-item {
73
+ display: flex;
74
+ align-items: center;
75
+ height: 36px;
76
+ padding: 0 12px;
77
+ cursor: pointer;
78
+ }
79
+ .ts-dropdown-menu-item:hover {
80
+ background: #e1e3f5;
81
+ color: #3643ba;
82
+ }
83
+
84
+ /* ######### */
85
+ /* GLOBALS */
86
+ * {
87
+ font-size: 14px;
88
+ outline: none;
89
+ box-sizing: border-box;
90
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
91
+ 'Helvetica Neue', sans-serif;
92
+ }
@@ -0,0 +1,131 @@
1
+ import * as React from 'react';
2
+ import { useEffect, useState } from 'react';
3
+ import { TsDropdownProps } from './TsDropdown.types';
4
+ import { TsIcon } from '../../TsIcon/TsIcon';
5
+ import { TsInput } from '../../TsInput/TsInput/TsInput';
6
+ import { useClickOutside } from '../../../hooks/use-click-outside';
7
+ import { capitalize } from '../../../utils/string.utils';
8
+ import './TsDropdown.css';
9
+ import { TsLoader } from '../../TsLoader/TsLoader';
10
+
11
+ export const TsDropdown = <T extends unknown>({
12
+ label,
13
+ options,
14
+ placeholder,
15
+ searchPlaceholder,
16
+ value,
17
+ disabled,
18
+ searchable,
19
+ getOptionValue,
20
+ getOptionLabel,
21
+ loadOptions,
22
+ onChange,
23
+ }: TsDropdownProps<T>) => {
24
+ const [showDropdownMenu, setShowDropdownMenu] = useState<boolean>(false);
25
+ const [searchValue, setSearchValue] = useState<string>('');
26
+ const [items, setItems] = useState<T[]>(options ?? []);
27
+ const [selectedItem, setSelectedItem] = useState<T | undefined>(value);
28
+ const [loading, setLoading] = useState<boolean>(false);
29
+ const [isFetchError, setIsFetchError] = useState<boolean>(false);
30
+ const ref = useClickOutside(() => setShowDropdownMenu(false));
31
+
32
+ // ########
33
+ // Fetchers
34
+ const fetchOptions = () => {
35
+ if (!options && loadOptions) {
36
+ setLoading(true);
37
+ setIsFetchError(false);
38
+ loadOptions(searchValue)
39
+ .then(setItems)
40
+ .catch(() => setIsFetchError(true))
41
+ .finally(() => setLoading(false));
42
+ }
43
+ };
44
+
45
+ // ########
46
+ // Handlers
47
+ const handleSelectItem = (item: T) => {
48
+ setSelectedItem(item);
49
+ setShowDropdownMenu(false);
50
+ onChange(item);
51
+ };
52
+ const handleShowDropdownMenu = () => !disabled && setShowDropdownMenu(true);
53
+
54
+ // ########
55
+ // Watchers
56
+ useEffect(fetchOptions, []);
57
+ useEffect(() => {
58
+ if (searchable && options) {
59
+ setItems(options.filter(option => getOptionLabel(option).toLowerCase().includes(searchValue.toLowerCase())));
60
+ }
61
+
62
+ if (searchable && !options && loadOptions) {
63
+ const timeOutId = setTimeout(fetchOptions, 500);
64
+ return () => clearTimeout(timeOutId);
65
+ }
66
+
67
+ return () => {};
68
+ }, [searchValue]);
69
+
70
+ // #########
71
+ // Rendering
72
+ return (
73
+ <div className="ts-dropdown-container" ref={ref}>
74
+ <div className="ts-dropdown-input-container">
75
+ {label && <div className="ts-dropdown-label">{label}</div>}
76
+ <div
77
+ className={`ts-dropdown-input ${disabled ? 'ts-dropdown-input--disabled' : ''}`}
78
+ onClick={handleShowDropdownMenu}
79
+ >
80
+ {!selectedItem && placeholder && <div className="ts-dropdown-input--placeholder">{placeholder}</div>}
81
+ {selectedItem && <div className="ts-dropdown-input--value">{getOptionLabel(selectedItem)}</div>}
82
+ <div className="ts-dropdown-input--chevron">
83
+ <TsIcon name="chevron-down" />
84
+ </div>
85
+ </div>
86
+ </div>
87
+
88
+ {!disabled && showDropdownMenu && (
89
+ <div className="ts-dropdown-menu-container">
90
+ {searchable && (
91
+ <div className="ts-dropdown-search">
92
+ <TsInput
93
+ icon="search"
94
+ placeholder={searchPlaceholder}
95
+ value={searchValue}
96
+ autoFocus
97
+ onChange={setSearchValue}
98
+ />
99
+ </div>
100
+ )}
101
+
102
+ <div className="ts-dropdown-menu">
103
+ {loading && (
104
+ <div className="ts-dropdown-menu-loader">
105
+ <TsLoader size="sm" />
106
+ </div>
107
+ )}
108
+ {!loading && isFetchError && (
109
+ <div className="ts-dropdown-menu-error-text">An error occured, try again later.</div>
110
+ )}
111
+ {!loading && !isFetchError && items.length === 0 && (
112
+ <div className="ts-dropdown-menu-error-text">No options found.</div>
113
+ )}
114
+ {!loading &&
115
+ !isFetchError &&
116
+ items.length > 0 &&
117
+ items.map(item => (
118
+ <div
119
+ key={getOptionValue(item)}
120
+ className="ts-dropdown-menu-item"
121
+ onClick={() => handleSelectItem(item)}
122
+ >
123
+ {capitalize(getOptionLabel(item))}
124
+ </div>
125
+ ))}
126
+ </div>
127
+ </div>
128
+ )}
129
+ </div>
130
+ );
131
+ };
@@ -0,0 +1,13 @@
1
+ export type TsDropdownProps<T> = {
2
+ label?: string;
3
+ options?: T[];
4
+ placeholder?: string;
5
+ searchPlaceholder?: string;
6
+ value?: T;
7
+ disabled?: boolean;
8
+ searchable?: boolean;
9
+ getOptionLabel: (option: T) => string;
10
+ getOptionValue: (option: T) => string;
11
+ loadOptions?: (search?: string) => Promise<T[]>;
12
+ onChange: (value?: T) => void;
13
+ };
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import { ArgsTable, Canvas, Meta, Story } from '@storybook/blocks';
3
+ import { TsDropdown } from '../TsDropdown';
4
+ import { fruits } from './fruits';
5
+
6
+ <Meta title="Components/Dropdown" />
7
+
8
+ export const dropdownArgTypes = {
9
+ label: {
10
+ control: 'text',
11
+ description: 'Label of the dropdown.',
12
+ },
13
+ placeholder: {
14
+ control: 'text',
15
+ description: 'Placeholder of the dropdown.',
16
+ },
17
+ searchPlaceholder: {
18
+ control: 'text',
19
+ description: 'Placeholder of the option search (only for searchable dropdowns).',
20
+ },
21
+ searchable: {
22
+ control: 'boolean',
23
+ description: 'Enable search on dropdown items.',
24
+ table: { defaultValue: { summary: 'false' } },
25
+ },
26
+ disabled: {
27
+ control: 'boolean',
28
+ description: 'Enable state of the dropdown.',
29
+ table: { defaultValue: { summary: 'false' } },
30
+ },
31
+ onChange: {
32
+ control: 'function',
33
+ description: 'The handler called when clicking on the dropdown.',
34
+ },
35
+ };
36
+
37
+ # Dropdown
38
+
39
+ Dropdowns show a list of options that can be used to select, filter or sort content.
40
+
41
+ ## Overview
42
+
43
+ <Canvas>
44
+ <Story
45
+ name="Overview"
46
+ args={{
47
+ label: 'Label',
48
+ options: fruits,
49
+ searchPlaceholder: '',
50
+ placeholder: 'Select an item',
51
+ disabled: false,
52
+ searchable: false,
53
+ getOptionLabel: function ol(fruit) {
54
+ return fruit.name;
55
+ },
56
+ getOptionValue: function ov(fruit) {
57
+ return fruit.id;
58
+ },
59
+ }}
60
+ argTypes={dropdownArgTypes}
61
+ >
62
+ {args => <TsDropdown {...args}>{args.children}</TsDropdown>}
63
+ </Story>
64
+ </Canvas>
65
+
66
+ ## Props
67
+
68
+ <ArgsTable story="Overview" of={TsDropdown} />
@@ -0,0 +1,47 @@
1
+ export const fruits = [
2
+ { name: 'Persimmon', id: 52 },
3
+ { name: 'Strawberry', id: 3 },
4
+ { name: 'Banana', id: 1 },
5
+ { name: 'Tomato', id: 5 },
6
+ { name: 'Pear', id: 4 },
7
+ { name: 'Durian', id: 60 },
8
+ { name: 'Blackberry', id: 64 },
9
+ { name: 'Lingonberry', id: 65 },
10
+ { name: 'Kiwi', id: 66 },
11
+ { name: 'Lychee', id: 67 },
12
+ { name: 'Pineapple', id: 10 },
13
+ { name: 'Fig', id: 68 },
14
+ { name: 'Gooseberry', id: 69 },
15
+ { name: 'Passionfruit', id: 70 },
16
+ { name: 'Plum', id: 71 },
17
+ { name: 'Orange', id: 2 },
18
+ { name: 'GreenApple', id: 72 },
19
+ { name: 'Raspberry', id: 23 },
20
+ { name: 'Watermelon', id: 25 },
21
+ { name: 'Lemon', id: 26 },
22
+ { name: 'Mango', id: 27 },
23
+ { name: 'Blueberry', id: 33 },
24
+ { name: 'Apple', id: 6 },
25
+ { name: 'Guava', id: 37 },
26
+ { name: 'Apricot', id: 35 },
27
+ { name: 'Papaya', id: 42 },
28
+ { name: 'Melon', id: 41 },
29
+ { name: 'Tangerine', id: 77 },
30
+ { name: 'Pitahaya', id: 78 },
31
+ { name: 'Lime', id: 44 },
32
+ { name: 'Pomegranate', id: 79 },
33
+ { name: 'Dragonfruit', id: 80 },
34
+ { name: 'Grape', id: 81 },
35
+ { name: 'Morus', id: 82 },
36
+ { name: 'Feijoa', id: 76 },
37
+ { name: 'Avocado', id: 84 },
38
+ { name: 'Kiwifruit', id: 85 },
39
+ { name: 'Cranberry', id: 87 },
40
+ { name: 'Cherry', id: 9 },
41
+ { name: 'Peach', id: 86 },
42
+ { name: 'Jackfruit', id: 94 },
43
+ { name: 'Horned Melon', id: 95 },
44
+ { name: 'Hazelnut', id: 96 },
45
+ { name: 'Pomelo', id: 98 },
46
+ { name: 'Mangosteen', id: 99 },
47
+ ];
@@ -12,6 +12,7 @@
12
12
  font-weight: 600;
13
13
  }
14
14
  input::placeholder {
15
+ color: #7a7a7a;
15
16
  font-weight: 400;
16
17
  }
17
18
  input:disabled {
@@ -19,6 +19,11 @@ export const inputArgTypes = {
19
19
  control: 'text',
20
20
  description: 'Placeholder of the input.',
21
21
  },
22
+ disabled: {
23
+ control: 'boolean',
24
+ description: 'Enable state of the input.',
25
+ table: { defaultValue: { summary: 'false' } },
26
+ },
22
27
  };
23
28
 
24
29
  # Input
@@ -30,7 +35,7 @@ Input allows the user to enter content and data when the expected user input is
30
35
  <Canvas>
31
36
  <Story
32
37
  name="Overview"
33
- args={{ label: '', placeholder: 'Enter some text', onChange: () => {} }}
38
+ args={{ label: 'Label', placeholder: 'Enter some text', disabled: false, onChange: () => {} }}
34
39
  argTypes={inputArgTypes}
35
40
  >
36
41
  {args => <TsInput {...args} />}
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './components/TsButton/TsButton';
2
2
  export * from './components/TsCheckbox/TsCheckbox';
3
+ export * from './components/TsDropdown/TsDropdown/TsDropdown';
3
4
  export * from './components/TsIcon/TsIcon';
4
5
  export * from './components/TsInput/TsInput/TsInput';
5
6
  export * from './components/TsLoader/TsLoader';