kelt-ui-kit-react 1.4.2 → 1.4.5

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kelt-ui-kit-react",
3
3
  "type": "module",
4
- "version": "1.4.2",
4
+ "version": "1.4.5",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -21,7 +21,7 @@ button {
21
21
  top: -10px;
22
22
  }
23
23
  &:disabled {
24
- opacity: 0.5;
24
+ opacity: 0.7;
25
25
  cursor: not-allowed;
26
26
  }
27
27
  &.button--primary {
@@ -1,13 +1,16 @@
1
- import { useEffect, useRef } from "react";
2
- import { Button } from "../button/Button";
3
- import { Icon } from "../icon/Icon";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { TypeInputEnum } from "../form/form.enum";
3
+ import { Input } from "../form/input/Input";
4
4
  import { Loader } from "../loader/Loader";
5
- import { Select } from "../select/Select";
6
5
  import "./dataTable.css";
7
6
  import {
8
7
  dataTableActionsInterface,
9
8
  dataTableColumnsInterface,
10
9
  } from "./dataTable.interface";
10
+ import { DataTableAction } from "./dataTableActions/DataTableAction";
11
+ import { DataTableColumn } from "./dataTableColumn/DataTableColumn";
12
+ import { DataTableLoading } from "./dataTableLoading/DataTableLoading";
13
+ import { DataTableRow } from "./dataTableRow/DataTableRow";
11
14
  export type DataTableProps<T> = {
12
15
  id: string;
13
16
  name?: string;
@@ -23,6 +26,10 @@ export type DataTableProps<T> = {
23
26
  onColumnClick?: (column: any) => void;
24
27
  onLoadMore?: () => void;
25
28
  hasMore?: boolean; // pour savoir s'il reste des données à charger
29
+ onSelectionChange?: (selectedRows: T[]) => void;
30
+ hideToggleAll?: boolean;
31
+ limitSelect?: number;
32
+ getRowId?: (row: T) => string | number;
26
33
  };
27
34
  export function DataTable<T>(props: DataTableProps<T>) {
28
35
  const {
@@ -38,11 +45,74 @@ export function DataTable<T>(props: DataTableProps<T>) {
38
45
  onRowClick,
39
46
  onColumnClick,
40
47
  onLoadMore,
48
+ emptyMessage,
41
49
  hasMore,
50
+ onSelectionChange,
51
+ hideToggleAll = false,
52
+ limitSelect = undefined,
53
+ getRowId,
42
54
  } = props;
43
55
 
44
56
  const loaderRef = useRef<HTMLDivElement | null>(null);
57
+ const [selectedRowIds, setSelectedRowIds] = useState<Set<string | number>>(
58
+ new Set()
59
+ );
60
+ const toggleSelectAll = useCallback(() => {
61
+ if (selectedRowIds.size === data.length) {
62
+ setSelectedRowIds(new Set());
63
+ onSelectionChange?.([]);
64
+ } else {
65
+ let allIds = data.map((d) =>
66
+ getRowId ? getRowId(d) : (d as any).id ?? d
67
+ );
68
+
69
+ if (limitSelect && allIds.length > limitSelect) {
70
+ allIds = allIds.slice(0, limitSelect);
71
+ }
45
72
 
73
+ const newSelection = new Set(allIds);
74
+ setSelectedRowIds(newSelection);
75
+ onSelectionChange?.(
76
+ data.filter((d) =>
77
+ newSelection.has(getRowId ? getRowId(d) : (d as any).id ?? d)
78
+ )
79
+ );
80
+ }
81
+ }, [data, onSelectionChange, limitSelect, getRowId, selectedRowIds.size]);
82
+
83
+ const handleClickRow = useCallback(
84
+ (e: React.MouseEvent, row: T) => {
85
+ e.stopPropagation();
86
+ if (onRowClick) {
87
+ onRowClick?.(row);
88
+ }
89
+ },
90
+ [onRowClick]
91
+ );
92
+ const toggleRow = useCallback(
93
+ (e: React.ChangeEvent<HTMLInputElement>, row: T) => {
94
+ e.stopPropagation();
95
+ const rowId = getRowId ? getRowId(row) : (row as any).id ?? row;
96
+ const newSelection = new Set(selectedRowIds);
97
+
98
+ if (newSelection.has(rowId)) {
99
+ newSelection.delete(rowId);
100
+ } else {
101
+ if (limitSelect && newSelection.size >= limitSelect) {
102
+ // 🚫 limite atteinte
103
+ return;
104
+ }
105
+ newSelection.add(rowId);
106
+ }
107
+
108
+ setSelectedRowIds(newSelection);
109
+ const selectedItems = data.filter((d) =>
110
+ newSelection.has(getRowId ? getRowId(d) : (d as any).id ?? d)
111
+ );
112
+ onSelectionChange?.(selectedItems);
113
+ },
114
+ [data, getRowId, limitSelect, onSelectionChange, selectedRowIds]
115
+ );
46
116
  useEffect(() => {
47
117
  if (!onLoadMore || !hasMore) return;
48
118
 
@@ -76,21 +146,29 @@ export function DataTable<T>(props: DataTableProps<T>) {
76
146
  <table className={`data-table ${className ? className : ""}`} id={id}>
77
147
  <thead>
78
148
  <tr>
149
+ {onSelectionChange && (
150
+ <th
151
+ className={`select-cell ${hideToggleAll ? "hidden" : ""}`}
152
+ style={{ width: "20px", textAlign: "center" }}
153
+ >
154
+ <Input
155
+ id={`select-th-${id}`}
156
+ name={`select-th-${id}`}
157
+ type={TypeInputEnum.CHECKBOX}
158
+ checked={
159
+ data.length > 0 && selectedRowIds.size === data.length
160
+ }
161
+ onChange={toggleSelectAll}
162
+ />
163
+ </th>
164
+ )}
79
165
  {columns.map((column, idx) => (
80
- <>
81
- <th
82
- key={`colonne-${column.id}-${idx}`}
83
- style={{
84
- minWidth: column.minWidth,
85
- maxWidth: column.maxWidth,
86
- width: column.width,
87
- textAlign: column.align || "left",
88
- }}
89
- onClick={() => onColumnClick?.(column)}
90
- >
91
- {column.label}
92
- </th>
93
- </>
166
+ <DataTableColumn
167
+ key={idx}
168
+ column={column}
169
+ idx={idx}
170
+ onColumnClick={onColumnClick}
171
+ />
94
172
  ))}
95
173
  {actions.length > 0 && (
96
174
  <th
@@ -120,113 +198,52 @@ export function DataTable<T>(props: DataTableProps<T>) {
120
198
  )}
121
199
  {!loading &&
122
200
  data.map((row, rowIndex) => (
123
- <tr key={rowIndex} onClick={() => onRowClick?.(row)}>
124
- {columns.map((column, idx) => {
125
- const value = (row as any)[column.id]; // On utilise [column.id] pour accéder aux bonnes propriétés
126
- const style = column.onRowStyle ? column.onRowStyle(row) : {};
127
- return (
128
- <>
129
- <td
130
- style={{
131
- minWidth: column.minWidth,
132
- textAlign: column.align || "left",
133
- maxWidth: column.maxWidth,
134
- width: column.width,
135
- ...style,
136
- }}
137
- key={`row-${column.id}-${idx}`}
138
- >
139
- {column.editable ? (
140
- column.type === "select" ? (
141
- <Select
142
- options={column.options ?? []}
143
- value={value}
144
- onChange={(e) => {
145
- if (column.onEdit) {
146
- column.onEdit(e, row);
147
- }
148
- }}
149
- />
150
- ) : (
151
- <input
152
- type={column.type}
153
- value={value}
154
- step="0.01"
155
- style={{ textAlign: column.align || "left" }}
156
- onChange={(e) => {
157
- if (column.onEdit) {
158
- column.onEdit(e.target.value, row);
159
- }
160
- }}
161
- />
162
- )
163
- ) : column.format ? (
164
- column.format(value)
165
- ) : (
166
- value
167
- )}
168
- </td>
169
- </>
170
- );
171
- })}
201
+ <tr key={rowIndex} onClick={(e) => handleClickRow?.(e, row)}>
202
+ {onSelectionChange && (
203
+ <td className="select-cell" style={{ textAlign: "center" }}>
204
+ <Input
205
+ id={`select-row-${rowIndex}`}
206
+ name={`select-row-${rowIndex}`}
207
+ type={TypeInputEnum.CHECKBOX}
208
+ disabled={
209
+ limitSelect !== undefined &&
210
+ selectedRowIds.size >= limitSelect &&
211
+ !selectedRowIds.has(
212
+ getRowId ? getRowId(row) : (row as any).id ?? row
213
+ )
214
+ }
215
+ checked={selectedRowIds.has(
216
+ getRowId ? getRowId(row) : (row as any).id ?? row
217
+ )}
218
+ onChange={(e) => toggleRow(e, row)} // empêche le clic de déclencher onRowClick
219
+ />
220
+ </td>
221
+ )}
222
+ {columns.map((column, idx) => (
223
+ <DataTableRow column={column} idx={idx} row={row} key={idx} />
224
+ ))}
172
225
  {actions.length > 0 && (
173
226
  <td align="right">
174
227
  <div className="d-flex align-items-center justify-content-end">
175
- {actions.map((action, index) => {
176
- let title = action.label;
177
- const display = action.shouldDisplay
178
- ? action.shouldDisplay(row)
179
- : true;
180
- if (!display) return null;
181
- if (action.labelButton) {
182
- title = action.labelButton
183
- ? action.labelButton(row)
184
- : action.label;
185
- }
186
- return (
187
- <>
188
- {action.type === "button" && (
189
- <Button
190
- title={title}
191
- key={`button-action-${action.id}-${index}`}
192
- onClick={() => action.onClick(row)}
193
- disabled={action.disabled}
194
- className={`data-table-action button--tertiary mr-2 w-100 ${
195
- action.classIcon ? action.classIcon : ""
196
- }`}
197
- />
198
- )}
199
- {action.type === "icon" && (
200
- <div
201
- key={`icon-action-${action.id}-${index}`}
202
- className={`mr-2 `}
203
- onClick={() => action.onClick(row)}
204
- >
205
- <Icon
206
- classIcon={action.classIcon ?? ""}
207
- key={action.id}
208
- />
209
- </div>
210
- )}
211
- </>
212
- );
213
- })}
228
+ {actions.map((action, index) => (
229
+ <DataTableAction
230
+ key={index}
231
+ action={action}
232
+ row={row}
233
+ index={index}
234
+ />
235
+ ))}
214
236
  </div>
215
237
  </td>
216
238
  )}
217
239
  </tr>
218
240
  ))}
219
241
  {!loading && data.length === 0 && (
220
- <tr>
221
- <td
222
- align="center"
223
- colSpan={columns.length + (actions.length > 0 ? 1 : 0)}
224
- >
225
- <div className="mt-3">
226
- {props.emptyMessage || "Aucune donnée disponible"}
227
- </div>
228
- </td>
229
- </tr>
242
+ <DataTableLoading
243
+ columns={columns}
244
+ actions={actions}
245
+ emptyMessage={emptyMessage}
246
+ />
230
247
  )}
231
248
  </tbody>
232
249
  </table>
@@ -54,6 +54,9 @@ export const DataTableView = () => {
54
54
  columns={dataTableColumns}
55
55
  id="datatable-example"
56
56
  name={"DataTable"}
57
+ onSelectionChange={(selectedRows) => {
58
+ console.log("Selected Rows: ", selectedRows);
59
+ }}
57
60
  />
58
61
  );
59
62
  };
@@ -5,7 +5,17 @@
5
5
  border-spacing: 5px;
6
6
  font-size: 14px;
7
7
  color: #333;
8
-
8
+ .select-cell {
9
+ min-width: 40px !important;
10
+ .input-container {
11
+ min-width: 20px;
12
+ input {
13
+ min-width: 20px;
14
+ width: 20px;
15
+ cursor: pointer;
16
+ }
17
+ }
18
+ }
9
19
  thead {
10
20
  margin-bottom: 1rem;
11
21
  position: sticky;
@@ -0,0 +1,44 @@
1
+ import { Button } from "../../button/Button";
2
+ import { Icon } from "../../icon/Icon";
3
+ import { dataTableActionsInterface } from "../dataTable.interface";
4
+ type DataTableActionProps = {
5
+ action: dataTableActionsInterface<any>;
6
+ row: any;
7
+ index?: number;
8
+ };
9
+ export const DataTableAction = ({
10
+ action,
11
+ row,
12
+ index,
13
+ }: DataTableActionProps) => {
14
+ let title = action.label;
15
+ const display = action.shouldDisplay ? action.shouldDisplay(row) : true;
16
+ if (!display) return null;
17
+ if (action.labelButton) {
18
+ title = action.labelButton ? action.labelButton(row) : action.label;
19
+ }
20
+ return (
21
+ <>
22
+ {action.type === "button" && (
23
+ <Button
24
+ title={title}
25
+ key={`button-action-${action.id}-${index}`}
26
+ onClick={() => action.onClick(row)}
27
+ disabled={action.disabled}
28
+ className={`data-table-action button--tertiary mr-2 w-100 ${
29
+ action.classIcon ? action.classIcon : ""
30
+ }`}
31
+ />
32
+ )}
33
+ {action.type === "icon" && (
34
+ <div
35
+ key={`icon-action-${action.id}-${index}`}
36
+ className={`mr-2 `}
37
+ onClick={() => action.onClick(row)}
38
+ >
39
+ <Icon classIcon={action.classIcon ?? ""} key={action.id} />
40
+ </div>
41
+ )}
42
+ </>
43
+ );
44
+ };
@@ -0,0 +1,27 @@
1
+ import { dataTableColumnsInterface } from "../dataTable.interface";
2
+
3
+ type DataTableColumnProps = {
4
+ column: dataTableColumnsInterface<any>;
5
+ idx: number;
6
+ onColumnClick?: (column: dataTableColumnsInterface<any>) => void;
7
+ };
8
+ export const DataTableColumn = ({
9
+ column,
10
+ idx,
11
+ onColumnClick,
12
+ }: DataTableColumnProps) => {
13
+ return (
14
+ <th
15
+ key={`colonne-${column.id}-${idx}`}
16
+ style={{
17
+ minWidth: column.minWidth,
18
+ maxWidth: column.maxWidth,
19
+ width: column.width,
20
+ textAlign: column.align || "left",
21
+ }}
22
+ onClick={() => onColumnClick?.(column)}
23
+ >
24
+ {column.label}
25
+ </th>
26
+ );
27
+ };
@@ -0,0 +1,21 @@
1
+ type DataTableLoadingProps = {
2
+ columns: any[];
3
+ actions: any[];
4
+ emptyMessage?: string;
5
+ };
6
+ export const DataTableLoading = ({
7
+ columns,
8
+ actions,
9
+ emptyMessage,
10
+ }: DataTableLoadingProps) => {
11
+ return (
12
+ <tr>
13
+ <td
14
+ align="center"
15
+ colSpan={columns.length + (actions.length > 0 ? 1 : 0)}
16
+ >
17
+ <div className="mt-3">{emptyMessage || "Aucune donnée disponible"}</div>
18
+ </td>
19
+ </tr>
20
+ );
21
+ };
@@ -0,0 +1,54 @@
1
+ import { Select } from "../../select/Select";
2
+ import { dataTableColumnsInterface } from "../dataTable.interface";
3
+
4
+ type DataTableRowProps = {
5
+ column: dataTableColumnsInterface<any>;
6
+ idx: number;
7
+ row: any;
8
+ };
9
+ export const DataTableRow = ({ column, idx, row }: DataTableRowProps) => {
10
+ const value = (row as any)[column.id]; // On utilise [column.id] pour accéder aux bonnes propriétés
11
+ const style = column.onRowStyle ? column.onRowStyle(row) : {};
12
+ return (
13
+ <td
14
+ style={{
15
+ minWidth: column.minWidth,
16
+ textAlign: column.align || "left",
17
+ maxWidth: column.maxWidth,
18
+ width: column.width,
19
+ ...style,
20
+ }}
21
+ key={`row-${column.id}-${idx}`}
22
+ >
23
+ {column.editable ? (
24
+ column.type === "select" ? (
25
+ <Select
26
+ options={column.options ?? []}
27
+ value={value}
28
+ onChange={(e) => {
29
+ if (column.onEdit) {
30
+ column.onEdit(e, row);
31
+ }
32
+ }}
33
+ />
34
+ ) : (
35
+ <input
36
+ type={column.type}
37
+ value={value}
38
+ step="0.01"
39
+ style={{ textAlign: column.align || "left" }}
40
+ onChange={(e) => {
41
+ if (column.onEdit) {
42
+ column.onEdit(e.target.value, row);
43
+ }
44
+ }}
45
+ />
46
+ )
47
+ ) : column.format ? (
48
+ column.format(value)
49
+ ) : (
50
+ value
51
+ )}
52
+ </td>
53
+ );
54
+ };
package/src/form/form.css CHANGED
@@ -8,6 +8,14 @@ input {
8
8
  outline: 1px solid #9696a7;
9
9
  }
10
10
  }
11
+ input[type="checkbox"],
12
+ input[type="radio"] {
13
+ min-width: auto;
14
+ padding: 0;
15
+ margin: 0;
16
+ height: 18px;
17
+ width: 18px;
18
+ }
11
19
  form {
12
20
  &.form-row {
13
21
  display: flex;
@@ -23,6 +23,7 @@ export interface InputProps {
23
23
  checked?: boolean;
24
24
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
25
25
  onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
26
+ onClick?: (e: React.MouseEvent<HTMLInputElement>) => void;
26
27
  }
27
28
 
28
29
  export const Input = forwardRef<HTMLInputElement, InputProps>(
@@ -47,6 +48,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
47
48
  checked,
48
49
  onChange,
49
50
  onBlur,
51
+ onClick,
50
52
  },
51
53
  ref
52
54
  ) => {
@@ -54,7 +56,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
54
56
  const [showPassword, setShowPassword] = useState(false);
55
57
 
56
58
  return (
57
- <div className="input-container">
59
+ <div className={`input-container ${`input-container-${type}`} `}>
58
60
  <input name={`hide-${name}`} type="text" style={{ display: "none" }} />
59
61
 
60
62
  <input
@@ -77,6 +79,10 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
77
79
  onChange={onChange}
78
80
  checked={checked}
79
81
  onBlur={onBlur}
82
+ onClick={(e) => {
83
+ e.stopPropagation();
84
+ onClick && onClick(e);
85
+ }}
80
86
  className={`input-field ${className ?? ""}`}
81
87
  onInvalid={onInvalid}
82
88
  />
@@ -2,6 +2,15 @@
2
2
  position: relative;
3
3
  min-width: 250px;
4
4
  width: 100%;
5
+ &.input-container-radio,
6
+ &.input-container-checkbox {
7
+ min-width: 10px;
8
+ width: 10px;
9
+ input {
10
+ width: 10px;
11
+ min-width: 10px;
12
+ }
13
+ }
5
14
  .input-password-icon {
6
15
  position: absolute;
7
16
  right: 10px;
@@ -12,5 +21,6 @@
12
21
  }
13
22
  input {
14
23
  width: 100%;
24
+ font-size: 0.8rem;
15
25
  }
16
26
  }
@@ -54,6 +54,7 @@
54
54
  border-radius: 0.25rem;
55
55
  color: #000;
56
56
  cursor: pointer;
57
+ font-size: 0.8rem;
57
58
  &:disabled {
58
59
  background-color: #e9ecef;
59
60
  color: #6c757d;
@@ -0,0 +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=