@dragunio/fe-libs 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -97,11 +97,78 @@ import {
97
97
  useCallback,
98
98
  useContext,
99
99
  useEffect,
100
- useMemo,
100
+ useMemo as useMemo2,
101
101
  useRef,
102
- useState
102
+ useState as useState2
103
103
  } from "react";
104
+
105
+ // packages/pagination/src/index.tsx
106
+ import { useMemo, useState } from "react";
104
107
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
108
+ var clamp = (value, min, max) => Math.min(Math.max(value, min), max);
109
+ var range = (start, end) => Array.from({ length: Math.max(0, end - start + 1) }, (_, index) => start + index);
110
+ function getItems(totalPages, page, siblingCount, boundaryCount) {
111
+ const visibleCount = boundaryCount * 2 + siblingCount * 2 + 3;
112
+ if (totalPages <= visibleCount) return range(1, totalPages);
113
+ const startPages = range(1, boundaryCount);
114
+ const endPages = range(totalPages - boundaryCount + 1, totalPages);
115
+ const siblingsStart = Math.max(Math.min(page - siblingCount, totalPages - boundaryCount - siblingCount * 2 - 1), boundaryCount + 2);
116
+ const siblingsEnd = Math.min(Math.max(page + siblingCount, boundaryCount + siblingCount * 2 + 2), totalPages - boundaryCount - 1);
117
+ return [
118
+ ...startPages,
119
+ ...siblingsStart > boundaryCount + 2 ? ["start-ellipsis"] : siblingsStart === boundaryCount + 2 ? [boundaryCount + 1] : [],
120
+ ...range(siblingsStart, siblingsEnd),
121
+ ...siblingsEnd < totalPages - boundaryCount - 1 ? ["end-ellipsis"] : siblingsEnd === totalPages - boundaryCount - 1 ? [totalPages - boundaryCount] : [],
122
+ ...endPages
123
+ ];
124
+ }
125
+ function Pagination({
126
+ page,
127
+ defaultPage = 1,
128
+ totalPages,
129
+ onPageChange,
130
+ siblingCount = 1,
131
+ boundaryCount = 1,
132
+ disabled = false,
133
+ previousLabel = "Previous page",
134
+ nextLabel = "Next page",
135
+ className,
136
+ ...props
137
+ }) {
138
+ const safeTotalPages = Math.max(1, Math.floor(totalPages));
139
+ const [uncontrolledPage, setUncontrolledPage] = useState(() => clamp(defaultPage, 1, safeTotalPages));
140
+ const currentPage = clamp(page ?? uncontrolledPage, 1, safeTotalPages);
141
+ const items = useMemo(
142
+ () => getItems(safeTotalPages, currentPage, Math.max(0, siblingCount), Math.max(0, boundaryCount)),
143
+ [safeTotalPages, currentPage, siblingCount, boundaryCount]
144
+ );
145
+ const selectPage = (nextPage) => {
146
+ const resolvedPage = clamp(nextPage, 1, safeTotalPages);
147
+ if (page === void 0) setUncontrolledPage(resolvedPage);
148
+ if (resolvedPage !== currentPage) onPageChange?.(resolvedPage);
149
+ };
150
+ return /* @__PURE__ */ jsxs3("nav", { className: ["fe-pagination", className].filter(Boolean).join(" "), "aria-label": "Pagination", ...props, children: [
151
+ /* @__PURE__ */ jsx3(Button, { className: "fe-pagination__button fe-pagination__button--control", hierarchy: "secondary-gray", size: "40", iconOnly: true, icon: "\u2039", "aria-label": previousLabel, disabled: disabled || currentPage === 1, onClick: () => selectPage(currentPage - 1) }),
152
+ items.map((item, index) => item === "start-ellipsis" || item === "end-ellipsis" ? /* @__PURE__ */ jsx3("span", { className: "fe-pagination__ellipsis", "aria-hidden": "true", children: "\u2026" }, `${item}-${index}`) : /* @__PURE__ */ jsx3(
153
+ Button,
154
+ {
155
+ className: "fe-pagination__button",
156
+ hierarchy: item === currentPage ? "primary" : "secondary-gray",
157
+ size: "40",
158
+ "aria-label": `Page ${item}`,
159
+ "aria-current": item === currentPage ? "page" : void 0,
160
+ disabled,
161
+ onClick: () => selectPage(item),
162
+ children: item
163
+ },
164
+ item
165
+ )),
166
+ /* @__PURE__ */ jsx3(Button, { className: "fe-pagination__button fe-pagination__button--control", hierarchy: "secondary-gray", size: "40", iconOnly: true, icon: "\u203A", "aria-label": nextLabel, disabled: disabled || currentPage === safeTotalPages, onClick: () => selectPage(currentPage + 1) })
167
+ ] });
168
+ }
169
+
170
+ // packages/table/src/index.tsx
171
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
105
172
  var TableContext = createContext({ striped: false });
106
173
  var MIN_COLUMN_WIDTH = 96;
107
174
  var MAX_COLUMN_WIDTH = 1200;
@@ -119,9 +186,9 @@ function autoColumnSize(columnIndex, headers, data) {
119
186
  const longestText = values.reduce((longest, value) => Math.max(longest, getText(value).length), 0);
120
187
  return Math.min(MAX_COLUMN_WIDTH, Math.max(MIN_COLUMN_WIDTH, longestText * 8 + 40));
121
188
  }
122
- function getInitialColumnSizes(headers, data, sizes, minColumnWidth = MIN_COLUMN_WIDTH) {
189
+ function getInitialColumnSizes(headers, data, sizes2, minColumnWidth = MIN_COLUMN_WIDTH) {
123
190
  return Array.from({ length: getColumnCount(headers, data) }, (_, columnIndex) => {
124
- const suppliedSize = sizes?.[columnIndex];
191
+ const suppliedSize = sizes2?.[columnIndex];
125
192
  return typeof suppliedSize === "number" && Number.isFinite(suppliedSize) ? Math.min(MAX_COLUMN_WIDTH, Math.max(minColumnWidth, suppliedSize)) : autoColumnSize(columnIndex, headers, data);
126
193
  });
127
194
  }
@@ -136,19 +203,31 @@ function DataTable({
136
203
  columnSizes,
137
204
  onColumnResize,
138
205
  minColumnWidth = MIN_COLUMN_WIDTH,
206
+ pagination,
207
+ pageSize,
208
+ page,
209
+ defaultPage = 1,
210
+ onPageChange,
211
+ paginationPosition = "bottom-right",
139
212
  className,
140
213
  ...props
141
214
  }) {
142
215
  const columnCount = getColumnCount(headers, data);
143
216
  const safeMinWidth = Math.max(1, minColumnWidth);
144
- const generatedSizes = useMemo(
217
+ const generatedSizes = useMemo2(
145
218
  () => getInitialColumnSizes(headers, data, defaultColumnSizes, safeMinWidth),
146
219
  [headers, data, defaultColumnSizes, safeMinWidth]
147
220
  );
148
- const [uncontrolledSizes, setUncontrolledSizes] = useState(generatedSizes);
221
+ const [uncontrolledSizes, setUncontrolledSizes] = useState2(generatedSizes);
222
+ const shouldPaginate = pagination ?? pageSize !== void 0;
223
+ const safePageSize = Math.max(1, Math.floor(pageSize ?? 10));
224
+ const totalPages = Math.max(1, Math.ceil(data.length / safePageSize));
225
+ const [uncontrolledPage, setUncontrolledPage] = useState2(() => Math.min(Math.max(1, defaultPage), totalPages));
226
+ const currentPage = Math.min(Math.max(1, page ?? uncontrolledPage), totalPages);
227
+ const pagedData = shouldPaginate ? data.slice((currentPage - 1) * safePageSize, currentPage * safePageSize) : data;
149
228
  const resizeState = useRef();
150
- const [isResizing, setIsResizing] = useState(false);
151
- const sizes = columnSizes ? getInitialColumnSizes(headers, data, columnSizes, safeMinWidth).map((size) => Math.max(safeMinWidth, size)) : uncontrolledSizes.map((size) => Math.max(safeMinWidth, size));
229
+ const [isResizing, setIsResizing] = useState2(false);
230
+ const sizes2 = columnSizes ? getInitialColumnSizes(headers, data, columnSizes, safeMinWidth).map((size) => Math.max(safeMinWidth, size)) : uncontrolledSizes.map((size) => Math.max(safeMinWidth, size));
152
231
  useEffect(() => {
153
232
  if (columnSizes) return;
154
233
  setUncontrolledSizes((currentSizes) => {
@@ -156,12 +235,15 @@ function DataTable({
156
235
  return sameSizes(currentSizes, nextSizes) ? currentSizes : nextSizes;
157
236
  });
158
237
  }, [columnSizes, generatedSizes]);
238
+ useEffect(() => {
239
+ if (page === void 0 && uncontrolledPage > totalPages) setUncontrolledPage(totalPages);
240
+ }, [page, totalPages, uncontrolledPage]);
159
241
  const updateColumnSize = useCallback((columnIndex, nextSize) => {
160
242
  const clampedSize = Math.min(MAX_COLUMN_WIDTH, Math.max(safeMinWidth, Math.round(nextSize)));
161
- const nextSizes = sizes.map((size, index) => index === columnIndex ? clampedSize : size);
243
+ const nextSizes = sizes2.map((size, index) => index === columnIndex ? clampedSize : size);
162
244
  if (!columnSizes) setUncontrolledSizes(nextSizes);
163
245
  onColumnResize?.(nextSizes, columnIndex);
164
- }, [columnSizes, onColumnResize, safeMinWidth, sizes]);
246
+ }, [columnSizes, onColumnResize, safeMinWidth, sizes2]);
165
247
  useEffect(() => {
166
248
  const onPointerMove = (event) => {
167
249
  const activeResize = resizeState.current;
@@ -181,74 +263,96 @@ function DataTable({
181
263
  window.removeEventListener("pointercancel", stopResizing);
182
264
  };
183
265
  }, [updateColumnSize]);
184
- return /* @__PURE__ */ jsx3("div", { className: "fe-table", "data-resizing": isResizing || void 0, children: /* @__PURE__ */ jsxs3("table", { className: ["fe-table__table", className].filter(Boolean).join(" "), "data-fe-table": true, "data-striped": striped || void 0, ...props, children: [
185
- /* @__PURE__ */ jsx3("colgroup", { children: sizes.map((size, index) => /* @__PURE__ */ jsx3("col", { style: { width: `${size}px` } }, index)) }),
186
- /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsx3("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ jsxs3("th", { scope: "col", children: [
187
- /* @__PURE__ */ jsx3("span", { className: "fe-table__header-content", children: headers[columnIndex] ?? `Column ${columnIndex + 1}` }),
188
- /* @__PURE__ */ jsx3(
189
- "span",
190
- {
191
- className: "fe-table__resize-handle",
192
- role: "separator",
193
- "aria-label": `Resize ${getText(headers[columnIndex]) || `column ${columnIndex + 1}`}`,
194
- "aria-orientation": "vertical",
195
- "aria-valuemin": safeMinWidth,
196
- "aria-valuemax": MAX_COLUMN_WIDTH,
197
- "aria-valuenow": sizes[columnIndex] ?? safeMinWidth,
198
- tabIndex: 0,
199
- onPointerDown: (event) => {
200
- event.preventDefault();
201
- resizeState.current = { columnIndex, startX: event.clientX, startWidth: sizes[columnIndex] ?? safeMinWidth };
202
- setIsResizing(true);
203
- event.currentTarget.setPointerCapture(event.pointerId);
204
- },
205
- onKeyDown: (event) => {
206
- if (event.key === "ArrowLeft") updateColumnSize(columnIndex, (sizes[columnIndex] ?? safeMinWidth) - 16);
207
- if (event.key === "ArrowRight") updateColumnSize(columnIndex, (sizes[columnIndex] ?? safeMinWidth) + 16);
266
+ const changePage = (nextPage) => {
267
+ if (page === void 0) setUncontrolledPage(nextPage);
268
+ if (nextPage !== currentPage) onPageChange?.(nextPage);
269
+ };
270
+ const resolvedPaginationPosition = paginationPosition === "middle-bottom" ? "bottom-center" : paginationPosition;
271
+ const paginationControl = shouldPaginate && totalPages > 1 ? /* @__PURE__ */ jsx4("div", { className: `fe-table__pagination fe-table__pagination--${resolvedPaginationPosition}`, children: /* @__PURE__ */ jsx4(Pagination, { page: currentPage, totalPages, onPageChange: changePage }) }) : null;
272
+ return /* @__PURE__ */ jsxs4("div", { className: "fe-table", "data-resizing": isResizing || void 0, children: [
273
+ resolvedPaginationPosition === "top-right" && paginationControl,
274
+ /* @__PURE__ */ jsxs4("table", { className: ["fe-table__table", className].filter(Boolean).join(" "), "data-fe-table": true, "data-striped": striped || void 0, ...props, children: [
275
+ /* @__PURE__ */ jsx4("colgroup", { children: sizes2.map((size, index) => /* @__PURE__ */ jsx4("col", { style: { width: `${size}px` } }, index)) }),
276
+ /* @__PURE__ */ jsx4("thead", { children: /* @__PURE__ */ jsx4("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ jsxs4("th", { scope: "col", children: [
277
+ /* @__PURE__ */ jsx4("span", { className: "fe-table__header-content", children: headers[columnIndex] ?? `Column ${columnIndex + 1}` }),
278
+ /* @__PURE__ */ jsx4(
279
+ "span",
280
+ {
281
+ className: "fe-table__resize-handle",
282
+ role: "separator",
283
+ "aria-label": `Resize ${getText(headers[columnIndex]) || `column ${columnIndex + 1}`}`,
284
+ "aria-orientation": "vertical",
285
+ "aria-valuemin": safeMinWidth,
286
+ "aria-valuemax": MAX_COLUMN_WIDTH,
287
+ "aria-valuenow": sizes2[columnIndex] ?? safeMinWidth,
288
+ tabIndex: 0,
289
+ onPointerDown: (event) => {
290
+ event.preventDefault();
291
+ resizeState.current = { columnIndex, startX: event.clientX, startWidth: sizes2[columnIndex] ?? safeMinWidth };
292
+ setIsResizing(true);
293
+ event.currentTarget.setPointerCapture(event.pointerId);
294
+ },
295
+ onKeyDown: (event) => {
296
+ if (event.key === "ArrowLeft") updateColumnSize(columnIndex, (sizes2[columnIndex] ?? safeMinWidth) - 16);
297
+ if (event.key === "ArrowRight") updateColumnSize(columnIndex, (sizes2[columnIndex] ?? safeMinWidth) + 16);
298
+ }
208
299
  }
209
- }
210
- )
211
- ] }, columnIndex)) }) }),
212
- /* @__PURE__ */ jsx3("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ jsx3("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ jsx3("td", { children: row[columnIndex] }, columnIndex)) }, rowIndex)) })
213
- ] }) });
300
+ )
301
+ ] }, columnIndex)) }) }),
302
+ /* @__PURE__ */ jsx4("tbody", { children: pagedData.map((row, rowIndex) => /* @__PURE__ */ jsx4("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ jsx4("td", { children: row[columnIndex] }, columnIndex)) }, shouldPaginate ? (currentPage - 1) * safePageSize + rowIndex : rowIndex)) })
303
+ ] }),
304
+ resolvedPaginationPosition !== "top-right" && paginationControl
305
+ ] });
214
306
  }
215
- function Table({ headers, data, striped, children, ...props }) {
216
- if (headers || data) return /* @__PURE__ */ jsx3(DataTable, { headers: headers ?? [], data: data ?? [], striped: striped ?? true, ...props });
217
- return /* @__PURE__ */ jsx3(TableContext.Provider, { value: { striped: striped ?? false }, children: /* @__PURE__ */ jsx3("table", { "data-fe-table": true, "data-striped": striped || void 0, ...props, children }) });
307
+ function Table({
308
+ headers,
309
+ data,
310
+ striped,
311
+ children,
312
+ pagination,
313
+ pageSize,
314
+ page,
315
+ defaultPage,
316
+ onPageChange,
317
+ paginationPosition,
318
+ ...props
319
+ }) {
320
+ if (headers || data) return /* @__PURE__ */ jsx4(DataTable, { headers: headers ?? [], data: data ?? [], striped: striped ?? true, pagination, pageSize, page, defaultPage, onPageChange, paginationPosition, ...props });
321
+ return /* @__PURE__ */ jsx4(TableContext.Provider, { value: { striped: striped ?? false }, children: /* @__PURE__ */ jsx4("table", { "data-fe-table": true, "data-striped": striped || void 0, ...props, children }) });
218
322
  }
219
323
  function TableHead(props) {
220
- return /* @__PURE__ */ jsx3("thead", { ...props });
324
+ return /* @__PURE__ */ jsx4("thead", { ...props });
221
325
  }
222
326
  function TableBody(props) {
223
- return /* @__PURE__ */ jsx3("tbody", { ...props });
327
+ return /* @__PURE__ */ jsx4("tbody", { ...props });
224
328
  }
225
329
  function TableRow({ children, ...props }) {
226
330
  const { striped } = useContext(TableContext);
227
- return /* @__PURE__ */ jsx3("tr", { "data-striped": striped || void 0, ...props, children });
331
+ return /* @__PURE__ */ jsx4("tr", { "data-striped": striped || void 0, ...props, children });
228
332
  }
229
333
  function TableCell({ children, ...props }) {
230
- return /* @__PURE__ */ jsx3("td", { ...props, children });
334
+ return /* @__PURE__ */ jsx4("td", { ...props, children });
231
335
  }
232
336
  function TableHeader({ children, ...props }) {
233
- return /* @__PURE__ */ jsx3("th", { scope: "col", ...props, children });
337
+ return /* @__PURE__ */ jsx4("th", { scope: "col", ...props, children });
234
338
  }
235
339
  function TableCaption(props) {
236
- return /* @__PURE__ */ jsx3("caption", { ...props });
340
+ return /* @__PURE__ */ jsx4("caption", { ...props });
237
341
  }
238
342
 
239
343
  // packages/toggle/src/index.tsx
240
344
  import { forwardRef as forwardRef2 } from "react";
241
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
345
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
242
346
  var Toggle = forwardRef2(function Toggle2({ color = "#16a34a", label, className, style, id, ...props }, ref) {
243
347
  const classes = ["fe-toggle", className].filter(Boolean).join(" ");
244
348
  const toggleStyle = {
245
349
  "--fe-toggle-color": color,
246
350
  ...style
247
351
  };
248
- return /* @__PURE__ */ jsxs4("label", { className: classes, style: toggleStyle, children: [
249
- /* @__PURE__ */ jsx4("input", { ref, id, className: "fe-toggle__input", type: "checkbox", role: "switch", ...props }),
250
- /* @__PURE__ */ jsx4("span", { className: "fe-toggle__control", "aria-hidden": "true" }),
251
- label && /* @__PURE__ */ jsx4("span", { className: "fe-toggle__label", children: label })
352
+ return /* @__PURE__ */ jsxs5("label", { className: classes, style: toggleStyle, children: [
353
+ /* @__PURE__ */ jsx5("input", { ref, id, className: "fe-toggle__input", type: "checkbox", role: "switch", ...props }),
354
+ /* @__PURE__ */ jsx5("span", { className: "fe-toggle__control", "aria-hidden": "true" }),
355
+ label && /* @__PURE__ */ jsx5("span", { className: "fe-toggle__label", children: label })
252
356
  ] });
253
357
  });
254
358
  Toggle.displayName = "Toggle";
@@ -256,7 +360,7 @@ Toggle.displayName = "Toggle";
256
360
  // packages/drawer/src/index.tsx
257
361
  import { useEffect as useEffect2 } from "react";
258
362
  import { createPortal } from "react-dom";
259
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
363
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
260
364
  function Drawer({ open, onClose, title, footer, side = "right", width = 400, fullScreen = false, children, closeOnBackdropClick = true, className, style, ...props }) {
261
365
  useEffect2(() => {
262
366
  if (!open) return;
@@ -268,15 +372,15 @@ function Drawer({ open, onClose, title, footer, side = "right", width = 400, ful
268
372
  }, [onClose, open]);
269
373
  if (!open || typeof document === "undefined") return null;
270
374
  const panelStyle = !fullScreen && (side === "left" || side === "right") ? { ...style, width: typeof width === "number" ? `${width}px` : width } : style;
271
- return createPortal(/* @__PURE__ */ jsx5("div", { className: "fe-drawer-backdrop", role: "presentation", onMouseDown: (event) => {
375
+ return createPortal(/* @__PURE__ */ jsx6("div", { className: "fe-drawer-backdrop", role: "presentation", onMouseDown: (event) => {
272
376
  if (event.target === event.currentTarget && closeOnBackdropClick) onClose();
273
- }, children: /* @__PURE__ */ jsxs5("aside", { className: ["fe-drawer", `fe-drawer--${side}`, fullScreen && "fe-drawer--full-screen", className].filter(Boolean).join(" "), style: panelStyle, role: "dialog", "aria-modal": "true", "aria-label": typeof title === "string" ? title : "Drawer", ...props, children: [
274
- /* @__PURE__ */ jsxs5("header", { className: "fe-drawer__header", children: [
275
- title && /* @__PURE__ */ jsx5("h2", { children: title }),
276
- /* @__PURE__ */ jsx5("button", { type: "button", "aria-label": "Close drawer", onClick: onClose, children: "\xD7" })
377
+ }, children: /* @__PURE__ */ jsxs6("aside", { className: ["fe-drawer", `fe-drawer--${side}`, fullScreen && "fe-drawer--full-screen", className].filter(Boolean).join(" "), style: panelStyle, role: "dialog", "aria-modal": "true", "aria-label": typeof title === "string" ? title : "Drawer", ...props, children: [
378
+ /* @__PURE__ */ jsxs6("header", { className: "fe-drawer__header", children: [
379
+ title && /* @__PURE__ */ jsx6("h2", { children: title }),
380
+ /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Close drawer", onClick: onClose, children: "\xD7" })
277
381
  ] }),
278
- /* @__PURE__ */ jsx5("div", { className: "fe-drawer__body", children }),
279
- footer && /* @__PURE__ */ jsx5("footer", { className: "fe-drawer__footer", children: footer })
382
+ /* @__PURE__ */ jsx6("div", { className: "fe-drawer__body", children }),
383
+ footer && /* @__PURE__ */ jsx6("footer", { className: "fe-drawer__footer", children: footer })
280
384
  ] }) }), document.body);
281
385
  }
282
386
 
@@ -285,29 +389,120 @@ import {
285
389
  forwardRef as forwardRef3,
286
390
  useEffect as useEffect3,
287
391
  useId,
392
+ useLayoutEffect,
288
393
  useRef as useRef2,
289
- useState as useState2
394
+ useState as useState3
290
395
  } from "react";
291
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
396
+ import { createPortal as createPortal2 } from "react-dom";
397
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
398
+ function getFieldError(error, errorMessage) {
399
+ if (errorMessage !== void 0 && errorMessage !== null) return errorMessage;
400
+ if (typeof error !== "boolean") return error;
401
+ return error ? "Invalid value" : void 0;
402
+ }
403
+ function getDescribedBy(existing, messageId2) {
404
+ return [existing, messageId2].filter(Boolean).join(" ") || void 0;
405
+ }
406
+ var TextInput = forwardRef3(function TextInput2({ value, defaultValue = "", onChange, onValueChange, label, helperText, error, errorMessage, clearable = false, onClear, clearLabel = "Clear input", className, disabled, readOnly, id, "aria-describedby": ariaDescribedBy, "aria-labelledby": ariaLabelledBy, ...props }, ref) {
407
+ const generatedId = useId();
408
+ const inputId = id ?? `${generatedId}-input`;
409
+ const labelId = `${generatedId}-label`;
410
+ const messageId2 = `${generatedId}-message`;
411
+ const controlled = value !== void 0;
412
+ const [internalValue, setInternalValue] = useState3(defaultValue);
413
+ const currentValue = controlled ? value : internalValue;
414
+ const fieldError = getFieldError(error, errorMessage);
415
+ const hasError = fieldError !== void 0 && fieldError !== false && fieldError !== null;
416
+ const displayedMessage = hasError ? fieldError : helperText;
417
+ const describedBy = getDescribedBy(ariaDescribedBy, displayedMessage ? messageId2 : void 0);
418
+ const clear = () => {
419
+ if (!controlled) setInternalValue("");
420
+ onValueChange?.("");
421
+ onClear?.();
422
+ };
423
+ const labelledBy = getDescribedBy(ariaLabelledBy, label ? labelId : void 0);
424
+ return /* @__PURE__ */ jsxs7("label", { className: ["fe-text-input", hasError && "fe-text-input--error", className].filter(Boolean).join(" "), children: [
425
+ label && /* @__PURE__ */ jsx7("span", { id: labelId, className: "fe-text-input__label", children: label }),
426
+ /* @__PURE__ */ jsxs7("span", { className: "fe-text-input__control", children: [
427
+ /* @__PURE__ */ jsx7("input", { ref, id: inputId, value: currentValue, disabled, readOnly, "aria-invalid": hasError || void 0, "aria-labelledby": labelledBy, "aria-describedby": describedBy, onChange: (event) => {
428
+ if (!controlled) setInternalValue(event.target.value);
429
+ onValueChange?.(event.target.value);
430
+ onChange?.(event);
431
+ }, ...props }),
432
+ clearable && currentValue && !disabled && !readOnly && /* @__PURE__ */ jsx7(Button, { hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: /* @__PURE__ */ jsx7(CloseIcon, { size: 14 }), "aria-label": clearLabel, onClick: clear, className: "fe-text-input__clear" })
433
+ ] }),
434
+ displayedMessage && /* @__PURE__ */ jsx7("span", { id: messageId2, className: hasError ? "fe-text-input__error" : "fe-text-input__helper", children: displayedMessage })
435
+ ] });
436
+ });
437
+ TextInput.displayName = "TextInput";
438
+ var TextArea = forwardRef3(function TextArea2({ value, defaultValue = "", onChange, onValueChange, label, helperText, error, errorMessage, clearable = false, onClear, clearLabel = "Clear text area", className, disabled, readOnly, id, "aria-describedby": ariaDescribedBy, "aria-labelledby": ariaLabelledBy, ...props }, ref) {
439
+ const generatedId = useId();
440
+ const inputId = id ?? `${generatedId}-input`;
441
+ const labelId = `${generatedId}-label`;
442
+ const messageId2 = `${generatedId}-message`;
443
+ const controlled = value !== void 0;
444
+ const [internalValue, setInternalValue] = useState3(defaultValue);
445
+ const currentValue = controlled ? value : internalValue;
446
+ const fieldError = getFieldError(error, errorMessage);
447
+ const hasError = fieldError !== void 0 && fieldError !== false && fieldError !== null;
448
+ const displayedMessage = hasError ? fieldError : helperText;
449
+ const describedBy = getDescribedBy(ariaDescribedBy, displayedMessage ? messageId2 : void 0);
450
+ const clear = () => {
451
+ if (!controlled) setInternalValue("");
452
+ onValueChange?.("");
453
+ onClear?.();
454
+ };
455
+ const labelledBy = getDescribedBy(ariaLabelledBy, label ? labelId : void 0);
456
+ return /* @__PURE__ */ jsxs7("label", { className: ["fe-text-area", hasError && "fe-text-area--error", className].filter(Boolean).join(" "), children: [
457
+ label && /* @__PURE__ */ jsx7("span", { id: labelId, className: "fe-text-area__label", children: label }),
458
+ /* @__PURE__ */ jsxs7("span", { className: "fe-text-area__control", children: [
459
+ /* @__PURE__ */ jsx7("textarea", { ref, id: inputId, value: currentValue, disabled, readOnly, "aria-invalid": hasError || void 0, "aria-labelledby": labelledBy, "aria-describedby": describedBy, onChange: (event) => {
460
+ if (!controlled) setInternalValue(event.target.value);
461
+ onValueChange?.(event.target.value);
462
+ onChange?.(event);
463
+ }, ...props }),
464
+ clearable && currentValue && !disabled && !readOnly && /* @__PURE__ */ jsx7(Button, { hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: /* @__PURE__ */ jsx7(CloseIcon, { size: 14 }), "aria-label": clearLabel, onClick: clear, className: "fe-text-area__clear" })
465
+ ] }),
466
+ displayedMessage && /* @__PURE__ */ jsx7("span", { id: messageId2, className: hasError ? "fe-text-area__error" : "fe-text-area__helper", children: displayedMessage })
467
+ ] });
468
+ });
469
+ TextArea.displayName = "TextArea";
292
470
  var Selection = forwardRef3(function Selection2({ className, label, helperText, error, options, placeholder = "Select an option", size = "medium", id, value, defaultValue, onChange, multiple = false, searchable = false, tag = false, clearable = false, disabled = false, name, onKeyDown, ...props }, ref) {
293
471
  const generatedId = useId();
294
472
  const listboxId = id ?? generatedId;
295
473
  const isControlled = value !== void 0;
296
- const [internalValue, setInternalValue] = useState2(defaultValue ?? (multiple ? [] : ""));
474
+ const [internalValue, setInternalValue] = useState3(defaultValue ?? (multiple ? [] : ""));
297
475
  const selectedValue = isControlled ? value : internalValue;
298
476
  const selectedValues = Array.isArray(selectedValue) ? selectedValue : selectedValue === "" || selectedValue === null || selectedValue === void 0 ? [] : [selectedValue];
299
477
  const selectedOptions = options.filter((option) => selectedValues.some((item) => String(item) === String(option.value)));
300
- const [open, setOpen] = useState2(false);
301
- const [query, setQuery] = useState2("");
302
- const [activeIndex, setActiveIndex] = useState2(0);
478
+ const [open, setOpen] = useState3(false);
479
+ const [query, setQuery] = useState3("");
480
+ const [activeIndex, setActiveIndex] = useState3(0);
303
481
  const rootRef = useRef2(null);
482
+ const menuRef = useRef2(null);
483
+ const [menuPosition, setMenuPosition] = useState3(null);
304
484
  useEffect3(() => {
305
485
  const close = (event) => {
306
- if (!rootRef.current?.contains(event.target)) setOpen(false);
486
+ const target = event.target;
487
+ if (!rootRef.current?.contains(target) && !menuRef.current?.contains(target)) setOpen(false);
307
488
  };
308
489
  document.addEventListener("mousedown", close);
309
490
  return () => document.removeEventListener("mousedown", close);
310
491
  }, []);
492
+ useLayoutEffect(() => {
493
+ if (!open) return;
494
+ const updateMenuPosition = () => {
495
+ const rect = rootRef.current?.getBoundingClientRect();
496
+ if (rect) setMenuPosition({ top: rect.bottom + 7, left: rect.left, width: rect.width });
497
+ };
498
+ updateMenuPosition();
499
+ window.addEventListener("resize", updateMenuPosition);
500
+ document.addEventListener("scroll", updateMenuPosition, true);
501
+ return () => {
502
+ window.removeEventListener("resize", updateMenuPosition);
503
+ document.removeEventListener("scroll", updateMenuPosition, true);
504
+ };
505
+ }, [open]);
311
506
  const filteredOptions = options.filter((option) => !query || (option.searchText ?? (typeof option.label === "string" ? option.label : String(option.value))).toLowerCase().includes(query.toLowerCase()));
312
507
  const commit = (next) => {
313
508
  if (!isControlled) setInternalValue(next);
@@ -344,39 +539,39 @@ var Selection = forwardRef3(function Selection2({ className, label, helperText,
344
539
  }
345
540
  if (event.key === "Escape") setOpen(false);
346
541
  };
347
- return /* @__PURE__ */ jsxs6("div", { ref, className: ["fe-selection", `fe-selection--${size}`, open && "fe-selection--open", clearable && selectedOptions.length > 0 && "fe-selection--clearable", error && "fe-selection--error", disabled && "fe-selection--disabled", className].filter(Boolean).join(" "), ...props, children: [
348
- label && /* @__PURE__ */ jsx6("span", { className: "fe-selection__label", children: label }),
349
- /* @__PURE__ */ jsxs6("div", { ref: rootRef, className: "fe-selection__control", children: [
350
- name && selectedValues.map((item) => /* @__PURE__ */ jsx6("input", { type: "hidden", name: multiple ? `${name}[]` : name, value: item }, String(item))),
351
- /* @__PURE__ */ jsxs6("button", { type: "button", className: "fe-selection__trigger", disabled, "aria-invalid": error || void 0, "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": listboxId, onClick: () => setOpen((current) => !current), onKeyDown: onTriggerKeyDown, children: [
352
- /* @__PURE__ */ jsx6("span", { className: ["fe-selection__value", !selectedOptions.length && "is-placeholder", tag && selectedOptions.length > 1 && "is-tags"].filter(Boolean).join(" "), children: displayLabel ? tag && multiple ? selectedOptions.map((option) => /* @__PURE__ */ jsxs6("span", { className: "fe-selection__tag", children: [
542
+ return /* @__PURE__ */ jsxs7("div", { ref, className: ["fe-selection", `fe-selection--${size}`, open && "fe-selection--open", clearable && selectedOptions.length > 0 && "fe-selection--clearable", error && "fe-selection--error", disabled && "fe-selection--disabled", className].filter(Boolean).join(" "), ...props, children: [
543
+ label && /* @__PURE__ */ jsx7("span", { className: "fe-selection__label", children: label }),
544
+ /* @__PURE__ */ jsxs7("div", { ref: rootRef, className: "fe-selection__control", children: [
545
+ name && selectedValues.map((item) => /* @__PURE__ */ jsx7("input", { type: "hidden", name: multiple ? `${name}[]` : name, value: item }, String(item))),
546
+ /* @__PURE__ */ jsxs7("button", { type: "button", className: "fe-selection__trigger", disabled, "aria-invalid": error || void 0, "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": listboxId, onClick: () => setOpen((current) => !current), onKeyDown: onTriggerKeyDown, children: [
547
+ /* @__PURE__ */ jsx7("span", { className: ["fe-selection__value", !selectedOptions.length && "is-placeholder", tag && selectedOptions.length > 1 && "is-tags"].filter(Boolean).join(" "), children: displayLabel ? tag && multiple ? selectedOptions.map((option) => /* @__PURE__ */ jsxs7("span", { className: "fe-selection__tag", children: [
353
548
  option.label,
354
- /* @__PURE__ */ jsx6("span", { "aria-hidden": "true", children: "\xD7" })
355
- ] }, String(option.value))) : displayLabel.map((item, index) => /* @__PURE__ */ jsxs6("span", { children: [
549
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\xD7" })
550
+ ] }, String(option.value))) : displayLabel.map((item, index) => /* @__PURE__ */ jsxs7("span", { children: [
356
551
  item,
357
552
  index < displayLabel.length - 1 ? ", " : ""
358
553
  ] }, index)) : placeholder }),
359
- /* @__PURE__ */ jsx6("span", { className: "fe-selection__actions", children: /* @__PURE__ */ jsx6("span", { className: "fe-selection__chevron", "aria-hidden": "true" }) })
554
+ /* @__PURE__ */ jsx7("span", { className: "fe-selection__actions", children: /* @__PURE__ */ jsx7("span", { className: "fe-selection__chevron", "aria-hidden": "true" }) })
360
555
  ] }),
361
- clearable && selectedOptions.length > 0 && /* @__PURE__ */ jsx6("button", { type: "button", className: "fe-selection__clear", "aria-label": "Clear selection", onClick: clear }),
362
- open && /* @__PURE__ */ jsxs6("div", { id: listboxId, className: "fe-selection__menu", role: "listbox", "aria-multiselectable": multiple || void 0, children: [
363
- searchable && /* @__PURE__ */ jsxs6("div", { className: "fe-selection__search", children: [
364
- /* @__PURE__ */ jsx6("span", { "aria-hidden": "true", children: "\u2315" }),
365
- /* @__PURE__ */ jsx6("input", { autoFocus: true, value: query, onChange: (event) => {
556
+ clearable && selectedOptions.length > 0 && /* @__PURE__ */ jsx7("button", { type: "button", className: "fe-selection__clear", "aria-label": "Clear selection", onClick: clear }),
557
+ open && menuPosition && createPortal2(/* @__PURE__ */ jsxs7("div", { ref: menuRef, id: listboxId, className: "fe-selection__menu", style: { position: "fixed", top: menuPosition.top, left: menuPosition.left, width: menuPosition.width }, role: "listbox", "aria-multiselectable": multiple || void 0, children: [
558
+ searchable && /* @__PURE__ */ jsxs7("div", { className: "fe-selection__search", children: [
559
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u2315" }),
560
+ /* @__PURE__ */ jsx7("input", { autoFocus: true, value: query, onChange: (event) => {
366
561
  setQuery(event.target.value);
367
562
  setActiveIndex(0);
368
563
  }, placeholder: "Search\u2026", "aria-label": "Search options" })
369
564
  ] }),
370
- /* @__PURE__ */ jsx6("div", { className: "fe-selection__options", children: filteredOptions.length ? filteredOptions.map((option, index) => {
565
+ /* @__PURE__ */ jsx7("div", { className: "fe-selection__options", children: filteredOptions.length ? filteredOptions.map((option, index) => {
371
566
  const isSelected = selectedValues.some((item) => String(item) === String(option.value));
372
- return /* @__PURE__ */ jsxs6("button", { type: "button", className: [isSelected && "is-selected", index === activeIndex && "is-active"].filter(Boolean).join(" "), role: "option", "aria-selected": isSelected, disabled: option.disabled, onMouseMove: () => setActiveIndex(index), onClick: () => choose(option), children: [
373
- /* @__PURE__ */ jsx6("span", { children: option.label }),
374
- isSelected && /* @__PURE__ */ jsx6("span", { className: "fe-selection__check", "aria-hidden": "true", children: "\u2713" })
567
+ return /* @__PURE__ */ jsxs7("button", { type: "button", className: [isSelected && "is-selected", index === activeIndex && "is-active"].filter(Boolean).join(" "), role: "option", "aria-selected": isSelected, disabled: option.disabled, onMouseMove: () => setActiveIndex(index), onClick: () => choose(option), children: [
568
+ /* @__PURE__ */ jsx7("span", { children: option.label }),
569
+ isSelected && /* @__PURE__ */ jsx7("span", { className: "fe-selection__check", "aria-hidden": "true", children: "\u2713" })
375
570
  ] }, String(option.value));
376
- }) : /* @__PURE__ */ jsx6("p", { className: "fe-selection__empty", children: "No matching options" }) })
377
- ] })
571
+ }) : /* @__PURE__ */ jsx7("p", { className: "fe-selection__empty", children: "No matching options" }) })
572
+ ] }), document.body)
378
573
  ] }),
379
- helperText && /* @__PURE__ */ jsx6("span", { className: "fe-selection__helper", children: helperText })
574
+ helperText && /* @__PURE__ */ jsx7("span", { className: "fe-selection__helper", children: helperText })
380
575
  ] });
381
576
  });
382
577
  Selection.displayName = "Selection";
@@ -413,12 +608,18 @@ function normalizeNumberInput(raw, options) {
413
608
  if (value !== numericValue) return { display: formatNumber(value, decimals, thousandSeparator, decimalSeparator), value };
414
609
  return { display: `${negative ? "-" : ""}${groupInteger(integer || "0", thousandSeparator)}${hasDecimal ? `${decimalSeparator}${fraction}` : ""}`, value };
415
610
  }
416
- var NumberInput = forwardRef3(function NumberInput2({ value, defaultValue = null, onValueChange, allowDecimal = true, decimals = 2, min, max, decimalSeparator = ".", thousandSeparator = ",", label, helperText, error, className, onBlur, onFocus, ...props }, ref) {
611
+ var NumberInput = forwardRef3(function NumberInput2({ value, defaultValue = null, onValueChange, allowDecimal = true, decimals = 2, min, max, decimalSeparator = ".", thousandSeparator = ",", label, helperText, error, errorMessage, className, onBlur, onFocus, "aria-describedby": ariaDescribedBy, ...props }, ref) {
612
+ const generatedId = useId();
613
+ const messageId2 = `${generatedId}-message`;
417
614
  const isControlled = value !== void 0;
418
- const [internalValue, setInternalValue] = useState2(defaultValue);
419
- const [focused, setFocused] = useState2(false);
615
+ const [internalValue, setInternalValue] = useState3(defaultValue);
616
+ const [focused, setFocused] = useState3(false);
420
617
  const actualValue = isControlled ? value ?? null : internalValue;
421
- const [text, setText] = useState2(actualValue === null ? "" : formatNumber(actualValue, decimals, thousandSeparator, decimalSeparator));
618
+ const [text, setText] = useState3(actualValue === null ? "" : formatNumber(actualValue, decimals, thousandSeparator, decimalSeparator));
619
+ const fieldError = getFieldError(error, errorMessage);
620
+ const hasError = fieldError !== void 0 && fieldError !== false && fieldError !== null;
621
+ const displayedMessage = hasError ? fieldError : helperText;
622
+ const describedBy = getDescribedBy(ariaDescribedBy, displayedMessage ? messageId2 : void 0);
422
623
  useEffect3(() => {
423
624
  if (!focused) setText(actualValue === null ? "" : formatNumber(actualValue, decimals, thousandSeparator, decimalSeparator));
424
625
  }, [actualValue, decimalSeparator, decimals, focused, thousandSeparator]);
@@ -429,32 +630,32 @@ var NumberInput = forwardRef3(function NumberInput2({ value, defaultValue = null
429
630
  if (!isControlled) setInternalValue(nextValue);
430
631
  onValueChange?.(nextValue);
431
632
  };
432
- return /* @__PURE__ */ jsxs6("label", { className: ["fe-number-input", error && "fe-number-input--error", className].filter(Boolean).join(" "), children: [
433
- label && /* @__PURE__ */ jsx6("span", { className: "fe-number-input__label", children: label }),
434
- /* @__PURE__ */ jsx6("input", { ref, inputMode: allowDecimal ? "decimal" : "numeric", value: text, "aria-invalid": error || void 0, onFocus: (event) => {
633
+ return /* @__PURE__ */ jsxs7("label", { className: ["fe-number-input", hasError && "fe-number-input--error", className].filter(Boolean).join(" "), children: [
634
+ label && /* @__PURE__ */ jsx7("span", { className: "fe-number-input__label", children: label }),
635
+ /* @__PURE__ */ jsx7("input", { ref, inputMode: allowDecimal ? "decimal" : "numeric", value: text, onFocus: (event) => {
435
636
  setFocused(true);
436
637
  onFocus?.(event);
437
638
  }, onChange: (event) => updateValue(event.target.value), onBlur: (event) => {
438
639
  setFocused(false);
439
640
  onBlur?.(event);
440
- }, ...props }),
441
- helperText && /* @__PURE__ */ jsx6("span", { className: "fe-number-input__helper", children: helperText })
641
+ }, ...props, "aria-invalid": hasError || void 0, "aria-describedby": describedBy }),
642
+ displayedMessage && /* @__PURE__ */ jsx7("span", { id: messageId2, className: hasError ? "fe-number-input__error" : "fe-number-input__helper", children: displayedMessage })
442
643
  ] });
443
644
  });
444
645
  NumberInput.displayName = "NumberInput";
445
646
  function CurrencyInput({ suffix = "VND", currencies, currency, defaultCurrency, onCurrencyChange, className, ...props }) {
446
- const [internalCurrency, setInternalCurrency] = useState2(defaultCurrency ?? currencies?.[0]?.code ?? "");
647
+ const [internalCurrency, setInternalCurrency] = useState3(defaultCurrency ?? currencies?.[0]?.code ?? "");
447
648
  const selectedCurrency = currency ?? internalCurrency;
448
649
  const current = currencies?.find((item) => item.code === selectedCurrency);
449
650
  const currencySuffix = current?.suffix ?? current?.code ?? suffix;
450
- return /* @__PURE__ */ jsxs6("div", { className: ["fe-currency-input", className].filter(Boolean).join(" "), children: [
451
- /* @__PURE__ */ jsx6(NumberInput, { ...props, className: "fe-currency-input__number" }),
452
- currencies?.length ? /* @__PURE__ */ jsx6("select", { className: "fe-currency-input__currency", "aria-label": "Currency", value: selectedCurrency, onChange: (event) => {
651
+ return /* @__PURE__ */ jsxs7("div", { className: ["fe-currency-input", className].filter(Boolean).join(" "), children: [
652
+ /* @__PURE__ */ jsx7(NumberInput, { ...props, className: "fe-currency-input__number" }),
653
+ currencies?.length ? /* @__PURE__ */ jsx7("select", { className: "fe-currency-input__currency", "aria-label": "Currency", value: selectedCurrency, onChange: (event) => {
453
654
  const next = currencies.find((item) => item.code === event.target.value);
454
655
  if (!next) return;
455
656
  if (currency === void 0) setInternalCurrency(next.code);
456
657
  onCurrencyChange?.(next);
457
- }, children: currencies.map((item) => /* @__PURE__ */ jsx6("option", { value: item.code, children: item.label ?? item.code }, item.code)) }) : /* @__PURE__ */ jsx6("span", { className: "fe-currency-input__suffix", children: currencySuffix })
658
+ }, children: currencies.map((item) => /* @__PURE__ */ jsx7("option", { value: item.code, children: item.label ?? item.code }, item.code)) }) : /* @__PURE__ */ jsx7("span", { className: "fe-currency-input__suffix", children: currencySuffix })
458
659
  ] });
459
660
  }
460
661
  var pad = (value) => String(value).padStart(2, "0");
@@ -477,13 +678,13 @@ function DateTimePicker(props) {
477
678
  const { label, format = "dd/MM/yyyy HH:mm", disabled, placeholder, locale = "en-US", className, mode = "single" } = props;
478
679
  const isRange = mode === "range";
479
680
  const controlled = props.value !== void 0;
480
- const [singleInternal, setSingleInternal] = useState2((!isRange ? props.defaultValue : null) ?? null);
481
- const [rangeInternal, setRangeInternal] = useState2((isRange ? props.defaultValue : void 0) ?? [null, null]);
681
+ const [singleInternal, setSingleInternal] = useState3((!isRange ? props.defaultValue : null) ?? null);
682
+ const [rangeInternal, setRangeInternal] = useState3((isRange ? props.defaultValue : void 0) ?? [null, null]);
482
683
  const singleValue = !isRange ? props.value ?? singleInternal : null;
483
684
  const rangeValue = isRange ? props.value ?? rangeInternal : [null, null];
484
- const [open, setOpen] = useState2(false);
485
- const [month, setMonth] = useState2(() => new Date((isRange ? rangeValue[0] : singleValue) ?? /* @__PURE__ */ new Date()));
486
- const [editingEnd, setEditingEnd] = useState2(false);
685
+ const [open, setOpen] = useState3(false);
686
+ const [month, setMonth] = useState3(() => new Date((isRange ? rangeValue[0] : singleValue) ?? /* @__PURE__ */ new Date()));
687
+ const [editingEnd, setEditingEnd] = useState3(false);
487
688
  const rootRef = useRef2(null);
488
689
  useEffect3(() => {
489
690
  const onPointerDown = (event) => {
@@ -527,42 +728,42 @@ function DateTimePicker(props) {
527
728
  if (isRange) setRange(editingEnd ? [rangeValue[0], next] : [next, rangeValue[1]]);
528
729
  else setSingle(next);
529
730
  };
530
- return /* @__PURE__ */ jsxs6("div", { ref: rootRef, className: ["fe-datetime-picker", className].filter(Boolean).join(" "), children: [
531
- label && /* @__PURE__ */ jsx6("span", { className: "fe-datetime-picker__label", children: label }),
532
- /* @__PURE__ */ jsxs6("button", { type: "button", className: "fe-datetime-picker__trigger", disabled, "aria-expanded": open, onClick: () => setOpen((current) => !current), children: [
533
- /* @__PURE__ */ jsx6("span", { "aria-hidden": "true", children: "\u25F7" }),
534
- /* @__PURE__ */ jsx6("span", { children: shownValue || placeholder || (isRange ? "Select date range" : "Select date and time") })
731
+ return /* @__PURE__ */ jsxs7("div", { ref: rootRef, className: ["fe-datetime-picker", className].filter(Boolean).join(" "), children: [
732
+ label && /* @__PURE__ */ jsx7("span", { className: "fe-datetime-picker__label", children: label }),
733
+ /* @__PURE__ */ jsxs7("button", { type: "button", className: "fe-datetime-picker__trigger", disabled, "aria-expanded": open, onClick: () => setOpen((current) => !current), children: [
734
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u25F7" }),
735
+ /* @__PURE__ */ jsx7("span", { children: shownValue || placeholder || (isRange ? "Select date range" : "Select date and time") })
535
736
  ] }),
536
- open && /* @__PURE__ */ jsxs6("div", { className: "fe-datetime-picker__popper", role: "dialog", "aria-label": "Date and time picker", children: [
537
- /* @__PURE__ */ jsxs6("div", { className: "fe-datetime-picker__month", children: [
538
- /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Previous month", onClick: () => setMonth((current) => new Date(current.getFullYear(), current.getMonth() - 1, 1)), children: "\u2039" }),
539
- /* @__PURE__ */ jsx6("strong", { children: month.toLocaleDateString(locale, { month: "long", year: "numeric" }) }),
540
- /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Next month", onClick: () => setMonth((current) => new Date(current.getFullYear(), current.getMonth() + 1, 1)), children: "\u203A" })
737
+ open && /* @__PURE__ */ jsxs7("div", { className: "fe-datetime-picker__popper", role: "dialog", "aria-label": "Date and time picker", children: [
738
+ /* @__PURE__ */ jsxs7("div", { className: "fe-datetime-picker__month", children: [
739
+ /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Previous month", onClick: () => setMonth((current) => new Date(current.getFullYear(), current.getMonth() - 1, 1)), children: "\u2039" }),
740
+ /* @__PURE__ */ jsx7("strong", { children: month.toLocaleDateString(locale, { month: "long", year: "numeric" }) }),
741
+ /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Next month", onClick: () => setMonth((current) => new Date(current.getFullYear(), current.getMonth() + 1, 1)), children: "\u203A" })
541
742
  ] }),
542
- /* @__PURE__ */ jsx6("div", { className: "fe-datetime-picker__weekdays", children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((day) => /* @__PURE__ */ jsx6("span", { children: day }, day)) }),
543
- /* @__PURE__ */ jsx6("div", { className: "fe-datetime-picker__days", children: days.map((day) => {
743
+ /* @__PURE__ */ jsx7("div", { className: "fe-datetime-picker__weekdays", children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((day) => /* @__PURE__ */ jsx7("span", { children: day }, day)) }),
744
+ /* @__PURE__ */ jsx7("div", { className: "fe-datetime-picker__days", children: days.map((day) => {
544
745
  const inMonth = day.getMonth() === month.getMonth();
545
746
  const selected = isRange ? sameDay(day, rangeValue[0]) || sameDay(day, rangeValue[1]) : sameDay(day, singleValue);
546
747
  const inRange = isRange && rangeValue[0] && rangeValue[1] && day > startOfDay(rangeValue[0]) && day < startOfDay(rangeValue[1]);
547
- return /* @__PURE__ */ jsx6("button", { type: "button", className: [!inMonth && "is-other-month", selected && "is-selected", inRange && "is-in-range"].filter(Boolean).join(" "), onClick: () => selectDay(day), children: day.getDate() }, day.toISOString());
748
+ return /* @__PURE__ */ jsx7("button", { type: "button", className: [!inMonth && "is-other-month", selected && "is-selected", inRange && "is-in-range"].filter(Boolean).join(" "), onClick: () => selectDay(day), children: day.getDate() }, day.toISOString());
548
749
  }) }),
549
- isRange && /* @__PURE__ */ jsxs6("div", { className: "fe-datetime-picker__range-tabs", children: [
550
- /* @__PURE__ */ jsx6("button", { type: "button", className: !editingEnd ? "is-active" : "", onClick: () => setEditingEnd(false), children: "Start" }),
551
- /* @__PURE__ */ jsx6("button", { type: "button", className: editingEnd ? "is-active" : "", onClick: () => setEditingEnd(true), children: "End" })
750
+ isRange && /* @__PURE__ */ jsxs7("div", { className: "fe-datetime-picker__range-tabs", children: [
751
+ /* @__PURE__ */ jsx7("button", { type: "button", className: !editingEnd ? "is-active" : "", onClick: () => setEditingEnd(false), children: "Start" }),
752
+ /* @__PURE__ */ jsx7("button", { type: "button", className: editingEnd ? "is-active" : "", onClick: () => setEditingEnd(true), children: "End" })
552
753
  ] }),
553
- /* @__PURE__ */ jsxs6("label", { className: "fe-datetime-picker__time", children: [
754
+ /* @__PURE__ */ jsxs7("label", { className: "fe-datetime-picker__time", children: [
554
755
  "Time",
555
- /* @__PURE__ */ jsx6("input", { type: "time", value: selectedForTime ? `${pad(selectedForTime.getHours())}:${pad(selectedForTime.getMinutes())}` : "00:00", onChange: (event) => setTime(event.target.value), disabled: !selectedForTime })
756
+ /* @__PURE__ */ jsx7("input", { type: "time", value: selectedForTime ? `${pad(selectedForTime.getHours())}:${pad(selectedForTime.getMinutes())}` : "00:00", onChange: (event) => setTime(event.target.value), disabled: !selectedForTime })
556
757
  ] })
557
758
  ] })
558
759
  ] });
559
760
  }
560
761
 
561
762
  // packages/tooltip/src/index.tsx
562
- import { cloneElement, useId as useId2, useState as useState3 } from "react";
563
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
763
+ import { cloneElement, useId as useId2, useState as useState4 } from "react";
764
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
564
765
  function Tooltip({ content, children, placement = "top", disabled, className, ...props }) {
565
- const [visible, setVisible] = useState3(false);
766
+ const [visible, setVisible] = useState4(false);
566
767
  const id = useId2();
567
768
  const trigger = cloneElement(children, { "aria-describedby": visible ? id : void 0, onMouseEnter: (event) => {
568
769
  children.props.onMouseEnter?.(event);
@@ -577,17 +778,17 @@ function Tooltip({ content, children, placement = "top", disabled, className, ..
577
778
  children.props.onBlur?.(event);
578
779
  setVisible(false);
579
780
  } });
580
- return /* @__PURE__ */ jsxs7("span", { className: ["fe-tooltip", `fe-tooltip--${placement}`, className].filter(Boolean).join(" "), ...props, children: [
781
+ return /* @__PURE__ */ jsxs8("span", { className: ["fe-tooltip", `fe-tooltip--${placement}`, className].filter(Boolean).join(" "), ...props, children: [
581
782
  trigger,
582
- visible && !disabled && /* @__PURE__ */ jsx7("span", { id, role: "tooltip", className: "fe-tooltip__content", children: content })
783
+ visible && !disabled && /* @__PURE__ */ jsx8("span", { id, role: "tooltip", className: "fe-tooltip__content", children: content })
583
784
  ] });
584
785
  }
585
786
 
586
787
  // packages/expandable-section/src/index.tsx
587
- import { useId as useId3, useState as useState4 } from "react";
588
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
788
+ import { useId as useId3, useState as useState5 } from "react";
789
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
589
790
  function ExpandableSection({ title, children, expanded, defaultExpanded = false, onExpandedChange, disabled, className, ...props }) {
590
- const [internalExpanded, setInternalExpanded] = useState4(defaultExpanded);
791
+ const [internalExpanded, setInternalExpanded] = useState5(defaultExpanded);
591
792
  const isExpanded = expanded ?? internalExpanded;
592
793
  const id = useId3();
593
794
  const toggle = () => {
@@ -596,26 +797,304 @@ function ExpandableSection({ title, children, expanded, defaultExpanded = false,
596
797
  if (expanded === void 0) setInternalExpanded(next);
597
798
  onExpandedChange?.(next);
598
799
  };
599
- return /* @__PURE__ */ jsxs8("section", { className: ["fe-expandable-section", isExpanded && "fe-expandable-section--expanded", disabled && "fe-expandable-section--disabled", className].filter(Boolean).join(" "), ...props, children: [
600
- /* @__PURE__ */ jsxs8("button", { className: "fe-expandable-section__trigger", type: "button", "aria-expanded": isExpanded, "aria-controls": id, disabled, onClick: toggle, children: [
601
- /* @__PURE__ */ jsx8("span", { children: title }),
602
- /* @__PURE__ */ jsx8("span", { className: "fe-expandable-section__chevron", "aria-hidden": "true" })
800
+ return /* @__PURE__ */ jsxs9("section", { className: ["fe-expandable-section", isExpanded && "fe-expandable-section--expanded", disabled && "fe-expandable-section--disabled", className].filter(Boolean).join(" "), ...props, children: [
801
+ /* @__PURE__ */ jsxs9("button", { className: "fe-expandable-section__trigger", type: "button", "aria-expanded": isExpanded, "aria-controls": id, disabled, onClick: toggle, children: [
802
+ /* @__PURE__ */ jsx9("span", { children: title }),
803
+ /* @__PURE__ */ jsx9("span", { className: "fe-expandable-section__chevron", "aria-hidden": "true" })
804
+ ] }),
805
+ /* @__PURE__ */ jsx9("div", { id, className: "fe-expandable-section__content", "aria-hidden": !isExpanded, children: /* @__PURE__ */ jsx9("div", { className: "fe-expandable-section__content-inner", children }) })
806
+ ] });
807
+ }
808
+
809
+ // packages/loading/src/index.tsx
810
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
811
+ var sizes = {
812
+ small: "16px",
813
+ medium: "24px",
814
+ large: "40px"
815
+ };
816
+ function Loading({ size = "medium", color = "#2878f0", label, className, style, ...props }) {
817
+ const diameter = typeof size === "number" ? `${size}px` : sizes[size];
818
+ const loadingStyle = {
819
+ "--fe-loading-size": diameter,
820
+ "--fe-loading-color": color,
821
+ ...style
822
+ };
823
+ return /* @__PURE__ */ jsxs10("span", { className: ["fe-loading", className].filter(Boolean).join(" "), style: loadingStyle, role: "status", ...props, children: [
824
+ /* @__PURE__ */ jsx10("span", { className: "fe-loading__spinner", "aria-hidden": "true" }),
825
+ label && /* @__PURE__ */ jsx10("span", { className: "fe-loading__label", children: label }),
826
+ !label && /* @__PURE__ */ jsx10("span", { className: "fe-loading__sr-only", children: "Loading" })
827
+ ] });
828
+ }
829
+
830
+ // packages/skeleton/src/index.tsx
831
+ import { Fragment as Fragment2, jsx as jsx11 } from "react/jsx-runtime";
832
+ var pixels = (value) => typeof value === "number" ? `${value}px` : value;
833
+ function SkeletonBase({ variant = "box", shape, width, height, count = 1, animated = true, className, style, ...props }) {
834
+ const resolvedVariant = shape ?? variant;
835
+ const resolvedWidth = pixels(width) ?? (resolvedVariant === "circle" ? "40px" : "100%");
836
+ const resolvedHeight = pixels(height) ?? (resolvedVariant === "circle" ? resolvedWidth : resolvedVariant === "line" ? "12px" : "80px");
837
+ const skeletonStyle = {
838
+ "--fe-skeleton-width": resolvedWidth,
839
+ "--fe-skeleton-height": resolvedHeight,
840
+ ...style
841
+ };
842
+ return /* @__PURE__ */ jsx11(Fragment2, { children: Array.from({ length: Math.max(1, count) }, (_, index) => /* @__PURE__ */ jsx11(
843
+ "div",
844
+ {
845
+ className: ["fe-skeleton", `fe-skeleton--${resolvedVariant}`, animated && "fe-skeleton--animated", className].filter(Boolean).join(" "),
846
+ style: skeletonStyle,
847
+ "aria-hidden": "true",
848
+ ...props
849
+ },
850
+ index
851
+ )) });
852
+ }
853
+ function SkeletonCircle({ size = 40, ...props }) {
854
+ return /* @__PURE__ */ jsx11(SkeletonBase, { variant: "circle", width: size, height: size, ...props });
855
+ }
856
+ function SkeletonSquare({ size = 80, ...props }) {
857
+ return /* @__PURE__ */ jsx11(SkeletonBase, { variant: "box", width: size, height: size, ...props });
858
+ }
859
+ function SkeletonBox({ width = "100%", height = 80, ...props }) {
860
+ return /* @__PURE__ */ jsx11(SkeletonBase, { variant: "box", width, height, ...props });
861
+ }
862
+ function SkeletonLine({ width = "100%", height = 12, ...props }) {
863
+ return /* @__PURE__ */ jsx11(SkeletonBase, { variant: "line", width, height, ...props });
864
+ }
865
+ var Skeleton = Object.assign(SkeletonBase, {
866
+ Circle: SkeletonCircle,
867
+ Square: SkeletonSquare,
868
+ Box: SkeletonBox,
869
+ Line: SkeletonLine
870
+ });
871
+
872
+ // packages/modal/src/index.tsx
873
+ import { useEffect as useEffect4, useId as useId4, useRef as useRef3 } from "react";
874
+ import { createPortal as createPortal3 } from "react-dom";
875
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
876
+ function Modal({
877
+ open,
878
+ onClose,
879
+ title,
880
+ description,
881
+ children,
882
+ footer,
883
+ actions = [],
884
+ size = "medium",
885
+ closeOnBackdropClick = true,
886
+ closeButton = true,
887
+ closeLabel = "Close modal",
888
+ className,
889
+ ...props
890
+ }) {
891
+ const titleId = useId4();
892
+ const panelRef = useRef3(null);
893
+ useEffect4(() => {
894
+ if (!open) return;
895
+ const onKeyDown = (event) => {
896
+ if (event.key === "Escape") onClose();
897
+ };
898
+ document.addEventListener("keydown", onKeyDown);
899
+ panelRef.current?.focus();
900
+ return () => document.removeEventListener("keydown", onKeyDown);
901
+ }, [onClose, open]);
902
+ if (!open || typeof document === "undefined") return null;
903
+ const hasFooter = footer || actions.length > 0;
904
+ return createPortal3(
905
+ /* @__PURE__ */ jsx12("div", { className: "fe-modal-backdrop", role: "presentation", onMouseDown: (event) => {
906
+ if (event.target === event.currentTarget && closeOnBackdropClick) onClose();
907
+ }, children: /* @__PURE__ */ jsxs11(
908
+ "div",
909
+ {
910
+ ref: panelRef,
911
+ className: ["fe-modal", `fe-modal--${size}`, className].filter(Boolean).join(" "),
912
+ role: "dialog",
913
+ "aria-modal": "true",
914
+ "aria-labelledby": title ? titleId : void 0,
915
+ "aria-label": title ? void 0 : "Modal",
916
+ tabIndex: -1,
917
+ ...props,
918
+ children: [
919
+ (title || description || closeButton) && /* @__PURE__ */ jsxs11("header", { className: "fe-modal__header", children: [
920
+ /* @__PURE__ */ jsxs11("div", { className: "fe-modal__heading", children: [
921
+ title && /* @__PURE__ */ jsx12("h2", { id: titleId, children: title }),
922
+ description && /* @__PURE__ */ jsx12("p", { children: description })
923
+ ] }),
924
+ closeButton && /* @__PURE__ */ jsx12(Button, { hierarchy: "tertiary-gray", size: "32", iconOnly: true, icon: /* @__PURE__ */ jsx12(CloseIcon, {}), "aria-label": closeLabel, onClick: onClose })
925
+ ] }),
926
+ /* @__PURE__ */ jsx12("div", { className: "fe-modal__body", children }),
927
+ hasFooter && /* @__PURE__ */ jsxs11("footer", { className: "fe-modal__footer", children: [
928
+ footer && /* @__PURE__ */ jsx12("div", { className: "fe-modal__footer-content", children: footer }),
929
+ actions.length > 0 && /* @__PURE__ */ jsx12("div", { className: "fe-modal__actions", children: actions.map(({ label, ...action }, index) => /* @__PURE__ */ jsx12(Button, { ...action, children: label }, index)) })
930
+ ] })
931
+ ]
932
+ }
933
+ ) }),
934
+ document.body
935
+ );
936
+ }
937
+
938
+ // packages/progress/src/index.tsx
939
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
940
+ function ProgressBar({ value, max = 100, label, showValue = true, size = "medium", color = "#2878f0", className, style, "aria-label": ariaLabel, ...props }) {
941
+ const indeterminate = value === null || value === void 0;
942
+ const percentage = indeterminate ? 0 : Math.min(100, Math.max(0, Number(value) / Math.max(1, max) * 100));
943
+ const progressStyle = {
944
+ "--fe-progress-value": `${percentage}%`,
945
+ "--fe-progress-color": color,
946
+ ...style
947
+ };
948
+ const visibleValue = `${Math.round(percentage)}%`;
949
+ return /* @__PURE__ */ jsxs12("div", { className: ["fe-progress", `fe-progress--${size}`, indeterminate && "fe-progress--indeterminate", className].filter(Boolean).join(" "), style: progressStyle, ...props, children: [
950
+ (label || !indeterminate && showValue) && /* @__PURE__ */ jsxs12("div", { className: "fe-progress__meta", children: [
951
+ label && /* @__PURE__ */ jsx13("span", { className: "fe-progress__label", children: label }),
952
+ !indeterminate && showValue && /* @__PURE__ */ jsx13("output", { className: "fe-progress__value", children: visibleValue })
603
953
  ] }),
604
- /* @__PURE__ */ jsx8("div", { id, className: "fe-expandable-section__content", "aria-hidden": !isExpanded, children: /* @__PURE__ */ jsx8("div", { className: "fe-expandable-section__content-inner", children }) })
954
+ /* @__PURE__ */ jsx13("div", { className: "fe-progress__track", role: "progressbar", "aria-label": ariaLabel ?? (typeof label === "string" ? label : "Progress"), "aria-valuemin": indeterminate ? void 0 : 0, "aria-valuemax": indeterminate ? void 0 : max, "aria-valuenow": indeterminate ? void 0 : Number(value), "aria-valuetext": indeterminate ? "Loading" : visibleValue, children: /* @__PURE__ */ jsx13("span", { className: "fe-progress__indicator" }) })
955
+ ] });
956
+ }
957
+
958
+ // packages/chip/src/index.tsx
959
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
960
+ var statuses = ["default", "info", "success", "warning", "error", "neutral", "pending", "processing"];
961
+ function Chip({ children, status = "default", color, variant = "subtle", dot = false, onRemove, removeLabel = "Remove", className, style, ...props }) {
962
+ const usesCustomColor = Boolean(color);
963
+ const chipStyle = {
964
+ ...usesCustomColor ? { "--fe-chip-custom-color": color } : {},
965
+ ...style
966
+ };
967
+ const resolvedStatus = statuses.includes(status) ? status : "default";
968
+ return /* @__PURE__ */ jsxs13("span", { className: ["fe-chip", `fe-chip--${variant}`, `fe-chip--${usesCustomColor ? "custom" : resolvedStatus}`, className].filter(Boolean).join(" "), style: chipStyle, ...props, children: [
969
+ dot && /* @__PURE__ */ jsx14("span", { className: "fe-chip__dot", "aria-hidden": "true" }),
970
+ /* @__PURE__ */ jsx14("span", { className: "fe-chip__content", children }),
971
+ onRemove && /* @__PURE__ */ jsx14(Button, { hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: /* @__PURE__ */ jsx14(CloseIcon, { size: 14 }), "aria-label": removeLabel, onClick: onRemove, className: "fe-chip__remove" })
605
972
  ] });
606
973
  }
974
+
975
+ // packages/message/src/index.tsx
976
+ import { useEffect as useEffect5, useState as useState6 } from "react";
977
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
978
+ var messageId = 0;
979
+ var entries = [];
980
+ var listeners = /* @__PURE__ */ new Set();
981
+ var timers = /* @__PURE__ */ new Map();
982
+ var publish = () => listeners.forEach((listener) => listener(entries));
983
+ function dismiss(id) {
984
+ const timer = timers.get(id);
985
+ if (timer) window.clearTimeout(timer);
986
+ timers.delete(id);
987
+ const entry = entries.find((item) => item.id === id);
988
+ entries = entries.filter((item) => item.id !== id);
989
+ entry?.onClose?.();
990
+ publish();
991
+ }
992
+ function show({ duration = 4500, severity = "info", ...options }) {
993
+ const id = ++messageId;
994
+ entries = [...entries, { id, duration, severity, ...options }];
995
+ publish();
996
+ if (duration !== null) timers.set(id, window.setTimeout(() => dismiss(id), duration));
997
+ return id;
998
+ }
999
+ var message = {
1000
+ show,
1001
+ success: (content, options = {}) => show({ content, severity: "success", ...options }),
1002
+ info: (content, options = {}) => show({ content, severity: "info", ...options }),
1003
+ warning: (content, options = {}) => show({ content, severity: "warning", ...options }),
1004
+ error: (content, options = {}) => show({ content, severity: "error", ...options }),
1005
+ dismiss,
1006
+ clear: () => [...entries].forEach((entry) => dismiss(entry.id))
1007
+ };
1008
+ var icons = { success: "\u2713", info: "i", warning: "!", error: "\xD7" };
1009
+ function Message({ maxCount = 4, closeLabel = "Close notification", className, ...props }) {
1010
+ const [visibleEntries, setVisibleEntries] = useState6(entries);
1011
+ useEffect5(() => {
1012
+ const listener = (nextEntries) => setVisibleEntries(nextEntries);
1013
+ listeners.add(listener);
1014
+ return () => {
1015
+ listeners.delete(listener);
1016
+ };
1017
+ }, []);
1018
+ return /* @__PURE__ */ jsx15("div", { className: ["fe-message-viewport", className].filter(Boolean).join(" "), "aria-live": "polite", ...props, children: visibleEntries.slice(-Math.max(1, maxCount)).map((entry) => /* @__PURE__ */ jsxs14("div", { className: `fe-message fe-message--${entry.severity ?? "info"}`, role: entry.severity === "error" ? "alert" : "status", children: [
1019
+ /* @__PURE__ */ jsx15("span", { className: "fe-message__icon", "aria-hidden": "true", children: icons[entry.severity ?? "info"] }),
1020
+ /* @__PURE__ */ jsxs14("div", { className: "fe-message__body", children: [
1021
+ entry.title && /* @__PURE__ */ jsx15("strong", { className: "fe-message__title", children: entry.title }),
1022
+ /* @__PURE__ */ jsx15("div", { className: "fe-message__content", children: entry.content })
1023
+ ] }),
1024
+ entry.action && /* @__PURE__ */ jsx15("span", { className: "fe-message__action", children: entry.action }),
1025
+ /* @__PURE__ */ jsx15(Button, { className: "fe-message__close", hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: "\xD7", "aria-label": closeLabel, onClick: () => dismiss(entry.id) })
1026
+ ] }, entry.id)) });
1027
+ }
1028
+ ((Message2) => {
1029
+ Message2.show = message.show;
1030
+ Message2.success = message.success;
1031
+ Message2.info = message.info;
1032
+ Message2.warning = message.warning;
1033
+ Message2.error = message.error;
1034
+ Message2.dismiss = message.dismiss;
1035
+ Message2.clear = message.clear;
1036
+ })(Message || (Message = {}));
1037
+
1038
+ // packages/snackbar/src/index.tsx
1039
+ import { useEffect as useEffect6 } from "react";
1040
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
1041
+ function Snackbar({
1042
+ open,
1043
+ message: message2,
1044
+ autoHideDuration = 5e3,
1045
+ onClose,
1046
+ action,
1047
+ anchorOrigin = {},
1048
+ closeLabel = "Close notification",
1049
+ className,
1050
+ ...props
1051
+ }) {
1052
+ const vertical = anchorOrigin.vertical ?? "bottom";
1053
+ const horizontal = anchorOrigin.horizontal ?? "center";
1054
+ useEffect6(() => {
1055
+ if (!open || autoHideDuration === null) return void 0;
1056
+ const timer = window.setTimeout(() => onClose?.("timeout"), autoHideDuration);
1057
+ return () => window.clearTimeout(timer);
1058
+ }, [open, autoHideDuration, onClose]);
1059
+ useEffect6(() => {
1060
+ if (!open) return void 0;
1061
+ const onKeyDown = (event) => {
1062
+ if (event.key === "Escape") onClose?.("escapeKeyDown");
1063
+ };
1064
+ window.addEventListener("keydown", onKeyDown);
1065
+ return () => window.removeEventListener("keydown", onKeyDown);
1066
+ }, [open, onClose]);
1067
+ if (!open) return null;
1068
+ return /* @__PURE__ */ jsx16("div", { className: ["fe-snackbar", `fe-snackbar--${vertical}`, `fe-snackbar--${horizontal}`, className].filter(Boolean).join(" "), ...props, children: /* @__PURE__ */ jsxs15("div", { className: "fe-snackbar__content", role: "status", "aria-live": "polite", children: [
1069
+ /* @__PURE__ */ jsx16("span", { className: "fe-snackbar__message", children: message2 }),
1070
+ action && /* @__PURE__ */ jsx16("span", { className: "fe-snackbar__action", children: action }),
1071
+ onClose && /* @__PURE__ */ jsx16(Button, { className: "fe-snackbar__close", hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: "\xD7", "aria-label": closeLabel, onClick: () => onClose("closeButton") })
1072
+ ] }) });
1073
+ }
607
1074
  export {
608
1075
  Button,
609
1076
  ChevronDownIcon,
1077
+ Chip,
610
1078
  CloseIcon,
611
1079
  CurrencyInput,
612
1080
  DateTimePicker,
613
1081
  Drawer,
614
1082
  ExpandableSection,
1083
+ Loading,
1084
+ Message,
1085
+ Modal,
615
1086
  NumberInput,
1087
+ Pagination,
616
1088
  PlusIcon,
1089
+ ProgressBar,
617
1090
  SearchIcon,
618
1091
  Selection,
1092
+ Skeleton,
1093
+ SkeletonBox,
1094
+ SkeletonCircle,
1095
+ SkeletonLine,
1096
+ SkeletonSquare,
1097
+ Snackbar,
619
1098
  Table,
620
1099
  TableBody,
621
1100
  TableCaption,
@@ -623,8 +1102,11 @@ export {
623
1102
  TableHead,
624
1103
  TableHeader,
625
1104
  TableRow,
1105
+ TextArea,
1106
+ TextInput,
626
1107
  Toggle,
627
1108
  Tooltip,
628
1109
  formatNumber,
629
- getIconSize
1110
+ getIconSize,
1111
+ message
630
1112
  };