kelt-ui-kit-react 2.0.5 → 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.
@@ -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,7 +1,7 @@
1
1
  {
2
2
  "name": "kelt-ui-kit-react",
3
3
  "type": "module",
4
- "version": "2.0.5",
4
+ "version": "2.0.8",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -37,6 +37,11 @@ export const Expand = ({
37
37
  }
38
38
  }, [currentExpand, setActive, item, active]);
39
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
+
40
45
  const scrollToHeader = useCallback(() => {
41
46
  const expandElement = expandRef.current;
42
47
  if (!expandElement) return;
@@ -70,7 +75,7 @@ export const Expand = ({
70
75
  }, []);
71
76
 
72
77
  useEffect(() => {
73
- if (!catchfocusCapture || catchFocusCaptureId && catchFocusCaptureId !== currentExpand?.id) return;
78
+ if (!canCatchFocus) return;
74
79
  if (active) {
75
80
  const timer1 = setTimeout(() => {
76
81
  scrollToHeader();
@@ -83,11 +88,11 @@ export const Expand = ({
83
88
  clearTimeout(timer2);
84
89
  };
85
90
  }
86
- }, [active, scrollToHeader, catchfocusCapture, catchFocusCaptureId]);
91
+ }, [active, scrollToHeader, canCatchFocus]);
87
92
 
88
93
  const handleFocusCapture = useCallback(
89
94
  (e: React.FocusEvent<HTMLDivElement>) => {
90
- if (!catchfocusCapture || catchFocusCaptureId && catchFocusCaptureId !== currentExpand?.id) return;
95
+ if (!canCatchFocus) return;
91
96
  const target = e.target as HTMLElement;
92
97
  if (
93
98
  active &&
@@ -137,7 +142,7 @@ export const Expand = ({
137
142
  setTimeout(performScroll, 300);
138
143
  }
139
144
  },
140
- [active, catchfocusCapture, catchFocusCaptureId],
145
+ [active, canCatchFocus],
141
146
  );
142
147
 
143
148
  const handleChange = useCallback(
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
+ };
@@ -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
+ };
@@ -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
+ `;
@@ -1,49 +1,49 @@
1
- import { createContext } from "react";
2
- import { GridResponsive } from "../grid.interface";
3
- import "./row.css";
4
-
5
- export type GridGap = boolean | number | string;
6
-
7
- export const RowResponsiveContext = createContext<GridResponsive>("app");
8
-
9
- type RowProps = {
10
- children: React.ReactNode;
11
- className?: string;
12
- scroll?: boolean;
13
- /** true (défaut) : gap par défaut, false : pas de gap, number : px, string : valeur CSS ("1rem") */
14
- gap?: GridGap;
15
- /** Largeur sur laquelle se basent les breakpoints des Col (défaut : "app") */
16
- responsive?: GridResponsive;
17
- };
18
-
19
- const getGutter = (gap: GridGap) => {
20
- if (gap === true) return undefined; // garde la valeur par défaut du CSS
21
- if (gap === false) return "0px";
22
- return typeof gap === "number" ? `${gap}px` : gap;
23
- };
24
-
25
- export const Row: React.FC<RowProps> = ({
26
- children,
27
- className,
28
- scroll,
29
- gap = true,
30
- responsive = "app",
31
- }) => {
32
- const gutter = getGutter(gap);
33
- return (
34
- <RowResponsiveContext.Provider value={responsive}>
35
- <div
36
- className={`grid-row ${className ?? ""} ${scroll ? "scroll" : ""} ${
37
- responsive === "row" ? "grid-row--responsive" : ""
38
- }`}
39
- style={
40
- gutter
41
- ? ({ "--grid-gutter": gutter } as React.CSSProperties)
42
- : undefined
43
- }
44
- >
45
- {children}
46
- </div>
47
- </RowResponsiveContext.Provider>
48
- );
49
- };
1
+ import { createContext } from "react";
2
+ import { GridResponsive } from "../grid.interface";
3
+ import "./row.css";
4
+
5
+ export type GridGap = boolean | number | string;
6
+
7
+ export const RowResponsiveContext = createContext<GridResponsive>("app");
8
+
9
+ type RowProps = {
10
+ children: React.ReactNode;
11
+ className?: string;
12
+ scroll?: boolean;
13
+ /** true (défaut) : gap par défaut, false : pas de gap, number : px, string : valeur CSS ("1rem") */
14
+ gap?: GridGap;
15
+ /** Largeur sur laquelle se basent les breakpoints des Col (défaut : "app") */
16
+ responsive?: GridResponsive;
17
+ };
18
+
19
+ const getGutter = (gap: GridGap) => {
20
+ if (gap === true) return undefined; // garde la valeur par défaut du CSS
21
+ if (gap === false) return "0px";
22
+ return typeof gap === "number" ? `${gap}px` : gap;
23
+ };
24
+
25
+ export const Row: React.FC<RowProps> = ({
26
+ children,
27
+ className,
28
+ scroll,
29
+ gap = true,
30
+ responsive = "app",
31
+ }) => {
32
+ const gutter = getGutter(gap);
33
+ return (
34
+ <RowResponsiveContext.Provider value={responsive}>
35
+ <div
36
+ className={`grid-row ${className ?? ""} ${scroll ? "scroll" : ""} ${
37
+ responsive === "row" ? "grid-row--responsive" : ""
38
+ }`}
39
+ style={
40
+ gutter
41
+ ? ({ "--grid-gutter": gutter } as React.CSSProperties)
42
+ : undefined
43
+ }
44
+ >
45
+ {children}
46
+ </div>
47
+ </RowResponsiveContext.Provider>
48
+ );
49
+ };
@@ -14,6 +14,7 @@ export const Toaster = () => {
14
14
  duration,
15
15
  hideToast,
16
16
  close,
17
+ position,
17
18
  } = useToasterStore();
18
19
  const onClose = useCallback(() => {
19
20
  hideToast();
@@ -32,7 +33,7 @@ export const Toaster = () => {
32
33
  if (!toasterRoot) return null;
33
34
 
34
35
  return ReactDOM.createPortal(
35
- <div className={`toaster toaster-${severity}`}>
36
+ <div className={`toaster toaster-${severity} toaster--${position}`}>
36
37
  {close && (
37
38
  <button
38
39
  type="button"
@@ -1,5 +1,11 @@
1
1
  import { create } from "zustand";
2
2
 
3
+ export type ToasterPosition =
4
+ | "top-right"
5
+ | "top-center"
6
+ | "bottom-right"
7
+ | "bottom-center";
8
+
3
9
  type ToasterState = {
4
10
  title: string;
5
11
  description?: string;
@@ -8,6 +14,7 @@ type ToasterState = {
8
14
  visible: boolean;
9
15
  duration: number;
10
16
  close?: boolean;
17
+ position: ToasterPosition;
11
18
  showToast: (
12
19
  title: string,
13
20
  options?: {
@@ -16,6 +23,8 @@ type ToasterState = {
16
23
  duration?: number;
17
24
  children?: React.ReactNode;
18
25
  close?: boolean;
26
+ /** Position à l'écran (défaut : "top-right") */
27
+ position?: ToasterPosition;
19
28
  }
20
29
  ) => void;
21
30
  hideToast: () => void;
@@ -28,6 +37,7 @@ export const useToasterStore = create<ToasterState>((set) => ({
28
37
  visible: false,
29
38
  duration: 2500,
30
39
  close: true,
40
+ position: "top-right",
31
41
  children: null,
32
42
  showToast: (title, options = {}) =>
33
43
  set(() => ({
@@ -38,6 +48,7 @@ export const useToasterStore = create<ToasterState>((set) => ({
38
48
  visible: true,
39
49
  children: options.children || null,
40
50
  close: options.close ?? true,
51
+ position: options.position ?? "top-right",
41
52
  })),
42
53
  hideToast: () => set(() => ({ visible: false })),
43
54
  }));
@@ -11,6 +11,19 @@
11
11
  width: 300px;
12
12
  border-radius: 4px;
13
13
  border: 1px solid #dede;
14
+ &.toaster--bottom-right,
15
+ &.toaster--bottom-center {
16
+ top: auto;
17
+ bottom: 0.5rem;
18
+ }
19
+ &.toaster--top-center,
20
+ &.toaster--bottom-center {
21
+ right: auto;
22
+ left: 50%;
23
+ transform: translateX(-50%);
24
+ max-width: calc(100vw - 1rem);
25
+ box-sizing: border-box;
26
+ }
14
27
  .toaster--close {
15
28
  position: absolute;
16
29
  top: 2px;