kelt-ui-kit-react 1.9.8 → 2.0.0

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.
@@ -12,6 +12,7 @@ type ToasterState = {
12
12
  severity?: "info" | "success" | "error" | "warning";
13
13
  duration?: number;
14
14
  children?: React.ReactNode;
15
+ close?: boolean;
15
16
  }) => void;
16
17
  hideToast: () => void;
17
18
  };
package/index.html CHANGED
@@ -5,8 +5,7 @@
5
5
  <meta charset="UTF-8" />
6
6
  <link rel="preconnect" href="https://fonts.googleapis.com">
7
7
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
8
- <link href="https://fonts.googleapis.com/css2?family=Raleway:ital,wght@0,100..900;1,100..900&display=swap"
9
- rel="stylesheet">
8
+ <link href="https://fonts.googleapis.com/css2?family=Lexend:wght@100..900&display=swap" rel="stylesheet">
10
9
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
11
10
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
12
11
  <title>Vite + React + TS</title>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kelt-ui-kit-react",
3
3
  "type": "module",
4
- "version": "1.9.8",
4
+ "version": "2.0.0",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -53,4 +53,4 @@
53
53
  "typescript": "^4.9.5",
54
54
  "vite-plugin-checker": "^0.8.0"
55
55
  }
56
- }
56
+ }
@@ -5,7 +5,7 @@ button {
5
5
  padding: 10px;
6
6
  border-radius: 5px;
7
7
  cursor: pointer;
8
- font-size: 16px;
8
+ font-size: 18px;
9
9
  font-weight: 500;
10
10
  transition: background-color 0.3s;
11
11
  display: flex;
@@ -14,7 +14,7 @@ button {
14
14
  position: relative;
15
15
  align-items: center;
16
16
  justify-content: center;
17
- font-family: "Raleway", sans-serif !important;
17
+ font-family: "Lexend", sans-serif;
18
18
  .badge {
19
19
  position: absolute;
20
20
  right: 10px;
package/src/card/card.css CHANGED
@@ -87,12 +87,12 @@
87
87
  }
88
88
 
89
89
  .card-title {
90
- font-size: 14px;
90
+ font-size: 16px;
91
91
  font-weight: 600;
92
92
  }
93
93
 
94
94
  .card-content {
95
- font-size: 14px;
95
+ font-size: 16px;
96
96
  font-weight: 400;
97
97
  display: none;
98
98
  }
@@ -54,7 +54,7 @@
54
54
  .data-table {
55
55
  width: 100%;
56
56
  border-spacing: 0;
57
- font-size: 14px;
57
+ font-size: 16px;
58
58
  color: #333;
59
59
 
60
60
  .select-cell {
@@ -84,7 +84,7 @@
84
84
  top: 0;
85
85
  z-index: 2;
86
86
  background-color: #fff;
87
- color: #bdbaba;
87
+ color: var(--tertiary-color);
88
88
  min-width: 100px;
89
89
  padding: .5rem;
90
90
  }
@@ -125,7 +125,7 @@
125
125
  border: 1px solid #dcdcdc;
126
126
  background-color: #f8f9fa;
127
127
  color: #333;
128
- font-size: 14px;
128
+ font-size: 16px;
129
129
  line-height: 1;
130
130
  cursor: pointer;
131
131
  transition: background-color 0.15s, border-color 0.15s;
@@ -77,7 +77,7 @@
77
77
  .expand-header-title-left {
78
78
  margin-right: 0.5rem;
79
79
  text-transform: uppercase;
80
- font-size: 14px;
80
+ font-size: 18px;
81
81
  font-weight: 500;
82
82
  }
83
83
  }
package/src/form/Form.tsx CHANGED
@@ -9,9 +9,35 @@ import { Button } from "../button/Button";
9
9
  import { Icon } from "../icon/Icon";
10
10
  import { Select } from "../select/Select"; // <--- ton composant
11
11
  import "./form.css";
12
- import { FormInterface, FormValuesInterface } from "./form.interface";
12
+ import {
13
+ FormInterface,
14
+ FormMode,
15
+ FormValuesInterface,
16
+ } from "./form.interface";
13
17
  import { Input } from "./input/Input";
18
+ import { InputPrice } from "./inputPrice/InputPrice";
14
19
  import { TextArea } from "./textArea/TextArea";
20
+ import { Col } from "../grid/col/Col";
21
+ import { Row } from "../grid/row/Row";
22
+ import { TypeInputEnum } from "./form.enum";
23
+
24
+ const euroFormatter = new Intl.NumberFormat("fr-FR", {
25
+ style: "currency",
26
+ currency: "EUR",
27
+ });
28
+
29
+ type FormFieldContainerProps = {
30
+ field: FormInterface;
31
+ gridEnabled: boolean;
32
+ children: React.ReactNode;
33
+ };
34
+
35
+ const FormFieldContainer = ({
36
+ field,
37
+ gridEnabled,
38
+ children,
39
+ }: FormFieldContainerProps) =>
40
+ gridEnabled ? <Col {...field.col}>{children}</Col> : <>{children}</>;
15
41
 
16
42
  export type FormProps<
17
43
  T extends { [key: string]: string | number | boolean | Date | string[] }
@@ -24,6 +50,7 @@ export type FormProps<
24
50
  children?: React.ReactNode;
25
51
  initialForm: FormInterface[];
26
52
  disabledSubmit?: boolean;
53
+ mode?: FormMode;
27
54
  onSubmit?: (values: FormValuesInterface<T>) => void;
28
55
  onChange?: (values: FormValuesInterface<T>) => void;
29
56
  };
@@ -40,6 +67,7 @@ export const DynamicForm = forwardRef(
40
67
  onChange,
41
68
  hideSubmit,
42
69
  disabledSubmit = false,
70
+ mode = "edit",
43
71
  className,
44
72
  }: FormProps<T>,
45
73
  ref: React.Ref<{
@@ -179,6 +207,52 @@ export const DynamicForm = forwardRef(
179
207
  setBtnDisabledSubmit,
180
208
  }));
181
209
 
210
+ const gridEnabled = initialForm.some((field) => field.col !== undefined);
211
+ const FieldsContainer = gridEnabled ? Row : React.Fragment;
212
+ const getReadValue = (field: FormInterface): React.ReactNode => {
213
+ const value = values[field.name];
214
+
215
+ if (field.formatReadValue) {
216
+ return field.formatReadValue(value ?? "");
217
+ }
218
+
219
+ if (field.type === TypeInputEnum.SELECT) {
220
+ const selectedValues = Array.isArray(value) ? value : [value];
221
+ const labels = selectedValues
222
+ .map(
223
+ (selectedValue) =>
224
+ field.options?.find((option) => option.value === selectedValue)
225
+ ?.label ?? selectedValue,
226
+ )
227
+ .filter((selectedValue) => selectedValue !== undefined);
228
+
229
+ return labels.join(", ") || "-";
230
+ }
231
+
232
+ if (field.type === TypeInputEnum.PRICE) {
233
+ const amount = Number(value);
234
+ return value === "" || value === undefined || Number.isNaN(amount)
235
+ ? "-"
236
+ : euroFormatter.format(amount);
237
+ }
238
+
239
+ if (typeof value === "boolean") {
240
+ return value ? "Oui" : "Non";
241
+ }
242
+
243
+ if (value instanceof Date) {
244
+ return value.toLocaleDateString();
245
+ }
246
+
247
+ if (Array.isArray(value)) {
248
+ return value.join(", ") || "-";
249
+ }
250
+
251
+ return value === undefined || value === null || value === ""
252
+ ? "-"
253
+ : String(value);
254
+ };
255
+
182
256
  return (
183
257
  <form
184
258
  autoComplete={autoComplete ?? "off"}
@@ -186,6 +260,7 @@ export const DynamicForm = forwardRef(
186
260
  className={className ?? ""}
187
261
  >
188
262
  <div className="form-content">
263
+ <FieldsContainer>
189
264
  {initialForm.map((v, key) => {
190
265
  const required = v.onRequired
191
266
  ? v.onRequired(v.value)
@@ -195,16 +270,26 @@ export const DynamicForm = forwardRef(
195
270
  : v.disabled ?? false;
196
271
 
197
272
  return (
198
- <div
199
- key={key}
200
- className={`d-flex flex-column ${
201
- v.className ?? ""
202
- } form-group form-group-${v.type}`}
203
- >
273
+ <FormFieldContainer key={key} field={v} gridEnabled={gridEnabled}>
274
+ <div
275
+ className={`d-flex flex-column ${
276
+ v.className ?? ""
277
+ } form-group form-group-${v.type}`}
278
+ >
204
279
  {v.label && <label>{v.label}</label>}
205
280
  {v.icon && <Icon classIcon={v.icon} />}
206
281
 
207
- {v.type === "select" ? (
282
+ {mode === "read" ? (
283
+ <div
284
+ className={`form-read-value ${
285
+ v.type === TypeInputEnum.TEXTAREA
286
+ ? "form-read-value--textarea"
287
+ : ""
288
+ }`}
289
+ >
290
+ {getReadValue(v)}
291
+ </div>
292
+ ) : v.type === "select" ? (
208
293
  <Select
209
294
  id={v.id ?? v.name}
210
295
  label={undefined}
@@ -236,6 +321,22 @@ export const DynamicForm = forwardRef(
236
321
  handleChangeTextArea(name, value, v)
237
322
  }
238
323
  />
324
+ ) : v.type === TypeInputEnum.PRICE ? (
325
+ <InputPrice
326
+ id={v.id ?? v.name}
327
+ ref={(el) => (inputRefs.current[v.name] = el)}
328
+ name={v.name}
329
+ placeholder={v.placeholder ?? ""}
330
+ autoComplete={v.autoComplete}
331
+ tabIndex={0}
332
+ disabled={disabled}
333
+ autoFocus={v.focus ?? false}
334
+ required={required}
335
+ value={String(values[v.name] ?? "")}
336
+ onChange={(value) =>
337
+ handleChangeTextArea(v.name, value, v)
338
+ }
339
+ />
239
340
  ) : (
240
341
  <Input
241
342
  id={v.id ?? v.name}
@@ -259,13 +360,15 @@ export const DynamicForm = forwardRef(
259
360
  onInvalid={v.onInvalid ? v.onInvalid : undefined}
260
361
  />
261
362
  )}
262
- </div>
363
+ </div>
364
+ </FormFieldContainer>
263
365
  );
264
366
  })}
265
367
  {children}
368
+ </FieldsContainer>
266
369
  </div>
267
370
  <div className="d-flex justify-content-end form-footer">
268
- {!hideSubmit && (
371
+ {mode === "edit" && !hideSubmit && (
269
372
  <Button
270
373
  id={`button-${id}`}
271
374
  disabled={btnDisabledSubmit}
@@ -18,12 +18,14 @@ export const FormView = (): JSX.Element => {
18
18
  name: "nom",
19
19
  type: TypeInputEnum.PASSWORD,
20
20
  required: true,
21
+ col: { xs: 12, md: 6 },
21
22
  },
22
23
  {
23
24
  value: 30,
24
25
  label: "Age",
25
26
  name: "age",
26
27
  type: TypeInputEnum.NUMBER,
28
+ col: { xs: 12, md: 6 },
27
29
  },
28
30
  {
29
31
  value: false,
@@ -55,5 +57,11 @@ export const FormView = (): JSX.Element => {
55
57
  const handleSubmit = (values: FormValuesInterface<FormData>) => {
56
58
  console.log("Form Submitted: ", values);
57
59
  };
58
- return <DynamicForm onSubmit={handleSubmit} initialForm={initialValues} />;
60
+ return (
61
+ <DynamicForm
62
+ mode="edit"
63
+ onSubmit={handleSubmit}
64
+ initialForm={initialValues}
65
+ />
66
+ );
59
67
  };
package/src/form/form.css CHANGED
@@ -59,4 +59,21 @@ form {
59
59
  button {
60
60
  margin-top: 1rem;
61
61
  }
62
+ .form-read-value {
63
+ width: 100%;
64
+ min-width: 250px;
65
+ min-height: calc(1em + 1rem + 2px);
66
+ box-sizing: border-box;
67
+ border: 1px solid transparent;
68
+ border-radius: 4px;
69
+ margin: 0.25rem 0;
70
+ padding: 0.5rem 1rem;
71
+ font-size: 0.8rem;
72
+ line-height: normal;
73
+ overflow-wrap: anywhere;
74
+ }
75
+ .form-read-value--textarea {
76
+ min-height: 3.25rem;
77
+ white-space: pre-wrap;
78
+ }
62
79
  }
@@ -10,4 +10,5 @@ export enum TypeInputEnum {
10
10
  DATE = "date",
11
11
  SELECT = "select",
12
12
  TEXTAREA = "textarea",
13
+ PRICE = "price",
13
14
  }
@@ -1,4 +1,7 @@
1
1
  import { TypeInputEnum } from "./form.enum";
2
+ import { GridBreakpoints } from "../grid/grid.interface";
3
+
4
+ export type FormMode = "read" | "edit";
2
5
 
3
6
  export type FormValuesInterface<
4
7
  T extends { [key: string]: string | number | boolean | Date | string[] }
@@ -25,6 +28,10 @@ export interface FormInterface {
25
28
  multiple?: boolean;
26
29
  options?: { value: string; label: string }[]; // <--- pour les <option>
27
30
  className?: string;
31
+ col?: GridBreakpoints;
32
+ formatReadValue?: (
33
+ value: string | number | boolean | Date | string[]
34
+ ) => React.ReactNode;
28
35
  autoComplete?: string;
29
36
  step?: string;
30
37
  onChange?: (
@@ -1,14 +1,12 @@
1
1
  import { ColStyled } from "./colStyled/ColStyled";
2
- type ColProps = {
3
- xs?: number;
4
- sm?: number;
5
- md?: number;
6
- lg?: number;
2
+ import { GridBreakpoints } from "../grid.interface";
3
+
4
+ type ColProps = GridBreakpoints & {
7
5
  children: React.ReactNode;
8
6
  };
9
- export const Col: React.FC<ColProps> = ({ xs, sm, md, lg, children }) => {
7
+ export const Col: React.FC<ColProps> = ({ xs, sm, md, lg, xl, children }) => {
10
8
  return (
11
- <ColStyled $xs={xs} $sm={sm} $md={md} $lg={lg}>
9
+ <ColStyled $xs={xs} $sm={sm} $md={md} $lg={lg} $xl={xl}>
12
10
  {children}
13
11
  </ColStyled>
14
12
  );
@@ -1,10 +1,13 @@
1
1
  import styled, { css } from "styled-components";
2
+ import { GridColumnSpan } from "../../grid.interface";
3
+
2
4
  const getColWidth = (size = 12) => `${(size / 12) * 100}%`;
3
5
  type ColStyledProps = {
4
- $xs?: number;
5
- $sm?: number;
6
- $md?: number;
7
- $lg?: number;
6
+ $xs?: GridColumnSpan;
7
+ $sm?: GridColumnSpan;
8
+ $md?: GridColumnSpan;
9
+ $lg?: GridColumnSpan;
10
+ $xl?: GridColumnSpan;
8
11
  };
9
12
  export const ColStyled = styled.div<ColStyledProps>`
10
13
  padding: 0 15px;
@@ -38,4 +41,13 @@ export const ColStyled = styled.div<ColStyledProps>`
38
41
  max-width: ${getColWidth($lg)};
39
42
  }
40
43
  `}
44
+
45
+ ${({ $xl }) =>
46
+ $xl &&
47
+ css`
48
+ @container (min-width: 1200px) {
49
+ flex: 0 0 ${getColWidth($xl)};
50
+ max-width: ${getColWidth($xl)};
51
+ }
52
+ `}
41
53
  `;
@@ -1,7 +1,13 @@
1
- export interface GridInterface {
2
- sm?: number;
3
- xs?: number;
4
- md?: number;
5
- lg?: number;
1
+ export type GridColumnSpan = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
2
+
3
+ export interface GridBreakpoints {
4
+ xs?: GridColumnSpan;
5
+ sm?: GridColumnSpan;
6
+ md?: GridColumnSpan;
7
+ lg?: GridColumnSpan;
8
+ xl?: GridColumnSpan;
9
+ }
10
+
11
+ export interface GridInterface extends GridBreakpoints {
6
12
  children: React.ReactNode;
7
13
  }
@@ -40,7 +40,7 @@ export const Header = ({
40
40
  className={`header-logo ${onClickLogo && "cursor-pointer"} `}
41
41
  onClick={onClickLogo}
42
42
  >
43
- {logo && <img src={logo} alt="logo" />}
43
+ {logo && logo}
44
44
  </span>
45
45
  </div>
46
46
  <div className="d-flex align-items-center">
@@ -1,5 +1,5 @@
1
1
  export interface HeaderInterface {
2
- logo?: string;
2
+ logo?: React.ReactNode;
3
3
  children?: React.ReactNode;
4
4
  positionFixed?: boolean;
5
5
  onClickMenu?: () => void;