kelt-ui-kit-react 1.8.2 → 1.8.3

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 (245) hide show
  1. package/.github/workflows/publish.yml +31 -0
  2. package/dist/_core/type/generiqueType.d.ts +4 -0
  3. package/dist/badge/Badge.d.ts +3 -1
  4. package/dist/button/button.interface.d.ts +1 -1
  5. package/dist/dataTable/DataTable.d.ts +17 -3
  6. package/dist/dataTable/dataTable.interface.d.ts +21 -3
  7. package/dist/dataTable/dataTableActions/DataTableAction.d.ts +2 -2
  8. package/dist/dataTable/dataTableColumn/DataTableColumn.d.ts +13 -7
  9. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.d.ts +10 -0
  10. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.d.ts +9 -0
  11. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.d.ts +9 -0
  12. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.d.ts +13 -0
  13. package/dist/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.d.ts +9 -0
  14. package/dist/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.d.ts +11 -0
  15. package/dist/dataTable/dataTableFilters/DataTableFilters.d.ts +7 -0
  16. package/dist/dataTable/dataTableLoading/DataTableLoading.d.ts +1 -3
  17. package/dist/dataTable/dataTableRow/DataTableRow.d.ts +5 -3
  18. package/dist/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.d.ts +12 -0
  19. package/dist/dataTable/dataTableRow/dataTableRowValue/DataTableRowValue.d.ts +11 -0
  20. package/dist/expands/expand/expand.d.ts +1 -1
  21. package/dist/form/form.enum.d.ts +1 -0
  22. package/dist/form/input/Input.d.ts +3 -0
  23. package/dist/form/inputNumber/InputNumber.d.ts +0 -2
  24. package/dist/icon/Icon.d.ts +1 -1
  25. package/dist/icon/icon.interface.d.ts +1 -0
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +3652 -2836
  28. package/dist/overlayPanel/OverlayPanel.d.ts +3 -1
  29. package/dist/select/Select.d.ts +4 -1
  30. package/dist/select/selectOption.interface.d.ts +1 -0
  31. package/dist/style.css +1 -1
  32. package/dist/tabs/Tabs.d.ts +2 -1
  33. package/kelt-ui-kit-react-1.6.9.tgz +0 -0
  34. package/package/README.md +46 -0
  35. package/package/index.html +20 -0
  36. package/package/package.json +56 -0
  37. package/package/public/favicon.ico +0 -0
  38. package/package/public/index.html +18 -0
  39. package/package/public/logo192.png +0 -0
  40. package/package/public/logo512.png +0 -0
  41. package/package/public/manifest.json +25 -0
  42. package/package/public/robots.txt +3 -0
  43. package/package/src/App.css +11 -0
  44. package/package/src/App.menu.tsx +249 -0
  45. package/package/src/App.routes.tsx +16 -0
  46. package/package/src/App.tsx +30 -0
  47. package/package/src/_core/hooks/HistoryProvider.tsx +25 -0
  48. package/package/src/_core/hooks/ImageChecker.tsx +26 -0
  49. package/package/src/_core/hooks/imageExist.tsx +9 -0
  50. package/package/src/_core/hooks/isEAN13.tsx +16 -0
  51. package/package/src/_core/hooks/objectToQueryParams.tsx +32 -0
  52. package/package/src/_core/hooks/useCurrency.tsx +18 -0
  53. package/package/src/_core/hooks/useDateutils.ts +64 -0
  54. package/package/src/_core/hooks/useIsMobile.ts +18 -0
  55. package/package/src/_core/hooks/useToken.tsx +19 -0
  56. package/package/src/_core/hooks/useValidate.tsx +18 -0
  57. package/package/src/_core/hooks/widgetSupport.tsx +39 -0
  58. package/package/src/action/Action.view.tsx +21 -0
  59. package/package/src/action/ButtonAction.tsx +32 -0
  60. package/package/src/action/action.css +20 -0
  61. package/package/src/badge/Badge.tsx +34 -0
  62. package/package/src/badge/Badge.view.tsx +15 -0
  63. package/package/src/badge/badge.css +55 -0
  64. package/package/src/button/Button.tsx +46 -0
  65. package/package/src/button/Button.view.tsx +85 -0
  66. package/package/src/button/button.css +53 -0
  67. package/package/src/button/button.interface.tsx +21 -0
  68. package/package/src/button/buttonActions/ButtonActions.tsx +104 -0
  69. package/package/src/button/buttonFile/ButtonFile.tsx +59 -0
  70. package/package/src/button/buttonFile/ButtonFile.view.tsx +70 -0
  71. package/package/src/button/buttonFilters/ButtonFilters.tsx +102 -0
  72. package/package/src/button/buttonFilters/buttonFilters.css +22 -0
  73. package/package/src/card/Card.tsx +125 -0
  74. package/package/src/card/Card.view.tsx +73 -0
  75. package/package/src/card/card.css +145 -0
  76. package/package/src/card/card.interface.tsx +9 -0
  77. package/package/src/card/cardAction/CardAction.tsx +135 -0
  78. package/package/src/card/cardAction/cardAction.css +10 -0
  79. package/package/src/card/cardAction.interface.tsx +10 -0
  80. package/package/src/card/hook/useCardInteractions.tsx +30 -0
  81. package/package/src/carousel/Carousel.css +44 -0
  82. package/package/src/carousel/Carousel.tsx +115 -0
  83. package/package/src/carousel/Carousel.view.tsx +13 -0
  84. package/package/src/damier/Damier.tsx +64 -0
  85. package/package/src/damier/Damier.view.tsx +31 -0
  86. package/package/src/damier/damier.css +50 -0
  87. package/package/src/damier/damierCell/DamierCell.tsx +18 -0
  88. package/package/src/damier/damierCell/damierCell.interface.tsx +5 -0
  89. package/package/src/dataTable/DataTable.tsx +259 -0
  90. package/package/src/dataTable/DataTable.view.tsx +62 -0
  91. package/package/src/dataTable/dataTable.css +30 -0
  92. package/package/src/dataTable/dataTable.interface.ts +30 -0
  93. package/package/src/dataTable/dataTableActions/DataTableAction.tsx +44 -0
  94. package/package/src/dataTable/dataTableColumn/DataTableColumn.tsx +27 -0
  95. package/package/src/dataTable/dataTableLoading/DataTableLoading.tsx +21 -0
  96. package/package/src/dataTable/dataTableRow/DataTableRow.tsx +54 -0
  97. package/package/src/datePicker/DatePicker.tsx +110 -0
  98. package/package/src/datePicker/DatePicker.view.tsx +9 -0
  99. package/package/src/datePicker/datePicker.css +77 -0
  100. package/package/src/expands/Expands.tsx +42 -0
  101. package/package/src/expands/Expands.view.tsx +90 -0
  102. package/package/src/expands/expand/expand.interface.tsx +8 -0
  103. package/package/src/expands/expand/expand.tsx +75 -0
  104. package/package/src/expands/expands.css +97 -0
  105. package/package/src/filAriane/FilAriane.tsx +57 -0
  106. package/package/src/filAriane/FilAriane.view.tsx +28 -0
  107. package/package/src/filAriane/filAriane.css +23 -0
  108. package/package/src/filAriane/filAriane.interface.tsx +6 -0
  109. package/package/src/form/Form.tsx +279 -0
  110. package/package/src/form/Form.view.tsx +59 -0
  111. package/package/src/form/form.css +62 -0
  112. package/package/src/form/form.enum.ts +13 -0
  113. package/package/src/form/form.interface.tsx +37 -0
  114. package/package/src/form/input/Input.tsx +106 -0
  115. package/package/src/form/input/input.css +26 -0
  116. package/package/src/form/inputNumber/InputNumber.tsx +132 -0
  117. package/package/src/form/inputNumber/InputNumber.view.tsx +15 -0
  118. package/package/src/form/inputPrice/InputPrice.tsx +42 -0
  119. package/package/src/form/inputPrice/InputPrice.view.tsx +15 -0
  120. package/package/src/form/textArea/TextArea.tsx +59 -0
  121. package/package/src/form/textArea/TextArea.view.tsx +34 -0
  122. package/package/src/form/textArea/textArea.css +9 -0
  123. package/package/src/grid/Grid.tsx +21 -0
  124. package/package/src/grid/Grid.view.tsx +24 -0
  125. package/package/src/grid/col/Col.tsx +15 -0
  126. package/package/src/grid/col/colStyled/ColStyled.tsx +41 -0
  127. package/package/src/grid/container/Container.tsx +8 -0
  128. package/package/src/grid/container/container.css +5 -0
  129. package/package/src/grid/grid.interface.tsx +7 -0
  130. package/package/src/grid/row/Row.tsx +12 -0
  131. package/package/src/grid/row/row.css +18 -0
  132. package/package/src/header/Header.tsx +62 -0
  133. package/package/src/header/Header.view.tsx +5 -0
  134. package/package/src/header/header.css +26 -0
  135. package/package/src/header/header.interface.tsx +10 -0
  136. package/package/src/home/Home.tsx +3 -0
  137. package/package/src/icon/Icon.tsx +6 -0
  138. package/package/src/icon/Icons.view.tsx +29 -0
  139. package/package/src/icon/icon.css +20 -0
  140. package/package/src/icon/icon.interface.tsx +6 -0
  141. package/package/src/icon/iconSize.enum.ts +7 -0
  142. package/package/src/index.css +502 -0
  143. package/package/src/index.ts +58 -0
  144. package/package/src/loader/Loader.tsx +37 -0
  145. package/package/src/loader/Loader.view.tsx +20 -0
  146. package/package/src/loader/loader.css +30 -0
  147. package/package/src/logo.svg +1 -0
  148. package/package/src/main.tsx +10 -0
  149. package/package/src/menus/Menus.tsx +42 -0
  150. package/package/src/menus/Menus.view.tsx +39 -0
  151. package/package/src/menus/menu/Menu.tsx +17 -0
  152. package/package/src/menus/menu/menu.interface.tsx +9 -0
  153. package/package/src/menus/menus.css +47 -0
  154. package/package/src/message/Message.tsx +30 -0
  155. package/package/src/message/Message.view.tsx +22 -0
  156. package/package/src/message/message.css +29 -0
  157. package/package/src/modal/Modal.tsx +59 -0
  158. package/package/src/modal/Modal.view.tsx +26 -0
  159. package/package/src/modal/modal.css +71 -0
  160. package/package/src/notFound/NotFound.tsx +3 -0
  161. package/package/src/overlayPanel/OverlayPanel.tsx +195 -0
  162. package/package/src/overlayPanel/OverlayPanel.view.tsx +25 -0
  163. package/package/src/overlayPanel/overlay.context.tsx +28 -0
  164. package/package/src/overlayPanel/overlayPanel.css +35 -0
  165. package/package/src/overlayPanel/overlayPanelStyled/OverlayPanelStyled.tsx +18 -0
  166. package/package/src/pdfViewer/PdfViewer.tsx +73 -0
  167. package/package/src/pdfViewer/PdfViewer.view.tsx +5 -0
  168. package/package/src/quantity/Quantity.tsx +103 -0
  169. package/package/src/quantity/Quantity.view.tsx +0 -0
  170. package/package/src/quantity/quantity.css +26 -0
  171. package/package/src/search/Search.tsx +161 -0
  172. package/package/src/search/Search.view.tsx +14 -0
  173. package/package/src/search/search.css +59 -0
  174. package/package/src/select/Select.tsx +103 -0
  175. package/package/src/select/Select.view.tsx +71 -0
  176. package/package/src/select/select.css +69 -0
  177. package/package/src/select/selectOption.interface.ts +4 -0
  178. package/package/src/sidebar/Sidebar.tsx +208 -0
  179. package/package/src/sidebar/Sidebar.view.tsx +17 -0
  180. package/package/src/sidebar/sidebar.css +104 -0
  181. package/package/src/sidebarData/SidebarData.tsx +19 -0
  182. package/package/src/sidebarData/SidebarData.view.tsx +19 -0
  183. package/package/src/sidebarData/sidebarData.css +27 -0
  184. package/package/src/tabs/Tabs.tsx +90 -0
  185. package/package/src/tabs/Tabs.view.tsx +20 -0
  186. package/package/src/tabs/tabs.css +24 -0
  187. package/package/src/toaster/Toaster.tsx +55 -0
  188. package/package/src/toaster/Toaster.view.tsx +24 -0
  189. package/package/src/toaster/store/useToasterStore.tsx +41 -0
  190. package/package/src/toaster/toaster.css +87 -0
  191. package/package/tsconfig.json +28 -0
  192. package/package/vite.config.ts +20 -0
  193. package/package/vite.config.ts.timestamp-1733262892554-a13dfef6e8a29.mjs +24 -0
  194. package/package/vite.config.ts.timestamp-1757847121745-e5a5bf54b81b.mjs +28 -0
  195. package/package/vite.config.ts.timestamp-1759742478362-4ac5c5a7db34b.mjs +28 -0
  196. package/package.json +1 -1
  197. package/src/_core/type/generiqueType.tsx +4 -0
  198. package/src/badge/Badge.tsx +9 -1
  199. package/src/badge/badge.css +4 -0
  200. package/src/button/Button.tsx +1 -1
  201. package/src/button/button.interface.tsx +1 -1
  202. package/src/card/card.css +23 -3
  203. package/src/card/cardAction/CardAction.tsx +4 -4
  204. package/src/dataTable/DataTable.tsx +670 -45
  205. package/src/dataTable/DataTable.view.tsx +47 -14
  206. package/src/dataTable/dataTable.css +153 -5
  207. package/src/dataTable/dataTable.interface.ts +21 -3
  208. package/src/dataTable/dataTableActions/DataTableAction.tsx +2 -2
  209. package/src/dataTable/dataTableColumn/DataTableColumn.tsx +64 -14
  210. package/src/dataTable/dataTableColumn/dataTableColumnFilter/DataTableColumnFilter.tsx +36 -0
  211. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilter.css +0 -0
  212. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDate/DataTableColumnFilterDate.tsx +39 -0
  213. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterDateRange/dataTableColumnFilterDateRange.tsx +80 -0
  214. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterEnum/DataTableColumnFilterEnum.tsx +89 -0
  215. package/src/dataTable/dataTableColumn/dataTableColumnFilter/dataTableColumnFilterString/DataTableColumnFilterString.tsx +37 -0
  216. package/src/dataTable/dataTableColumn/dataTableColumnSort/DataTableColumnSort.tsx +42 -0
  217. package/src/dataTable/dataTableFilters/DataTableFilters.tsx +91 -0
  218. package/src/dataTable/dataTableFilters/dataTableFilters.css +16 -0
  219. package/src/dataTable/dataTableLoading/DataTableLoading.tsx +4 -15
  220. package/src/dataTable/dataTableRow/DataTableRow.tsx +60 -33
  221. package/src/dataTable/dataTableRow/dataTableRowOverlay/DataTableRowOverlay.tsx +80 -0
  222. package/src/dataTable/dataTableRow/dataTableRowValue/DataTableRowValue.tsx +123 -0
  223. package/src/expands/Expands.tsx +2 -16
  224. package/src/expands/expand/expand.tsx +6 -18
  225. package/src/form/Form.tsx +13 -12
  226. package/src/form/form.css +13 -2
  227. package/src/form/form.enum.ts +1 -0
  228. package/src/form/input/Input.tsx +61 -7
  229. package/src/form/input/input.css +23 -1
  230. package/src/form/inputNumber/InputNumber.tsx +6 -11
  231. package/src/form/inputPrice/InputPrice.tsx +2 -4
  232. package/src/icon/Icon.tsx +6 -2
  233. package/src/icon/icon.interface.tsx +1 -0
  234. package/src/index.ts +2 -0
  235. package/src/overlayPanel/OverlayPanel.tsx +23 -10
  236. package/src/overlayPanel/overlay.context.tsx +6 -4
  237. package/src/overlayPanel/overlayPanel.css +10 -1
  238. package/src/select/Select.tsx +134 -38
  239. package/src/select/select.css +65 -14
  240. package/src/select/selectOption.interface.ts +1 -0
  241. package/src/sidebar/Sidebar.tsx +9 -80
  242. package/src/sidebar/sidebar.css +1 -18
  243. package/src/tabs/Tabs.tsx +19 -1
  244. package/vite.config.ts.timestamp-1757847121745-e5a5bf54b81b.mjs +28 -28
  245. package/vite.config.ts.timestamp-1759742478362-4ac5c5a7db34b.mjs +28 -28
@@ -1,5 +1,7 @@
1
- import { useCallback, useRef } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { Icon } from "../icon/Icon";
3
+
4
+ import { OverlayPanel } from "../overlayPanel/OverlayPanel";
3
5
  import "./select.css";
4
6
  import { SelectOptionInterface } from "./selectOption.interface";
5
7
 
@@ -11,6 +13,8 @@ export interface SelectProps {
11
13
  name?: string;
12
14
  defaultValue?: string | string[];
13
15
  multiple?: boolean;
16
+ showOptions?: boolean;
17
+ usePortal?: boolean;
14
18
  required?: boolean;
15
19
  value?: string | string[];
16
20
  className?: string;
@@ -18,6 +22,7 @@ export interface SelectProps {
18
22
  onChangeMultiple?: (value: string[]) => void;
19
23
  onChange?: (value: string) => void;
20
24
  placeholder?: string;
25
+ classNameOverlay?: string;
21
26
  }
22
27
 
23
28
  export const Select = ({
@@ -27,38 +32,92 @@ export const Select = ({
27
32
  className,
28
33
  disabled,
29
34
  multiple = false,
30
- required = false,
35
+ showOptions = false,
31
36
  defaultValue,
32
- name,
37
+ usePortal = false,
33
38
  onChange,
34
39
  onChangeMultiple,
35
40
  value,
36
41
  placeholder,
42
+ classNameOverlay,
37
43
  }: SelectProps): JSX.Element => {
38
- const inputRefs = useRef<{ [key: string]: HTMLSelectElement | null }>({});
44
+ const [showOverlay, setShowOverlay] = useState(false);
45
+ const [overlayRef, setOverlayRef] = useState<HTMLElement | null>(null);
46
+ const triggerRef = useRef<HTMLButtonElement | null>(null);
47
+ const hasAutoOpened = useRef(false);
48
+ const optionsListRef = useRef<HTMLDivElement | null>(null);
39
49
 
40
- const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
41
- let selectedValue: string | string[];
42
- if (multiple) {
43
- selectedValue = Array.from(
44
- event.target.selectedOptions,
45
- (opt) => opt.value
50
+ useEffect(() => {
51
+ if (!showOverlay) return;
52
+ const timer = setTimeout(() => {
53
+ const container = optionsListRef.current;
54
+ if (!container) return;
55
+ const selected = container.querySelector<HTMLElement>(
56
+ ".select-overlay-option.selected",
46
57
  );
58
+ selected?.scrollIntoView({ block: "nearest" });
59
+ }, 0);
60
+ return () => clearTimeout(timer);
61
+ }, [showOverlay]);
62
+ const selectedValues = Array.isArray(value)
63
+ ? value
64
+ : value
65
+ ? [value]
66
+ : defaultValue
67
+ ? Array.isArray(defaultValue)
68
+ ? defaultValue
69
+ : [defaultValue]
70
+ : [];
71
+
72
+ const getDisplayValue = () => {
73
+ if (selectedValues.length === 0) return placeholder || "";
74
+ if (multiple) {
75
+ const labels = selectedValues
76
+ .map((v) => options.find((opt) => opt.value === v)?.label)
77
+ .filter((l): l is string => !!l);
78
+ return labels.join(", ");
79
+ }
80
+ const selected = options.find((opt) => opt.value === selectedValues[0]);
81
+ return selected?.label || "";
82
+ };
83
+
84
+ const handleOptionClick = (optionValue: string) => {
85
+ if (multiple) {
86
+ const newValues = selectedValues.includes(optionValue)
87
+ ? selectedValues.filter((v) => v !== optionValue)
88
+ : [...selectedValues, optionValue];
47
89
  if (onChangeMultiple) {
48
- onChangeMultiple(selectedValue as string[]);
90
+ onChangeMultiple(newValues);
49
91
  }
50
92
  } else {
51
- selectedValue = event.target.value;
52
93
  if (onChange) {
53
- onChange(selectedValue);
94
+ onChange(optionValue);
54
95
  }
96
+ setShowOverlay(false);
55
97
  }
56
98
  };
99
+
57
100
  const resetValue = useCallback(() => {
58
- if (onChange) {
101
+ if (multiple && onChangeMultiple) {
102
+ onChangeMultiple([]);
103
+ } else if (onChange) {
59
104
  onChange("");
60
105
  }
61
- }, [onChange]);
106
+ }, [onChange, onChangeMultiple, multiple]);
107
+
108
+ useEffect(() => {
109
+ if (
110
+ showOptions &&
111
+ !disabled &&
112
+ !hasAutoOpened.current &&
113
+ triggerRef.current
114
+ ) {
115
+ setOverlayRef(triggerRef.current);
116
+ setShowOverlay(true);
117
+ hasAutoOpened.current = true;
118
+ }
119
+ }, [showOptions, disabled]);
120
+
62
121
  return (
63
122
  <div id={id} className={`select-container ${className ? className : ""}`}>
64
123
  {label && (
@@ -66,36 +125,73 @@ export const Select = ({
66
125
  {label}
67
126
  </label>
68
127
  )}
69
- <select
70
- defaultValue=""
71
- required={required}
72
- name={name}
73
- disabled={disabled}
128
+ <button
129
+ ref={triggerRef}
74
130
  id={`select-${id}`}
75
- className="select-container-select"
76
- value={value ?? defaultValue ?? (multiple ? [] : "")}
77
- onChange={handleChange}
78
- multiple={multiple}
79
- ref={(el) => (inputRefs.current[id ?? "select"] = el)}
131
+ disabled={disabled}
132
+ className="select-container-button"
133
+ onClick={(e) => {
134
+ setOverlayRef(e.currentTarget as HTMLElement);
135
+ setShowOverlay(true);
136
+ }}
137
+ type="button"
138
+ aria-valuetext={getDisplayValue() || placeholder}
139
+ aria-haspopup="listbox"
140
+ aria-expanded={showOverlay}
80
141
  >
81
- {placeholder && !defaultValue && !multiple && (
82
- <option selected disabled hidden value="">
83
- {placeholder}
84
- </option>
85
- )}
86
- {options.map((option, index) => (
87
- <option key={index} value={option.value}>
88
- {option.label}
89
- </option>
90
- ))}
91
- </select>
92
- {value !== "" && placeholder && (
142
+ <span className="select-container-value">
143
+ {getDisplayValue() || (
144
+ <span className="placeholder">{placeholder}</span>
145
+ )}
146
+ </span>
147
+ </button>
148
+
149
+ {showOverlay && overlayRef && (
150
+ <OverlayPanel
151
+ show={showOverlay}
152
+ position="bottomLeft"
153
+ className={classNameOverlay ?? ""}
154
+ closeOverlay={() => setShowOverlay(false)}
155
+ referenceElement={overlayRef}
156
+ usePortal={usePortal}
157
+ children={
158
+ <div className="select-overlay-options" ref={optionsListRef}>
159
+ {options.map((option) => (
160
+ <div
161
+ key={option.value}
162
+ aria-valuetext={option.label}
163
+ className={`select-overlay-option ${option.disabled ? "disabled" : ""} ${
164
+ selectedValues.includes(option.value) ? "selected" : ""
165
+ }`}
166
+ onClick={() =>
167
+ !option.disabled && handleOptionClick(option.value)
168
+ }
169
+ role="option"
170
+ aria-selected={selectedValues.includes(option.value)}
171
+ >
172
+ {multiple && (
173
+ <input
174
+ type="checkbox"
175
+ checked={selectedValues.includes(option.value)}
176
+ readOnly
177
+ className="select-overlay-checkbox"
178
+ />
179
+ )}
180
+ <span>{option.label}</span>
181
+ </div>
182
+ ))}
183
+ </div>
184
+ }
185
+ />
186
+ )}
187
+
188
+ {selectedValues.length > 0 && placeholder && (
93
189
  <div
94
190
  className="clear-btn"
95
191
  onClick={resetValue}
96
192
  aria-label="Réinitialiser"
97
193
  >
98
- <Icon classIcon="bi-x" />
194
+ <Icon classIcon="bi-x-circle" />
99
195
  </div>
100
196
  )}
101
197
  </div>
@@ -1,5 +1,6 @@
1
1
  .select-container {
2
2
  position: relative;
3
+
3
4
  &::after,
4
5
  &::before {
5
6
  --size: 0.3rem;
@@ -8,23 +9,19 @@
8
9
  right: 1rem;
9
10
  pointer-events: none;
10
11
  }
11
- select:has(option[value=""]:checked) {
12
- color: gray;
13
- }
14
- option {
15
- color: #000;
16
- }
12
+
17
13
  .clear-btn {
18
14
  position: absolute;
19
- top: 2px;
15
+ top: 50%;
16
+ transform: translateY(-50%);
20
17
  display: flex;
21
18
  align-items: center;
22
19
  z-index: 2;
23
20
  justify-content: center;
24
- bottom: 0;
25
- right: 30px;
21
+ right: 10px;
26
22
  cursor: pointer;
27
23
  }
24
+
28
25
  &::before {
29
26
  border-left: var(--size) solid transparent;
30
27
  border-right: var(--size) solid transparent;
@@ -38,15 +35,18 @@
38
35
  border-top: var(--size) solid black;
39
36
  top: 55%;
40
37
  }
38
+
41
39
  .select-container-label {
42
40
  font-weight: bold;
43
41
  color: #333;
44
42
  margin-bottom: 2px;
43
+ display: block;
45
44
  }
46
- .select-container-select {
45
+
46
+ .select-container-button {
47
47
  appearance: none;
48
48
  width: 100%;
49
- font-size: 1rem;
49
+ font-size: 0.8rem;
50
50
  padding: 0.5rem 1rem;
51
51
  margin: 0.25rem 0;
52
52
  background-color: #fff;
@@ -54,16 +54,67 @@
54
54
  border-radius: 0.25rem;
55
55
  color: #000;
56
56
  cursor: pointer;
57
- font-size: 0.8rem;
57
+ text-align: left;
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: space-between;
61
+ padding-right: 2rem;
62
+
63
+ .select-container-value {
64
+ flex: 1;
65
+
66
+ .placeholder {
67
+ color: gray;
68
+ }
69
+ }
70
+
58
71
  &:disabled {
59
72
  background-color: #e9ecef;
60
73
  color: #6c757d;
61
74
  cursor: not-allowed;
62
75
  }
76
+
63
77
  &:focus {
64
- border-color: #007bff;
65
78
  outline: none;
66
79
  background-color: #fff;
67
80
  }
81
+
82
+
83
+ }
84
+
85
+ .select-overlay-options {
86
+ display: flex;
87
+ flex-direction: column;
88
+ max-height: 300px;
89
+ overflow-y: auto;
90
+ padding: 0.5rem 0;
91
+ background-color: #fff;
92
+ border: 1px solid #b2a1a1;
93
+ border-radius: 0.25rem;
94
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
95
+
96
+ .select-overlay-option {
97
+ padding: 0.5rem 1rem;
98
+ cursor: pointer;
99
+ display: flex;
100
+ align-items: center;
101
+ gap: 0.5rem;
102
+ user-select: none;
103
+
104
+ .select-overlay-checkbox {
105
+ min-width: 20px;
106
+ width: 20px;
107
+ cursor: pointer;
108
+ }
109
+
110
+ &:hover {
111
+ background-color: #f0f0f0;
112
+ }
113
+
114
+ &.selected {
115
+ background-color: #e7f3ff;
116
+ color: #007bff;
117
+ }
118
+ }
68
119
  }
69
- }
120
+ }
@@ -1,4 +1,5 @@
1
1
  export interface SelectOptionInterface {
2
2
  value: string;
3
3
  label: string;
4
+ disabled?: boolean;
4
5
  }
@@ -15,9 +15,6 @@ export interface SidebarProps {
15
15
  onClose?: (close: boolean) => void;
16
16
  }
17
17
 
18
- const SWIPE_CLOSE_THRESHOLD = 60; // px à parcourir pour fermer
19
- const SWIPE_AXIS_LOCK_THRESHOLD = 10; // px pour déterminer l'axe du geste
20
-
21
18
  export const Sidebar = ({
22
19
  open,
23
20
  children,
@@ -30,13 +27,10 @@ export const Sidebar = ({
30
27
  displayOverlay = true,
31
28
  }: SidebarProps): JSX.Element => {
32
29
  const [isOpen, setIsOpen] = useState(open || false);
33
- const [dragX, setDragX] = useState(0);
34
- const [isDragging, setIsDragging] = useState(false);
35
30
  const sidebarRef = useRef<HTMLDivElement | null>(null);
36
31
  const rootRef = useRef<HTMLDivElement | null>(null);
37
32
  const touchStartX = useRef(0);
38
33
  const touchStartY = useRef(0);
39
- const axisLocked = useRef<"x" | "y" | null>(null);
40
34
 
41
35
  // Créer le conteneur une seule fois
42
36
  useEffect(() => {
@@ -68,7 +62,7 @@ export const Sidebar = ({
68
62
  }, [onClose]);
69
63
 
70
64
  const handleClickOutside = useCallback(
71
- (event: MouseEvent | TouchEvent) => {
65
+ (event: MouseEvent) => {
72
66
  if (!closeOnClickOutside) return;
73
67
  if (
74
68
  sidebarRef.current &&
@@ -84,87 +78,33 @@ export const Sidebar = ({
84
78
  const touch = event.touches[0];
85
79
  touchStartX.current = touch.clientX;
86
80
  touchStartY.current = touch.clientY;
87
- axisLocked.current = null;
88
- setIsDragging(false);
89
- setDragX(0);
90
81
  }, []);
91
82
 
92
- const handleTouchMove = useCallback(
83
+ const handleTouchEnd = useCallback(
93
84
  (event: React.TouchEvent) => {
94
85
  if (!isOpen) return;
95
86
 
96
- const touch = event.touches[0];
87
+ const touch = event.changedTouches[0];
97
88
  const deltaX = touch.clientX - touchStartX.current;
98
- const deltaY = touch.clientY - touchStartY.current;
99
-
100
- // Détermine l'axe du geste une seule fois, dès qu'on a un déplacement suffisant
101
- if (!axisLocked.current) {
102
- if (
103
- Math.abs(deltaX) < SWIPE_AXIS_LOCK_THRESHOLD &&
104
- Math.abs(deltaY) < SWIPE_AXIS_LOCK_THRESHOLD
105
- ) {
106
- return; // pas encore assez de mouvement pour trancher
107
- }
108
- axisLocked.current = Math.abs(deltaX) > Math.abs(deltaY) ? "x" : "y";
109
- }
110
-
111
- if (axisLocked.current !== "x") return; // c'est un scroll vertical, on laisse faire
112
-
113
- // On gère le swipe horizontal nous-mêmes : on empêche le scroll natif
114
- // et on bloque le bounce/scroll de la page pendant le drag
115
- event.preventDefault();
89
+ const deltaY = Math.abs(touch.clientY - touchStartY.current);
116
90
 
117
- setIsDragging(true);
118
- // On ne permet que le swipe vers la droite (fermeture), pas de drag négatif
119
- setDragX(Math.max(0, deltaX));
120
- },
121
- [isOpen],
122
- );
123
-
124
- const handleTouchEnd = useCallback(
125
- (event: React.TouchEvent) => {
126
- if (!isOpen) {
127
- return;
128
- }
129
-
130
- if (axisLocked.current === "x") {
131
- const touch = event.changedTouches[0];
132
- const deltaX = touch.clientX - touchStartX.current;
133
-
134
- if (deltaX > SWIPE_CLOSE_THRESHOLD) {
135
- toggleSidebar();
136
- }
91
+ // Ferme seulement sur un vrai swipe horizontal gauche -> droite.
92
+ if (deltaX > 60 && deltaY < 40) {
93
+ toggleSidebar();
137
94
  }
138
-
139
- setIsDragging(false);
140
- setDragX(0);
141
- axisLocked.current = null;
142
95
  },
143
96
  [isOpen, toggleSidebar],
144
97
  );
145
98
 
146
- const handleOverlayTouchEnd = useCallback(
147
- (event: React.TouchEvent) => {
148
- event.preventDefault();
149
- toggleSidebar();
150
- },
151
- [toggleSidebar],
152
- );
153
-
154
99
  useEffect(() => {
155
100
  if (isOpen) {
156
101
  document.addEventListener("mousedown", handleClickOutside);
157
- document.addEventListener("touchstart", handleClickOutside, {
158
- passive: true,
159
- });
160
102
  } else {
161
103
  document.removeEventListener("mousedown", handleClickOutside);
162
- document.removeEventListener("touchstart", handleClickOutside);
163
104
  }
164
105
 
165
106
  return () => {
166
107
  document.removeEventListener("mousedown", handleClickOutside);
167
- document.removeEventListener("touchstart", handleClickOutside);
168
108
  };
169
109
  }, [isOpen, handleClickOutside]);
170
110
 
@@ -174,23 +114,12 @@ export const Sidebar = ({
174
114
 
175
115
  return ReactDOM.createPortal(
176
116
  <>
177
- {isOpen && displayOverlay && (
178
- <div
179
- className="overlay"
180
- onClick={toggleSidebar}
181
- onTouchEnd={handleOverlayTouchEnd}
182
- ></div>
183
- )}
117
+ {isOpen && displayOverlay && <div className="overlay"></div>}
184
118
  <div
185
119
  ref={sidebarRef}
186
- style={{
187
- ...(width ? { width } : {}),
188
- transform: dragX ? `translateX(${dragX}px)` : undefined,
189
- transition: isDragging ? "none" : undefined,
190
- }}
120
+ style={width ? { width: width } : {}}
191
121
  className={`sidebar ${isOpen ? "open" : "closed"} ${className ?? ""}`}
192
122
  onTouchStart={handleTouchStart}
193
- onTouchMove={handleTouchMove}
194
123
  onTouchEnd={handleTouchEnd}
195
124
  >
196
125
  <div className="sidebar-header">
@@ -1,18 +1,3 @@
1
- .sidebar {
2
- touch-action: pan-y;
3
- /* laisse le scroll vertical natif, gère le X nous-même */
4
- will-change: transform;
5
- }
6
-
7
- .overlay {
8
- touch-action: none;
9
- }
10
-
11
- .toggle-btn {
12
- -webkit-tap-highlight-color: transparent;
13
- touch-action: manipulation;
14
- }
15
-
16
1
  .sidebar {
17
2
  width: 200px;
18
3
  height: 100vh;
@@ -25,8 +10,6 @@
25
10
  transform: translateX(0);
26
11
  z-index: 2;
27
12
 
28
-
29
-
30
13
  &:not(.full) {
31
14
  inset: 0;
32
15
  }
@@ -101,4 +84,4 @@
101
84
  width: 40vw;
102
85
  }
103
86
  }
104
- }
87
+ }
package/src/tabs/Tabs.tsx CHANGED
@@ -15,6 +15,7 @@ export type TabsProps = {
15
15
  className?: string;
16
16
  tabClassName?: string;
17
17
  panelClassName?: string;
18
+ keepMountedVisited?: boolean;
18
19
  };
19
20
 
20
21
  export const Tabs = ({
@@ -25,9 +26,13 @@ export const Tabs = ({
25
26
  className = "",
26
27
  tabClassName = "",
27
28
  panelClassName = "",
29
+ keepMountedVisited = false,
28
30
  }: TabsProps) => {
29
31
  const isControlled = typeof index === "number";
30
32
  const [active, setActive] = useState<number>(defaultIndex);
33
+ const [visitedTabs, setVisitedTabs] = useState<Set<number>>(
34
+ () => new Set([defaultIndex]),
35
+ );
31
36
  const current = isControlled ? (index as number) : active;
32
37
 
33
38
  useEffect(() => {
@@ -37,6 +42,18 @@ export const Tabs = ({
37
42
  }
38
43
  }, [defaultIndex, items.length, isControlled]);
39
44
 
45
+ useEffect(() => {
46
+ setVisitedTabs((prev) => {
47
+ if (prev.has(current)) {
48
+ return prev;
49
+ }
50
+
51
+ const next = new Set(prev);
52
+ next.add(current);
53
+ return next;
54
+ });
55
+ }, [current]);
56
+
40
57
  const tabsRef = useRef<Array<HTMLButtonElement | null>>([]);
41
58
 
42
59
  const activate = (next: number) => {
@@ -82,7 +99,8 @@ export const Tabs = ({
82
99
  hidden={current !== i}
83
100
  className={`${panelClassName} mt-3`}
84
101
  >
85
- {current === i && t.panel}
102
+ {(current === i || (keepMountedVisited && visitedTabs.has(i))) &&
103
+ t.panel}
86
104
  </div>
87
105
  ))}
88
106
  </div>
@@ -1,28 +1,28 @@
1
- // vite.config.ts
2
- import { defineConfig } from "file:///C:/Users/laulautte/travail/kelt/ui-kit-react/node_modules/vite/dist/node/index.js";
3
- import checker from "file:///C:/Users/laulautte/travail/kelt/ui-kit-react/node_modules/vite-plugin-checker/dist/esm/main.js";
4
- import dts from "file:///C:/Users/laulautte/travail/kelt/ui-kit-react/node_modules/vite-plugin-dts/dist/index.mjs";
5
- var vite_config_default = defineConfig({
6
- plugins: [dts(), checker({ typescript: true })],
7
- server: {
8
- port: 2552
9
- // Remplace 3000 par le port souhaité
10
- },
11
- build: {
12
- lib: {
13
- entry: "./src/index.ts",
14
- // Remplacer par l'entrée correcte
15
- formats: ["es"],
16
- // Génère un module ES
17
- name: "kelt-ui-kit-react",
18
- fileName: "index"
19
- },
20
- rollupOptions: {
21
- external: ["react", "react-dom"]
22
- }
23
- }
24
- });
25
- export {
26
- vite_config_default as default
27
- };
28
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJDOlxcXFxVc2Vyc1xcXFxsYXVsYXV0dGVcXFxcdHJhdmFpbFxcXFxrZWx0XFxcXHVpLWtpdC1yZWFjdFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiQzpcXFxcVXNlcnNcXFxcbGF1bGF1dHRlXFxcXHRyYXZhaWxcXFxca2VsdFxcXFx1aS1raXQtcmVhY3RcXFxcdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL0M6L1VzZXJzL2xhdWxhdXR0ZS90cmF2YWlsL2tlbHQvdWkta2l0LXJlYWN0L3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSBcInZpdGVcIjtcclxuaW1wb3J0IGNoZWNrZXIgZnJvbSBcInZpdGUtcGx1Z2luLWNoZWNrZXJcIjtcclxuaW1wb3J0IGR0cyBmcm9tIFwidml0ZS1wbHVnaW4tZHRzXCI7XHJcbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XHJcbiAgcGx1Z2luczogW2R0cygpLCBjaGVja2VyKHsgdHlwZXNjcmlwdDogdHJ1ZSB9KV0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBwb3J0OiAyNTUyLCAvLyBSZW1wbGFjZSAzMDAwIHBhciBsZSBwb3J0IHNvdWhhaXRcdTAwRTlcclxuICB9LFxyXG4gIGJ1aWxkOiB7XHJcbiAgICBsaWI6IHtcclxuICAgICAgZW50cnk6IFwiLi9zcmMvaW5kZXgudHNcIiwgLy8gUmVtcGxhY2VyIHBhciBsJ2VudHJcdTAwRTllIGNvcnJlY3RlXHJcbiAgICAgIGZvcm1hdHM6IFtcImVzXCJdLCAvLyBHXHUwMEU5blx1MDBFOHJlIHVuIG1vZHVsZSBFU1xyXG4gICAgICBuYW1lOiBcImtlbHQtdWkta2l0LXJlYWN0XCIsXHJcbiAgICAgIGZpbGVOYW1lOiBcImluZGV4XCIsXHJcbiAgICB9LFxyXG4gICAgcm9sbHVwT3B0aW9uczoge1xyXG4gICAgICBleHRlcm5hbDogW1wicmVhY3RcIiwgXCJyZWFjdC1kb21cIl0sXHJcbiAgICB9LFxyXG4gIH0sXHJcbn0pO1xyXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQWtVLFNBQVMsb0JBQW9CO0FBQy9WLE9BQU8sYUFBYTtBQUNwQixPQUFPLFNBQVM7QUFDaEIsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUyxDQUFDLElBQUksR0FBRyxRQUFRLEVBQUUsWUFBWSxLQUFLLENBQUMsQ0FBQztBQUFBLEVBQzlDLFFBQVE7QUFBQSxJQUNOLE1BQU07QUFBQTtBQUFBLEVBQ1I7QUFBQSxFQUNBLE9BQU87QUFBQSxJQUNMLEtBQUs7QUFBQSxNQUNILE9BQU87QUFBQTtBQUFBLE1BQ1AsU0FBUyxDQUFDLElBQUk7QUFBQTtBQUFBLE1BQ2QsTUFBTTtBQUFBLE1BQ04sVUFBVTtBQUFBLElBQ1o7QUFBQSxJQUNBLGVBQWU7QUFBQSxNQUNiLFVBQVUsQ0FBQyxTQUFTLFdBQVc7QUFBQSxJQUNqQztBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=
1
+ // vite.config.ts
2
+ import { defineConfig } from "file:///C:/Users/laulautte/travail/kelt/ui-kit-react/node_modules/vite/dist/node/index.js";
3
+ import checker from "file:///C:/Users/laulautte/travail/kelt/ui-kit-react/node_modules/vite-plugin-checker/dist/esm/main.js";
4
+ import dts from "file:///C:/Users/laulautte/travail/kelt/ui-kit-react/node_modules/vite-plugin-dts/dist/index.mjs";
5
+ var vite_config_default = defineConfig({
6
+ plugins: [dts(), checker({ typescript: true })],
7
+ server: {
8
+ port: 2552
9
+ // Remplace 3000 par le port souhaité
10
+ },
11
+ build: {
12
+ lib: {
13
+ entry: "./src/index.ts",
14
+ // Remplacer par l'entrée correcte
15
+ formats: ["es"],
16
+ // Génère un module ES
17
+ name: "kelt-ui-kit-react",
18
+ fileName: "index"
19
+ },
20
+ rollupOptions: {
21
+ external: ["react", "react-dom"]
22
+ }
23
+ }
24
+ });
25
+ export {
26
+ vite_config_default as default
27
+ };
28
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJDOlxcXFxVc2Vyc1xcXFxsYXVsYXV0dGVcXFxcdHJhdmFpbFxcXFxrZWx0XFxcXHVpLWtpdC1yZWFjdFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiQzpcXFxcVXNlcnNcXFxcbGF1bGF1dHRlXFxcXHRyYXZhaWxcXFxca2VsdFxcXFx1aS1raXQtcmVhY3RcXFxcdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL0M6L1VzZXJzL2xhdWxhdXR0ZS90cmF2YWlsL2tlbHQvdWkta2l0LXJlYWN0L3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSBcInZpdGVcIjtcclxuaW1wb3J0IGNoZWNrZXIgZnJvbSBcInZpdGUtcGx1Z2luLWNoZWNrZXJcIjtcclxuaW1wb3J0IGR0cyBmcm9tIFwidml0ZS1wbHVnaW4tZHRzXCI7XHJcbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XHJcbiAgcGx1Z2luczogW2R0cygpLCBjaGVja2VyKHsgdHlwZXNjcmlwdDogdHJ1ZSB9KV0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBwb3J0OiAyNTUyLCAvLyBSZW1wbGFjZSAzMDAwIHBhciBsZSBwb3J0IHNvdWhhaXRcdTAwRTlcclxuICB9LFxyXG4gIGJ1aWxkOiB7XHJcbiAgICBsaWI6IHtcclxuICAgICAgZW50cnk6IFwiLi9zcmMvaW5kZXgudHNcIiwgLy8gUmVtcGxhY2VyIHBhciBsJ2VudHJcdTAwRTllIGNvcnJlY3RlXHJcbiAgICAgIGZvcm1hdHM6IFtcImVzXCJdLCAvLyBHXHUwMEU5blx1MDBFOHJlIHVuIG1vZHVsZSBFU1xyXG4gICAgICBuYW1lOiBcImtlbHQtdWkta2l0LXJlYWN0XCIsXHJcbiAgICAgIGZpbGVOYW1lOiBcImluZGV4XCIsXHJcbiAgICB9LFxyXG4gICAgcm9sbHVwT3B0aW9uczoge1xyXG4gICAgICBleHRlcm5hbDogW1wicmVhY3RcIiwgXCJyZWFjdC1kb21cIl0sXHJcbiAgICB9LFxyXG4gIH0sXHJcbn0pO1xyXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQWtVLFNBQVMsb0JBQW9CO0FBQy9WLE9BQU8sYUFBYTtBQUNwQixPQUFPLFNBQVM7QUFDaEIsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUyxDQUFDLElBQUksR0FBRyxRQUFRLEVBQUUsWUFBWSxLQUFLLENBQUMsQ0FBQztBQUFBLEVBQzlDLFFBQVE7QUFBQSxJQUNOLE1BQU07QUFBQTtBQUFBLEVBQ1I7QUFBQSxFQUNBLE9BQU87QUFBQSxJQUNMLEtBQUs7QUFBQSxNQUNILE9BQU87QUFBQTtBQUFBLE1BQ1AsU0FBUyxDQUFDLElBQUk7QUFBQTtBQUFBLE1BQ2QsTUFBTTtBQUFBLE1BQ04sVUFBVTtBQUFBLElBQ1o7QUFBQSxJQUNBLGVBQWU7QUFBQSxNQUNiLFVBQVUsQ0FBQyxTQUFTLFdBQVc7QUFBQSxJQUNqQztBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=