@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.cjs CHANGED
@@ -135,6 +135,12 @@ function InfoIcon(props) {
135
135
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 11v5M12 7.5h.01" })
136
136
  ] });
137
137
  }
138
+ function SettingsIcon(props) {
139
+ return /* @__PURE__ */ jsxRuntime.jsxs(IconBase, { ...props, children: [
140
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "3" }),
141
+ /* @__PURE__ */ jsxRuntime.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" })
142
+ ] });
143
+ }
138
144
  function EyeIcon(props) {
139
145
  return /* @__PURE__ */ jsxRuntime.jsxs(IconBase, { ...props, children: [
140
146
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 12s3.5-6.5 10-6.5S22 12 22 12s-3.5 6.5-10 6.5S2 12 2 12Z" }),
@@ -283,13 +289,15 @@ var Drawer = React.forwardRef(
283
289
  closeOnOutsideClick = true,
284
290
  closeOnEsc = true,
285
291
  children,
292
+ className,
286
293
  ...rest
287
294
  }, ref) => {
288
295
  const {
289
296
  isOpen,
290
297
  phase: state,
291
298
  open: doOpen,
292
- close: doClose
299
+ close: doClose,
300
+ toggle
293
301
  } = headless.useDisclosureState({
294
302
  ...open !== void 0 ? { open } : {},
295
303
  defaultOpen,
@@ -298,15 +306,17 @@ var Drawer = React.forwardRef(
298
306
  });
299
307
  const drawerRef = React.useRef(null);
300
308
  const zIndexRef = React.useRef(null);
309
+ const fermer = React.useRef(doClose);
310
+ fermer.current = doClose;
301
311
  React.useImperativeHandle(
302
312
  ref,
303
313
  () => ({
304
314
  open: doOpen,
305
315
  close: doClose,
306
- toggle: () => isOpen ? doClose() : doOpen(),
316
+ toggle,
307
317
  isOpen: () => isOpen
308
318
  }),
309
- [isOpen, open]
319
+ [isOpen, doOpen, doClose, toggle]
310
320
  );
311
321
  React.useEffect(() => {
312
322
  if (!isOpen) return;
@@ -323,7 +333,7 @@ var Drawer = React.forwardRef(
323
333
  const onKeyDown = (e) => {
324
334
  if (!closeOnEsc || e.key !== "Escape") return;
325
335
  if (headless.overlayStack.isTop(zIndexRef.current)) {
326
- doClose();
336
+ fermer.current();
327
337
  }
328
338
  };
329
339
  window.addEventListener("keydown", onKeyDown);
@@ -342,6 +352,7 @@ var Drawer = React.forwardRef(
342
352
  "div",
343
353
  {
344
354
  className: "sia-drawer-root",
355
+ "data-state": state,
345
356
  style: { zIndex: zIndexRef.current ?? void 0 },
346
357
  children: [
347
358
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-drawer-overlay" }),
@@ -356,7 +367,8 @@ var Drawer = React.forwardRef(
356
367
  "sia-drawer",
357
368
  `sia-drawer-${position}`,
358
369
  `sia-drawer-${size}`,
359
- state === "closing" && "is-closing"
370
+ state === "closing" && "is-closing",
371
+ className
360
372
  ),
361
373
  ...rest,
362
374
  children
@@ -390,7 +402,7 @@ function DrawerClose() {
390
402
  className: "sia-drawer-close",
391
403
  onClick: ctx.close,
392
404
  "aria-label": "Fermer",
393
- children: "\u2715"
405
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { "aria-hidden": "true" })
394
406
  }
395
407
  );
396
408
  }
@@ -623,7 +635,14 @@ function Ligne({
623
635
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
624
636
  item.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-sidebar__icon", "aria-hidden": "true", children: item.icon }),
625
637
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-sidebar__label", children: item.label }),
626
- item.badge !== void 0 && !collapsed && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-sidebar__badge", children: item.badge }),
638
+ item.badge !== void 0 && !collapsed && /* @__PURE__ */ jsxRuntime.jsx(
639
+ "span",
640
+ {
641
+ className: "sia-sidebar__badge",
642
+ "data-sia-badge": typeof item.badge === "string" || typeof item.badge === "number" ? String(item.badge) : void 0,
643
+ children: item.badge
644
+ }
645
+ ),
627
646
  porteUnePastille && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-sidebar__dot", "aria-hidden": "true" }),
628
647
  aDesEnfants && !collapsed && /* @__PURE__ */ jsxRuntime.jsx(
629
648
  "span",
@@ -785,7 +804,14 @@ function NoeudRail(props) {
785
804
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ...panelProps, children: [
786
805
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sia-sidebar-flyout__title", children: [
787
806
  item.label,
788
- item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-sidebar__badge", children: item.badge })
807
+ item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
808
+ "span",
809
+ {
810
+ className: "sia-sidebar__badge",
811
+ "data-sia-badge": typeof item.badge === "string" || typeof item.badge === "number" ? String(item.badge) : void 0,
812
+ children: item.badge
813
+ }
814
+ )
789
815
  ] }),
790
816
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-sidebar-flyout__body", children: enfants.map((enfant) => /* @__PURE__ */ jsxRuntime.jsx(
791
817
  Noeud,
@@ -891,6 +917,50 @@ var BottomTabs = React.memo(function BottomTabs2({
891
917
  )
892
918
  ] });
893
919
  });
920
+ function AppShellBrand({
921
+ icon,
922
+ name,
923
+ description,
924
+ className,
925
+ ...props
926
+ }) {
927
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("sia-shell-brand", className), ...props, children: [
928
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-shell-brand__icon", "aria-hidden": "true", children: icon }),
929
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-shell-brand__copy", children: [
930
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-shell-brand__name", children: name }),
931
+ description !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-shell-brand__description", children: description })
932
+ ] })
933
+ ] });
934
+ }
935
+ function AppShellRailItem({
936
+ as = "div",
937
+ icon,
938
+ label,
939
+ className,
940
+ href,
941
+ type = "button",
942
+ disabled,
943
+ ...props
944
+ }) {
945
+ const Component = as;
946
+ const elementProps = {
947
+ ...props,
948
+ ...as === "a" && href ? { href } : {},
949
+ ...as === "button" ? { type, disabled } : {}
950
+ };
951
+ return /* @__PURE__ */ jsxRuntime.jsxs(
952
+ Component,
953
+ {
954
+ className: utils.cn("sia-shell-rail-item", className),
955
+ "aria-label": props["aria-label"] ?? (as !== "div" ? label : void 0),
956
+ ...elementProps,
957
+ children: [
958
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-shell-rail-item__icon", "aria-hidden": "true", children: icon }),
959
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-shell-rail-item__label", children: label })
960
+ ]
961
+ }
962
+ );
963
+ }
894
964
  function AppShell({
895
965
  nav,
896
966
  children,
@@ -1033,10 +1103,19 @@ function AppShell({
1033
1103
  ]
1034
1104
  }
1035
1105
  ),
1036
- /* @__PURE__ */ jsxRuntime.jsxs("main", { className: utils.cn("sia-shell__content", `sia-shell__content--${contentWidth}`), children: [
1037
- page && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-shell__page", children: page }),
1038
- children
1039
- ] }),
1106
+ /* @__PURE__ */ jsxRuntime.jsxs(
1107
+ "main",
1108
+ {
1109
+ className: utils.cn(
1110
+ "sia-shell__content",
1111
+ `sia-shell__content--${contentWidth}`
1112
+ ),
1113
+ children: [
1114
+ page && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-shell__page", children: page }),
1115
+ children
1116
+ ]
1117
+ }
1118
+ ),
1040
1119
  footer && /* @__PURE__ */ jsxRuntime.jsx("footer", { className: "sia-shell__footer", children: footer })
1041
1120
  ] }),
1042
1121
  compact && montrerOnglets && /* @__PURE__ */ jsxRuntime.jsx(
@@ -1049,7 +1128,7 @@ function AppShell({
1049
1128
  ...montrerTiroir ? { onMore: () => setTiroirOuvert(true) } : {}
1050
1129
  }
1051
1130
  ),
1052
- compact && montrerTiroir && /* @__PURE__ */ jsxRuntime.jsx(
1131
+ compact && montrerTiroir && /* @__PURE__ */ jsxRuntime.jsxs(
1053
1132
  Drawer,
1054
1133
  {
1055
1134
  open: tiroirOuvert,
@@ -1057,7 +1136,13 @@ function AppShell({
1057
1136
  position: side,
1058
1137
  size: "sm",
1059
1138
  className: "sia-shell__drawer",
1060
- children: barre
1139
+ children: [
1140
+ /* @__PURE__ */ jsxRuntime.jsxs(Drawer.Header, { children: [
1141
+ /* @__PURE__ */ jsxRuntime.jsx(Drawer.Title, { children: "Navigation" }),
1142
+ /* @__PURE__ */ jsxRuntime.jsx(Drawer.Close, {})
1143
+ ] }),
1144
+ /* @__PURE__ */ jsxRuntime.jsx(Drawer.Body, { children: barre })
1145
+ ]
1061
1146
  }
1062
1147
  )
1063
1148
  ]
@@ -1958,9 +2043,9 @@ function Calendar({
1958
2043
  if (mode === void 0) setPanelMode(next);
1959
2044
  onPanelChange?.(monthIso(controlledMonth), next);
1960
2045
  };
1961
- const isDisabled = (date) => Boolean(
2046
+ const isDisabled = React.useCallback((date) => Boolean(
1962
2047
  disabledDate?.(date) || validRange && (date < validRange[0] || date > validRange[1])
1963
- );
2048
+ ), [disabledDate, validRange]);
1964
2049
  const selectDate = (date) => {
1965
2050
  if (isDisabled(date)) return;
1966
2051
  if (selectionMode === "range") {
@@ -1994,7 +2079,7 @@ function Calendar({
1994
2079
  ...rangeEdge ? { rangeEdge } : {}
1995
2080
  };
1996
2081
  });
1997
- }, [year, monthIndex, firstDayOfWeek, disabledDate, validRange, currentValue, currentRange.start, currentRange.end]);
2082
+ }, [year, monthIndex, firstDayOfWeek, isDisabled, currentValue, currentRange.start, currentRange.end]);
1998
2083
  const weekdays = Array.from({ length: 7 }, (_, index) => new Intl.DateTimeFormat(locale, { weekday: "short" }).format(new Date(2024, 0, 7 + firstDayOfWeek + index)));
1999
2084
  const monthName = new Intl.DateTimeFormat(locale, { month: "long" }).format(controlledMonth);
2000
2085
  const updateFromString = (next) => setMonth(parseMonth(next));
@@ -2641,6 +2726,8 @@ function Timer({ duration = 6e4, initialValue = 0, direction = "down", running =
2641
2726
  const initial = direction === "down" ? duration : initialValue;
2642
2727
  const [value, setValue] = React.useState(initial);
2643
2728
  const finishCalled = React.useRef(false);
2729
+ const latest = React.useRef({ value, onChange, onFinish });
2730
+ latest.current = { value, onChange, onFinish };
2644
2731
  React.useEffect(() => {
2645
2732
  setValue(direction === "down" ? duration : initialValue);
2646
2733
  finishCalled.current = false;
@@ -2648,15 +2735,15 @@ function Timer({ duration = 6e4, initialValue = 0, direction = "down", running =
2648
2735
  React.useEffect(() => {
2649
2736
  if (!running) return;
2650
2737
  const startedAt = Date.now();
2651
- const base = value;
2738
+ const base = latest.current.value;
2652
2739
  const update = () => {
2653
2740
  const elapsed = Date.now() - startedAt;
2654
2741
  const next = direction === "down" ? Math.max(0, base - elapsed) : base + elapsed;
2655
2742
  setValue(next);
2656
- onChange?.(next);
2743
+ latest.current.onChange?.(next);
2657
2744
  if (direction === "down" && next === 0 && !finishCalled.current) {
2658
2745
  finishCalled.current = true;
2659
- onFinish?.();
2746
+ latest.current.onFinish?.();
2660
2747
  }
2661
2748
  };
2662
2749
  update();
@@ -2678,6 +2765,270 @@ function PageHeader({ title, description, eyebrow, actions, className }) {
2678
2765
  actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-page-header__actions", children: actions })
2679
2766
  ] });
2680
2767
  }
2768
+ function optionsFromChildren(children) {
2769
+ return React.Children.toArray(children).flatMap((child) => {
2770
+ if (!React.isValidElement(child))
2771
+ return [];
2772
+ const value = child.props.value;
2773
+ if (value === void 0) return [];
2774
+ return [{
2775
+ value: String(value),
2776
+ label: child.props.children,
2777
+ ...child.props.disabled !== void 0 ? { disabled: child.props.disabled } : {}
2778
+ }];
2779
+ });
2780
+ }
2781
+ function optionText(option) {
2782
+ return [option.label, option.description, ...option.keywords ?? []].map((value) => typeof value === "string" || typeof value === "number" ? String(value) : "").join(" ");
2783
+ }
2784
+ var Select = React.forwardRef(function Select2({
2785
+ id,
2786
+ name,
2787
+ options,
2788
+ children,
2789
+ value,
2790
+ defaultValue = "",
2791
+ onValueChange,
2792
+ placeholder = "S\xE9lectionner",
2793
+ searchable = false,
2794
+ searchPlaceholder = "Rechercher...",
2795
+ fetcher,
2796
+ debounce = 250,
2797
+ loadingText = "Chargement des options...",
2798
+ emptyText = "Aucun r\xE9sultat",
2799
+ errorText = "Impossible de charger les options",
2800
+ clearable = false,
2801
+ disabled = false,
2802
+ invalid = false,
2803
+ required = false,
2804
+ placement = "bottom-start",
2805
+ renderValue,
2806
+ variant = "default",
2807
+ className,
2808
+ "aria-invalid": ariaInvalid,
2809
+ "aria-required": ariaRequired,
2810
+ ...ariaProps
2811
+ }, forwardedRef) {
2812
+ const generatedId = React.useId();
2813
+ const controlId = id ?? `sia-select-${generatedId.replace(/:/g, "")}`;
2814
+ const listboxId = `${controlId}-listbox`;
2815
+ const triggerRef = React.useRef(null);
2816
+ const searchRef = React.useRef(null);
2817
+ const staticOptions = React.useMemo(
2818
+ () => options ?? optionsFromChildren(children),
2819
+ [children, options]
2820
+ );
2821
+ const [internalValue, setInternalValue] = React.useState(defaultValue);
2822
+ const [open, setOpen] = React.useState(false);
2823
+ const [query, setQuery] = React.useState("");
2824
+ const [remoteOptions, setRemoteOptions] = React.useState([]);
2825
+ const [selectedRemoteOption, setSelectedRemoteOption] = React.useState();
2826
+ const [loading, setLoading] = React.useState(false);
2827
+ const [loadError, setLoadError] = React.useState(false);
2828
+ const [activeIndex, setActiveIndex] = React.useState(-1);
2829
+ const currentValue = value ?? internalValue;
2830
+ const sourceOptions = fetcher ? remoteOptions : staticOptions;
2831
+ const visibleOptions = React.useMemo(() => {
2832
+ if (fetcher) return sourceOptions;
2833
+ const needle = query.trim();
2834
+ return needle ? sourceOptions.filter((option) => utils.looseIncludes(optionText(option), needle)) : sourceOptions;
2835
+ }, [fetcher, query, sourceOptions]);
2836
+ const selectedOption = [...staticOptions, ...remoteOptions].find(
2837
+ (option) => option.value === currentValue
2838
+ ) ?? (selectedRemoteOption?.value === currentValue ? selectedRemoteOption : void 0);
2839
+ React.useEffect(() => {
2840
+ if (!open || !fetcher) return;
2841
+ const controller = new AbortController();
2842
+ const timeout = window.setTimeout(async () => {
2843
+ setLoading(true);
2844
+ setLoadError(false);
2845
+ try {
2846
+ const nextOptions = await fetcher(query, controller.signal);
2847
+ if (!controller.signal.aborted) setRemoteOptions(nextOptions);
2848
+ } catch {
2849
+ if (!controller.signal.aborted) setLoadError(true);
2850
+ } finally {
2851
+ if (!controller.signal.aborted) setLoading(false);
2852
+ }
2853
+ }, debounce);
2854
+ return () => {
2855
+ window.clearTimeout(timeout);
2856
+ controller.abort();
2857
+ };
2858
+ }, [debounce, fetcher, open, query]);
2859
+ React.useEffect(() => {
2860
+ if (!open) return;
2861
+ const selected = visibleOptions.findIndex((option) => option.value === currentValue);
2862
+ setActiveIndex(selected >= 0 ? selected : visibleOptions.findIndex((option) => !option.disabled));
2863
+ if (searchable || fetcher) window.setTimeout(() => searchRef.current?.focus(), 0);
2864
+ }, [currentValue, fetcher, open, searchable, visibleOptions]);
2865
+ const setTriggerRef = (node) => {
2866
+ triggerRef.current = node;
2867
+ if (typeof forwardedRef === "function") forwardedRef(node);
2868
+ else if (forwardedRef) forwardedRef.current = node;
2869
+ };
2870
+ const changeValue = (nextValue, option) => {
2871
+ if (value === void 0) setInternalValue(nextValue);
2872
+ onValueChange?.(nextValue, option);
2873
+ };
2874
+ const choose = (option) => {
2875
+ if (option.disabled) return;
2876
+ if (fetcher) setSelectedRemoteOption(option);
2877
+ changeValue(option.value, option);
2878
+ setOpen(false);
2879
+ setQuery("");
2880
+ window.setTimeout(() => triggerRef.current?.focus(), 0);
2881
+ };
2882
+ const moveActive = (direction) => {
2883
+ if (!visibleOptions.length) return;
2884
+ let next = activeIndex;
2885
+ for (let count = 0; count < visibleOptions.length; count += 1) {
2886
+ next = (next + direction + visibleOptions.length) % visibleOptions.length;
2887
+ if (!visibleOptions[next]?.disabled) break;
2888
+ }
2889
+ setActiveIndex(next);
2890
+ };
2891
+ const handleKeyDown = (event) => {
2892
+ if (["ArrowDown", "ArrowUp"].includes(event.key)) {
2893
+ event.preventDefault();
2894
+ if (!open) setOpen(true);
2895
+ else moveActive(event.key === "ArrowDown" ? 1 : -1);
2896
+ return;
2897
+ }
2898
+ if (event.key === "Home" && open) {
2899
+ event.preventDefault();
2900
+ setActiveIndex(visibleOptions.findIndex((option) => !option.disabled));
2901
+ } else if (event.key === "End" && open) {
2902
+ event.preventDefault();
2903
+ for (let index = visibleOptions.length - 1; index >= 0; index -= 1) {
2904
+ if (!visibleOptions[index]?.disabled) {
2905
+ setActiveIndex(index);
2906
+ break;
2907
+ }
2908
+ }
2909
+ } else if (event.key === "Enter" && open && activeIndex >= 0) {
2910
+ event.preventDefault();
2911
+ const active = visibleOptions[activeIndex];
2912
+ if (active) choose(active);
2913
+ } else if (event.key === "Escape" && open) {
2914
+ event.preventDefault();
2915
+ setOpen(false);
2916
+ triggerRef.current?.focus();
2917
+ } else if (!open && clearable && currentValue && ["Backspace", "Delete"].includes(event.key)) {
2918
+ event.preventDefault();
2919
+ changeValue("");
2920
+ }
2921
+ };
2922
+ const showSearch = searchable || Boolean(fetcher);
2923
+ const activeOption = activeIndex >= 0 ? visibleOptions[activeIndex] : void 0;
2924
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2925
+ "div",
2926
+ {
2927
+ className: utils.cn(
2928
+ "sia-select",
2929
+ variant === "embedded" && "sia-select--embedded",
2930
+ disabled && "sia-select--disabled",
2931
+ className
2932
+ ),
2933
+ children: [
2934
+ name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: currentValue }),
2935
+ /* @__PURE__ */ jsxRuntime.jsxs(
2936
+ "button",
2937
+ {
2938
+ ref: setTriggerRef,
2939
+ id: controlId,
2940
+ type: "button",
2941
+ className: utils.cn("sia-select__trigger", invalid && "sia-select__trigger--invalid"),
2942
+ role: "combobox",
2943
+ "aria-haspopup": "listbox",
2944
+ "aria-expanded": open,
2945
+ "aria-controls": listboxId,
2946
+ "aria-activedescendant": open && activeOption ? `${listboxId}-${activeIndex}` : void 0,
2947
+ "aria-invalid": ariaInvalid ?? (invalid || void 0),
2948
+ "aria-required": ariaRequired ?? (required || void 0),
2949
+ disabled,
2950
+ onClick: () => setOpen((current) => !current),
2951
+ onKeyDown: handleKeyDown,
2952
+ ...ariaProps,
2953
+ children: [
2954
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: utils.cn("sia-select__value", !selectedOption && "sia-select__placeholder"), children: renderValue ? renderValue(selectedOption) : selectedOption?.label ?? placeholder }),
2955
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-select__actions", children: [
2956
+ clearable && currentValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
2957
+ "span",
2958
+ {
2959
+ className: "sia-select__clear",
2960
+ "aria-hidden": "true",
2961
+ title: "Effacer la s\xE9lection",
2962
+ onClick: (event) => {
2963
+ event.stopPropagation();
2964
+ changeValue("");
2965
+ },
2966
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
2967
+ }
2968
+ ),
2969
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { className: "sia-select__chevron", "aria-hidden": "true" })
2970
+ ] })
2971
+ ]
2972
+ }
2973
+ ),
2974
+ /* @__PURE__ */ jsxRuntime.jsx(
2975
+ Overlay,
2976
+ {
2977
+ open,
2978
+ anchorRef: triggerRef,
2979
+ onOpenChange: setOpen,
2980
+ placement,
2981
+ matchAnchorWidth: true,
2982
+ className: "sia-select__overlay",
2983
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sia-select__panel", onKeyDown: handleKeyDown, children: [
2984
+ showSearch && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sia-select__search-wrap", children: [
2985
+ /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { "aria-hidden": "true" }),
2986
+ /* @__PURE__ */ jsxRuntime.jsx(
2987
+ "input",
2988
+ {
2989
+ ref: searchRef,
2990
+ className: "sia-select__search",
2991
+ value: query,
2992
+ placeholder: searchPlaceholder,
2993
+ "aria-label": searchPlaceholder,
2994
+ onChange: (event) => setQuery(event.target.value)
2995
+ }
2996
+ ),
2997
+ loading && /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: "xs", label: "Chargement" })
2998
+ ] }),
2999
+ /* @__PURE__ */ jsxRuntime.jsx("div", { id: listboxId, role: "listbox", "aria-busy": loading || void 0, className: "sia-select__options", children: loading && !visibleOptions.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-select__status", children: loadingText }) : loadError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-select__status sia-select__status--error", children: errorText }) : !visibleOptions.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-select__status", children: emptyText }) : visibleOptions.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs(
3000
+ "button",
3001
+ {
3002
+ id: `${listboxId}-${index}`,
3003
+ type: "button",
3004
+ role: "option",
3005
+ "aria-selected": currentValue === option.value,
3006
+ disabled: option.disabled,
3007
+ className: utils.cn(
3008
+ "sia-select__option",
3009
+ index === activeIndex && "sia-select__option--active"
3010
+ ),
3011
+ onMouseEnter: () => setActiveIndex(index),
3012
+ onClick: () => choose(option),
3013
+ children: [
3014
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-select__option-content", children: [
3015
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: option.label }),
3016
+ option.description && /* @__PURE__ */ jsxRuntime.jsx("small", { children: option.description })
3017
+ ] }),
3018
+ currentValue === option.value && /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { "aria-hidden": "true" })
3019
+ ]
3020
+ },
3021
+ option.value
3022
+ )) })
3023
+ ] })
3024
+ }
3025
+ )
3026
+ ]
3027
+ }
3028
+ );
3029
+ });
3030
+ Select.displayName = "Select";
3031
+ var DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50, 100];
2681
3032
  function Pagination({
2682
3033
  page,
2683
3034
  totalPages,
@@ -2688,6 +3039,11 @@ function Pagination({
2688
3039
  jumpTo = false,
2689
3040
  showTotal = false,
2690
3041
  compact = false,
3042
+ pageSize,
3043
+ onPageSizeChange,
3044
+ pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
3045
+ pageSizeLabel,
3046
+ renderPageSize,
2691
3047
  ariaLabel,
2692
3048
  className
2693
3049
  }) {
@@ -2698,8 +3054,17 @@ function Pagination({
2698
3054
  const current = Math.min(Math.max(page, 1), Math.max(totalPages, 1));
2699
3055
  const [draft, setDraft] = React.useState(String(current));
2700
3056
  React.useEffect(() => setDraft(String(current)), [current]);
2701
- if (totalPages <= 1) return null;
2702
- const items = headless.paginationItems(current, totalPages, siblingCount);
3057
+ const tailles = React.useMemo(() => {
3058
+ if (pageSize === void 0 || pageSizeOptions.includes(pageSize)) {
3059
+ return pageSizeOptions;
3060
+ }
3061
+ return [...pageSizeOptions, pageSize].sort((a, b) => a - b);
3062
+ }, [pageSize, pageSizeOptions]);
3063
+ const choixTaille = pageSize !== void 0 && onPageSizeChange && tailles.length > 0;
3064
+ const plusieursPages = totalPages > 1;
3065
+ const tailleLibelle = pageSizeLabel ?? locale.perPage;
3066
+ if (!plusieursPages && !choixTaille) return null;
3067
+ const items = plusieursPages ? headless.paginationItems(current, totalPages, siblingCount) : [];
2703
3068
  const jump = (event) => {
2704
3069
  event.preventDefault();
2705
3070
  const asked = Number(draft);
@@ -2719,71 +3084,97 @@ function Pagination({
2719
3084
  ),
2720
3085
  "aria-label": ariaLabel ?? "Pagination",
2721
3086
  children: [
2722
- /* @__PURE__ */ jsxRuntime.jsxs(
2723
- "button",
2724
- {
2725
- type: "button",
2726
- className: "sia-pagination__step",
2727
- disabled: current === 1,
2728
- "aria-label": previousLabel,
2729
- onClick: () => onPageChange(current - 1),
2730
- children: [
2731
- /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, {}),
2732
- !compact && /* @__PURE__ */ jsxRuntime.jsx("span", { children: previousLabel })
2733
- ]
2734
- }
2735
- ),
2736
- items.map(
2737
- (item) => typeof item === "number" ? /* @__PURE__ */ jsxRuntime.jsx(
3087
+ plusieursPages && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sia-pagination__pages", children: [
3088
+ /* @__PURE__ */ jsxRuntime.jsxs(
3089
+ "button",
3090
+ {
3091
+ type: "button",
3092
+ className: "sia-pagination__step",
3093
+ disabled: current === 1,
3094
+ "aria-label": previousLabel,
3095
+ onClick: () => onPageChange(current - 1),
3096
+ children: [
3097
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, {}),
3098
+ !compact && /* @__PURE__ */ jsxRuntime.jsx("span", { children: previousLabel })
3099
+ ]
3100
+ }
3101
+ ),
3102
+ items.map(
3103
+ (item) => typeof item === "number" ? /* @__PURE__ */ jsxRuntime.jsx(
3104
+ "button",
3105
+ {
3106
+ type: "button",
3107
+ className: "sia-pagination__page",
3108
+ "aria-current": item === current ? "page" : void 0,
3109
+ onClick: () => onPageChange(item),
3110
+ children: item
3111
+ },
3112
+ item
3113
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-pagination__gap", "aria-hidden": "true", children: "\u2026" }, item)
3114
+ ),
3115
+ /* @__PURE__ */ jsxRuntime.jsxs(
2738
3116
  "button",
2739
3117
  {
2740
3118
  type: "button",
2741
- className: "sia-pagination__page",
2742
- "aria-current": item === current ? "page" : void 0,
2743
- onClick: () => onPageChange(item),
2744
- children: item
2745
- },
2746
- item
2747
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-pagination__gap", "aria-hidden": "true", children: "\u2026" }, item)
2748
- ),
2749
- /* @__PURE__ */ jsxRuntime.jsxs(
2750
- "button",
2751
- {
2752
- type: "button",
2753
- className: "sia-pagination__step",
2754
- disabled: current === totalPages,
2755
- "aria-label": nextLabel,
2756
- onClick: () => onPageChange(current + 1),
2757
- children: [
2758
- !compact && /* @__PURE__ */ jsxRuntime.jsx("span", { children: nextLabel }),
2759
- /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, {})
2760
- ]
2761
- }
2762
- ),
2763
- showTotal && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-pagination__total", children: [
2764
- locale.page,
2765
- " ",
2766
- current,
2767
- " / ",
2768
- totalPages
2769
- ] }),
2770
- jumpTo && /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "sia-pagination__jump", onSubmit: jump, children: [
2771
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: jumpId, children: locale.goToPage }),
2772
- /* @__PURE__ */ jsxRuntime.jsx(
2773
- "input",
2774
- {
2775
- id: jumpId,
2776
- type: "text",
2777
- inputMode: "numeric",
2778
- value: draft,
2779
- "aria-label": locale.goToPage,
2780
- onChange: (event) => setDraft(event.target.value.replace(/\D/g, "")),
2781
- onBlur: jump
3119
+ className: "sia-pagination__step",
3120
+ disabled: current === totalPages,
3121
+ "aria-label": nextLabel,
3122
+ onClick: () => onPageChange(current + 1),
3123
+ children: [
3124
+ !compact && /* @__PURE__ */ jsxRuntime.jsx("span", { children: nextLabel }),
3125
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, {})
3126
+ ]
2782
3127
  }
2783
- ),
2784
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-pagination__jump-total", "aria-hidden": "true", children: [
2785
- "/ ",
3128
+ )
3129
+ ] }),
3130
+ (choixTaille || plusieursPages && (showTotal || jumpTo)) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sia-pagination__settings", children: [
3131
+ plusieursPages && showTotal && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-pagination__total", children: [
3132
+ locale.page,
3133
+ " ",
3134
+ current,
3135
+ " / ",
2786
3136
  totalPages
3137
+ ] }),
3138
+ choixTaille && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-pagination__size", children: renderPageSize ? renderPageSize({
3139
+ pageSize,
3140
+ options: tailles,
3141
+ onChange: onPageSizeChange,
3142
+ label: tailleLibelle
3143
+ }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3144
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: tailleLibelle }),
3145
+ /* @__PURE__ */ jsxRuntime.jsx(
3146
+ Select,
3147
+ {
3148
+ variant: "embedded",
3149
+ "aria-label": tailleLibelle,
3150
+ placement: "top-end",
3151
+ value: String(pageSize),
3152
+ options: tailles.map((taille) => ({
3153
+ value: String(taille),
3154
+ label: String(taille)
3155
+ })),
3156
+ onValueChange: (valeur) => onPageSizeChange(Number(valeur))
3157
+ }
3158
+ )
3159
+ ] }) }),
3160
+ plusieursPages && jumpTo && /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "sia-pagination__jump", onSubmit: jump, children: [
3161
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: jumpId, children: locale.goToPage }),
3162
+ /* @__PURE__ */ jsxRuntime.jsx(
3163
+ "input",
3164
+ {
3165
+ id: jumpId,
3166
+ type: "text",
3167
+ inputMode: "numeric",
3168
+ value: draft,
3169
+ "aria-label": locale.goToPage,
3170
+ onChange: (event) => setDraft(event.target.value.replace(/\D/g, "")),
3171
+ onBlur: jump
3172
+ }
3173
+ ),
3174
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-pagination__jump-total", "aria-hidden": "true", children: [
3175
+ "/ ",
3176
+ totalPages
3177
+ ] })
2787
3178
  ] })
2788
3179
  ] })
2789
3180
  ]
@@ -3069,6 +3460,7 @@ function DataTable({
3069
3460
  searchKeys,
3070
3461
  searchPlaceholder,
3071
3462
  onSearch,
3463
+ searchDelay = 0,
3072
3464
  layout = "auto",
3073
3465
  maxHeight,
3074
3466
  stickyHeader = false,
@@ -3187,9 +3579,10 @@ function DataTable({
3187
3579
  {
3188
3580
  value: recherche,
3189
3581
  placeholder: searchPlaceholder ?? locale.search,
3582
+ ...onSearch && searchDelay > 0 ? { onSearch, searchDelay } : {},
3190
3583
  onValueChange: (v) => {
3191
3584
  setRecherche(v);
3192
- onSearch?.(v);
3585
+ if (searchDelay <= 0) onSearch?.(v);
3193
3586
  }
3194
3587
  }
3195
3588
  ),
@@ -3982,255 +4375,6 @@ function DateTimePicker({ value, defaultValue = { date: "", time: "" }, onValueC
3982
4375
  /* @__PURE__ */ jsxRuntime.jsx(TimePicker, { ...timeProps, value: current.time, minuteStep, secondStep, showSeconds, hourFormat, needConfirm, tone, ...stateProps, "aria-label": "Heure", onValueChange: (time) => update({ ...current, time }) })
3983
4376
  ] });
3984
4377
  }
3985
- function optionsFromChildren(children) {
3986
- return React.Children.toArray(children).flatMap((child) => {
3987
- if (!React.isValidElement(child))
3988
- return [];
3989
- const value = child.props.value;
3990
- if (value === void 0) return [];
3991
- return [{
3992
- value: String(value),
3993
- label: child.props.children,
3994
- ...child.props.disabled !== void 0 ? { disabled: child.props.disabled } : {}
3995
- }];
3996
- });
3997
- }
3998
- function optionText(option) {
3999
- return [option.label, option.description, ...option.keywords ?? []].map((value) => typeof value === "string" || typeof value === "number" ? String(value) : "").join(" ").toLocaleLowerCase();
4000
- }
4001
- var Select = React.forwardRef(function Select2({
4002
- id,
4003
- name,
4004
- options,
4005
- children,
4006
- value,
4007
- defaultValue = "",
4008
- onValueChange,
4009
- placeholder = "S\xE9lectionner",
4010
- searchable = false,
4011
- searchPlaceholder = "Rechercher...",
4012
- fetcher,
4013
- debounce = 250,
4014
- loadingText = "Chargement des options...",
4015
- emptyText = "Aucun r\xE9sultat",
4016
- errorText = "Impossible de charger les options",
4017
- clearable = false,
4018
- disabled = false,
4019
- invalid = false,
4020
- required = false,
4021
- placement = "bottom-start",
4022
- className,
4023
- "aria-invalid": ariaInvalid,
4024
- "aria-required": ariaRequired,
4025
- ...ariaProps
4026
- }, forwardedRef) {
4027
- const generatedId = React.useId();
4028
- const controlId = id ?? `sia-select-${generatedId.replace(/:/g, "")}`;
4029
- const listboxId = `${controlId}-listbox`;
4030
- const triggerRef = React.useRef(null);
4031
- const searchRef = React.useRef(null);
4032
- const staticOptions = React.useMemo(
4033
- () => options ?? optionsFromChildren(children),
4034
- [children, options]
4035
- );
4036
- const [internalValue, setInternalValue] = React.useState(defaultValue);
4037
- const [open, setOpen] = React.useState(false);
4038
- const [query, setQuery] = React.useState("");
4039
- const [remoteOptions, setRemoteOptions] = React.useState([]);
4040
- const [selectedRemoteOption, setSelectedRemoteOption] = React.useState();
4041
- const [loading, setLoading] = React.useState(false);
4042
- const [loadError, setLoadError] = React.useState(false);
4043
- const [activeIndex, setActiveIndex] = React.useState(-1);
4044
- const currentValue = value ?? internalValue;
4045
- const sourceOptions = fetcher ? remoteOptions : staticOptions;
4046
- const visibleOptions = React.useMemo(() => {
4047
- if (fetcher) return sourceOptions;
4048
- const needle = query.trim().toLocaleLowerCase();
4049
- return needle ? sourceOptions.filter((option) => optionText(option).includes(needle)) : sourceOptions;
4050
- }, [fetcher, query, sourceOptions]);
4051
- const selectedOption = [...staticOptions, ...remoteOptions].find(
4052
- (option) => option.value === currentValue
4053
- ) ?? (selectedRemoteOption?.value === currentValue ? selectedRemoteOption : void 0);
4054
- React.useEffect(() => {
4055
- if (!open || !fetcher) return;
4056
- const controller = new AbortController();
4057
- const timeout = window.setTimeout(async () => {
4058
- setLoading(true);
4059
- setLoadError(false);
4060
- try {
4061
- const nextOptions = await fetcher(query, controller.signal);
4062
- if (!controller.signal.aborted) setRemoteOptions(nextOptions);
4063
- } catch {
4064
- if (!controller.signal.aborted) setLoadError(true);
4065
- } finally {
4066
- if (!controller.signal.aborted) setLoading(false);
4067
- }
4068
- }, debounce);
4069
- return () => {
4070
- window.clearTimeout(timeout);
4071
- controller.abort();
4072
- };
4073
- }, [debounce, fetcher, open, query]);
4074
- React.useEffect(() => {
4075
- if (!open) return;
4076
- setActiveIndex(visibleOptions.findIndex((option) => option.value === currentValue));
4077
- if (searchable || fetcher) window.setTimeout(() => searchRef.current?.focus(), 0);
4078
- }, [currentValue, fetcher, open, searchable, visibleOptions]);
4079
- const setTriggerRef = (node) => {
4080
- triggerRef.current = node;
4081
- if (typeof forwardedRef === "function") forwardedRef(node);
4082
- else if (forwardedRef) forwardedRef.current = node;
4083
- };
4084
- const changeValue = (nextValue, option) => {
4085
- if (value === void 0) setInternalValue(nextValue);
4086
- onValueChange?.(nextValue, option);
4087
- };
4088
- const choose = (option) => {
4089
- if (option.disabled) return;
4090
- if (fetcher) setSelectedRemoteOption(option);
4091
- changeValue(option.value, option);
4092
- setOpen(false);
4093
- setQuery("");
4094
- window.setTimeout(() => triggerRef.current?.focus(), 0);
4095
- };
4096
- const moveActive = (direction) => {
4097
- if (!visibleOptions.length) return;
4098
- let next = activeIndex;
4099
- for (let count = 0; count < visibleOptions.length; count += 1) {
4100
- next = (next + direction + visibleOptions.length) % visibleOptions.length;
4101
- if (!visibleOptions[next]?.disabled) break;
4102
- }
4103
- setActiveIndex(next);
4104
- };
4105
- const handleKeyDown = (event) => {
4106
- if (["ArrowDown", "ArrowUp"].includes(event.key)) {
4107
- event.preventDefault();
4108
- if (!open) setOpen(true);
4109
- else moveActive(event.key === "ArrowDown" ? 1 : -1);
4110
- return;
4111
- }
4112
- if (event.key === "Home" && open) {
4113
- event.preventDefault();
4114
- setActiveIndex(visibleOptions.findIndex((option) => !option.disabled));
4115
- } else if (event.key === "End" && open) {
4116
- event.preventDefault();
4117
- for (let index = visibleOptions.length - 1; index >= 0; index -= 1) {
4118
- if (!visibleOptions[index]?.disabled) {
4119
- setActiveIndex(index);
4120
- break;
4121
- }
4122
- }
4123
- } else if (event.key === "Enter" && open && activeIndex >= 0) {
4124
- event.preventDefault();
4125
- const active = visibleOptions[activeIndex];
4126
- if (active) choose(active);
4127
- } else if (event.key === "Escape" && open) {
4128
- event.preventDefault();
4129
- setOpen(false);
4130
- triggerRef.current?.focus();
4131
- } else if (!open && clearable && currentValue && ["Backspace", "Delete"].includes(event.key)) {
4132
- event.preventDefault();
4133
- changeValue("");
4134
- }
4135
- };
4136
- const showSearch = searchable || Boolean(fetcher);
4137
- const activeOption = activeIndex >= 0 ? visibleOptions[activeIndex] : void 0;
4138
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("sia-select", disabled && "sia-select--disabled", className), children: [
4139
- name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: currentValue }),
4140
- /* @__PURE__ */ jsxRuntime.jsxs(
4141
- "button",
4142
- {
4143
- ref: setTriggerRef,
4144
- id: controlId,
4145
- type: "button",
4146
- className: utils.cn("sia-select__trigger", invalid && "sia-select__trigger--invalid"),
4147
- role: "combobox",
4148
- "aria-haspopup": "listbox",
4149
- "aria-expanded": open,
4150
- "aria-controls": listboxId,
4151
- "aria-activedescendant": open && activeOption ? `${listboxId}-${activeIndex}` : void 0,
4152
- "aria-invalid": ariaInvalid ?? (invalid || void 0),
4153
- "aria-required": ariaRequired ?? (required || void 0),
4154
- disabled,
4155
- onClick: () => setOpen((current) => !current),
4156
- onKeyDown: handleKeyDown,
4157
- ...ariaProps,
4158
- children: [
4159
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: utils.cn("sia-select__value", !selectedOption && "sia-select__placeholder"), children: selectedOption?.label ?? placeholder }),
4160
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-select__actions", children: [
4161
- clearable && currentValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
4162
- "span",
4163
- {
4164
- className: "sia-select__clear",
4165
- "aria-hidden": "true",
4166
- title: "Effacer la s\xE9lection",
4167
- onClick: (event) => {
4168
- event.stopPropagation();
4169
- changeValue("");
4170
- },
4171
- children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, {})
4172
- }
4173
- ),
4174
- /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { className: "sia-select__chevron", "aria-hidden": "true" })
4175
- ] })
4176
- ]
4177
- }
4178
- ),
4179
- /* @__PURE__ */ jsxRuntime.jsx(
4180
- Overlay,
4181
- {
4182
- open,
4183
- anchorRef: triggerRef,
4184
- onOpenChange: setOpen,
4185
- placement,
4186
- matchAnchorWidth: true,
4187
- className: "sia-select__overlay",
4188
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sia-select__panel", onKeyDown: handleKeyDown, children: [
4189
- showSearch && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sia-select__search-wrap", children: [
4190
- /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { "aria-hidden": "true" }),
4191
- /* @__PURE__ */ jsxRuntime.jsx(
4192
- "input",
4193
- {
4194
- ref: searchRef,
4195
- className: "sia-select__search",
4196
- value: query,
4197
- placeholder: searchPlaceholder,
4198
- "aria-label": searchPlaceholder,
4199
- onChange: (event) => setQuery(event.target.value)
4200
- }
4201
- ),
4202
- loading && /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: "xs", label: "Chargement" })
4203
- ] }),
4204
- /* @__PURE__ */ jsxRuntime.jsx("div", { id: listboxId, role: "listbox", "aria-busy": loading || void 0, className: "sia-select__options", children: loading && !visibleOptions.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-select__status", children: loadingText }) : loadError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-select__status sia-select__status--error", children: errorText }) : !visibleOptions.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-select__status", children: emptyText }) : visibleOptions.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs(
4205
- "button",
4206
- {
4207
- id: `${listboxId}-${index}`,
4208
- type: "button",
4209
- role: "option",
4210
- "aria-selected": currentValue === option.value,
4211
- disabled: option.disabled,
4212
- className: utils.cn(
4213
- "sia-select__option",
4214
- index === activeIndex && "sia-select__option--active"
4215
- ),
4216
- onMouseEnter: () => setActiveIndex(index),
4217
- onClick: () => choose(option),
4218
- children: [
4219
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-select__option-content", children: [
4220
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: option.label }),
4221
- option.description && /* @__PURE__ */ jsxRuntime.jsx("small", { children: option.description })
4222
- ] }),
4223
- currentValue === option.value && /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { "aria-hidden": "true" })
4224
- ]
4225
- },
4226
- option.value
4227
- )) })
4228
- ] })
4229
- }
4230
- )
4231
- ] });
4232
- });
4233
- Select.displayName = "Select";
4234
4378
  function MultiSelect({
4235
4379
  id,
4236
4380
  options,
@@ -5217,6 +5361,14 @@ function Field(rawProps) {
5217
5361
  function isGroup(entry) {
5218
5362
  return "group" in entry;
5219
5363
  }
5364
+ function formFieldNames(fields) {
5365
+ const noms = [];
5366
+ for (const entree of fields) {
5367
+ if (isGroup(entree)) noms.push(...entree.fields.map((f) => f.name));
5368
+ else noms.push(entree.name);
5369
+ }
5370
+ return noms;
5371
+ }
5220
5372
  var VIDE = {};
5221
5373
  function Form({
5222
5374
  fields,
@@ -5241,6 +5393,26 @@ function Form({
5241
5393
  ...onSubmit ? { onSubmit } : {}
5242
5394
  });
5243
5395
  const form = adapter ?? interne;
5396
+ const locale = headless.useSiaLocale();
5397
+ const [echec, setEchec] = React.useState(null);
5398
+ const envoyer = () => {
5399
+ setEchec(null);
5400
+ form.submit().catch((error) => {
5401
+ const { fields: erreurs, message } = headless.readSubmitError(error);
5402
+ const connus = new Set(formFieldNames(fields));
5403
+ const sousLesChamps = {};
5404
+ const ailleurs = [];
5405
+ for (const [nom, texte] of Object.entries(erreurs)) {
5406
+ if (connus.has(nom)) sousLesChamps[nom] = texte;
5407
+ else ailleurs.push(texte);
5408
+ }
5409
+ form.setErrors(sousLesChamps);
5410
+ if (ailleurs.length > 0) setEchec(ailleurs.join(" "));
5411
+ else if (Object.keys(sousLesChamps).length === 0) {
5412
+ setEchec(message ?? locale.submitError);
5413
+ }
5414
+ });
5415
+ };
5244
5416
  const busy = loading || form.state.isSubmitting;
5245
5417
  const submitDisabled = busy || requireDirty && !form.state.isDirty || requireValid && !form.state.isValid;
5246
5418
  const grille = (nombre) => ({
@@ -5276,7 +5448,7 @@ function Form({
5276
5448
  noValidate: true,
5277
5449
  onSubmit: (event) => {
5278
5450
  event.preventDefault();
5279
- void form.submit();
5451
+ envoyer();
5280
5452
  },
5281
5453
  children: [
5282
5454
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sia-form-grid", style: grille(columns), children: fields.map(
@@ -5295,6 +5467,15 @@ function Form({
5295
5467
  )
5296
5468
  ] }, entry.group) : rendre(entry)
5297
5469
  ) }),
5470
+ echec && /* @__PURE__ */ jsxRuntime.jsx(
5471
+ Alert,
5472
+ {
5473
+ tone: "danger",
5474
+ className: "sia-form-error",
5475
+ onDismiss: () => setEchec(null),
5476
+ children: echec
5477
+ }
5478
+ ),
5298
5479
  actions ?? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sia-form-actions", children: [
5299
5480
  showClear && /* @__PURE__ */ jsxRuntime.jsx(
5300
5481
  Button,
@@ -5330,23 +5511,15 @@ function Descriptions({ items, title, columns = 2, bordered = false, className }
5330
5511
  ] }, item.key)) })
5331
5512
  ] });
5332
5513
  }
5333
- function nomsDeChamps(fields) {
5334
- const noms = [];
5335
- for (const entree of fields) {
5336
- if ("group" in entree) noms.push(...entree.fields.map((f) => f.name));
5337
- else noms.push(entree.name);
5338
- }
5339
- return noms;
5340
- }
5341
5514
  function valeursVides(fields) {
5342
5515
  const valeurs = {};
5343
- for (const nom of nomsDeChamps(fields)) valeurs[nom] = "";
5516
+ for (const nom of formFieldNames(fields)) valeurs[nom] = "";
5344
5517
  return valeurs;
5345
5518
  }
5346
5519
  function valeursDeLigne(row, fields) {
5347
5520
  const source = row;
5348
5521
  const valeurs = {};
5349
- for (const nom of nomsDeChamps(fields)) {
5522
+ for (const nom of formFieldNames(fields)) {
5350
5523
  const valeur = source[nom];
5351
5524
  valeurs[nom] = typeof valeur === "string" || typeof valeur === "number" || typeof valeur === "boolean" || Array.isArray(valeur) ? valeur : "";
5352
5525
  }
@@ -5391,11 +5564,12 @@ function CrudDialogs({
5391
5564
  submitText: dialog?.submitText ?? (creation ? "Cr\xE9er" : "Enregistrer"),
5392
5565
  requireDirty: modification,
5393
5566
  onSubmit: async (values) => {
5394
- await onSubmit?.(values, {
5567
+ const resultat = await onSubmit?.(values, {
5395
5568
  mode: creation ? "create" : "edit",
5396
5569
  ...state?.row !== void 0 ? { row: state.row } : {},
5397
5570
  ...state?.index !== void 0 ? { index: state.index } : {}
5398
5571
  });
5572
+ if (headless.hasFormErrors(resultat)) return resultat;
5399
5573
  onClose();
5400
5574
  }
5401
5575
  },
@@ -5479,6 +5653,7 @@ function CrudPage({
5479
5653
  searchKeys: clesDeRecherche,
5480
5654
  searchPlaceholder,
5481
5655
  onSearch,
5656
+ searchDelay,
5482
5657
  loading = false,
5483
5658
  error,
5484
5659
  onRetry,
@@ -5486,6 +5661,9 @@ function CrudPage({
5486
5661
  page,
5487
5662
  totalPages,
5488
5663
  onPageChange,
5664
+ pageSize,
5665
+ onPageSizeChange,
5666
+ pageSizeOptions,
5489
5667
  table,
5490
5668
  children,
5491
5669
  className
@@ -5565,9 +5743,12 @@ function CrudPage({
5565
5743
  if (!extraRowActions) {
5566
5744
  return actionsDeLigne.length > 0 ? actionsDeLigne : void 0;
5567
5745
  }
5746
+ const suppression = actionsDeLigne.filter((a) => a.key === "delete");
5747
+ const autres = actionsDeLigne.filter((a) => a.key !== "delete");
5568
5748
  return (row, index) => [
5569
- ...actionsDeLigne,
5570
- ...typeof extraRowActions === "function" ? extraRowActions(row, index) : extraRowActions
5749
+ ...autres,
5750
+ ...typeof extraRowActions === "function" ? extraRowActions(row, index) : extraRowActions,
5751
+ ...suppression
5571
5752
  ];
5572
5753
  }, [actionsDeLigne, extraRowActions]);
5573
5754
  const creation = operations?.create === false ? null : operations?.create ?? {};
@@ -5601,6 +5782,7 @@ function CrudPage({
5601
5782
  ...searchKeys ? { searchKeys } : {},
5602
5783
  ...searchPlaceholder ? { searchPlaceholder } : {},
5603
5784
  ...onSearch ? { onSearch } : {},
5785
+ ...searchDelay !== void 0 ? { searchDelay } : {},
5604
5786
  ...toutesLesActions ? { rowActions: toutesLesActions } : {},
5605
5787
  ...can ? { can } : {},
5606
5788
  loading,
@@ -5663,7 +5845,10 @@ function CrudPage({
5663
5845
  page,
5664
5846
  totalPages,
5665
5847
  onPageChange,
5666
- showTotal: true
5848
+ showTotal: true,
5849
+ ...pageSize !== void 0 ? { pageSize } : {},
5850
+ ...onPageSizeChange ? { onPageSizeChange } : {},
5851
+ ...pageSizeOptions ? { pageSizeOptions } : {}
5667
5852
  }
5668
5853
  )
5669
5854
  ] });
@@ -6126,6 +6311,14 @@ var PhoneInput = React.forwardRef(
6126
6311
  [countries, initial, picked, value]
6127
6312
  );
6128
6313
  const national = value.startsWith(country.dial) ? value.slice(country.dial.length) : digitsOnly(value);
6314
+ const options = React.useMemo(
6315
+ () => countries.map((entry) => ({
6316
+ value: entry.code,
6317
+ label: `${entry.flag} ${entry.label} (${entry.dial})`,
6318
+ keywords: [entry.code]
6319
+ })),
6320
+ [countries]
6321
+ );
6129
6322
  const commit = (next) => {
6130
6323
  if (!controlled) setInternal(next);
6131
6324
  onValueChange?.(next);
@@ -6147,35 +6340,31 @@ var PhoneInput = React.forwardRef(
6147
6340
  if (country.digits) next = next.slice(0, country.digits);
6148
6341
  commit(next ? `${country.dial}${next}` : "");
6149
6342
  },
6150
- left: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-phone__country", children: [
6343
+ left: lockCountry ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-phone__country", children: [
6151
6344
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-phone__flag", "aria-hidden": "true", children: country.flag }),
6152
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-phone__dial", children: country.dial }),
6153
- !lockCountry && /* @__PURE__ */ jsxRuntime.jsx(
6154
- "select",
6155
- {
6156
- className: "sia-phone__select",
6157
- "aria-label": locale.countryCode,
6158
- value: country.code,
6159
- disabled,
6160
- onChange: (event) => {
6161
- const next = countries.find(
6162
- (entry) => entry.code === event.target.value
6163
- );
6164
- if (!next) return;
6165
- setPicked(next.code);
6166
- commit(national ? `${next.dial}${national}` : "");
6167
- },
6168
- children: countries.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: entry.code, children: [
6169
- entry.flag,
6170
- " ",
6171
- entry.label,
6172
- " (",
6173
- entry.dial,
6174
- ")"
6175
- ] }, entry.code))
6345
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-phone__dial", children: country.dial })
6346
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
6347
+ Select,
6348
+ {
6349
+ variant: "embedded",
6350
+ searchable: true,
6351
+ className: "sia-phone__country",
6352
+ "aria-label": locale.countryCode,
6353
+ options,
6354
+ value: country.code,
6355
+ disabled: disabled ?? false,
6356
+ renderValue: () => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sia-phone__value", children: [
6357
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-phone__flag", "aria-hidden": "true", children: country.flag }),
6358
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sia-phone__dial", children: country.dial })
6359
+ ] }),
6360
+ onValueChange: (code) => {
6361
+ const next = countries.find((entry) => entry.code === code);
6362
+ if (!next) return;
6363
+ setPicked(next.code);
6364
+ commit(national ? `${next.dial}${national}` : "");
6176
6365
  }
6177
- )
6178
- ] })
6366
+ }
6367
+ )
6179
6368
  }
6180
6369
  );
6181
6370
  }
@@ -7140,6 +7329,8 @@ exports.Alert = Alert;
7140
7329
  exports.AlertCircleIcon = AlertCircleIcon;
7141
7330
  exports.AmountDisplay = AmountDisplay;
7142
7331
  exports.AppShell = AppShell;
7332
+ exports.AppShellBrand = AppShellBrand;
7333
+ exports.AppShellRailItem = AppShellRailItem;
7143
7334
  exports.ArrowRightIcon = ArrowRightIcon;
7144
7335
  exports.AspectRatio = AspectRatio;
7145
7336
  exports.AsyncState = AsyncState;
@@ -7179,6 +7370,7 @@ exports.CrudDialogs = CrudDialogs;
7179
7370
  exports.CrudPage = CrudPage;
7180
7371
  exports.CurrencyInput = CurrencyInput;
7181
7372
  exports.DEFAULT_COUNTRIES = DEFAULT_COUNTRIES;
7373
+ exports.DEFAULT_PAGE_SIZE_OPTIONS = DEFAULT_PAGE_SIZE_OPTIONS;
7182
7374
  exports.DataTable = DataTable;
7183
7375
  exports.DatePicker = DatePicker;
7184
7376
  exports.DateRangeFilter = DateRangeFilter;
@@ -7248,6 +7440,7 @@ exports.ScrollArea = ScrollArea;
7248
7440
  exports.SearchIcon = SearchIcon;
7249
7441
  exports.SearchInput = SearchInput;
7250
7442
  exports.Select = Select;
7443
+ exports.SettingsIcon = SettingsIcon;
7251
7444
  exports.Sidebar = Sidebar;
7252
7445
  exports.Skeleton = Skeleton;
7253
7446
  exports.Slider = Slider;
@@ -7288,6 +7481,7 @@ exports.YearPicker = YearPicker;
7288
7481
  exports.confirmUnsavedNavigation = confirmUnsavedNavigation;
7289
7482
  exports.createUrlTableStorage = createUrlTableStorage;
7290
7483
  exports.defineResource = defineResource;
7484
+ exports.formFieldNames = formFieldNames;
7291
7485
  exports.formatDuration = formatDuration;
7292
7486
  exports.formatTimeValue = formatTimeValue;
7293
7487
  exports.initialsOf = initialsOf;