kelt-ui-kit-react 2.0.2 → 2.0.4
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/dist/card/Card.d.ts +4 -1
- package/dist/expands/Expands.d.ts +1 -0
- package/dist/expands/expand/expand.d.ts +2 -1
- package/dist/form/Form.d.ts +6 -0
- package/dist/form/form.interface.d.ts +5 -3
- package/dist/grid/Grid.d.ts +5 -2
- package/dist/grid/col/colStyled/ColStyled.d.ts +2 -1
- package/dist/grid/grid.interface.d.ts +12 -1
- package/dist/grid/row/Row.d.ts +7 -0
- package/dist/index.js +3043 -2884
- package/dist/overlayPanel/OverlayPanel.d.ts +4 -2
- package/dist/overlayPanel/overlayPanelStyled/OverlayPanelStyled.d.ts +2 -0
- package/dist/select/Select.d.ts +6 -1
- package/dist/select/selectOption.interface.d.ts +1 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/card/Card.tsx +37 -11
- package/src/card/card.css +2 -3
- package/src/expands/Expands.tsx +2 -0
- package/src/expands/expand/expand.tsx +7 -1
- package/src/form/Form.tsx +42 -12
- package/src/form/form.css +15 -2
- package/src/form/form.interface.tsx +5 -3
- package/src/form/input/input.css +6 -2
- package/src/grid/Grid.tsx +34 -21
- package/src/grid/col/Col.tsx +24 -13
- package/src/grid/col/col.css +3 -0
- package/src/grid/col/colStyled/ColStyled.tsx +59 -55
- package/src/grid/container/container.css +2 -2
- package/src/grid/grid.interface.tsx +15 -1
- package/src/grid/row/Row.tsx +49 -12
- package/src/grid/row/row.css +9 -1
- package/src/overlayPanel/OverlayPanel.tsx +71 -18
- package/src/overlayPanel/overlay.context.tsx +6 -4
- package/src/overlayPanel/overlayPanel.css +10 -1
- package/src/overlayPanel/overlayPanelStyled/OverlayPanelStyled.tsx +7 -2
- package/src/select/Select.tsx +178 -39
- package/src/select/select.css +94 -13
- package/src/select/selectOption.interface.ts +1 -0
package/package.json
CHANGED
package/src/card/Card.tsx
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { useCallback, useState } from "react";
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
2
|
import { Icon } from "../icon/Icon";
|
|
3
3
|
import { PositionOverlay } from "../overlayPanel/OverlayPanel";
|
|
4
4
|
import "./card.css";
|
|
5
5
|
import { CardInterface } from "./card.interface";
|
|
6
6
|
import { CardActionInterface } from "./cardAction.interface";
|
|
7
7
|
import { CardAction } from "./cardAction/CardAction";
|
|
8
|
+
import { GridInterface, GridResponsive } from "../grid/grid.interface";
|
|
9
|
+
import { Grid } from "../grid/Grid";
|
|
8
10
|
type CardProps = {
|
|
9
11
|
item: CardInterface;
|
|
10
12
|
actions?: CardActionInterface[];
|
|
@@ -14,6 +16,8 @@ type CardProps = {
|
|
|
14
16
|
children?: JSX.Element;
|
|
15
17
|
customAction?: JSX.Element;
|
|
16
18
|
positionOverlay?: PositionOverlay;
|
|
19
|
+
/** Largeur sur laquelle se basent les breakpoints du footer, défaut : "app" */
|
|
20
|
+
responsive?: GridResponsive;
|
|
17
21
|
};
|
|
18
22
|
|
|
19
23
|
// Composant Card modifié
|
|
@@ -25,6 +29,7 @@ export const Card = ({
|
|
|
25
29
|
onClose,
|
|
26
30
|
customAction,
|
|
27
31
|
positionOverlay,
|
|
32
|
+
responsive = "app",
|
|
28
33
|
children,
|
|
29
34
|
}: CardProps): JSX.Element => {
|
|
30
35
|
const [cliqueAction, setCliqueAction] = useState(false);
|
|
@@ -69,14 +74,38 @@ export const Card = ({
|
|
|
69
74
|
cliqueAction,
|
|
70
75
|
]
|
|
71
76
|
);
|
|
77
|
+
const customeActionGrid: GridInterface[] = useMemo(() => {
|
|
78
|
+
return customAction ? [
|
|
79
|
+
{
|
|
80
|
+
xs: 12,
|
|
81
|
+
md: 6,
|
|
82
|
+
children: (
|
|
83
|
+
<div className="card-custom-action">{customAction}</div>
|
|
84
|
+
),
|
|
85
|
+
}
|
|
86
|
+
] : [];
|
|
87
|
+
}, [customAction]);
|
|
72
88
|
|
|
89
|
+
const footerGrid: GridInterface[] = useMemo(() => {
|
|
90
|
+
return [
|
|
91
|
+
...customeActionGrid,
|
|
92
|
+
{
|
|
93
|
+
xs: 12,
|
|
94
|
+
md: customAction ? 6 : 12,
|
|
95
|
+
children: (
|
|
96
|
+
<div className="d-flex flex-1 align-items-center">
|
|
97
|
+
{item.prix && <div className="card-prix">{item.prix}</div>}
|
|
98
|
+
</div>
|
|
99
|
+
),
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}, [customAction, item.prix]);
|
|
73
103
|
return (
|
|
74
104
|
<div
|
|
75
105
|
onContextMenu={(e) => e.preventDefault()}
|
|
76
106
|
onMouseDown={(e) => onClickCard(e, item)}
|
|
77
|
-
className={`card ${className ?? ""} ${
|
|
78
|
-
|
|
79
|
-
} `}
|
|
107
|
+
className={`card ${className ?? ""} ${cliqueAction ? "card--active" : ""
|
|
108
|
+
} `}
|
|
80
109
|
>
|
|
81
110
|
{children}
|
|
82
111
|
{!children && (
|
|
@@ -92,14 +121,11 @@ export const Card = ({
|
|
|
92
121
|
<div className="card-content">{item.description}</div>
|
|
93
122
|
)}
|
|
94
123
|
</div>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<
|
|
98
|
-
)}
|
|
99
|
-
<div className="d-flex flex-1 align-items-center">
|
|
100
|
-
{item.prix && <div className="card-prix">{item.prix}</div>}
|
|
124
|
+
{footerGrid.length > 0 && (
|
|
125
|
+
<div className="card-footer">
|
|
126
|
+
<Grid items={footerGrid} responsive={responsive} />
|
|
101
127
|
</div>
|
|
102
|
-
|
|
128
|
+
)}
|
|
103
129
|
{onClose && (
|
|
104
130
|
<div
|
|
105
131
|
className="ml-2 cursor-pointer"
|
package/src/card/card.css
CHANGED
|
@@ -69,8 +69,7 @@
|
|
|
69
69
|
flex: 1;
|
|
70
70
|
display: flex;
|
|
71
71
|
justify-content: flex-end;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
72
|
+
} }
|
|
74
73
|
|
|
75
74
|
&:not(.card--horizontal) {
|
|
76
75
|
.card-title {
|
|
@@ -97,7 +96,7 @@
|
|
|
97
96
|
display: none;
|
|
98
97
|
}
|
|
99
98
|
}
|
|
100
|
-
@container (min-width: 992px) {
|
|
99
|
+
@container app-container (min-width: 992px) {
|
|
101
100
|
.card {
|
|
102
101
|
.card-custom-action {
|
|
103
102
|
justify-content: end;
|
package/src/expands/Expands.tsx
CHANGED
|
@@ -9,6 +9,7 @@ type ExpandProps = {
|
|
|
9
9
|
onChange?: (item: ExpandInterface) => void;
|
|
10
10
|
currentItem?: ExpandInterface;
|
|
11
11
|
showArrow?: boolean;
|
|
12
|
+
catchfocusCapture?: boolean;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
export const Expands = (props: ExpandProps) => {
|
|
@@ -34,6 +35,7 @@ export const Expands = (props: ExpandProps) => {
|
|
|
34
35
|
showArrow={props.showArrow}
|
|
35
36
|
onChange={onChangeExpand}
|
|
36
37
|
currentExpand={currentExpand}
|
|
38
|
+
catchfocusCapture={props.catchfocusCapture}
|
|
37
39
|
/>
|
|
38
40
|
))}
|
|
39
41
|
</div>
|
|
@@ -7,6 +7,8 @@ export type ExpandProps = {
|
|
|
7
7
|
item: ExpandInterface;
|
|
8
8
|
className?: string;
|
|
9
9
|
showArrow?: boolean;
|
|
10
|
+
catchfocusCapture?: boolean
|
|
11
|
+
|
|
10
12
|
onChange: (item: ExpandInterface) => void;
|
|
11
13
|
};
|
|
12
14
|
export const Expand = ({
|
|
@@ -14,12 +16,14 @@ export const Expand = ({
|
|
|
14
16
|
item,
|
|
15
17
|
className,
|
|
16
18
|
showArrow = true,
|
|
19
|
+
catchfocusCapture = false,
|
|
17
20
|
onChange,
|
|
18
21
|
}: ExpandProps): JSX.Element => {
|
|
19
22
|
const [active, setActive] = useState<boolean>(false);
|
|
20
23
|
const expandRef = useRef<HTMLDivElement>(null);
|
|
21
24
|
|
|
22
25
|
useEffect(() => {
|
|
26
|
+
|
|
23
27
|
if (item && currentExpand) {
|
|
24
28
|
if (currentExpand.id === item.id) {
|
|
25
29
|
if (!active) {
|
|
@@ -66,6 +70,7 @@ export const Expand = ({
|
|
|
66
70
|
}, []);
|
|
67
71
|
|
|
68
72
|
useEffect(() => {
|
|
73
|
+
if (!catchfocusCapture) return;
|
|
69
74
|
if (active) {
|
|
70
75
|
const timer1 = setTimeout(() => {
|
|
71
76
|
scrollToHeader();
|
|
@@ -78,10 +83,11 @@ export const Expand = ({
|
|
|
78
83
|
clearTimeout(timer2);
|
|
79
84
|
};
|
|
80
85
|
}
|
|
81
|
-
}, [active, scrollToHeader]);
|
|
86
|
+
}, [active, scrollToHeader, catchfocusCapture]);
|
|
82
87
|
|
|
83
88
|
const handleFocusCapture = useCallback(
|
|
84
89
|
(e: React.FocusEvent<HTMLDivElement>) => {
|
|
90
|
+
if (!catchfocusCapture) return;
|
|
85
91
|
const target = e.target as HTMLElement;
|
|
86
92
|
if (
|
|
87
93
|
active &&
|
package/src/form/Form.tsx
CHANGED
|
@@ -20,6 +20,7 @@ import { InputPrice } from "./inputPrice/InputPrice";
|
|
|
20
20
|
import { TextArea } from "./textArea/TextArea";
|
|
21
21
|
import { Col } from "../grid/col/Col";
|
|
22
22
|
import { Row } from "../grid/row/Row";
|
|
23
|
+
import { GridBreakpoints, GridResponsive } from "../grid/grid.interface";
|
|
23
24
|
import { TypeInputEnum } from "./form.enum";
|
|
24
25
|
import { AddressSearch } from "../addressSearch/AddressSearch";
|
|
25
26
|
|
|
@@ -54,6 +55,11 @@ export type FormProps<
|
|
|
54
55
|
sections?: FormSection[];
|
|
55
56
|
disabledSubmit?: boolean;
|
|
56
57
|
mode?: FormMode;
|
|
58
|
+
/** Largeur sur laquelle se basent les breakpoints des champs (col), défaut : "app" */
|
|
59
|
+
responsive?: GridResponsive;
|
|
60
|
+
/** Place le bouton submit dans la grid (dernière ligne) avec ces tailles de col.
|
|
61
|
+
* Ignoré si les champs n'utilisent pas la grid : le bouton reste dans le footer. */
|
|
62
|
+
submitCol?: GridBreakpoints;
|
|
57
63
|
onSubmit?: (values: FormValuesInterface<T>) => void;
|
|
58
64
|
onChange?: (values: FormValuesInterface<T>) => void;
|
|
59
65
|
};
|
|
@@ -73,6 +79,8 @@ export const DynamicForm = forwardRef(
|
|
|
73
79
|
disabledSubmit = false,
|
|
74
80
|
mode = "edit",
|
|
75
81
|
className,
|
|
82
|
+
responsive,
|
|
83
|
+
submitCol,
|
|
76
84
|
}: FormProps<T>,
|
|
77
85
|
ref: React.Ref<{
|
|
78
86
|
clearInput?: (name: string) => void;
|
|
@@ -272,12 +280,30 @@ export const DynamicForm = forwardRef(
|
|
|
272
280
|
: String(value);
|
|
273
281
|
};
|
|
274
282
|
|
|
275
|
-
const
|
|
283
|
+
const submitButton = mode === "edit" && !hideSubmit && (
|
|
284
|
+
<Button
|
|
285
|
+
id={`button-${id}`}
|
|
286
|
+
disabled={btnDisabledSubmit}
|
|
287
|
+
title={title || "submit"}
|
|
288
|
+
/>
|
|
289
|
+
);
|
|
290
|
+
// Le bouton va dans la grid de la dernière liste de champs, si elle en a une
|
|
291
|
+
const lastFields = sections
|
|
292
|
+
? sections[sections.length - 1]?.fields ?? []
|
|
293
|
+
: initialForm ?? [];
|
|
294
|
+
const submitInGrid =
|
|
295
|
+
!!submitCol && lastFields.some((field) => field.col !== undefined);
|
|
296
|
+
|
|
297
|
+
const renderFields = (fields: FormInterface[], withSubmit = false) => {
|
|
276
298
|
const gridEnabled = fields.some((field) => field.col !== undefined);
|
|
277
|
-
const FieldsContainer = gridEnabled
|
|
299
|
+
const FieldsContainer: React.ElementType = gridEnabled
|
|
300
|
+
? Row
|
|
301
|
+
: React.Fragment;
|
|
302
|
+
// Un Fragment n'accepte pas de props : on ne les passe qu'à la Row
|
|
303
|
+
const containerProps = gridEnabled ? { responsive } : {};
|
|
278
304
|
|
|
279
305
|
return (
|
|
280
|
-
<FieldsContainer>
|
|
306
|
+
<FieldsContainer {...containerProps}>
|
|
281
307
|
{fields.map((v, key) => {
|
|
282
308
|
const required = v.onRequired
|
|
283
309
|
? v.onRequired(v.value)
|
|
@@ -339,6 +365,8 @@ export const DynamicForm = forwardRef(
|
|
|
339
365
|
options={v.options ?? []}
|
|
340
366
|
disabled={disabled}
|
|
341
367
|
multiple={v.multiple}
|
|
368
|
+
usePortal={v.usePortal}
|
|
369
|
+
searchable={v.searchable}
|
|
342
370
|
value={values[v.name] as any}
|
|
343
371
|
required={required}
|
|
344
372
|
defaultValue={v.value as string | string[]}
|
|
@@ -406,6 +434,11 @@ export const DynamicForm = forwardRef(
|
|
|
406
434
|
</FormFieldContainer>
|
|
407
435
|
);
|
|
408
436
|
})}
|
|
437
|
+
{withSubmit && submitButton && (
|
|
438
|
+
<Col {...submitCol}>
|
|
439
|
+
<div className="form-submit-col">{submitButton}</div>
|
|
440
|
+
</Col>
|
|
441
|
+
)}
|
|
409
442
|
</FieldsContainer>
|
|
410
443
|
);
|
|
411
444
|
};
|
|
@@ -426,20 +459,17 @@ export const DynamicForm = forwardRef(
|
|
|
426
459
|
{section.title && (
|
|
427
460
|
<h3 className="form-section-title">{section.title}</h3>
|
|
428
461
|
)}
|
|
429
|
-
{renderFields(
|
|
462
|
+
{renderFields(
|
|
463
|
+
section.fields,
|
|
464
|
+
submitInGrid && index === sections.length - 1
|
|
465
|
+
)}
|
|
430
466
|
</section>
|
|
431
467
|
))
|
|
432
|
-
: renderFields(initialForm ?? [])}
|
|
468
|
+
: renderFields(initialForm ?? [], submitInGrid)}
|
|
433
469
|
{children}
|
|
434
470
|
</div>
|
|
435
471
|
<div className="d-flex justify-content-end form-footer">
|
|
436
|
-
{
|
|
437
|
-
<Button
|
|
438
|
-
id={`button-${id}`}
|
|
439
|
-
disabled={btnDisabledSubmit}
|
|
440
|
-
title={title || "submit"}
|
|
441
|
-
/>
|
|
442
|
-
)}
|
|
472
|
+
{!submitInGrid && submitButton}
|
|
443
473
|
</div>
|
|
444
474
|
</form>
|
|
445
475
|
);
|
package/src/form/form.css
CHANGED
|
@@ -25,7 +25,7 @@ form {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
.form-section-title {
|
|
28
|
-
margin: 0
|
|
28
|
+
margin: 0 0 0.5rem;
|
|
29
29
|
padding-bottom: 0.5rem;
|
|
30
30
|
border-bottom: 1px solid #dedede;
|
|
31
31
|
color: var(--primary-color);
|
|
@@ -87,7 +87,7 @@ form {
|
|
|
87
87
|
.form-read-value {
|
|
88
88
|
width: 100%;
|
|
89
89
|
min-width: 250px;
|
|
90
|
-
min-height:
|
|
90
|
+
min-height: 0px;
|
|
91
91
|
box-sizing: border-box;
|
|
92
92
|
border: 1px solid transparent;
|
|
93
93
|
border-radius: 4px;
|
|
@@ -105,6 +105,19 @@ form {
|
|
|
105
105
|
|
|
106
106
|
.grid-row {
|
|
107
107
|
|
|
108
|
+
/* Bouton submit placé dans la grid (submitCol) : aligné en bas, sur les inputs */
|
|
109
|
+
.form-submit-col {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: flex-end;
|
|
112
|
+
justify-content: flex-end;
|
|
113
|
+
height: 100%;
|
|
114
|
+
|
|
115
|
+
button {
|
|
116
|
+
margin-top: 0;
|
|
117
|
+
margin-bottom: 0.25rem;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
108
121
|
.form-group,
|
|
109
122
|
.input-container,
|
|
110
123
|
.select-container,
|
|
@@ -24,7 +24,7 @@ export interface FormSection {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export interface FormInterface {
|
|
27
|
-
value: string | number | boolean | Date;
|
|
27
|
+
value: string | number | boolean | Date | string[];
|
|
28
28
|
id?: string;
|
|
29
29
|
label?: string;
|
|
30
30
|
focus?: boolean;
|
|
@@ -36,6 +36,8 @@ export interface FormInterface {
|
|
|
36
36
|
condition?: FormCondition;
|
|
37
37
|
icon?: string;
|
|
38
38
|
multiple?: boolean;
|
|
39
|
+
usePortal?: boolean;
|
|
40
|
+
searchable?: boolean;
|
|
39
41
|
options?: { value: string; label: string }[]; // <--- pour les <option>
|
|
40
42
|
className?: string;
|
|
41
43
|
col?: GridBreakpoints;
|
|
@@ -59,7 +61,7 @@ export interface FormInterface {
|
|
|
59
61
|
value: string | number | boolean | Date | string[],
|
|
60
62
|
name?: string
|
|
61
63
|
) => void;
|
|
62
|
-
onRequired?: (value: string | number | boolean | Date) => boolean;
|
|
63
|
-
onDisabled?: (value: string | number | boolean | Date) => boolean;
|
|
64
|
+
onRequired?: (value: string | number | boolean | Date | string[]) => boolean;
|
|
65
|
+
onDisabled?: (value: string | number | boolean | Date | string[]) => boolean;
|
|
64
66
|
onInvalid?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
65
67
|
}
|
package/src/form/input/input.css
CHANGED
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
position: relative;
|
|
3
3
|
min-width: 250px;
|
|
4
4
|
width: 100%;
|
|
5
|
+
|
|
5
6
|
&.input-container-radio,
|
|
6
7
|
&.input-container-checkbox {
|
|
7
8
|
min-width: 10px;
|
|
8
9
|
width: 10px;
|
|
10
|
+
|
|
9
11
|
input {
|
|
10
|
-
width:
|
|
12
|
+
width: 20px;
|
|
11
13
|
min-width: 10px;
|
|
12
14
|
}
|
|
13
15
|
}
|
|
16
|
+
|
|
14
17
|
.input-password-icon {
|
|
15
18
|
position: absolute;
|
|
16
19
|
right: 10px;
|
|
@@ -19,8 +22,9 @@
|
|
|
19
22
|
display: flex;
|
|
20
23
|
align-items: center;
|
|
21
24
|
}
|
|
25
|
+
|
|
22
26
|
input {
|
|
23
27
|
width: 100%;
|
|
24
28
|
font-size: 0.8rem;
|
|
25
29
|
}
|
|
26
|
-
}
|
|
30
|
+
}
|
package/src/grid/Grid.tsx
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
|
-
import { Col } from "./col/Col";
|
|
2
|
-
import { Container } from "./container/Container";
|
|
3
|
-
import { GridInterface } from "./grid.interface";
|
|
4
|
-
import { Row } from "./row/Row";
|
|
5
|
-
type GridProps = {
|
|
6
|
-
items: GridInterface[];
|
|
7
|
-
className?: string;
|
|
8
|
-
scroll?: boolean;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
import { Col } from "./col/Col";
|
|
2
|
+
import { Container } from "./container/Container";
|
|
3
|
+
import { GridInterface, GridResponsive } from "./grid.interface";
|
|
4
|
+
import { GridGap, Row } from "./row/Row";
|
|
5
|
+
type GridProps = {
|
|
6
|
+
items: GridInterface[];
|
|
7
|
+
className?: string;
|
|
8
|
+
scroll?: boolean;
|
|
9
|
+
gap?: GridGap;
|
|
10
|
+
responsive?: GridResponsive;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const Grid = ({
|
|
14
|
+
items,
|
|
15
|
+
className,
|
|
16
|
+
scroll,
|
|
17
|
+
gap,
|
|
18
|
+
responsive,
|
|
19
|
+
}: GridProps) => {
|
|
20
|
+
return (
|
|
21
|
+
<Container>
|
|
22
|
+
<Row
|
|
23
|
+
scroll={scroll}
|
|
24
|
+
className={className}
|
|
25
|
+
gap={gap}
|
|
26
|
+
responsive={responsive}
|
|
27
|
+
>
|
|
28
|
+
{items.map((item, id) => (
|
|
29
|
+
<Col key={id} {...item} />
|
|
30
|
+
))}
|
|
31
|
+
</Row>
|
|
32
|
+
</Container>
|
|
33
|
+
);
|
|
34
|
+
};
|
package/src/grid/col/Col.tsx
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { ColStyled } from "./colStyled/ColStyled";
|
|
3
|
+
import { GridBreakpoints } from "../grid.interface";
|
|
4
|
+
import { RowResponsiveContext } from "../row/Row";
|
|
5
|
+
import "./col.css"
|
|
6
|
+
type ColProps = GridBreakpoints & {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
};
|
|
9
|
+
export const Col: React.FC<ColProps> = ({ xs, sm, md, lg, xl, children }) => {
|
|
10
|
+
const responsive = useContext(RowResponsiveContext);
|
|
11
|
+
return (
|
|
12
|
+
<ColStyled
|
|
13
|
+
className="col"
|
|
14
|
+
$responsive={responsive}
|
|
15
|
+
$xs={xs}
|
|
16
|
+
$sm={sm}
|
|
17
|
+
$md={md}
|
|
18
|
+
$lg={lg}
|
|
19
|
+
$xl={xl}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</ColStyled>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -1,55 +1,59 @@
|
|
|
1
|
-
import styled, { css } from "styled-components";
|
|
2
|
-
import { GridColumnSpan } from "../../grid.interface";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
$
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
$
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
1
|
+
import styled, { css } from "styled-components";
|
|
2
|
+
import { GridColumnSpan, GridResponsive } from "../../grid.interface";
|
|
3
|
+
|
|
4
|
+
// Largeur = part de la ligne moins la part des gaps qui revient à la colonne.
|
|
5
|
+
// Pour une ligne dont les spans font 12, la somme des largeurs + les gaps = 100%.
|
|
6
|
+
const getColWidth = (size: number) =>
|
|
7
|
+
`calc(${(size / 12) * 100}% - var(--grid-gutter, 0px) * ${1 - size / 12})`;
|
|
8
|
+
|
|
9
|
+
// "auto" : base 0 et grow 1, les colonnes "auto" d'une ligne se partagent
|
|
10
|
+
// la place restante (gaps déduits) à parts égales.
|
|
11
|
+
const colSize = (size: GridColumnSpan = 12) =>
|
|
12
|
+
size === "auto"
|
|
13
|
+
? `flex: 1 1 0; max-width: 100%;`
|
|
14
|
+
: `flex: 0 0 ${getColWidth(size)}; max-width: ${getColWidth(size)};`;
|
|
15
|
+
|
|
16
|
+
// Règle de breakpoint selon la largeur de référence choisie sur la Row
|
|
17
|
+
const breakpoint = (responsive: GridResponsive, minWidth: number) => {
|
|
18
|
+
switch (responsive) {
|
|
19
|
+
case "viewport":
|
|
20
|
+
return `@media (min-width: ${minWidth}px)`;
|
|
21
|
+
case "row":
|
|
22
|
+
return `@container kelt-grid (min-width: ${minWidth}px)`;
|
|
23
|
+
default:
|
|
24
|
+
return `@container app-container (min-width: ${minWidth}px)`;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type ColStyledProps = {
|
|
29
|
+
$responsive: GridResponsive;
|
|
30
|
+
$xs?: GridColumnSpan;
|
|
31
|
+
$sm?: GridColumnSpan;
|
|
32
|
+
$md?: GridColumnSpan;
|
|
33
|
+
$lg?: GridColumnSpan;
|
|
34
|
+
$xl?: GridColumnSpan;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const colAt = (
|
|
38
|
+
responsive: GridResponsive,
|
|
39
|
+
minWidth: number,
|
|
40
|
+
size?: GridColumnSpan
|
|
41
|
+
) =>
|
|
42
|
+
size &&
|
|
43
|
+
css`
|
|
44
|
+
${breakpoint(responsive, minWidth)} {
|
|
45
|
+
${colSize(size)}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
export const ColStyled = styled.div<ColStyledProps>`
|
|
50
|
+
box-sizing: border-box;
|
|
51
|
+
min-width: 0;
|
|
52
|
+
${({ $xs }) => colSize($xs)}
|
|
53
|
+
margin-bottom: 0.5rem;
|
|
54
|
+
margin-top: 0.5rem;
|
|
55
|
+
${({ $responsive, $sm }) => colAt($responsive, 576, $sm)}
|
|
56
|
+
${({ $responsive, $md }) => colAt($responsive, 768, $md)}
|
|
57
|
+
${({ $responsive, $lg }) => colAt($responsive, 992, $lg)}
|
|
58
|
+
${({ $responsive, $xl }) => colAt($responsive, 1200, $xl)}
|
|
59
|
+
`;
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Largeur de référence pour les breakpoints des Col :
|
|
3
|
+
* - "app" (défaut) : le conteneur app-container
|
|
4
|
+
* - "viewport" : la fenêtre (@media)
|
|
5
|
+
* - "row" : la Row elle-même (utile pour une grid dans une card, un panneau...)
|
|
6
|
+
*/
|
|
7
|
+
export type GridResponsive = "app" | "viewport" | "row";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Nombre de colonnes sur 12, ou "auto" : la colonne partage la place restante
|
|
11
|
+
* de la ligne à parts égales avec les autres "auto" (ex. 5 champs sur une ligne).
|
|
12
|
+
*/
|
|
13
|
+
export type GridColumnSpan =
|
|
14
|
+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
|
|
15
|
+
| "auto";
|
|
2
16
|
|
|
3
17
|
export interface GridBreakpoints {
|
|
4
18
|
xs?: GridColumnSpan;
|