@sia-ui/react-web 0.3.2 → 0.5.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.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { createContext, forwardRef, useRef, useImperativeHandle, useEffect, memo, useContext, useState, useId, useMemo, useCallback, useLayoutEffect, Children, isValidElement, cloneElement, Fragment as Fragment$1 } from 'react';
3
- import { cn, toneClass, formatCurrency, parseCurrency, formatTimeAgo, formatDate, normalizeIdentifier, truncate, formatDateTime, formatNumber } from '@sia-ui/utils';
4
- import { useDisclosureState, overlayStack, navContains, useHoverIntent, navLeaves, navSome, useComponentDefaults, useSiaLocale, computeFloatingPosition, useAccordionState, navPath, filterNavTree, navKeyForPath, paginationItems, useTableQuery, useSliderState, useLocalForm, useSession, useToasts, toastStore, toast, useRovingIndex } from '@sia-ui/headless';
3
+ import { cn, toneClass, looseIncludes, formatCurrency, parseCurrency, formatTimeAgo, formatDate, normalizeIdentifier, truncate, formatDateTime, formatNumber } from '@sia-ui/utils';
4
+ import { useDisclosureState, overlayStack, navContains, useHoverIntent, navLeaves, navSome, useComponentDefaults, useSiaLocale, computeFloatingPosition, useAccordionState, navPath, filterNavTree, navKeyForPath, paginationItems, useTableQuery, useSliderState, useLocalForm, hasFormErrors, useSession, useToasts, toastStore, toast, readSubmitError, useRovingIndex } from '@sia-ui/headless';
5
5
  export { toast, toastStore } from '@sia-ui/headless';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
  import { useOutsideClick, Portal, useMediaQuery, usePortal, useControllableState } from '@sia-ui/react';
@@ -115,6 +115,12 @@ function InfoIcon(props) {
115
115
  /* @__PURE__ */ jsx("path", { d: "M12 11v5M12 7.5h.01" })
116
116
  ] });
117
117
  }
118
+ function SettingsIcon(props) {
119
+ return /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
120
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
121
+ /* @__PURE__ */ jsx("path", { d: "M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1A1.7 1.7 0 0 0 9 4.6 1.7 1.7 0 0 0 10 3v-.2h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.6 1h.2v4H21a1.7 1.7 0 0 0-1.6 1Z" })
122
+ ] });
123
+ }
118
124
  function EyeIcon(props) {
119
125
  return /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
120
126
  /* @__PURE__ */ jsx("path", { d: "M2 12s3.5-6.5 10-6.5S22 12 22 12s-3.5 6.5-10 6.5S2 12 2 12Z" }),
@@ -263,13 +269,15 @@ var Drawer = forwardRef(
263
269
  closeOnOutsideClick = true,
264
270
  closeOnEsc = true,
265
271
  children,
272
+ className,
266
273
  ...rest
267
274
  }, ref) => {
268
275
  const {
269
276
  isOpen,
270
277
  phase: state,
271
278
  open: doOpen,
272
- close: doClose
279
+ close: doClose,
280
+ toggle
273
281
  } = useDisclosureState({
274
282
  ...open !== void 0 ? { open } : {},
275
283
  defaultOpen,
@@ -278,15 +286,17 @@ var Drawer = forwardRef(
278
286
  });
279
287
  const drawerRef = useRef(null);
280
288
  const zIndexRef = useRef(null);
289
+ const fermer = useRef(doClose);
290
+ fermer.current = doClose;
281
291
  useImperativeHandle(
282
292
  ref,
283
293
  () => ({
284
294
  open: doOpen,
285
295
  close: doClose,
286
- toggle: () => isOpen ? doClose() : doOpen(),
296
+ toggle,
287
297
  isOpen: () => isOpen
288
298
  }),
289
- [isOpen, open]
299
+ [isOpen, doOpen, doClose, toggle]
290
300
  );
291
301
  useEffect(() => {
292
302
  if (!isOpen) return;
@@ -303,7 +313,7 @@ var Drawer = forwardRef(
303
313
  const onKeyDown = (e) => {
304
314
  if (!closeOnEsc || e.key !== "Escape") return;
305
315
  if (overlayStack.isTop(zIndexRef.current)) {
306
- doClose();
316
+ fermer.current();
307
317
  }
308
318
  };
309
319
  window.addEventListener("keydown", onKeyDown);
@@ -322,6 +332,7 @@ var Drawer = forwardRef(
322
332
  "div",
323
333
  {
324
334
  className: "sia-drawer-root",
335
+ "data-state": state,
325
336
  style: { zIndex: zIndexRef.current ?? void 0 },
326
337
  children: [
327
338
  /* @__PURE__ */ jsx("div", { className: "sia-drawer-overlay" }),
@@ -336,7 +347,8 @@ var Drawer = forwardRef(
336
347
  "sia-drawer",
337
348
  `sia-drawer-${position}`,
338
349
  `sia-drawer-${size}`,
339
- state === "closing" && "is-closing"
350
+ state === "closing" && "is-closing",
351
+ className
340
352
  ),
341
353
  ...rest,
342
354
  children
@@ -370,7 +382,7 @@ function DrawerClose() {
370
382
  className: "sia-drawer-close",
371
383
  onClick: ctx.close,
372
384
  "aria-label": "Fermer",
373
- children: "\u2715"
385
+ children: /* @__PURE__ */ jsx(XIcon, { "aria-hidden": "true" })
374
386
  }
375
387
  );
376
388
  }
@@ -603,7 +615,14 @@ function Ligne({
603
615
  return /* @__PURE__ */ jsxs(Fragment, { children: [
604
616
  item.icon && /* @__PURE__ */ jsx("span", { className: "sia-sidebar__icon", "aria-hidden": "true", children: item.icon }),
605
617
  /* @__PURE__ */ jsx("span", { className: "sia-sidebar__label", children: item.label }),
606
- item.badge !== void 0 && !collapsed && /* @__PURE__ */ jsx("span", { className: "sia-sidebar__badge", children: item.badge }),
618
+ item.badge !== void 0 && !collapsed && /* @__PURE__ */ jsx(
619
+ "span",
620
+ {
621
+ className: "sia-sidebar__badge",
622
+ "data-sia-badge": typeof item.badge === "string" || typeof item.badge === "number" ? String(item.badge) : void 0,
623
+ children: item.badge
624
+ }
625
+ ),
607
626
  porteUnePastille && /* @__PURE__ */ jsx("span", { className: "sia-sidebar__dot", "aria-hidden": "true" }),
608
627
  aDesEnfants && !collapsed && /* @__PURE__ */ jsx(
609
628
  "span",
@@ -765,7 +784,14 @@ function NoeudRail(props) {
765
784
  children: /* @__PURE__ */ jsxs("div", { ...panelProps, children: [
766
785
  /* @__PURE__ */ jsxs("div", { className: "sia-sidebar-flyout__title", children: [
767
786
  item.label,
768
- item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "sia-sidebar__badge", children: item.badge })
787
+ item.badge !== void 0 && /* @__PURE__ */ jsx(
788
+ "span",
789
+ {
790
+ className: "sia-sidebar__badge",
791
+ "data-sia-badge": typeof item.badge === "string" || typeof item.badge === "number" ? String(item.badge) : void 0,
792
+ children: item.badge
793
+ }
794
+ )
769
795
  ] }),
770
796
  /* @__PURE__ */ jsx("div", { className: "sia-sidebar-flyout__body", children: enfants.map((enfant) => /* @__PURE__ */ jsx(
771
797
  Noeud,
@@ -871,6 +897,50 @@ var BottomTabs = memo(function BottomTabs2({
871
897
  )
872
898
  ] });
873
899
  });
900
+ function AppShellBrand({
901
+ icon,
902
+ name,
903
+ description,
904
+ className,
905
+ ...props
906
+ }) {
907
+ return /* @__PURE__ */ jsxs("div", { className: cn("sia-shell-brand", className), ...props, children: [
908
+ /* @__PURE__ */ jsx("span", { className: "sia-shell-brand__icon", "aria-hidden": "true", children: icon }),
909
+ /* @__PURE__ */ jsxs("span", { className: "sia-shell-brand__copy", children: [
910
+ /* @__PURE__ */ jsx("span", { className: "sia-shell-brand__name", children: name }),
911
+ description !== void 0 && /* @__PURE__ */ jsx("span", { className: "sia-shell-brand__description", children: description })
912
+ ] })
913
+ ] });
914
+ }
915
+ function AppShellRailItem({
916
+ as = "div",
917
+ icon,
918
+ label,
919
+ className,
920
+ href,
921
+ type = "button",
922
+ disabled,
923
+ ...props
924
+ }) {
925
+ const Component = as;
926
+ const elementProps = {
927
+ ...props,
928
+ ...as === "a" && href ? { href } : {},
929
+ ...as === "button" ? { type, disabled } : {}
930
+ };
931
+ return /* @__PURE__ */ jsxs(
932
+ Component,
933
+ {
934
+ className: cn("sia-shell-rail-item", className),
935
+ "aria-label": props["aria-label"] ?? (as !== "div" ? label : void 0),
936
+ ...elementProps,
937
+ children: [
938
+ /* @__PURE__ */ jsx("span", { className: "sia-shell-rail-item__icon", "aria-hidden": "true", children: icon }),
939
+ /* @__PURE__ */ jsx("span", { className: "sia-shell-rail-item__label", children: label })
940
+ ]
941
+ }
942
+ );
943
+ }
874
944
  function AppShell({
875
945
  nav,
876
946
  children,
@@ -1013,10 +1083,19 @@ function AppShell({
1013
1083
  ]
1014
1084
  }
1015
1085
  ),
1016
- /* @__PURE__ */ jsxs("main", { className: cn("sia-shell__content", `sia-shell__content--${contentWidth}`), children: [
1017
- page && /* @__PURE__ */ jsx("div", { className: "sia-shell__page", children: page }),
1018
- children
1019
- ] }),
1086
+ /* @__PURE__ */ jsxs(
1087
+ "main",
1088
+ {
1089
+ className: cn(
1090
+ "sia-shell__content",
1091
+ `sia-shell__content--${contentWidth}`
1092
+ ),
1093
+ children: [
1094
+ page && /* @__PURE__ */ jsx("div", { className: "sia-shell__page", children: page }),
1095
+ children
1096
+ ]
1097
+ }
1098
+ ),
1020
1099
  footer && /* @__PURE__ */ jsx("footer", { className: "sia-shell__footer", children: footer })
1021
1100
  ] }),
1022
1101
  compact && montrerOnglets && /* @__PURE__ */ jsx(
@@ -1029,7 +1108,7 @@ function AppShell({
1029
1108
  ...montrerTiroir ? { onMore: () => setTiroirOuvert(true) } : {}
1030
1109
  }
1031
1110
  ),
1032
- compact && montrerTiroir && /* @__PURE__ */ jsx(
1111
+ compact && montrerTiroir && /* @__PURE__ */ jsxs(
1033
1112
  Drawer,
1034
1113
  {
1035
1114
  open: tiroirOuvert,
@@ -1037,7 +1116,13 @@ function AppShell({
1037
1116
  position: side,
1038
1117
  size: "sm",
1039
1118
  className: "sia-shell__drawer",
1040
- children: barre
1119
+ children: [
1120
+ /* @__PURE__ */ jsxs(Drawer.Header, { children: [
1121
+ /* @__PURE__ */ jsx(Drawer.Title, { children: "Navigation" }),
1122
+ /* @__PURE__ */ jsx(Drawer.Close, {})
1123
+ ] }),
1124
+ /* @__PURE__ */ jsx(Drawer.Body, { children: barre })
1125
+ ]
1041
1126
  }
1042
1127
  )
1043
1128
  ]
@@ -1938,9 +2023,9 @@ function Calendar({
1938
2023
  if (mode === void 0) setPanelMode(next);
1939
2024
  onPanelChange?.(monthIso(controlledMonth), next);
1940
2025
  };
1941
- const isDisabled = (date) => Boolean(
2026
+ const isDisabled = useCallback((date) => Boolean(
1942
2027
  disabledDate?.(date) || validRange && (date < validRange[0] || date > validRange[1])
1943
- );
2028
+ ), [disabledDate, validRange]);
1944
2029
  const selectDate = (date) => {
1945
2030
  if (isDisabled(date)) return;
1946
2031
  if (selectionMode === "range") {
@@ -1974,7 +2059,7 @@ function Calendar({
1974
2059
  ...rangeEdge ? { rangeEdge } : {}
1975
2060
  };
1976
2061
  });
1977
- }, [year, monthIndex, firstDayOfWeek, disabledDate, validRange, currentValue, currentRange.start, currentRange.end]);
2062
+ }, [year, monthIndex, firstDayOfWeek, isDisabled, currentValue, currentRange.start, currentRange.end]);
1978
2063
  const weekdays = Array.from({ length: 7 }, (_, index) => new Intl.DateTimeFormat(locale, { weekday: "short" }).format(new Date(2024, 0, 7 + firstDayOfWeek + index)));
1979
2064
  const monthName = new Intl.DateTimeFormat(locale, { month: "long" }).format(controlledMonth);
1980
2065
  const updateFromString = (next) => setMonth(parseMonth(next));
@@ -2621,6 +2706,8 @@ function Timer({ duration = 6e4, initialValue = 0, direction = "down", running =
2621
2706
  const initial = direction === "down" ? duration : initialValue;
2622
2707
  const [value, setValue] = useState(initial);
2623
2708
  const finishCalled = useRef(false);
2709
+ const latest = useRef({ value, onChange, onFinish });
2710
+ latest.current = { value, onChange, onFinish };
2624
2711
  useEffect(() => {
2625
2712
  setValue(direction === "down" ? duration : initialValue);
2626
2713
  finishCalled.current = false;
@@ -2628,15 +2715,15 @@ function Timer({ duration = 6e4, initialValue = 0, direction = "down", running =
2628
2715
  useEffect(() => {
2629
2716
  if (!running) return;
2630
2717
  const startedAt = Date.now();
2631
- const base = value;
2718
+ const base = latest.current.value;
2632
2719
  const update = () => {
2633
2720
  const elapsed = Date.now() - startedAt;
2634
2721
  const next = direction === "down" ? Math.max(0, base - elapsed) : base + elapsed;
2635
2722
  setValue(next);
2636
- onChange?.(next);
2723
+ latest.current.onChange?.(next);
2637
2724
  if (direction === "down" && next === 0 && !finishCalled.current) {
2638
2725
  finishCalled.current = true;
2639
- onFinish?.();
2726
+ latest.current.onFinish?.();
2640
2727
  }
2641
2728
  };
2642
2729
  update();
@@ -2658,6 +2745,270 @@ function PageHeader({ title, description, eyebrow, actions, className }) {
2658
2745
  actions && /* @__PURE__ */ jsx("div", { className: "sia-page-header__actions", children: actions })
2659
2746
  ] });
2660
2747
  }
2748
+ function optionsFromChildren(children) {
2749
+ return Children.toArray(children).flatMap((child) => {
2750
+ if (!isValidElement(child))
2751
+ return [];
2752
+ const value = child.props.value;
2753
+ if (value === void 0) return [];
2754
+ return [{
2755
+ value: String(value),
2756
+ label: child.props.children,
2757
+ ...child.props.disabled !== void 0 ? { disabled: child.props.disabled } : {}
2758
+ }];
2759
+ });
2760
+ }
2761
+ function optionText(option) {
2762
+ return [option.label, option.description, ...option.keywords ?? []].map((value) => typeof value === "string" || typeof value === "number" ? String(value) : "").join(" ");
2763
+ }
2764
+ var Select = forwardRef(function Select2({
2765
+ id,
2766
+ name,
2767
+ options,
2768
+ children,
2769
+ value,
2770
+ defaultValue = "",
2771
+ onValueChange,
2772
+ placeholder = "S\xE9lectionner",
2773
+ searchable = false,
2774
+ searchPlaceholder = "Rechercher...",
2775
+ fetcher,
2776
+ debounce = 250,
2777
+ loadingText = "Chargement des options...",
2778
+ emptyText = "Aucun r\xE9sultat",
2779
+ errorText = "Impossible de charger les options",
2780
+ clearable = false,
2781
+ disabled = false,
2782
+ invalid = false,
2783
+ required = false,
2784
+ placement = "bottom-start",
2785
+ renderValue,
2786
+ variant = "default",
2787
+ className,
2788
+ "aria-invalid": ariaInvalid,
2789
+ "aria-required": ariaRequired,
2790
+ ...ariaProps
2791
+ }, forwardedRef) {
2792
+ const generatedId = useId();
2793
+ const controlId = id ?? `sia-select-${generatedId.replace(/:/g, "")}`;
2794
+ const listboxId = `${controlId}-listbox`;
2795
+ const triggerRef = useRef(null);
2796
+ const searchRef = useRef(null);
2797
+ const staticOptions = useMemo(
2798
+ () => options ?? optionsFromChildren(children),
2799
+ [children, options]
2800
+ );
2801
+ const [internalValue, setInternalValue] = useState(defaultValue);
2802
+ const [open, setOpen] = useState(false);
2803
+ const [query, setQuery] = useState("");
2804
+ const [remoteOptions, setRemoteOptions] = useState([]);
2805
+ const [selectedRemoteOption, setSelectedRemoteOption] = useState();
2806
+ const [loading, setLoading] = useState(false);
2807
+ const [loadError, setLoadError] = useState(false);
2808
+ const [activeIndex, setActiveIndex] = useState(-1);
2809
+ const currentValue = value ?? internalValue;
2810
+ const sourceOptions = fetcher ? remoteOptions : staticOptions;
2811
+ const visibleOptions = useMemo(() => {
2812
+ if (fetcher) return sourceOptions;
2813
+ const needle = query.trim();
2814
+ return needle ? sourceOptions.filter((option) => looseIncludes(optionText(option), needle)) : sourceOptions;
2815
+ }, [fetcher, query, sourceOptions]);
2816
+ const selectedOption = [...staticOptions, ...remoteOptions].find(
2817
+ (option) => option.value === currentValue
2818
+ ) ?? (selectedRemoteOption?.value === currentValue ? selectedRemoteOption : void 0);
2819
+ useEffect(() => {
2820
+ if (!open || !fetcher) return;
2821
+ const controller = new AbortController();
2822
+ const timeout = window.setTimeout(async () => {
2823
+ setLoading(true);
2824
+ setLoadError(false);
2825
+ try {
2826
+ const nextOptions = await fetcher(query, controller.signal);
2827
+ if (!controller.signal.aborted) setRemoteOptions(nextOptions);
2828
+ } catch {
2829
+ if (!controller.signal.aborted) setLoadError(true);
2830
+ } finally {
2831
+ if (!controller.signal.aborted) setLoading(false);
2832
+ }
2833
+ }, debounce);
2834
+ return () => {
2835
+ window.clearTimeout(timeout);
2836
+ controller.abort();
2837
+ };
2838
+ }, [debounce, fetcher, open, query]);
2839
+ useEffect(() => {
2840
+ if (!open) return;
2841
+ const selected = visibleOptions.findIndex((option) => option.value === currentValue);
2842
+ setActiveIndex(selected >= 0 ? selected : visibleOptions.findIndex((option) => !option.disabled));
2843
+ if (searchable || fetcher) window.setTimeout(() => searchRef.current?.focus(), 0);
2844
+ }, [currentValue, fetcher, open, searchable, visibleOptions]);
2845
+ const setTriggerRef = (node) => {
2846
+ triggerRef.current = node;
2847
+ if (typeof forwardedRef === "function") forwardedRef(node);
2848
+ else if (forwardedRef) forwardedRef.current = node;
2849
+ };
2850
+ const changeValue = (nextValue, option) => {
2851
+ if (value === void 0) setInternalValue(nextValue);
2852
+ onValueChange?.(nextValue, option);
2853
+ };
2854
+ const choose = (option) => {
2855
+ if (option.disabled) return;
2856
+ if (fetcher) setSelectedRemoteOption(option);
2857
+ changeValue(option.value, option);
2858
+ setOpen(false);
2859
+ setQuery("");
2860
+ window.setTimeout(() => triggerRef.current?.focus(), 0);
2861
+ };
2862
+ const moveActive = (direction) => {
2863
+ if (!visibleOptions.length) return;
2864
+ let next = activeIndex;
2865
+ for (let count = 0; count < visibleOptions.length; count += 1) {
2866
+ next = (next + direction + visibleOptions.length) % visibleOptions.length;
2867
+ if (!visibleOptions[next]?.disabled) break;
2868
+ }
2869
+ setActiveIndex(next);
2870
+ };
2871
+ const handleKeyDown = (event) => {
2872
+ if (["ArrowDown", "ArrowUp"].includes(event.key)) {
2873
+ event.preventDefault();
2874
+ if (!open) setOpen(true);
2875
+ else moveActive(event.key === "ArrowDown" ? 1 : -1);
2876
+ return;
2877
+ }
2878
+ if (event.key === "Home" && open) {
2879
+ event.preventDefault();
2880
+ setActiveIndex(visibleOptions.findIndex((option) => !option.disabled));
2881
+ } else if (event.key === "End" && open) {
2882
+ event.preventDefault();
2883
+ for (let index = visibleOptions.length - 1; index >= 0; index -= 1) {
2884
+ if (!visibleOptions[index]?.disabled) {
2885
+ setActiveIndex(index);
2886
+ break;
2887
+ }
2888
+ }
2889
+ } else if (event.key === "Enter" && open && activeIndex >= 0) {
2890
+ event.preventDefault();
2891
+ const active = visibleOptions[activeIndex];
2892
+ if (active) choose(active);
2893
+ } else if (event.key === "Escape" && open) {
2894
+ event.preventDefault();
2895
+ setOpen(false);
2896
+ triggerRef.current?.focus();
2897
+ } else if (!open && clearable && currentValue && ["Backspace", "Delete"].includes(event.key)) {
2898
+ event.preventDefault();
2899
+ changeValue("");
2900
+ }
2901
+ };
2902
+ const showSearch = searchable || Boolean(fetcher);
2903
+ const activeOption = activeIndex >= 0 ? visibleOptions[activeIndex] : void 0;
2904
+ return /* @__PURE__ */ jsxs(
2905
+ "div",
2906
+ {
2907
+ className: cn(
2908
+ "sia-select",
2909
+ variant === "embedded" && "sia-select--embedded",
2910
+ disabled && "sia-select--disabled",
2911
+ className
2912
+ ),
2913
+ children: [
2914
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: currentValue }),
2915
+ /* @__PURE__ */ jsxs(
2916
+ "button",
2917
+ {
2918
+ ref: setTriggerRef,
2919
+ id: controlId,
2920
+ type: "button",
2921
+ className: cn("sia-select__trigger", invalid && "sia-select__trigger--invalid"),
2922
+ role: "combobox",
2923
+ "aria-haspopup": "listbox",
2924
+ "aria-expanded": open,
2925
+ "aria-controls": listboxId,
2926
+ "aria-activedescendant": open && activeOption ? `${listboxId}-${activeIndex}` : void 0,
2927
+ "aria-invalid": ariaInvalid ?? (invalid || void 0),
2928
+ "aria-required": ariaRequired ?? (required || void 0),
2929
+ disabled,
2930
+ onClick: () => setOpen((current) => !current),
2931
+ onKeyDown: handleKeyDown,
2932
+ ...ariaProps,
2933
+ children: [
2934
+ /* @__PURE__ */ jsx("span", { className: cn("sia-select__value", !selectedOption && "sia-select__placeholder"), children: renderValue ? renderValue(selectedOption) : selectedOption?.label ?? placeholder }),
2935
+ /* @__PURE__ */ jsxs("span", { className: "sia-select__actions", children: [
2936
+ clearable && currentValue && !disabled && /* @__PURE__ */ jsx(
2937
+ "span",
2938
+ {
2939
+ className: "sia-select__clear",
2940
+ "aria-hidden": "true",
2941
+ title: "Effacer la s\xE9lection",
2942
+ onClick: (event) => {
2943
+ event.stopPropagation();
2944
+ changeValue("");
2945
+ },
2946
+ children: /* @__PURE__ */ jsx(XIcon, {})
2947
+ }
2948
+ ),
2949
+ /* @__PURE__ */ jsx(ChevronDownIcon, { className: "sia-select__chevron", "aria-hidden": "true" })
2950
+ ] })
2951
+ ]
2952
+ }
2953
+ ),
2954
+ /* @__PURE__ */ jsx(
2955
+ Overlay,
2956
+ {
2957
+ open,
2958
+ anchorRef: triggerRef,
2959
+ onOpenChange: setOpen,
2960
+ placement,
2961
+ matchAnchorWidth: true,
2962
+ className: "sia-select__overlay",
2963
+ children: /* @__PURE__ */ jsxs("div", { className: "sia-select__panel", onKeyDown: handleKeyDown, children: [
2964
+ showSearch && /* @__PURE__ */ jsxs("div", { className: "sia-select__search-wrap", children: [
2965
+ /* @__PURE__ */ jsx(SearchIcon, { "aria-hidden": "true" }),
2966
+ /* @__PURE__ */ jsx(
2967
+ "input",
2968
+ {
2969
+ ref: searchRef,
2970
+ className: "sia-select__search",
2971
+ value: query,
2972
+ placeholder: searchPlaceholder,
2973
+ "aria-label": searchPlaceholder,
2974
+ onChange: (event) => setQuery(event.target.value)
2975
+ }
2976
+ ),
2977
+ loading && /* @__PURE__ */ jsx(Spinner, { size: "xs", label: "Chargement" })
2978
+ ] }),
2979
+ /* @__PURE__ */ jsx("div", { id: listboxId, role: "listbox", "aria-busy": loading || void 0, className: "sia-select__options", children: loading && !visibleOptions.length ? /* @__PURE__ */ jsx("div", { className: "sia-select__status", children: loadingText }) : loadError ? /* @__PURE__ */ jsx("div", { className: "sia-select__status sia-select__status--error", children: errorText }) : !visibleOptions.length ? /* @__PURE__ */ jsx("div", { className: "sia-select__status", children: emptyText }) : visibleOptions.map((option, index) => /* @__PURE__ */ jsxs(
2980
+ "button",
2981
+ {
2982
+ id: `${listboxId}-${index}`,
2983
+ type: "button",
2984
+ role: "option",
2985
+ "aria-selected": currentValue === option.value,
2986
+ disabled: option.disabled,
2987
+ className: cn(
2988
+ "sia-select__option",
2989
+ index === activeIndex && "sia-select__option--active"
2990
+ ),
2991
+ onMouseEnter: () => setActiveIndex(index),
2992
+ onClick: () => choose(option),
2993
+ children: [
2994
+ /* @__PURE__ */ jsxs("span", { className: "sia-select__option-content", children: [
2995
+ /* @__PURE__ */ jsx("span", { children: option.label }),
2996
+ option.description && /* @__PURE__ */ jsx("small", { children: option.description })
2997
+ ] }),
2998
+ currentValue === option.value && /* @__PURE__ */ jsx(CheckIcon, { "aria-hidden": "true" })
2999
+ ]
3000
+ },
3001
+ option.value
3002
+ )) })
3003
+ ] })
3004
+ }
3005
+ )
3006
+ ]
3007
+ }
3008
+ );
3009
+ });
3010
+ Select.displayName = "Select";
3011
+ var DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50, 100];
2661
3012
  function Pagination({
2662
3013
  page,
2663
3014
  totalPages,
@@ -2668,6 +3019,11 @@ function Pagination({
2668
3019
  jumpTo = false,
2669
3020
  showTotal = false,
2670
3021
  compact = false,
3022
+ pageSize,
3023
+ onPageSizeChange,
3024
+ pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
3025
+ pageSizeLabel,
3026
+ renderPageSize,
2671
3027
  ariaLabel,
2672
3028
  className
2673
3029
  }) {
@@ -2678,8 +3034,17 @@ function Pagination({
2678
3034
  const current = Math.min(Math.max(page, 1), Math.max(totalPages, 1));
2679
3035
  const [draft, setDraft] = useState(String(current));
2680
3036
  useEffect(() => setDraft(String(current)), [current]);
2681
- if (totalPages <= 1) return null;
2682
- const items = paginationItems(current, totalPages, siblingCount);
3037
+ const tailles = useMemo(() => {
3038
+ if (pageSize === void 0 || pageSizeOptions.includes(pageSize)) {
3039
+ return pageSizeOptions;
3040
+ }
3041
+ return [...pageSizeOptions, pageSize].sort((a, b) => a - b);
3042
+ }, [pageSize, pageSizeOptions]);
3043
+ const choixTaille = pageSize !== void 0 && onPageSizeChange && tailles.length > 0;
3044
+ const plusieursPages = totalPages > 1;
3045
+ const tailleLibelle = pageSizeLabel ?? locale.perPage;
3046
+ if (!plusieursPages && !choixTaille) return null;
3047
+ const items = plusieursPages ? paginationItems(current, totalPages, siblingCount) : [];
2683
3048
  const jump = (event) => {
2684
3049
  event.preventDefault();
2685
3050
  const asked = Number(draft);
@@ -2699,71 +3064,97 @@ function Pagination({
2699
3064
  ),
2700
3065
  "aria-label": ariaLabel ?? "Pagination",
2701
3066
  children: [
2702
- /* @__PURE__ */ jsxs(
2703
- "button",
2704
- {
2705
- type: "button",
2706
- className: "sia-pagination__step",
2707
- disabled: current === 1,
2708
- "aria-label": previousLabel,
2709
- onClick: () => onPageChange(current - 1),
2710
- children: [
2711
- /* @__PURE__ */ jsx(ChevronLeftIcon, {}),
2712
- !compact && /* @__PURE__ */ jsx("span", { children: previousLabel })
2713
- ]
2714
- }
2715
- ),
2716
- items.map(
2717
- (item) => typeof item === "number" ? /* @__PURE__ */ jsx(
3067
+ plusieursPages && /* @__PURE__ */ jsxs("div", { className: "sia-pagination__pages", children: [
3068
+ /* @__PURE__ */ jsxs(
3069
+ "button",
3070
+ {
3071
+ type: "button",
3072
+ className: "sia-pagination__step",
3073
+ disabled: current === 1,
3074
+ "aria-label": previousLabel,
3075
+ onClick: () => onPageChange(current - 1),
3076
+ children: [
3077
+ /* @__PURE__ */ jsx(ChevronLeftIcon, {}),
3078
+ !compact && /* @__PURE__ */ jsx("span", { children: previousLabel })
3079
+ ]
3080
+ }
3081
+ ),
3082
+ items.map(
3083
+ (item) => typeof item === "number" ? /* @__PURE__ */ jsx(
3084
+ "button",
3085
+ {
3086
+ type: "button",
3087
+ className: "sia-pagination__page",
3088
+ "aria-current": item === current ? "page" : void 0,
3089
+ onClick: () => onPageChange(item),
3090
+ children: item
3091
+ },
3092
+ item
3093
+ ) : /* @__PURE__ */ jsx("span", { className: "sia-pagination__gap", "aria-hidden": "true", children: "\u2026" }, item)
3094
+ ),
3095
+ /* @__PURE__ */ jsxs(
2718
3096
  "button",
2719
3097
  {
2720
3098
  type: "button",
2721
- className: "sia-pagination__page",
2722
- "aria-current": item === current ? "page" : void 0,
2723
- onClick: () => onPageChange(item),
2724
- children: item
2725
- },
2726
- item
2727
- ) : /* @__PURE__ */ jsx("span", { className: "sia-pagination__gap", "aria-hidden": "true", children: "\u2026" }, item)
2728
- ),
2729
- /* @__PURE__ */ jsxs(
2730
- "button",
2731
- {
2732
- type: "button",
2733
- className: "sia-pagination__step",
2734
- disabled: current === totalPages,
2735
- "aria-label": nextLabel,
2736
- onClick: () => onPageChange(current + 1),
2737
- children: [
2738
- !compact && /* @__PURE__ */ jsx("span", { children: nextLabel }),
2739
- /* @__PURE__ */ jsx(ChevronRightIcon, {})
2740
- ]
2741
- }
2742
- ),
2743
- showTotal && /* @__PURE__ */ jsxs("span", { className: "sia-pagination__total", children: [
2744
- locale.page,
2745
- " ",
2746
- current,
2747
- " / ",
2748
- totalPages
2749
- ] }),
2750
- jumpTo && /* @__PURE__ */ jsxs("form", { className: "sia-pagination__jump", onSubmit: jump, children: [
2751
- /* @__PURE__ */ jsx("label", { htmlFor: jumpId, children: locale.goToPage }),
2752
- /* @__PURE__ */ jsx(
2753
- "input",
2754
- {
2755
- id: jumpId,
2756
- type: "text",
2757
- inputMode: "numeric",
2758
- value: draft,
2759
- "aria-label": locale.goToPage,
2760
- onChange: (event) => setDraft(event.target.value.replace(/\D/g, "")),
2761
- onBlur: jump
3099
+ className: "sia-pagination__step",
3100
+ disabled: current === totalPages,
3101
+ "aria-label": nextLabel,
3102
+ onClick: () => onPageChange(current + 1),
3103
+ children: [
3104
+ !compact && /* @__PURE__ */ jsx("span", { children: nextLabel }),
3105
+ /* @__PURE__ */ jsx(ChevronRightIcon, {})
3106
+ ]
2762
3107
  }
2763
- ),
2764
- /* @__PURE__ */ jsxs("span", { className: "sia-pagination__jump-total", "aria-hidden": "true", children: [
2765
- "/ ",
3108
+ )
3109
+ ] }),
3110
+ (choixTaille || plusieursPages && (showTotal || jumpTo)) && /* @__PURE__ */ jsxs("div", { className: "sia-pagination__settings", children: [
3111
+ plusieursPages && showTotal && /* @__PURE__ */ jsxs("span", { className: "sia-pagination__total", children: [
3112
+ locale.page,
3113
+ " ",
3114
+ current,
3115
+ " / ",
2766
3116
  totalPages
3117
+ ] }),
3118
+ choixTaille && /* @__PURE__ */ jsx("div", { className: "sia-pagination__size", children: renderPageSize ? renderPageSize({
3119
+ pageSize,
3120
+ options: tailles,
3121
+ onChange: onPageSizeChange,
3122
+ label: tailleLibelle
3123
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3124
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: tailleLibelle }),
3125
+ /* @__PURE__ */ jsx(
3126
+ Select,
3127
+ {
3128
+ variant: "embedded",
3129
+ "aria-label": tailleLibelle,
3130
+ placement: "top-end",
3131
+ value: String(pageSize),
3132
+ options: tailles.map((taille) => ({
3133
+ value: String(taille),
3134
+ label: String(taille)
3135
+ })),
3136
+ onValueChange: (valeur) => onPageSizeChange(Number(valeur))
3137
+ }
3138
+ )
3139
+ ] }) }),
3140
+ plusieursPages && jumpTo && /* @__PURE__ */ jsxs("form", { className: "sia-pagination__jump", onSubmit: jump, children: [
3141
+ /* @__PURE__ */ jsx("label", { htmlFor: jumpId, children: locale.goToPage }),
3142
+ /* @__PURE__ */ jsx(
3143
+ "input",
3144
+ {
3145
+ id: jumpId,
3146
+ type: "text",
3147
+ inputMode: "numeric",
3148
+ value: draft,
3149
+ "aria-label": locale.goToPage,
3150
+ onChange: (event) => setDraft(event.target.value.replace(/\D/g, "")),
3151
+ onBlur: jump
3152
+ }
3153
+ ),
3154
+ /* @__PURE__ */ jsxs("span", { className: "sia-pagination__jump-total", "aria-hidden": "true", children: [
3155
+ "/ ",
3156
+ totalPages
3157
+ ] })
2767
3158
  ] })
2768
3159
  ] })
2769
3160
  ]
@@ -3049,6 +3440,7 @@ function DataTable({
3049
3440
  searchKeys,
3050
3441
  searchPlaceholder,
3051
3442
  onSearch,
3443
+ searchDelay = 0,
3052
3444
  layout = "auto",
3053
3445
  maxHeight,
3054
3446
  stickyHeader = false,
@@ -3167,9 +3559,10 @@ function DataTable({
3167
3559
  {
3168
3560
  value: recherche,
3169
3561
  placeholder: searchPlaceholder ?? locale.search,
3562
+ ...onSearch && searchDelay > 0 ? { onSearch, searchDelay } : {},
3170
3563
  onValueChange: (v) => {
3171
3564
  setRecherche(v);
3172
- onSearch?.(v);
3565
+ if (searchDelay <= 0) onSearch?.(v);
3173
3566
  }
3174
3567
  }
3175
3568
  ),
@@ -3962,255 +4355,6 @@ function DateTimePicker({ value, defaultValue = { date: "", time: "" }, onValueC
3962
4355
  /* @__PURE__ */ jsx(TimePicker, { ...timeProps, value: current.time, minuteStep, secondStep, showSeconds, hourFormat, needConfirm, tone, ...stateProps, "aria-label": "Heure", onValueChange: (time) => update({ ...current, time }) })
3963
4356
  ] });
3964
4357
  }
3965
- function optionsFromChildren(children) {
3966
- return Children.toArray(children).flatMap((child) => {
3967
- if (!isValidElement(child))
3968
- return [];
3969
- const value = child.props.value;
3970
- if (value === void 0) return [];
3971
- return [{
3972
- value: String(value),
3973
- label: child.props.children,
3974
- ...child.props.disabled !== void 0 ? { disabled: child.props.disabled } : {}
3975
- }];
3976
- });
3977
- }
3978
- function optionText(option) {
3979
- return [option.label, option.description, ...option.keywords ?? []].map((value) => typeof value === "string" || typeof value === "number" ? String(value) : "").join(" ").toLocaleLowerCase();
3980
- }
3981
- var Select = forwardRef(function Select2({
3982
- id,
3983
- name,
3984
- options,
3985
- children,
3986
- value,
3987
- defaultValue = "",
3988
- onValueChange,
3989
- placeholder = "S\xE9lectionner",
3990
- searchable = false,
3991
- searchPlaceholder = "Rechercher...",
3992
- fetcher,
3993
- debounce = 250,
3994
- loadingText = "Chargement des options...",
3995
- emptyText = "Aucun r\xE9sultat",
3996
- errorText = "Impossible de charger les options",
3997
- clearable = false,
3998
- disabled = false,
3999
- invalid = false,
4000
- required = false,
4001
- placement = "bottom-start",
4002
- className,
4003
- "aria-invalid": ariaInvalid,
4004
- "aria-required": ariaRequired,
4005
- ...ariaProps
4006
- }, forwardedRef) {
4007
- const generatedId = useId();
4008
- const controlId = id ?? `sia-select-${generatedId.replace(/:/g, "")}`;
4009
- const listboxId = `${controlId}-listbox`;
4010
- const triggerRef = useRef(null);
4011
- const searchRef = useRef(null);
4012
- const staticOptions = useMemo(
4013
- () => options ?? optionsFromChildren(children),
4014
- [children, options]
4015
- );
4016
- const [internalValue, setInternalValue] = useState(defaultValue);
4017
- const [open, setOpen] = useState(false);
4018
- const [query, setQuery] = useState("");
4019
- const [remoteOptions, setRemoteOptions] = useState([]);
4020
- const [selectedRemoteOption, setSelectedRemoteOption] = useState();
4021
- const [loading, setLoading] = useState(false);
4022
- const [loadError, setLoadError] = useState(false);
4023
- const [activeIndex, setActiveIndex] = useState(-1);
4024
- const currentValue = value ?? internalValue;
4025
- const sourceOptions = fetcher ? remoteOptions : staticOptions;
4026
- const visibleOptions = useMemo(() => {
4027
- if (fetcher) return sourceOptions;
4028
- const needle = query.trim().toLocaleLowerCase();
4029
- return needle ? sourceOptions.filter((option) => optionText(option).includes(needle)) : sourceOptions;
4030
- }, [fetcher, query, sourceOptions]);
4031
- const selectedOption = [...staticOptions, ...remoteOptions].find(
4032
- (option) => option.value === currentValue
4033
- ) ?? (selectedRemoteOption?.value === currentValue ? selectedRemoteOption : void 0);
4034
- useEffect(() => {
4035
- if (!open || !fetcher) return;
4036
- const controller = new AbortController();
4037
- const timeout = window.setTimeout(async () => {
4038
- setLoading(true);
4039
- setLoadError(false);
4040
- try {
4041
- const nextOptions = await fetcher(query, controller.signal);
4042
- if (!controller.signal.aborted) setRemoteOptions(nextOptions);
4043
- } catch {
4044
- if (!controller.signal.aborted) setLoadError(true);
4045
- } finally {
4046
- if (!controller.signal.aborted) setLoading(false);
4047
- }
4048
- }, debounce);
4049
- return () => {
4050
- window.clearTimeout(timeout);
4051
- controller.abort();
4052
- };
4053
- }, [debounce, fetcher, open, query]);
4054
- useEffect(() => {
4055
- if (!open) return;
4056
- setActiveIndex(visibleOptions.findIndex((option) => option.value === currentValue));
4057
- if (searchable || fetcher) window.setTimeout(() => searchRef.current?.focus(), 0);
4058
- }, [currentValue, fetcher, open, searchable, visibleOptions]);
4059
- const setTriggerRef = (node) => {
4060
- triggerRef.current = node;
4061
- if (typeof forwardedRef === "function") forwardedRef(node);
4062
- else if (forwardedRef) forwardedRef.current = node;
4063
- };
4064
- const changeValue = (nextValue, option) => {
4065
- if (value === void 0) setInternalValue(nextValue);
4066
- onValueChange?.(nextValue, option);
4067
- };
4068
- const choose = (option) => {
4069
- if (option.disabled) return;
4070
- if (fetcher) setSelectedRemoteOption(option);
4071
- changeValue(option.value, option);
4072
- setOpen(false);
4073
- setQuery("");
4074
- window.setTimeout(() => triggerRef.current?.focus(), 0);
4075
- };
4076
- const moveActive = (direction) => {
4077
- if (!visibleOptions.length) return;
4078
- let next = activeIndex;
4079
- for (let count = 0; count < visibleOptions.length; count += 1) {
4080
- next = (next + direction + visibleOptions.length) % visibleOptions.length;
4081
- if (!visibleOptions[next]?.disabled) break;
4082
- }
4083
- setActiveIndex(next);
4084
- };
4085
- const handleKeyDown = (event) => {
4086
- if (["ArrowDown", "ArrowUp"].includes(event.key)) {
4087
- event.preventDefault();
4088
- if (!open) setOpen(true);
4089
- else moveActive(event.key === "ArrowDown" ? 1 : -1);
4090
- return;
4091
- }
4092
- if (event.key === "Home" && open) {
4093
- event.preventDefault();
4094
- setActiveIndex(visibleOptions.findIndex((option) => !option.disabled));
4095
- } else if (event.key === "End" && open) {
4096
- event.preventDefault();
4097
- for (let index = visibleOptions.length - 1; index >= 0; index -= 1) {
4098
- if (!visibleOptions[index]?.disabled) {
4099
- setActiveIndex(index);
4100
- break;
4101
- }
4102
- }
4103
- } else if (event.key === "Enter" && open && activeIndex >= 0) {
4104
- event.preventDefault();
4105
- const active = visibleOptions[activeIndex];
4106
- if (active) choose(active);
4107
- } else if (event.key === "Escape" && open) {
4108
- event.preventDefault();
4109
- setOpen(false);
4110
- triggerRef.current?.focus();
4111
- } else if (!open && clearable && currentValue && ["Backspace", "Delete"].includes(event.key)) {
4112
- event.preventDefault();
4113
- changeValue("");
4114
- }
4115
- };
4116
- const showSearch = searchable || Boolean(fetcher);
4117
- const activeOption = activeIndex >= 0 ? visibleOptions[activeIndex] : void 0;
4118
- return /* @__PURE__ */ jsxs("div", { className: cn("sia-select", disabled && "sia-select--disabled", className), children: [
4119
- name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: currentValue }),
4120
- /* @__PURE__ */ jsxs(
4121
- "button",
4122
- {
4123
- ref: setTriggerRef,
4124
- id: controlId,
4125
- type: "button",
4126
- className: cn("sia-select__trigger", invalid && "sia-select__trigger--invalid"),
4127
- role: "combobox",
4128
- "aria-haspopup": "listbox",
4129
- "aria-expanded": open,
4130
- "aria-controls": listboxId,
4131
- "aria-activedescendant": open && activeOption ? `${listboxId}-${activeIndex}` : void 0,
4132
- "aria-invalid": ariaInvalid ?? (invalid || void 0),
4133
- "aria-required": ariaRequired ?? (required || void 0),
4134
- disabled,
4135
- onClick: () => setOpen((current) => !current),
4136
- onKeyDown: handleKeyDown,
4137
- ...ariaProps,
4138
- children: [
4139
- /* @__PURE__ */ jsx("span", { className: cn("sia-select__value", !selectedOption && "sia-select__placeholder"), children: selectedOption?.label ?? placeholder }),
4140
- /* @__PURE__ */ jsxs("span", { className: "sia-select__actions", children: [
4141
- clearable && currentValue && !disabled && /* @__PURE__ */ jsx(
4142
- "span",
4143
- {
4144
- className: "sia-select__clear",
4145
- "aria-hidden": "true",
4146
- title: "Effacer la s\xE9lection",
4147
- onClick: (event) => {
4148
- event.stopPropagation();
4149
- changeValue("");
4150
- },
4151
- children: /* @__PURE__ */ jsx(XIcon, {})
4152
- }
4153
- ),
4154
- /* @__PURE__ */ jsx(ChevronDownIcon, { className: "sia-select__chevron", "aria-hidden": "true" })
4155
- ] })
4156
- ]
4157
- }
4158
- ),
4159
- /* @__PURE__ */ jsx(
4160
- Overlay,
4161
- {
4162
- open,
4163
- anchorRef: triggerRef,
4164
- onOpenChange: setOpen,
4165
- placement,
4166
- matchAnchorWidth: true,
4167
- className: "sia-select__overlay",
4168
- children: /* @__PURE__ */ jsxs("div", { className: "sia-select__panel", onKeyDown: handleKeyDown, children: [
4169
- showSearch && /* @__PURE__ */ jsxs("div", { className: "sia-select__search-wrap", children: [
4170
- /* @__PURE__ */ jsx(SearchIcon, { "aria-hidden": "true" }),
4171
- /* @__PURE__ */ jsx(
4172
- "input",
4173
- {
4174
- ref: searchRef,
4175
- className: "sia-select__search",
4176
- value: query,
4177
- placeholder: searchPlaceholder,
4178
- "aria-label": searchPlaceholder,
4179
- onChange: (event) => setQuery(event.target.value)
4180
- }
4181
- ),
4182
- loading && /* @__PURE__ */ jsx(Spinner, { size: "xs", label: "Chargement" })
4183
- ] }),
4184
- /* @__PURE__ */ jsx("div", { id: listboxId, role: "listbox", "aria-busy": loading || void 0, className: "sia-select__options", children: loading && !visibleOptions.length ? /* @__PURE__ */ jsx("div", { className: "sia-select__status", children: loadingText }) : loadError ? /* @__PURE__ */ jsx("div", { className: "sia-select__status sia-select__status--error", children: errorText }) : !visibleOptions.length ? /* @__PURE__ */ jsx("div", { className: "sia-select__status", children: emptyText }) : visibleOptions.map((option, index) => /* @__PURE__ */ jsxs(
4185
- "button",
4186
- {
4187
- id: `${listboxId}-${index}`,
4188
- type: "button",
4189
- role: "option",
4190
- "aria-selected": currentValue === option.value,
4191
- disabled: option.disabled,
4192
- className: cn(
4193
- "sia-select__option",
4194
- index === activeIndex && "sia-select__option--active"
4195
- ),
4196
- onMouseEnter: () => setActiveIndex(index),
4197
- onClick: () => choose(option),
4198
- children: [
4199
- /* @__PURE__ */ jsxs("span", { className: "sia-select__option-content", children: [
4200
- /* @__PURE__ */ jsx("span", { children: option.label }),
4201
- option.description && /* @__PURE__ */ jsx("small", { children: option.description })
4202
- ] }),
4203
- currentValue === option.value && /* @__PURE__ */ jsx(CheckIcon, { "aria-hidden": "true" })
4204
- ]
4205
- },
4206
- option.value
4207
- )) })
4208
- ] })
4209
- }
4210
- )
4211
- ] });
4212
- });
4213
- Select.displayName = "Select";
4214
4358
  function MultiSelect({
4215
4359
  id,
4216
4360
  options,
@@ -5197,6 +5341,14 @@ function Field(rawProps) {
5197
5341
  function isGroup(entry) {
5198
5342
  return "group" in entry;
5199
5343
  }
5344
+ function formFieldNames(fields) {
5345
+ const noms = [];
5346
+ for (const entree of fields) {
5347
+ if (isGroup(entree)) noms.push(...entree.fields.map((f) => f.name));
5348
+ else noms.push(entree.name);
5349
+ }
5350
+ return noms;
5351
+ }
5200
5352
  var VIDE = {};
5201
5353
  function Form({
5202
5354
  fields,
@@ -5221,6 +5373,26 @@ function Form({
5221
5373
  ...onSubmit ? { onSubmit } : {}
5222
5374
  });
5223
5375
  const form = adapter ?? interne;
5376
+ const locale = useSiaLocale();
5377
+ const [echec, setEchec] = useState(null);
5378
+ const envoyer = () => {
5379
+ setEchec(null);
5380
+ form.submit().catch((error) => {
5381
+ const { fields: erreurs, message } = readSubmitError(error);
5382
+ const connus = new Set(formFieldNames(fields));
5383
+ const sousLesChamps = {};
5384
+ const ailleurs = [];
5385
+ for (const [nom, texte] of Object.entries(erreurs)) {
5386
+ if (connus.has(nom)) sousLesChamps[nom] = texte;
5387
+ else ailleurs.push(texte);
5388
+ }
5389
+ form.setErrors(sousLesChamps);
5390
+ if (ailleurs.length > 0) setEchec(ailleurs.join(" "));
5391
+ else if (Object.keys(sousLesChamps).length === 0) {
5392
+ setEchec(message ?? locale.submitError);
5393
+ }
5394
+ });
5395
+ };
5224
5396
  const busy = loading || form.state.isSubmitting;
5225
5397
  const submitDisabled = busy || requireDirty && !form.state.isDirty || requireValid && !form.state.isValid;
5226
5398
  const grille = (nombre) => ({
@@ -5256,7 +5428,7 @@ function Form({
5256
5428
  noValidate: true,
5257
5429
  onSubmit: (event) => {
5258
5430
  event.preventDefault();
5259
- void form.submit();
5431
+ envoyer();
5260
5432
  },
5261
5433
  children: [
5262
5434
  /* @__PURE__ */ jsx("div", { className: "sia-form-grid", style: grille(columns), children: fields.map(
@@ -5275,6 +5447,15 @@ function Form({
5275
5447
  )
5276
5448
  ] }, entry.group) : rendre(entry)
5277
5449
  ) }),
5450
+ echec && /* @__PURE__ */ jsx(
5451
+ Alert,
5452
+ {
5453
+ tone: "danger",
5454
+ className: "sia-form-error",
5455
+ onDismiss: () => setEchec(null),
5456
+ children: echec
5457
+ }
5458
+ ),
5278
5459
  actions ?? /* @__PURE__ */ jsxs("div", { className: "sia-form-actions", children: [
5279
5460
  showClear && /* @__PURE__ */ jsx(
5280
5461
  Button,
@@ -5310,23 +5491,15 @@ function Descriptions({ items, title, columns = 2, bordered = false, className }
5310
5491
  ] }, item.key)) })
5311
5492
  ] });
5312
5493
  }
5313
- function nomsDeChamps(fields) {
5314
- const noms = [];
5315
- for (const entree of fields) {
5316
- if ("group" in entree) noms.push(...entree.fields.map((f) => f.name));
5317
- else noms.push(entree.name);
5318
- }
5319
- return noms;
5320
- }
5321
5494
  function valeursVides(fields) {
5322
5495
  const valeurs = {};
5323
- for (const nom of nomsDeChamps(fields)) valeurs[nom] = "";
5496
+ for (const nom of formFieldNames(fields)) valeurs[nom] = "";
5324
5497
  return valeurs;
5325
5498
  }
5326
5499
  function valeursDeLigne(row, fields) {
5327
5500
  const source = row;
5328
5501
  const valeurs = {};
5329
- for (const nom of nomsDeChamps(fields)) {
5502
+ for (const nom of formFieldNames(fields)) {
5330
5503
  const valeur = source[nom];
5331
5504
  valeurs[nom] = typeof valeur === "string" || typeof valeur === "number" || typeof valeur === "boolean" || Array.isArray(valeur) ? valeur : "";
5332
5505
  }
@@ -5371,11 +5544,12 @@ function CrudDialogs({
5371
5544
  submitText: dialog?.submitText ?? (creation ? "Cr\xE9er" : "Enregistrer"),
5372
5545
  requireDirty: modification,
5373
5546
  onSubmit: async (values) => {
5374
- await onSubmit?.(values, {
5547
+ const resultat = await onSubmit?.(values, {
5375
5548
  mode: creation ? "create" : "edit",
5376
5549
  ...state?.row !== void 0 ? { row: state.row } : {},
5377
5550
  ...state?.index !== void 0 ? { index: state.index } : {}
5378
5551
  });
5552
+ if (hasFormErrors(resultat)) return resultat;
5379
5553
  onClose();
5380
5554
  }
5381
5555
  },
@@ -5459,6 +5633,7 @@ function CrudPage({
5459
5633
  searchKeys: clesDeRecherche,
5460
5634
  searchPlaceholder,
5461
5635
  onSearch,
5636
+ searchDelay,
5462
5637
  loading = false,
5463
5638
  error,
5464
5639
  onRetry,
@@ -5466,6 +5641,9 @@ function CrudPage({
5466
5641
  page,
5467
5642
  totalPages,
5468
5643
  onPageChange,
5644
+ pageSize,
5645
+ onPageSizeChange,
5646
+ pageSizeOptions,
5469
5647
  table,
5470
5648
  children,
5471
5649
  className
@@ -5545,9 +5723,12 @@ function CrudPage({
5545
5723
  if (!extraRowActions) {
5546
5724
  return actionsDeLigne.length > 0 ? actionsDeLigne : void 0;
5547
5725
  }
5726
+ const suppression = actionsDeLigne.filter((a) => a.key === "delete");
5727
+ const autres = actionsDeLigne.filter((a) => a.key !== "delete");
5548
5728
  return (row, index) => [
5549
- ...actionsDeLigne,
5550
- ...typeof extraRowActions === "function" ? extraRowActions(row, index) : extraRowActions
5729
+ ...autres,
5730
+ ...typeof extraRowActions === "function" ? extraRowActions(row, index) : extraRowActions,
5731
+ ...suppression
5551
5732
  ];
5552
5733
  }, [actionsDeLigne, extraRowActions]);
5553
5734
  const creation = operations?.create === false ? null : operations?.create ?? {};
@@ -5581,6 +5762,7 @@ function CrudPage({
5581
5762
  ...searchKeys ? { searchKeys } : {},
5582
5763
  ...searchPlaceholder ? { searchPlaceholder } : {},
5583
5764
  ...onSearch ? { onSearch } : {},
5765
+ ...searchDelay !== void 0 ? { searchDelay } : {},
5584
5766
  ...toutesLesActions ? { rowActions: toutesLesActions } : {},
5585
5767
  ...can ? { can } : {},
5586
5768
  loading,
@@ -5643,7 +5825,10 @@ function CrudPage({
5643
5825
  page,
5644
5826
  totalPages,
5645
5827
  onPageChange,
5646
- showTotal: true
5828
+ showTotal: true,
5829
+ ...pageSize !== void 0 ? { pageSize } : {},
5830
+ ...onPageSizeChange ? { onPageSizeChange } : {},
5831
+ ...pageSizeOptions ? { pageSizeOptions } : {}
5647
5832
  }
5648
5833
  )
5649
5834
  ] });
@@ -6106,6 +6291,14 @@ var PhoneInput = forwardRef(
6106
6291
  [countries, initial, picked, value]
6107
6292
  );
6108
6293
  const national = value.startsWith(country.dial) ? value.slice(country.dial.length) : digitsOnly(value);
6294
+ const options = useMemo(
6295
+ () => countries.map((entry) => ({
6296
+ value: entry.code,
6297
+ label: `${entry.flag} ${entry.label} (${entry.dial})`,
6298
+ keywords: [entry.code]
6299
+ })),
6300
+ [countries]
6301
+ );
6109
6302
  const commit = (next) => {
6110
6303
  if (!controlled) setInternal(next);
6111
6304
  onValueChange?.(next);
@@ -6127,35 +6320,31 @@ var PhoneInput = forwardRef(
6127
6320
  if (country.digits) next = next.slice(0, country.digits);
6128
6321
  commit(next ? `${country.dial}${next}` : "");
6129
6322
  },
6130
- left: /* @__PURE__ */ jsxs("span", { className: "sia-phone__country", children: [
6323
+ left: lockCountry ? /* @__PURE__ */ jsxs("span", { className: "sia-phone__country", children: [
6131
6324
  /* @__PURE__ */ jsx("span", { className: "sia-phone__flag", "aria-hidden": "true", children: country.flag }),
6132
- /* @__PURE__ */ jsx("span", { className: "sia-phone__dial", children: country.dial }),
6133
- !lockCountry && /* @__PURE__ */ jsx(
6134
- "select",
6135
- {
6136
- className: "sia-phone__select",
6137
- "aria-label": locale.countryCode,
6138
- value: country.code,
6139
- disabled,
6140
- onChange: (event) => {
6141
- const next = countries.find(
6142
- (entry) => entry.code === event.target.value
6143
- );
6144
- if (!next) return;
6145
- setPicked(next.code);
6146
- commit(national ? `${next.dial}${national}` : "");
6147
- },
6148
- children: countries.map((entry) => /* @__PURE__ */ jsxs("option", { value: entry.code, children: [
6149
- entry.flag,
6150
- " ",
6151
- entry.label,
6152
- " (",
6153
- entry.dial,
6154
- ")"
6155
- ] }, entry.code))
6325
+ /* @__PURE__ */ jsx("span", { className: "sia-phone__dial", children: country.dial })
6326
+ ] }) : /* @__PURE__ */ jsx(
6327
+ Select,
6328
+ {
6329
+ variant: "embedded",
6330
+ searchable: true,
6331
+ className: "sia-phone__country",
6332
+ "aria-label": locale.countryCode,
6333
+ options,
6334
+ value: country.code,
6335
+ disabled: disabled ?? false,
6336
+ renderValue: () => /* @__PURE__ */ jsxs("span", { className: "sia-phone__value", children: [
6337
+ /* @__PURE__ */ jsx("span", { className: "sia-phone__flag", "aria-hidden": "true", children: country.flag }),
6338
+ /* @__PURE__ */ jsx("span", { className: "sia-phone__dial", children: country.dial })
6339
+ ] }),
6340
+ onValueChange: (code) => {
6341
+ const next = countries.find((entry) => entry.code === code);
6342
+ if (!next) return;
6343
+ setPicked(next.code);
6344
+ commit(national ? `${next.dial}${national}` : "");
6156
6345
  }
6157
- )
6158
- ] })
6346
+ }
6347
+ )
6159
6348
  }
6160
6349
  );
6161
6350
  }
@@ -7107,4 +7296,4 @@ function YearPicker({ value, defaultValue = (/* @__PURE__ */ new Date()).getFull
7107
7296
  return /* @__PURE__ */ jsx("select", { className: cn("sia-year-picker", className), value, defaultValue: value === void 0 ? defaultValue : void 0, onChange: (event) => onValueChange?.(Number(event.target.value)), children: years.map((year) => /* @__PURE__ */ jsx("option", { value: year, children: year }, year)) });
7108
7297
  }
7109
7298
 
7110
- export { Accordion, Alert, AlertCircleIcon, AmountDisplay, AppShell, ArrowRightIcon, AspectRatio, AsyncState, AuditMeta, AuthLayout, Autocomplete, Avatar, AvatarGroup, Badge, BottomTabs, Breadcrumb, Button, Calendar, CalendarIcon, Can, Card, CardBody, CardFooter, CardHeader, Chart, CheckIcon, Checkbox, CheckboxGroup, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ClockIcon, Collapse, ColorPicker, CommandPalette, ConfirmDialog, Container, ContextMenu, Countdown, CrudDialogs, CrudPage, CurrencyInput, DEFAULT_COUNTRIES, DataTable, DatePicker, DateRangeFilter, DateRangePicker, DateTimePicker, Descriptions, Divider, Drawer, DrawerBody, DrawerClose, DrawerFooter, DrawerHeader, DrawerTitle, DropdownMenu, DurationDisplay, EmptyState, EntityMeta, ErrorState, EventCalendar, EyeIcon, EyeOffIcon, Field, FileUpload, FiltersBar, Form, Field as FormField, Heading, HoverCard, IconButton, ImageUpload, InfoIcon, InlineCode, Input, InputNumber, JsonEditor, KpiGrid, Label, MarkdownEditor, MenuList, MiniCalendar, MinusIcon, Modal, MonthPicker, MoreHorizontalIcon, MultiSelect, OtpInput, Overlay, PageHeader, Pagination, PasswordInput, PermissionGate, PhoneInput, PlusIcon, Popover, Progress, QRCode, Radio, RadioButton, RadioGroup, Rating, ReferenceSelect, RelativeTime, RequireSession, ResizablePanel, RichTextEditor, ScrollArea, SearchIcon, SearchInput, Select, Sidebar, Skeleton, Slider, Sparkline, Spinner, Stack, StarIcon, StatCard, Statistic, Steps, Switch, Tabs, TabsContent, TabsList, TabsTrigger, TagsInput, Text, Textarea, TimePicker, TimeRangePicker, Timeline, Timer, ToastProvider, Toaster, Toggle, ToggleGroup, Tooltip, Tour, Transfer, TrashIcon, Tree, TreeSelect, UnsavedChangesGuard, UploadIcon, WeekPicker, XIcon, YearPicker, confirmUnsavedNavigation, createUrlTableStorage, defineResource, formatDuration, formatTimeValue, initialsOf, parseTimeValue, resourceColumns, resourceDefaults, resourceDetailColumns, resourceFormFields, resourcePermissions, resourceRowKey, resourceSearchKeys, resourceValidator, tintForName, useDataTableQuery, useToast };
7299
+ export { Accordion, Alert, AlertCircleIcon, AmountDisplay, AppShell, AppShellBrand, AppShellRailItem, ArrowRightIcon, AspectRatio, AsyncState, AuditMeta, AuthLayout, Autocomplete, Avatar, AvatarGroup, Badge, BottomTabs, Breadcrumb, Button, Calendar, CalendarIcon, Can, Card, CardBody, CardFooter, CardHeader, Chart, CheckIcon, Checkbox, CheckboxGroup, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ClockIcon, Collapse, ColorPicker, CommandPalette, ConfirmDialog, Container, ContextMenu, Countdown, CrudDialogs, CrudPage, CurrencyInput, DEFAULT_COUNTRIES, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, DatePicker, DateRangeFilter, DateRangePicker, DateTimePicker, Descriptions, Divider, Drawer, DrawerBody, DrawerClose, DrawerFooter, DrawerHeader, DrawerTitle, DropdownMenu, DurationDisplay, EmptyState, EntityMeta, ErrorState, EventCalendar, EyeIcon, EyeOffIcon, Field, FileUpload, FiltersBar, Form, Field as FormField, Heading, HoverCard, IconButton, ImageUpload, InfoIcon, InlineCode, Input, InputNumber, JsonEditor, KpiGrid, Label, MarkdownEditor, MenuList, MiniCalendar, MinusIcon, Modal, MonthPicker, MoreHorizontalIcon, MultiSelect, OtpInput, Overlay, PageHeader, Pagination, PasswordInput, PermissionGate, PhoneInput, PlusIcon, Popover, Progress, QRCode, Radio, RadioButton, RadioGroup, Rating, ReferenceSelect, RelativeTime, RequireSession, ResizablePanel, RichTextEditor, ScrollArea, SearchIcon, SearchInput, Select, SettingsIcon, Sidebar, Skeleton, Slider, Sparkline, Spinner, Stack, StarIcon, StatCard, Statistic, Steps, Switch, Tabs, TabsContent, TabsList, TabsTrigger, TagsInput, Text, Textarea, TimePicker, TimeRangePicker, Timeline, Timer, ToastProvider, Toaster, Toggle, ToggleGroup, Tooltip, Tour, Transfer, TrashIcon, Tree, TreeSelect, UnsavedChangesGuard, UploadIcon, WeekPicker, XIcon, YearPicker, confirmUnsavedNavigation, createUrlTableStorage, defineResource, formFieldNames, formatDuration, formatTimeValue, initialsOf, parseTimeValue, resourceColumns, resourceDefaults, resourceDetailColumns, resourceFormFields, resourcePermissions, resourceRowKey, resourceSearchKeys, resourceValidator, tintForName, useDataTableQuery, useToast };