kelt-ui-kit-react 2.0.6 → 2.0.8
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/index.js +357 -354
- package/dist/style.css +1 -1
- package/dist/toaster/store/useToasterStore.d.ts +4 -0
- package/package.json +1 -1
- package/src/expands/expand/expand.tsx +191 -191
- package/src/grid/Grid.tsx +34 -34
- package/src/grid/col/Col.tsx +24 -24
- package/src/grid/col/col.css +3 -3
- package/src/grid/col/colStyled/ColStyled.tsx +59 -59
- package/src/grid/row/Row.tsx +49 -49
- package/src/toaster/Toaster.tsx +2 -1
- package/src/toaster/store/useToasterStore.tsx +11 -0
- package/src/toaster/toaster.css +13 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
+
export type ToasterPosition = "top-right" | "top-center" | "bottom-right" | "bottom-center";
|
|
2
3
|
type ToasterState = {
|
|
3
4
|
title: string;
|
|
4
5
|
description?: string;
|
|
@@ -7,12 +8,15 @@ type ToasterState = {
|
|
|
7
8
|
visible: boolean;
|
|
8
9
|
duration: number;
|
|
9
10
|
close?: boolean;
|
|
11
|
+
position: ToasterPosition;
|
|
10
12
|
showToast: (title: string, options?: {
|
|
11
13
|
description?: string;
|
|
12
14
|
severity?: "info" | "success" | "error" | "warning";
|
|
13
15
|
duration?: number;
|
|
14
16
|
children?: React.ReactNode;
|
|
15
17
|
close?: boolean;
|
|
18
|
+
/** Position à l'écran (défaut : "top-right") */
|
|
19
|
+
position?: ToasterPosition;
|
|
16
20
|
}) => void;
|
|
17
21
|
hideToast: () => void;
|
|
18
22
|
};
|
package/package.json
CHANGED
|
@@ -1,191 +1,191 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
import { Icon } from "../../icon/Icon";
|
|
3
|
-
import { ExpandInterface } from "./expand.interface";
|
|
4
|
-
|
|
5
|
-
export type ExpandProps = {
|
|
6
|
-
currentExpand?: ExpandInterface;
|
|
7
|
-
item: ExpandInterface;
|
|
8
|
-
className?: string;
|
|
9
|
-
showArrow?: boolean;
|
|
10
|
-
catchfocusCapture?: boolean
|
|
11
|
-
catchFocusCaptureId?: number;
|
|
12
|
-
onChange: (item: ExpandInterface) => void;
|
|
13
|
-
};
|
|
14
|
-
export const Expand = ({
|
|
15
|
-
currentExpand,
|
|
16
|
-
item,
|
|
17
|
-
className,
|
|
18
|
-
showArrow = true,
|
|
19
|
-
catchfocusCapture = false,
|
|
20
|
-
catchFocusCaptureId,
|
|
21
|
-
onChange,
|
|
22
|
-
}: ExpandProps): JSX.Element => {
|
|
23
|
-
const [active, setActive] = useState<boolean>(false);
|
|
24
|
-
const expandRef = useRef<HTMLDivElement>(null);
|
|
25
|
-
|
|
26
|
-
useEffect(() => {
|
|
27
|
-
if (item && currentExpand) {
|
|
28
|
-
if (currentExpand.id === item.id) {
|
|
29
|
-
if (!active) {
|
|
30
|
-
setActive(true);
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
setActive(false);
|
|
34
|
-
}
|
|
35
|
-
} else {
|
|
36
|
-
setActive(false);
|
|
37
|
-
}
|
|
38
|
-
}, [currentExpand, setActive, item, active]);
|
|
39
|
-
|
|
40
|
-
// Si catchFocusCaptureId est fourni, seul l'expand portant cet id gère le focus / scroll
|
|
41
|
-
const canCatchFocus =
|
|
42
|
-
catchfocusCapture &&
|
|
43
|
-
(catchFocusCaptureId === undefined || catchFocusCaptureId === item.id);
|
|
44
|
-
|
|
45
|
-
const scrollToHeader = useCallback(() => {
|
|
46
|
-
const expandElement = expandRef.current;
|
|
47
|
-
if (!expandElement) return;
|
|
48
|
-
|
|
49
|
-
let scrollParent: HTMLElement | null = expandElement.parentElement;
|
|
50
|
-
while (scrollParent) {
|
|
51
|
-
const style = window.getComputedStyle(scrollParent);
|
|
52
|
-
const isScrollable =
|
|
53
|
-
/(auto|scroll)/.test(style.overflow + style.overflowY) &&
|
|
54
|
-
scrollParent.scrollHeight > scrollParent.clientHeight;
|
|
55
|
-
|
|
56
|
-
if (isScrollable) {
|
|
57
|
-
const parentRect = scrollParent.getBoundingClientRect();
|
|
58
|
-
const targetRect = expandElement.getBoundingClientRect();
|
|
59
|
-
const offsetTop =
|
|
60
|
-
targetRect.top - parentRect.top + scrollParent.scrollTop;
|
|
61
|
-
|
|
62
|
-
scrollParent.scrollTo({
|
|
63
|
-
top: offsetTop,
|
|
64
|
-
behavior: "smooth",
|
|
65
|
-
});
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
scrollParent = scrollParent.parentElement;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const header =
|
|
72
|
-
expandElement.querySelector<HTMLElement>(".expand-header") ||
|
|
73
|
-
expandElement;
|
|
74
|
-
header.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
75
|
-
}, []);
|
|
76
|
-
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (!canCatchFocus) return;
|
|
79
|
-
if (active) {
|
|
80
|
-
const timer1 = setTimeout(() => {
|
|
81
|
-
scrollToHeader();
|
|
82
|
-
}, 100);
|
|
83
|
-
const timer2 = setTimeout(() => {
|
|
84
|
-
scrollToHeader();
|
|
85
|
-
}, 350);
|
|
86
|
-
return () => {
|
|
87
|
-
clearTimeout(timer1);
|
|
88
|
-
clearTimeout(timer2);
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
}, [active, scrollToHeader, canCatchFocus]);
|
|
92
|
-
|
|
93
|
-
const handleFocusCapture = useCallback(
|
|
94
|
-
(e: React.FocusEvent<HTMLDivElement>) => {
|
|
95
|
-
if (!canCatchFocus) return;
|
|
96
|
-
const target = e.target as HTMLElement;
|
|
97
|
-
if (
|
|
98
|
-
active &&
|
|
99
|
-
(target.tagName === "INPUT" ||
|
|
100
|
-
target.tagName === "TEXTAREA" ||
|
|
101
|
-
target.tagName === "SELECT")
|
|
102
|
-
) {
|
|
103
|
-
const expandElement = expandRef.current;
|
|
104
|
-
if (!expandElement) return;
|
|
105
|
-
|
|
106
|
-
const performScroll = () => {
|
|
107
|
-
let scrollParent: HTMLElement | null = expandElement.parentElement;
|
|
108
|
-
while (scrollParent) {
|
|
109
|
-
const style = window.getComputedStyle(scrollParent);
|
|
110
|
-
const isScrollable =
|
|
111
|
-
/(auto|scroll)/.test(style.overflow + style.overflowY) &&
|
|
112
|
-
scrollParent.scrollHeight > scrollParent.clientHeight;
|
|
113
|
-
|
|
114
|
-
if (isScrollable) {
|
|
115
|
-
const parentRect = scrollParent.getBoundingClientRect();
|
|
116
|
-
const headerRect = expandElement.getBoundingClientRect();
|
|
117
|
-
const headerOffsetTop =
|
|
118
|
-
headerRect.top - parentRect.top + scrollParent.scrollTop;
|
|
119
|
-
|
|
120
|
-
// Si on a déjà scrollé vers ou au-delà du header, on ne remonte JAMAIS au header
|
|
121
|
-
if (scrollParent.scrollTop >= headerOffsetTop - 10) {
|
|
122
|
-
const targetRect = target.getBoundingClientRect();
|
|
123
|
-
if (targetRect.bottom > parentRect.bottom) {
|
|
124
|
-
target.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
125
|
-
}
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Si on n'a pas encore scrollé jusqu'au header, on y va
|
|
130
|
-
scrollParent.scrollTo({
|
|
131
|
-
top: headerOffsetTop,
|
|
132
|
-
behavior: "smooth",
|
|
133
|
-
});
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
scrollParent = scrollParent.parentElement;
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
performScroll();
|
|
141
|
-
// Répéter après l'ouverture du clavier virtuel (spécifique tablettes / mobiles Android)
|
|
142
|
-
setTimeout(performScroll, 300);
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
[active, canCatchFocus],
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const handleChange = useCallback(
|
|
149
|
-
(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
|
150
|
-
e.stopPropagation();
|
|
151
|
-
if (onChange) {
|
|
152
|
-
onChange(item);
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
[onChange, item],
|
|
156
|
-
);
|
|
157
|
-
return (
|
|
158
|
-
<div
|
|
159
|
-
ref={expandRef}
|
|
160
|
-
onClick={handleChange}
|
|
161
|
-
onFocusCapture={handleFocusCapture}
|
|
162
|
-
className={`expand ${active ? "expand--active" : ""} ${className ?? ""}`}
|
|
163
|
-
>
|
|
164
|
-
<div className={`expand-header`}>
|
|
165
|
-
<div className="expand-header-content">
|
|
166
|
-
<div className="expand-header-content-default">
|
|
167
|
-
{active && showArrow && (
|
|
168
|
-
<Icon classIcon="bi bi-chevron-down mr-2"></Icon>
|
|
169
|
-
)}
|
|
170
|
-
{!active && showArrow && (
|
|
171
|
-
<Icon classIcon="bi bi-chevron-right mr-2"></Icon>
|
|
172
|
-
)}
|
|
173
|
-
<span className="flex-1 expand-header-title-left">
|
|
174
|
-
{item.title}
|
|
175
|
-
</span>
|
|
176
|
-
{item.description && (
|
|
177
|
-
<span className="expand-header-title-right">
|
|
178
|
-
{item.description}
|
|
179
|
-
</span>
|
|
180
|
-
)}
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
<div className="expand-body">
|
|
185
|
-
<div>{item.headerContent}</div>
|
|
186
|
-
{item.content}
|
|
187
|
-
</div>
|
|
188
|
-
{item.footer && <div className="expand-footer">{item.footer}</div>}
|
|
189
|
-
</div>
|
|
190
|
-
);
|
|
191
|
-
};
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Icon } from "../../icon/Icon";
|
|
3
|
+
import { ExpandInterface } from "./expand.interface";
|
|
4
|
+
|
|
5
|
+
export type ExpandProps = {
|
|
6
|
+
currentExpand?: ExpandInterface;
|
|
7
|
+
item: ExpandInterface;
|
|
8
|
+
className?: string;
|
|
9
|
+
showArrow?: boolean;
|
|
10
|
+
catchfocusCapture?: boolean
|
|
11
|
+
catchFocusCaptureId?: number;
|
|
12
|
+
onChange: (item: ExpandInterface) => void;
|
|
13
|
+
};
|
|
14
|
+
export const Expand = ({
|
|
15
|
+
currentExpand,
|
|
16
|
+
item,
|
|
17
|
+
className,
|
|
18
|
+
showArrow = true,
|
|
19
|
+
catchfocusCapture = false,
|
|
20
|
+
catchFocusCaptureId,
|
|
21
|
+
onChange,
|
|
22
|
+
}: ExpandProps): JSX.Element => {
|
|
23
|
+
const [active, setActive] = useState<boolean>(false);
|
|
24
|
+
const expandRef = useRef<HTMLDivElement>(null);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (item && currentExpand) {
|
|
28
|
+
if (currentExpand.id === item.id) {
|
|
29
|
+
if (!active) {
|
|
30
|
+
setActive(true);
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
setActive(false);
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
setActive(false);
|
|
37
|
+
}
|
|
38
|
+
}, [currentExpand, setActive, item, active]);
|
|
39
|
+
|
|
40
|
+
// Si catchFocusCaptureId est fourni, seul l'expand portant cet id gère le focus / scroll
|
|
41
|
+
const canCatchFocus =
|
|
42
|
+
catchfocusCapture &&
|
|
43
|
+
(catchFocusCaptureId === undefined || catchFocusCaptureId === item.id);
|
|
44
|
+
|
|
45
|
+
const scrollToHeader = useCallback(() => {
|
|
46
|
+
const expandElement = expandRef.current;
|
|
47
|
+
if (!expandElement) return;
|
|
48
|
+
|
|
49
|
+
let scrollParent: HTMLElement | null = expandElement.parentElement;
|
|
50
|
+
while (scrollParent) {
|
|
51
|
+
const style = window.getComputedStyle(scrollParent);
|
|
52
|
+
const isScrollable =
|
|
53
|
+
/(auto|scroll)/.test(style.overflow + style.overflowY) &&
|
|
54
|
+
scrollParent.scrollHeight > scrollParent.clientHeight;
|
|
55
|
+
|
|
56
|
+
if (isScrollable) {
|
|
57
|
+
const parentRect = scrollParent.getBoundingClientRect();
|
|
58
|
+
const targetRect = expandElement.getBoundingClientRect();
|
|
59
|
+
const offsetTop =
|
|
60
|
+
targetRect.top - parentRect.top + scrollParent.scrollTop;
|
|
61
|
+
|
|
62
|
+
scrollParent.scrollTo({
|
|
63
|
+
top: offsetTop,
|
|
64
|
+
behavior: "smooth",
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
scrollParent = scrollParent.parentElement;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const header =
|
|
72
|
+
expandElement.querySelector<HTMLElement>(".expand-header") ||
|
|
73
|
+
expandElement;
|
|
74
|
+
header.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
75
|
+
}, []);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (!canCatchFocus) return;
|
|
79
|
+
if (active) {
|
|
80
|
+
const timer1 = setTimeout(() => {
|
|
81
|
+
scrollToHeader();
|
|
82
|
+
}, 100);
|
|
83
|
+
const timer2 = setTimeout(() => {
|
|
84
|
+
scrollToHeader();
|
|
85
|
+
}, 350);
|
|
86
|
+
return () => {
|
|
87
|
+
clearTimeout(timer1);
|
|
88
|
+
clearTimeout(timer2);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}, [active, scrollToHeader, canCatchFocus]);
|
|
92
|
+
|
|
93
|
+
const handleFocusCapture = useCallback(
|
|
94
|
+
(e: React.FocusEvent<HTMLDivElement>) => {
|
|
95
|
+
if (!canCatchFocus) return;
|
|
96
|
+
const target = e.target as HTMLElement;
|
|
97
|
+
if (
|
|
98
|
+
active &&
|
|
99
|
+
(target.tagName === "INPUT" ||
|
|
100
|
+
target.tagName === "TEXTAREA" ||
|
|
101
|
+
target.tagName === "SELECT")
|
|
102
|
+
) {
|
|
103
|
+
const expandElement = expandRef.current;
|
|
104
|
+
if (!expandElement) return;
|
|
105
|
+
|
|
106
|
+
const performScroll = () => {
|
|
107
|
+
let scrollParent: HTMLElement | null = expandElement.parentElement;
|
|
108
|
+
while (scrollParent) {
|
|
109
|
+
const style = window.getComputedStyle(scrollParent);
|
|
110
|
+
const isScrollable =
|
|
111
|
+
/(auto|scroll)/.test(style.overflow + style.overflowY) &&
|
|
112
|
+
scrollParent.scrollHeight > scrollParent.clientHeight;
|
|
113
|
+
|
|
114
|
+
if (isScrollable) {
|
|
115
|
+
const parentRect = scrollParent.getBoundingClientRect();
|
|
116
|
+
const headerRect = expandElement.getBoundingClientRect();
|
|
117
|
+
const headerOffsetTop =
|
|
118
|
+
headerRect.top - parentRect.top + scrollParent.scrollTop;
|
|
119
|
+
|
|
120
|
+
// Si on a déjà scrollé vers ou au-delà du header, on ne remonte JAMAIS au header
|
|
121
|
+
if (scrollParent.scrollTop >= headerOffsetTop - 10) {
|
|
122
|
+
const targetRect = target.getBoundingClientRect();
|
|
123
|
+
if (targetRect.bottom > parentRect.bottom) {
|
|
124
|
+
target.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Si on n'a pas encore scrollé jusqu'au header, on y va
|
|
130
|
+
scrollParent.scrollTo({
|
|
131
|
+
top: headerOffsetTop,
|
|
132
|
+
behavior: "smooth",
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
scrollParent = scrollParent.parentElement;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
performScroll();
|
|
141
|
+
// Répéter après l'ouverture du clavier virtuel (spécifique tablettes / mobiles Android)
|
|
142
|
+
setTimeout(performScroll, 300);
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
[active, canCatchFocus],
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const handleChange = useCallback(
|
|
149
|
+
(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
|
150
|
+
e.stopPropagation();
|
|
151
|
+
if (onChange) {
|
|
152
|
+
onChange(item);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
[onChange, item],
|
|
156
|
+
);
|
|
157
|
+
return (
|
|
158
|
+
<div
|
|
159
|
+
ref={expandRef}
|
|
160
|
+
onClick={handleChange}
|
|
161
|
+
onFocusCapture={handleFocusCapture}
|
|
162
|
+
className={`expand ${active ? "expand--active" : ""} ${className ?? ""}`}
|
|
163
|
+
>
|
|
164
|
+
<div className={`expand-header`}>
|
|
165
|
+
<div className="expand-header-content">
|
|
166
|
+
<div className="expand-header-content-default">
|
|
167
|
+
{active && showArrow && (
|
|
168
|
+
<Icon classIcon="bi bi-chevron-down mr-2"></Icon>
|
|
169
|
+
)}
|
|
170
|
+
{!active && showArrow && (
|
|
171
|
+
<Icon classIcon="bi bi-chevron-right mr-2"></Icon>
|
|
172
|
+
)}
|
|
173
|
+
<span className="flex-1 expand-header-title-left">
|
|
174
|
+
{item.title}
|
|
175
|
+
</span>
|
|
176
|
+
{item.description && (
|
|
177
|
+
<span className="expand-header-title-right">
|
|
178
|
+
{item.description}
|
|
179
|
+
</span>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
<div className="expand-body">
|
|
185
|
+
<div>{item.headerContent}</div>
|
|
186
|
+
{item.content}
|
|
187
|
+
</div>
|
|
188
|
+
{item.footer && <div className="expand-footer">{item.footer}</div>}
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
};
|
package/src/grid/Grid.tsx
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
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
|
-
};
|
|
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,24 +1,24 @@
|
|
|
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
|
+
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
|
+
};
|
package/src/grid/col/col.css
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
.col {
|
|
2
|
-
padding: 0;
|
|
3
|
-
}
|
|
1
|
+
.col {
|
|
2
|
+
padding: 0;
|
|
3
|
+
}
|
|
@@ -1,59 +1,59 @@
|
|
|
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
|
+
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
|
+
`;
|