@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.js CHANGED
@@ -22,15 +22,27 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Button: () => Button,
24
24
  ChevronDownIcon: () => ChevronDownIcon,
25
+ Chip: () => Chip,
25
26
  CloseIcon: () => CloseIcon,
26
27
  CurrencyInput: () => CurrencyInput,
27
28
  DateTimePicker: () => DateTimePicker,
28
29
  Drawer: () => Drawer,
29
30
  ExpandableSection: () => ExpandableSection,
31
+ Loading: () => Loading,
32
+ Message: () => Message,
33
+ Modal: () => Modal,
30
34
  NumberInput: () => NumberInput,
35
+ Pagination: () => Pagination,
31
36
  PlusIcon: () => PlusIcon,
37
+ ProgressBar: () => ProgressBar,
32
38
  SearchIcon: () => SearchIcon,
33
39
  Selection: () => Selection,
40
+ Skeleton: () => Skeleton,
41
+ SkeletonBox: () => SkeletonBox,
42
+ SkeletonCircle: () => SkeletonCircle,
43
+ SkeletonLine: () => SkeletonLine,
44
+ SkeletonSquare: () => SkeletonSquare,
45
+ Snackbar: () => Snackbar,
34
46
  Table: () => Table,
35
47
  TableBody: () => TableBody,
36
48
  TableCaption: () => TableCaption,
@@ -38,10 +50,13 @@ __export(index_exports, {
38
50
  TableHead: () => TableHead,
39
51
  TableHeader: () => TableHeader,
40
52
  TableRow: () => TableRow,
53
+ TextArea: () => TextArea,
54
+ TextInput: () => TextInput,
41
55
  Toggle: () => Toggle,
42
56
  Tooltip: () => Tooltip,
43
57
  formatNumber: () => formatNumber,
44
- getIconSize: () => getIconSize
58
+ getIconSize: () => getIconSize,
59
+ message: () => message
45
60
  });
46
61
  module.exports = __toCommonJS(index_exports);
47
62
 
@@ -138,9 +153,76 @@ function SearchIcon(props) {
138
153
  }
139
154
 
140
155
  // packages/table/src/index.tsx
156
+ var import_react3 = require("react");
157
+
158
+ // packages/pagination/src/index.tsx
141
159
  var import_react2 = require("react");
142
160
  var import_jsx_runtime3 = require("react/jsx-runtime");
143
- var TableContext = (0, import_react2.createContext)({ striped: false });
161
+ var clamp = (value, min, max) => Math.min(Math.max(value, min), max);
162
+ var range = (start, end) => Array.from({ length: Math.max(0, end - start + 1) }, (_, index) => start + index);
163
+ function getItems(totalPages, page, siblingCount, boundaryCount) {
164
+ const visibleCount = boundaryCount * 2 + siblingCount * 2 + 3;
165
+ if (totalPages <= visibleCount) return range(1, totalPages);
166
+ const startPages = range(1, boundaryCount);
167
+ const endPages = range(totalPages - boundaryCount + 1, totalPages);
168
+ const siblingsStart = Math.max(Math.min(page - siblingCount, totalPages - boundaryCount - siblingCount * 2 - 1), boundaryCount + 2);
169
+ const siblingsEnd = Math.min(Math.max(page + siblingCount, boundaryCount + siblingCount * 2 + 2), totalPages - boundaryCount - 1);
170
+ return [
171
+ ...startPages,
172
+ ...siblingsStart > boundaryCount + 2 ? ["start-ellipsis"] : siblingsStart === boundaryCount + 2 ? [boundaryCount + 1] : [],
173
+ ...range(siblingsStart, siblingsEnd),
174
+ ...siblingsEnd < totalPages - boundaryCount - 1 ? ["end-ellipsis"] : siblingsEnd === totalPages - boundaryCount - 1 ? [totalPages - boundaryCount] : [],
175
+ ...endPages
176
+ ];
177
+ }
178
+ function Pagination({
179
+ page,
180
+ defaultPage = 1,
181
+ totalPages,
182
+ onPageChange,
183
+ siblingCount = 1,
184
+ boundaryCount = 1,
185
+ disabled = false,
186
+ previousLabel = "Previous page",
187
+ nextLabel = "Next page",
188
+ className,
189
+ ...props
190
+ }) {
191
+ const safeTotalPages = Math.max(1, Math.floor(totalPages));
192
+ const [uncontrolledPage, setUncontrolledPage] = (0, import_react2.useState)(() => clamp(defaultPage, 1, safeTotalPages));
193
+ const currentPage = clamp(page ?? uncontrolledPage, 1, safeTotalPages);
194
+ const items = (0, import_react2.useMemo)(
195
+ () => getItems(safeTotalPages, currentPage, Math.max(0, siblingCount), Math.max(0, boundaryCount)),
196
+ [safeTotalPages, currentPage, siblingCount, boundaryCount]
197
+ );
198
+ const selectPage = (nextPage) => {
199
+ const resolvedPage = clamp(nextPage, 1, safeTotalPages);
200
+ if (page === void 0) setUncontrolledPage(resolvedPage);
201
+ if (resolvedPage !== currentPage) onPageChange?.(resolvedPage);
202
+ };
203
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("nav", { className: ["fe-pagination", className].filter(Boolean).join(" "), "aria-label": "Pagination", ...props, children: [
204
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(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) }),
205
+ items.map((item, index) => item === "start-ellipsis" || item === "end-ellipsis" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-pagination__ellipsis", "aria-hidden": "true", children: "\u2026" }, `${item}-${index}`) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
206
+ Button,
207
+ {
208
+ className: "fe-pagination__button",
209
+ hierarchy: item === currentPage ? "primary" : "secondary-gray",
210
+ size: "40",
211
+ "aria-label": `Page ${item}`,
212
+ "aria-current": item === currentPage ? "page" : void 0,
213
+ disabled,
214
+ onClick: () => selectPage(item),
215
+ children: item
216
+ },
217
+ item
218
+ )),
219
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(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) })
220
+ ] });
221
+ }
222
+
223
+ // packages/table/src/index.tsx
224
+ var import_jsx_runtime4 = require("react/jsx-runtime");
225
+ var TableContext = (0, import_react3.createContext)({ striped: false });
144
226
  var MIN_COLUMN_WIDTH = 96;
145
227
  var MAX_COLUMN_WIDTH = 1200;
146
228
  function getColumnCount(headers, data) {
@@ -149,7 +231,7 @@ function getColumnCount(headers, data) {
149
231
  function getText(value) {
150
232
  if (typeof value === "string" || typeof value === "number") return String(value);
151
233
  if (Array.isArray(value)) return value.map(getText).join("");
152
- if ((0, import_react2.isValidElement)(value)) return getText(value.props.children);
234
+ if ((0, import_react3.isValidElement)(value)) return getText(value.props.children);
153
235
  return "";
154
236
  }
155
237
  function autoColumnSize(columnIndex, headers, data) {
@@ -157,9 +239,9 @@ function autoColumnSize(columnIndex, headers, data) {
157
239
  const longestText = values.reduce((longest, value) => Math.max(longest, getText(value).length), 0);
158
240
  return Math.min(MAX_COLUMN_WIDTH, Math.max(MIN_COLUMN_WIDTH, longestText * 8 + 40));
159
241
  }
160
- function getInitialColumnSizes(headers, data, sizes, minColumnWidth = MIN_COLUMN_WIDTH) {
242
+ function getInitialColumnSizes(headers, data, sizes2, minColumnWidth = MIN_COLUMN_WIDTH) {
161
243
  return Array.from({ length: getColumnCount(headers, data) }, (_, columnIndex) => {
162
- const suppliedSize = sizes?.[columnIndex];
244
+ const suppliedSize = sizes2?.[columnIndex];
163
245
  return typeof suppliedSize === "number" && Number.isFinite(suppliedSize) ? Math.min(MAX_COLUMN_WIDTH, Math.max(minColumnWidth, suppliedSize)) : autoColumnSize(columnIndex, headers, data);
164
246
  });
165
247
  }
@@ -174,33 +256,48 @@ function DataTable({
174
256
  columnSizes,
175
257
  onColumnResize,
176
258
  minColumnWidth = MIN_COLUMN_WIDTH,
259
+ pagination,
260
+ pageSize,
261
+ page,
262
+ defaultPage = 1,
263
+ onPageChange,
264
+ paginationPosition = "bottom-right",
177
265
  className,
178
266
  ...props
179
267
  }) {
180
268
  const columnCount = getColumnCount(headers, data);
181
269
  const safeMinWidth = Math.max(1, minColumnWidth);
182
- const generatedSizes = (0, import_react2.useMemo)(
270
+ const generatedSizes = (0, import_react3.useMemo)(
183
271
  () => getInitialColumnSizes(headers, data, defaultColumnSizes, safeMinWidth),
184
272
  [headers, data, defaultColumnSizes, safeMinWidth]
185
273
  );
186
- const [uncontrolledSizes, setUncontrolledSizes] = (0, import_react2.useState)(generatedSizes);
187
- const resizeState = (0, import_react2.useRef)();
188
- const [isResizing, setIsResizing] = (0, import_react2.useState)(false);
189
- const sizes = columnSizes ? getInitialColumnSizes(headers, data, columnSizes, safeMinWidth).map((size) => Math.max(safeMinWidth, size)) : uncontrolledSizes.map((size) => Math.max(safeMinWidth, size));
190
- (0, import_react2.useEffect)(() => {
274
+ const [uncontrolledSizes, setUncontrolledSizes] = (0, import_react3.useState)(generatedSizes);
275
+ const shouldPaginate = pagination ?? pageSize !== void 0;
276
+ const safePageSize = Math.max(1, Math.floor(pageSize ?? 10));
277
+ const totalPages = Math.max(1, Math.ceil(data.length / safePageSize));
278
+ const [uncontrolledPage, setUncontrolledPage] = (0, import_react3.useState)(() => Math.min(Math.max(1, defaultPage), totalPages));
279
+ const currentPage = Math.min(Math.max(1, page ?? uncontrolledPage), totalPages);
280
+ const pagedData = shouldPaginate ? data.slice((currentPage - 1) * safePageSize, currentPage * safePageSize) : data;
281
+ const resizeState = (0, import_react3.useRef)();
282
+ const [isResizing, setIsResizing] = (0, import_react3.useState)(false);
283
+ const sizes2 = columnSizes ? getInitialColumnSizes(headers, data, columnSizes, safeMinWidth).map((size) => Math.max(safeMinWidth, size)) : uncontrolledSizes.map((size) => Math.max(safeMinWidth, size));
284
+ (0, import_react3.useEffect)(() => {
191
285
  if (columnSizes) return;
192
286
  setUncontrolledSizes((currentSizes) => {
193
287
  const nextSizes = generatedSizes.map((generatedSize, index) => currentSizes[index] ?? generatedSize);
194
288
  return sameSizes(currentSizes, nextSizes) ? currentSizes : nextSizes;
195
289
  });
196
290
  }, [columnSizes, generatedSizes]);
197
- const updateColumnSize = (0, import_react2.useCallback)((columnIndex, nextSize) => {
291
+ (0, import_react3.useEffect)(() => {
292
+ if (page === void 0 && uncontrolledPage > totalPages) setUncontrolledPage(totalPages);
293
+ }, [page, totalPages, uncontrolledPage]);
294
+ const updateColumnSize = (0, import_react3.useCallback)((columnIndex, nextSize) => {
198
295
  const clampedSize = Math.min(MAX_COLUMN_WIDTH, Math.max(safeMinWidth, Math.round(nextSize)));
199
- const nextSizes = sizes.map((size, index) => index === columnIndex ? clampedSize : size);
296
+ const nextSizes = sizes2.map((size, index) => index === columnIndex ? clampedSize : size);
200
297
  if (!columnSizes) setUncontrolledSizes(nextSizes);
201
298
  onColumnResize?.(nextSizes, columnIndex);
202
- }, [columnSizes, onColumnResize, safeMinWidth, sizes]);
203
- (0, import_react2.useEffect)(() => {
299
+ }, [columnSizes, onColumnResize, safeMinWidth, sizes2]);
300
+ (0, import_react3.useEffect)(() => {
204
301
  const onPointerMove = (event) => {
205
302
  const activeResize = resizeState.current;
206
303
  if (!activeResize) return;
@@ -219,84 +316,106 @@ function DataTable({
219
316
  window.removeEventListener("pointercancel", stopResizing);
220
317
  };
221
318
  }, [updateColumnSize]);
222
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fe-table", "data-resizing": isResizing || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { className: ["fe-table__table", className].filter(Boolean).join(" "), "data-fe-table": true, "data-striped": striped || void 0, ...props, children: [
223
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("colgroup", { children: sizes.map((size, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("col", { style: { width: `${size}px` } }, index)) }),
224
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("th", { scope: "col", children: [
225
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-table__header-content", children: headers[columnIndex] ?? `Column ${columnIndex + 1}` }),
226
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
227
- "span",
228
- {
229
- className: "fe-table__resize-handle",
230
- role: "separator",
231
- "aria-label": `Resize ${getText(headers[columnIndex]) || `column ${columnIndex + 1}`}`,
232
- "aria-orientation": "vertical",
233
- "aria-valuemin": safeMinWidth,
234
- "aria-valuemax": MAX_COLUMN_WIDTH,
235
- "aria-valuenow": sizes[columnIndex] ?? safeMinWidth,
236
- tabIndex: 0,
237
- onPointerDown: (event) => {
238
- event.preventDefault();
239
- resizeState.current = { columnIndex, startX: event.clientX, startWidth: sizes[columnIndex] ?? safeMinWidth };
240
- setIsResizing(true);
241
- event.currentTarget.setPointerCapture(event.pointerId);
242
- },
243
- onKeyDown: (event) => {
244
- if (event.key === "ArrowLeft") updateColumnSize(columnIndex, (sizes[columnIndex] ?? safeMinWidth) - 16);
245
- if (event.key === "ArrowRight") updateColumnSize(columnIndex, (sizes[columnIndex] ?? safeMinWidth) + 16);
319
+ const changePage = (nextPage) => {
320
+ if (page === void 0) setUncontrolledPage(nextPage);
321
+ if (nextPage !== currentPage) onPageChange?.(nextPage);
322
+ };
323
+ const resolvedPaginationPosition = paginationPosition === "middle-bottom" ? "bottom-center" : paginationPosition;
324
+ const paginationControl = shouldPaginate && totalPages > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `fe-table__pagination fe-table__pagination--${resolvedPaginationPosition}`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Pagination, { page: currentPage, totalPages, onPageChange: changePage }) }) : null;
325
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "fe-table", "data-resizing": isResizing || void 0, children: [
326
+ resolvedPaginationPosition === "top-right" && paginationControl,
327
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("table", { className: ["fe-table__table", className].filter(Boolean).join(" "), "data-fe-table": true, "data-striped": striped || void 0, ...props, children: [
328
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("colgroup", { children: sizes2.map((size, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("col", { style: { width: `${size}px` } }, index)) }),
329
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("th", { scope: "col", children: [
330
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "fe-table__header-content", children: headers[columnIndex] ?? `Column ${columnIndex + 1}` }),
331
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
332
+ "span",
333
+ {
334
+ className: "fe-table__resize-handle",
335
+ role: "separator",
336
+ "aria-label": `Resize ${getText(headers[columnIndex]) || `column ${columnIndex + 1}`}`,
337
+ "aria-orientation": "vertical",
338
+ "aria-valuemin": safeMinWidth,
339
+ "aria-valuemax": MAX_COLUMN_WIDTH,
340
+ "aria-valuenow": sizes2[columnIndex] ?? safeMinWidth,
341
+ tabIndex: 0,
342
+ onPointerDown: (event) => {
343
+ event.preventDefault();
344
+ resizeState.current = { columnIndex, startX: event.clientX, startWidth: sizes2[columnIndex] ?? safeMinWidth };
345
+ setIsResizing(true);
346
+ event.currentTarget.setPointerCapture(event.pointerId);
347
+ },
348
+ onKeyDown: (event) => {
349
+ if (event.key === "ArrowLeft") updateColumnSize(columnIndex, (sizes2[columnIndex] ?? safeMinWidth) - 16);
350
+ if (event.key === "ArrowRight") updateColumnSize(columnIndex, (sizes2[columnIndex] ?? safeMinWidth) + 16);
351
+ }
246
352
  }
247
- }
248
- )
249
- ] }, columnIndex)) }) }),
250
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { children: row[columnIndex] }, columnIndex)) }, rowIndex)) })
251
- ] }) });
353
+ )
354
+ ] }, columnIndex)) }) }),
355
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: pagedData.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: row[columnIndex] }, columnIndex)) }, shouldPaginate ? (currentPage - 1) * safePageSize + rowIndex : rowIndex)) })
356
+ ] }),
357
+ resolvedPaginationPosition !== "top-right" && paginationControl
358
+ ] });
252
359
  }
253
- function Table({ headers, data, striped, children, ...props }) {
254
- if (headers || data) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DataTable, { headers: headers ?? [], data: data ?? [], striped: striped ?? true, ...props });
255
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TableContext.Provider, { value: { striped: striped ?? false }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("table", { "data-fe-table": true, "data-striped": striped || void 0, ...props, children }) });
360
+ function Table({
361
+ headers,
362
+ data,
363
+ striped,
364
+ children,
365
+ pagination,
366
+ pageSize,
367
+ page,
368
+ defaultPage,
369
+ onPageChange,
370
+ paginationPosition,
371
+ ...props
372
+ }) {
373
+ if (headers || data) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DataTable, { headers: headers ?? [], data: data ?? [], striped: striped ?? true, pagination, pageSize, page, defaultPage, onPageChange, paginationPosition, ...props });
374
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TableContext.Provider, { value: { striped: striped ?? false }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("table", { "data-fe-table": true, "data-striped": striped || void 0, ...props, children }) });
256
375
  }
257
376
  function TableHead(props) {
258
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { ...props });
377
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("thead", { ...props });
259
378
  }
260
379
  function TableBody(props) {
261
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { ...props });
380
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { ...props });
262
381
  }
263
382
  function TableRow({ children, ...props }) {
264
- const { striped } = (0, import_react2.useContext)(TableContext);
265
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tr", { "data-striped": striped || void 0, ...props, children });
383
+ const { striped } = (0, import_react3.useContext)(TableContext);
384
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tr", { "data-striped": striped || void 0, ...props, children });
266
385
  }
267
386
  function TableCell({ children, ...props }) {
268
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { ...props, children });
387
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { ...props, children });
269
388
  }
270
389
  function TableHeader({ children, ...props }) {
271
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { scope: "col", ...props, children });
390
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { scope: "col", ...props, children });
272
391
  }
273
392
  function TableCaption(props) {
274
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("caption", { ...props });
393
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("caption", { ...props });
275
394
  }
276
395
 
277
396
  // packages/toggle/src/index.tsx
278
- var import_react3 = require("react");
279
- var import_jsx_runtime4 = require("react/jsx-runtime");
280
- var Toggle = (0, import_react3.forwardRef)(function Toggle2({ color = "#16a34a", label, className, style, id, ...props }, ref) {
397
+ var import_react4 = require("react");
398
+ var import_jsx_runtime5 = require("react/jsx-runtime");
399
+ var Toggle = (0, import_react4.forwardRef)(function Toggle2({ color = "#16a34a", label, className, style, id, ...props }, ref) {
281
400
  const classes = ["fe-toggle", className].filter(Boolean).join(" ");
282
401
  const toggleStyle = {
283
402
  "--fe-toggle-color": color,
284
403
  ...style
285
404
  };
286
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: classes, style: toggleStyle, children: [
287
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { ref, id, className: "fe-toggle__input", type: "checkbox", role: "switch", ...props }),
288
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "fe-toggle__control", "aria-hidden": "true" }),
289
- label && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "fe-toggle__label", children: label })
405
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: classes, style: toggleStyle, children: [
406
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { ref, id, className: "fe-toggle__input", type: "checkbox", role: "switch", ...props }),
407
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "fe-toggle__control", "aria-hidden": "true" }),
408
+ label && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "fe-toggle__label", children: label })
290
409
  ] });
291
410
  });
292
411
  Toggle.displayName = "Toggle";
293
412
 
294
413
  // packages/drawer/src/index.tsx
295
- var import_react4 = require("react");
414
+ var import_react5 = require("react");
296
415
  var import_react_dom = require("react-dom");
297
- var import_jsx_runtime5 = require("react/jsx-runtime");
416
+ var import_jsx_runtime6 = require("react/jsx-runtime");
298
417
  function Drawer({ open, onClose, title, footer, side = "right", width = 400, fullScreen = false, children, closeOnBackdropClick = true, className, style, ...props }) {
299
- (0, import_react4.useEffect)(() => {
418
+ (0, import_react5.useEffect)(() => {
300
419
  if (!open) return;
301
420
  const onKeyDown = (event) => {
302
421
  if (event.key === "Escape") onClose();
@@ -306,40 +425,130 @@ function Drawer({ open, onClose, title, footer, side = "right", width = 400, ful
306
425
  }, [onClose, open]);
307
426
  if (!open || typeof document === "undefined") return null;
308
427
  const panelStyle = !fullScreen && (side === "left" || side === "right") ? { ...style, width: typeof width === "number" ? `${width}px` : width } : style;
309
- return (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "fe-drawer-backdrop", role: "presentation", onMouseDown: (event) => {
428
+ return (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fe-drawer-backdrop", role: "presentation", onMouseDown: (event) => {
310
429
  if (event.target === event.currentTarget && closeOnBackdropClick) onClose();
311
- }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("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: [
312
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { className: "fe-drawer__header", children: [
313
- title && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { children: title }),
314
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "aria-label": "Close drawer", onClick: onClose, children: "\xD7" })
430
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
431
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("header", { className: "fe-drawer__header", children: [
432
+ title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { children: title }),
433
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", "aria-label": "Close drawer", onClick: onClose, children: "\xD7" })
315
434
  ] }),
316
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "fe-drawer__body", children }),
317
- footer && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("footer", { className: "fe-drawer__footer", children: footer })
435
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fe-drawer__body", children }),
436
+ footer && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("footer", { className: "fe-drawer__footer", children: footer })
318
437
  ] }) }), document.body);
319
438
  }
320
439
 
321
440
  // packages/form-controls/src/index.tsx
322
- var import_react5 = require("react");
323
- var import_jsx_runtime6 = require("react/jsx-runtime");
324
- var Selection = (0, import_react5.forwardRef)(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) {
325
- const generatedId = (0, import_react5.useId)();
441
+ var import_react6 = require("react");
442
+ var import_react_dom2 = require("react-dom");
443
+ var import_jsx_runtime7 = require("react/jsx-runtime");
444
+ function getFieldError(error, errorMessage) {
445
+ if (errorMessage !== void 0 && errorMessage !== null) return errorMessage;
446
+ if (typeof error !== "boolean") return error;
447
+ return error ? "Invalid value" : void 0;
448
+ }
449
+ function getDescribedBy(existing, messageId2) {
450
+ return [existing, messageId2].filter(Boolean).join(" ") || void 0;
451
+ }
452
+ var TextInput = (0, import_react6.forwardRef)(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) {
453
+ const generatedId = (0, import_react6.useId)();
454
+ const inputId = id ?? `${generatedId}-input`;
455
+ const labelId = `${generatedId}-label`;
456
+ const messageId2 = `${generatedId}-message`;
457
+ const controlled = value !== void 0;
458
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue);
459
+ const currentValue = controlled ? value : internalValue;
460
+ const fieldError = getFieldError(error, errorMessage);
461
+ const hasError = fieldError !== void 0 && fieldError !== false && fieldError !== null;
462
+ const displayedMessage = hasError ? fieldError : helperText;
463
+ const describedBy = getDescribedBy(ariaDescribedBy, displayedMessage ? messageId2 : void 0);
464
+ const clear = () => {
465
+ if (!controlled) setInternalValue("");
466
+ onValueChange?.("");
467
+ onClear?.();
468
+ };
469
+ const labelledBy = getDescribedBy(ariaLabelledBy, label ? labelId : void 0);
470
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: ["fe-text-input", hasError && "fe-text-input--error", className].filter(Boolean).join(" "), children: [
471
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: labelId, className: "fe-text-input__label", children: label }),
472
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "fe-text-input__control", children: [
473
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { ref, id: inputId, value: currentValue, disabled, readOnly, "aria-invalid": hasError || void 0, "aria-labelledby": labelledBy, "aria-describedby": describedBy, onChange: (event) => {
474
+ if (!controlled) setInternalValue(event.target.value);
475
+ onValueChange?.(event.target.value);
476
+ onChange?.(event);
477
+ }, ...props }),
478
+ clearable && currentValue && !disabled && !readOnly && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CloseIcon, { size: 14 }), "aria-label": clearLabel, onClick: clear, className: "fe-text-input__clear" })
479
+ ] }),
480
+ displayedMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: messageId2, className: hasError ? "fe-text-input__error" : "fe-text-input__helper", children: displayedMessage })
481
+ ] });
482
+ });
483
+ TextInput.displayName = "TextInput";
484
+ var TextArea = (0, import_react6.forwardRef)(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) {
485
+ const generatedId = (0, import_react6.useId)();
486
+ const inputId = id ?? `${generatedId}-input`;
487
+ const labelId = `${generatedId}-label`;
488
+ const messageId2 = `${generatedId}-message`;
489
+ const controlled = value !== void 0;
490
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue);
491
+ const currentValue = controlled ? value : internalValue;
492
+ const fieldError = getFieldError(error, errorMessage);
493
+ const hasError = fieldError !== void 0 && fieldError !== false && fieldError !== null;
494
+ const displayedMessage = hasError ? fieldError : helperText;
495
+ const describedBy = getDescribedBy(ariaDescribedBy, displayedMessage ? messageId2 : void 0);
496
+ const clear = () => {
497
+ if (!controlled) setInternalValue("");
498
+ onValueChange?.("");
499
+ onClear?.();
500
+ };
501
+ const labelledBy = getDescribedBy(ariaLabelledBy, label ? labelId : void 0);
502
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: ["fe-text-area", hasError && "fe-text-area--error", className].filter(Boolean).join(" "), children: [
503
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: labelId, className: "fe-text-area__label", children: label }),
504
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "fe-text-area__control", children: [
505
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("textarea", { ref, id: inputId, value: currentValue, disabled, readOnly, "aria-invalid": hasError || void 0, "aria-labelledby": labelledBy, "aria-describedby": describedBy, onChange: (event) => {
506
+ if (!controlled) setInternalValue(event.target.value);
507
+ onValueChange?.(event.target.value);
508
+ onChange?.(event);
509
+ }, ...props }),
510
+ clearable && currentValue && !disabled && !readOnly && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CloseIcon, { size: 14 }), "aria-label": clearLabel, onClick: clear, className: "fe-text-area__clear" })
511
+ ] }),
512
+ displayedMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: messageId2, className: hasError ? "fe-text-area__error" : "fe-text-area__helper", children: displayedMessage })
513
+ ] });
514
+ });
515
+ TextArea.displayName = "TextArea";
516
+ var Selection = (0, import_react6.forwardRef)(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) {
517
+ const generatedId = (0, import_react6.useId)();
326
518
  const listboxId = id ?? generatedId;
327
519
  const isControlled = value !== void 0;
328
- const [internalValue, setInternalValue] = (0, import_react5.useState)(defaultValue ?? (multiple ? [] : ""));
520
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue ?? (multiple ? [] : ""));
329
521
  const selectedValue = isControlled ? value : internalValue;
330
522
  const selectedValues = Array.isArray(selectedValue) ? selectedValue : selectedValue === "" || selectedValue === null || selectedValue === void 0 ? [] : [selectedValue];
331
523
  const selectedOptions = options.filter((option) => selectedValues.some((item) => String(item) === String(option.value)));
332
- const [open, setOpen] = (0, import_react5.useState)(false);
333
- const [query, setQuery] = (0, import_react5.useState)("");
334
- const [activeIndex, setActiveIndex] = (0, import_react5.useState)(0);
335
- const rootRef = (0, import_react5.useRef)(null);
336
- (0, import_react5.useEffect)(() => {
524
+ const [open, setOpen] = (0, import_react6.useState)(false);
525
+ const [query, setQuery] = (0, import_react6.useState)("");
526
+ const [activeIndex, setActiveIndex] = (0, import_react6.useState)(0);
527
+ const rootRef = (0, import_react6.useRef)(null);
528
+ const menuRef = (0, import_react6.useRef)(null);
529
+ const [menuPosition, setMenuPosition] = (0, import_react6.useState)(null);
530
+ (0, import_react6.useEffect)(() => {
337
531
  const close = (event) => {
338
- if (!rootRef.current?.contains(event.target)) setOpen(false);
532
+ const target = event.target;
533
+ if (!rootRef.current?.contains(target) && !menuRef.current?.contains(target)) setOpen(false);
339
534
  };
340
535
  document.addEventListener("mousedown", close);
341
536
  return () => document.removeEventListener("mousedown", close);
342
537
  }, []);
538
+ (0, import_react6.useLayoutEffect)(() => {
539
+ if (!open) return;
540
+ const updateMenuPosition = () => {
541
+ const rect = rootRef.current?.getBoundingClientRect();
542
+ if (rect) setMenuPosition({ top: rect.bottom + 7, left: rect.left, width: rect.width });
543
+ };
544
+ updateMenuPosition();
545
+ window.addEventListener("resize", updateMenuPosition);
546
+ document.addEventListener("scroll", updateMenuPosition, true);
547
+ return () => {
548
+ window.removeEventListener("resize", updateMenuPosition);
549
+ document.removeEventListener("scroll", updateMenuPosition, true);
550
+ };
551
+ }, [open]);
343
552
  const filteredOptions = options.filter((option) => !query || (option.searchText ?? (typeof option.label === "string" ? option.label : String(option.value))).toLowerCase().includes(query.toLowerCase()));
344
553
  const commit = (next) => {
345
554
  if (!isControlled) setInternalValue(next);
@@ -376,39 +585,39 @@ var Selection = (0, import_react5.forwardRef)(function Selection2({ className, l
376
585
  }
377
586
  if (event.key === "Escape") setOpen(false);
378
587
  };
379
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
380
- label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-selection__label", children: label }),
381
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: rootRef, className: "fe-selection__control", children: [
382
- name && selectedValues.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "hidden", name: multiple ? `${name}[]` : name, value: item }, String(item))),
383
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
384
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "fe-selection__tag", children: [
588
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("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: [
589
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-selection__label", children: label }),
590
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref: rootRef, className: "fe-selection__control", children: [
591
+ name && selectedValues.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "hidden", name: multiple ? `${name}[]` : name, value: item }, String(item))),
592
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("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: [
593
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "fe-selection__tag", children: [
385
594
  option.label,
386
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "aria-hidden": "true", children: "\xD7" })
387
- ] }, String(option.value))) : displayLabel.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
595
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": "true", children: "\xD7" })
596
+ ] }, String(option.value))) : displayLabel.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
388
597
  item,
389
598
  index < displayLabel.length - 1 ? ", " : ""
390
599
  ] }, index)) : placeholder }),
391
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-selection__actions", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-selection__chevron", "aria-hidden": "true" }) })
600
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-selection__actions", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-selection__chevron", "aria-hidden": "true" }) })
392
601
  ] }),
393
- clearable && selectedOptions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "fe-selection__clear", "aria-label": "Clear selection", onClick: clear }),
394
- open && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { id: listboxId, className: "fe-selection__menu", role: "listbox", "aria-multiselectable": multiple || void 0, children: [
395
- searchable && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "fe-selection__search", children: [
396
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "aria-hidden": "true", children: "\u2315" }),
397
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { autoFocus: true, value: query, onChange: (event) => {
602
+ clearable && selectedOptions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "fe-selection__clear", "aria-label": "Clear selection", onClick: clear }),
603
+ open && menuPosition && (0, import_react_dom2.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("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: [
604
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "fe-selection__search", children: [
605
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": "true", children: "\u2315" }),
606
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { autoFocus: true, value: query, onChange: (event) => {
398
607
  setQuery(event.target.value);
399
608
  setActiveIndex(0);
400
609
  }, placeholder: "Search\u2026", "aria-label": "Search options" })
401
610
  ] }),
402
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fe-selection__options", children: filteredOptions.length ? filteredOptions.map((option, index) => {
611
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "fe-selection__options", children: filteredOptions.length ? filteredOptions.map((option, index) => {
403
612
  const isSelected = selectedValues.some((item) => String(item) === String(option.value));
404
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
405
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: option.label }),
406
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-selection__check", "aria-hidden": "true", children: "\u2713" })
613
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("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: [
614
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: option.label }),
615
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-selection__check", "aria-hidden": "true", children: "\u2713" })
407
616
  ] }, String(option.value));
408
- }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "fe-selection__empty", children: "No matching options" }) })
409
- ] })
617
+ }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "fe-selection__empty", children: "No matching options" }) })
618
+ ] }), document.body)
410
619
  ] }),
411
- helperText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-selection__helper", children: helperText })
620
+ helperText && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-selection__helper", children: helperText })
412
621
  ] });
413
622
  });
414
623
  Selection.displayName = "Selection";
@@ -445,13 +654,19 @@ function normalizeNumberInput(raw, options) {
445
654
  if (value !== numericValue) return { display: formatNumber(value, decimals, thousandSeparator, decimalSeparator), value };
446
655
  return { display: `${negative ? "-" : ""}${groupInteger(integer || "0", thousandSeparator)}${hasDecimal ? `${decimalSeparator}${fraction}` : ""}`, value };
447
656
  }
448
- var NumberInput = (0, import_react5.forwardRef)(function NumberInput2({ value, defaultValue = null, onValueChange, allowDecimal = true, decimals = 2, min, max, decimalSeparator = ".", thousandSeparator = ",", label, helperText, error, className, onBlur, onFocus, ...props }, ref) {
657
+ var NumberInput = (0, import_react6.forwardRef)(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) {
658
+ const generatedId = (0, import_react6.useId)();
659
+ const messageId2 = `${generatedId}-message`;
449
660
  const isControlled = value !== void 0;
450
- const [internalValue, setInternalValue] = (0, import_react5.useState)(defaultValue);
451
- const [focused, setFocused] = (0, import_react5.useState)(false);
661
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue);
662
+ const [focused, setFocused] = (0, import_react6.useState)(false);
452
663
  const actualValue = isControlled ? value ?? null : internalValue;
453
- const [text, setText] = (0, import_react5.useState)(actualValue === null ? "" : formatNumber(actualValue, decimals, thousandSeparator, decimalSeparator));
454
- (0, import_react5.useEffect)(() => {
664
+ const [text, setText] = (0, import_react6.useState)(actualValue === null ? "" : formatNumber(actualValue, decimals, thousandSeparator, decimalSeparator));
665
+ const fieldError = getFieldError(error, errorMessage);
666
+ const hasError = fieldError !== void 0 && fieldError !== false && fieldError !== null;
667
+ const displayedMessage = hasError ? fieldError : helperText;
668
+ const describedBy = getDescribedBy(ariaDescribedBy, displayedMessage ? messageId2 : void 0);
669
+ (0, import_react6.useEffect)(() => {
455
670
  if (!focused) setText(actualValue === null ? "" : formatNumber(actualValue, decimals, thousandSeparator, decimalSeparator));
456
671
  }, [actualValue, decimalSeparator, decimals, focused, thousandSeparator]);
457
672
  const updateValue = (nextText) => {
@@ -461,32 +676,32 @@ var NumberInput = (0, import_react5.forwardRef)(function NumberInput2({ value, d
461
676
  if (!isControlled) setInternalValue(nextValue);
462
677
  onValueChange?.(nextValue);
463
678
  };
464
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: ["fe-number-input", error && "fe-number-input--error", className].filter(Boolean).join(" "), children: [
465
- label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-number-input__label", children: label }),
466
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { ref, inputMode: allowDecimal ? "decimal" : "numeric", value: text, "aria-invalid": error || void 0, onFocus: (event) => {
679
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: ["fe-number-input", hasError && "fe-number-input--error", className].filter(Boolean).join(" "), children: [
680
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-number-input__label", children: label }),
681
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { ref, inputMode: allowDecimal ? "decimal" : "numeric", value: text, onFocus: (event) => {
467
682
  setFocused(true);
468
683
  onFocus?.(event);
469
684
  }, onChange: (event) => updateValue(event.target.value), onBlur: (event) => {
470
685
  setFocused(false);
471
686
  onBlur?.(event);
472
- }, ...props }),
473
- helperText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-number-input__helper", children: helperText })
687
+ }, ...props, "aria-invalid": hasError || void 0, "aria-describedby": describedBy }),
688
+ displayedMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: messageId2, className: hasError ? "fe-number-input__error" : "fe-number-input__helper", children: displayedMessage })
474
689
  ] });
475
690
  });
476
691
  NumberInput.displayName = "NumberInput";
477
692
  function CurrencyInput({ suffix = "VND", currencies, currency, defaultCurrency, onCurrencyChange, className, ...props }) {
478
- const [internalCurrency, setInternalCurrency] = (0, import_react5.useState)(defaultCurrency ?? currencies?.[0]?.code ?? "");
693
+ const [internalCurrency, setInternalCurrency] = (0, import_react6.useState)(defaultCurrency ?? currencies?.[0]?.code ?? "");
479
694
  const selectedCurrency = currency ?? internalCurrency;
480
695
  const current = currencies?.find((item) => item.code === selectedCurrency);
481
696
  const currencySuffix = current?.suffix ?? current?.code ?? suffix;
482
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: ["fe-currency-input", className].filter(Boolean).join(" "), children: [
483
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumberInput, { ...props, className: "fe-currency-input__number" }),
484
- currencies?.length ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { className: "fe-currency-input__currency", "aria-label": "Currency", value: selectedCurrency, onChange: (event) => {
697
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: ["fe-currency-input", className].filter(Boolean).join(" "), children: [
698
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NumberInput, { ...props, className: "fe-currency-input__number" }),
699
+ currencies?.length ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { className: "fe-currency-input__currency", "aria-label": "Currency", value: selectedCurrency, onChange: (event) => {
485
700
  const next = currencies.find((item) => item.code === event.target.value);
486
701
  if (!next) return;
487
702
  if (currency === void 0) setInternalCurrency(next.code);
488
703
  onCurrencyChange?.(next);
489
- }, children: currencies.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: item.code, children: item.label ?? item.code }, item.code)) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-currency-input__suffix", children: currencySuffix })
704
+ }, children: currencies.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: item.code, children: item.label ?? item.code }, item.code)) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-currency-input__suffix", children: currencySuffix })
490
705
  ] });
491
706
  }
492
707
  var pad = (value) => String(value).padStart(2, "0");
@@ -509,15 +724,15 @@ function DateTimePicker(props) {
509
724
  const { label, format = "dd/MM/yyyy HH:mm", disabled, placeholder, locale = "en-US", className, mode = "single" } = props;
510
725
  const isRange = mode === "range";
511
726
  const controlled = props.value !== void 0;
512
- const [singleInternal, setSingleInternal] = (0, import_react5.useState)((!isRange ? props.defaultValue : null) ?? null);
513
- const [rangeInternal, setRangeInternal] = (0, import_react5.useState)((isRange ? props.defaultValue : void 0) ?? [null, null]);
727
+ const [singleInternal, setSingleInternal] = (0, import_react6.useState)((!isRange ? props.defaultValue : null) ?? null);
728
+ const [rangeInternal, setRangeInternal] = (0, import_react6.useState)((isRange ? props.defaultValue : void 0) ?? [null, null]);
514
729
  const singleValue = !isRange ? props.value ?? singleInternal : null;
515
730
  const rangeValue = isRange ? props.value ?? rangeInternal : [null, null];
516
- const [open, setOpen] = (0, import_react5.useState)(false);
517
- const [month, setMonth] = (0, import_react5.useState)(() => new Date((isRange ? rangeValue[0] : singleValue) ?? /* @__PURE__ */ new Date()));
518
- const [editingEnd, setEditingEnd] = (0, import_react5.useState)(false);
519
- const rootRef = (0, import_react5.useRef)(null);
520
- (0, import_react5.useEffect)(() => {
731
+ const [open, setOpen] = (0, import_react6.useState)(false);
732
+ const [month, setMonth] = (0, import_react6.useState)(() => new Date((isRange ? rangeValue[0] : singleValue) ?? /* @__PURE__ */ new Date()));
733
+ const [editingEnd, setEditingEnd] = (0, import_react6.useState)(false);
734
+ const rootRef = (0, import_react6.useRef)(null);
735
+ (0, import_react6.useEffect)(() => {
521
736
  const onPointerDown = (event) => {
522
737
  if (!rootRef.current?.contains(event.target)) setOpen(false);
523
738
  };
@@ -559,44 +774,44 @@ function DateTimePicker(props) {
559
774
  if (isRange) setRange(editingEnd ? [rangeValue[0], next] : [next, rangeValue[1]]);
560
775
  else setSingle(next);
561
776
  };
562
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: rootRef, className: ["fe-datetime-picker", className].filter(Boolean).join(" "), children: [
563
- label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-datetime-picker__label", children: label }),
564
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("button", { type: "button", className: "fe-datetime-picker__trigger", disabled, "aria-expanded": open, onClick: () => setOpen((current) => !current), children: [
565
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "aria-hidden": "true", children: "\u25F7" }),
566
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: shownValue || placeholder || (isRange ? "Select date range" : "Select date and time") })
777
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref: rootRef, className: ["fe-datetime-picker", className].filter(Boolean).join(" "), children: [
778
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-datetime-picker__label", children: label }),
779
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("button", { type: "button", className: "fe-datetime-picker__trigger", disabled, "aria-expanded": open, onClick: () => setOpen((current) => !current), children: [
780
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": "true", children: "\u25F7" }),
781
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: shownValue || placeholder || (isRange ? "Select date range" : "Select date and time") })
567
782
  ] }),
568
- open && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "fe-datetime-picker__popper", role: "dialog", "aria-label": "Date and time picker", children: [
569
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "fe-datetime-picker__month", children: [
570
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", "aria-label": "Previous month", onClick: () => setMonth((current) => new Date(current.getFullYear(), current.getMonth() - 1, 1)), children: "\u2039" }),
571
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: month.toLocaleDateString(locale, { month: "long", year: "numeric" }) }),
572
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", "aria-label": "Next month", onClick: () => setMonth((current) => new Date(current.getFullYear(), current.getMonth() + 1, 1)), children: "\u203A" })
783
+ open && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "fe-datetime-picker__popper", role: "dialog", "aria-label": "Date and time picker", children: [
784
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "fe-datetime-picker__month", children: [
785
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "aria-label": "Previous month", onClick: () => setMonth((current) => new Date(current.getFullYear(), current.getMonth() - 1, 1)), children: "\u2039" }),
786
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: month.toLocaleDateString(locale, { month: "long", year: "numeric" }) }),
787
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", "aria-label": "Next month", onClick: () => setMonth((current) => new Date(current.getFullYear(), current.getMonth() + 1, 1)), children: "\u203A" })
573
788
  ] }),
574
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fe-datetime-picker__weekdays", children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((day) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: day }, day)) }),
575
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fe-datetime-picker__days", children: days.map((day) => {
789
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "fe-datetime-picker__weekdays", children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((day) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: day }, day)) }),
790
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "fe-datetime-picker__days", children: days.map((day) => {
576
791
  const inMonth = day.getMonth() === month.getMonth();
577
792
  const selected = isRange ? sameDay(day, rangeValue[0]) || sameDay(day, rangeValue[1]) : sameDay(day, singleValue);
578
793
  const inRange = isRange && rangeValue[0] && rangeValue[1] && day > startOfDay(rangeValue[0]) && day < startOfDay(rangeValue[1]);
579
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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());
794
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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());
580
795
  }) }),
581
- isRange && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "fe-datetime-picker__range-tabs", children: [
582
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: !editingEnd ? "is-active" : "", onClick: () => setEditingEnd(false), children: "Start" }),
583
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: editingEnd ? "is-active" : "", onClick: () => setEditingEnd(true), children: "End" })
796
+ isRange && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "fe-datetime-picker__range-tabs", children: [
797
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: !editingEnd ? "is-active" : "", onClick: () => setEditingEnd(false), children: "Start" }),
798
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: editingEnd ? "is-active" : "", onClick: () => setEditingEnd(true), children: "End" })
584
799
  ] }),
585
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "fe-datetime-picker__time", children: [
800
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "fe-datetime-picker__time", children: [
586
801
  "Time",
587
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "time", value: selectedForTime ? `${pad(selectedForTime.getHours())}:${pad(selectedForTime.getMinutes())}` : "00:00", onChange: (event) => setTime(event.target.value), disabled: !selectedForTime })
802
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "time", value: selectedForTime ? `${pad(selectedForTime.getHours())}:${pad(selectedForTime.getMinutes())}` : "00:00", onChange: (event) => setTime(event.target.value), disabled: !selectedForTime })
588
803
  ] })
589
804
  ] })
590
805
  ] });
591
806
  }
592
807
 
593
808
  // packages/tooltip/src/index.tsx
594
- var import_react6 = require("react");
595
- var import_jsx_runtime7 = require("react/jsx-runtime");
809
+ var import_react7 = require("react");
810
+ var import_jsx_runtime8 = require("react/jsx-runtime");
596
811
  function Tooltip({ content, children, placement = "top", disabled, className, ...props }) {
597
- const [visible, setVisible] = (0, import_react6.useState)(false);
598
- const id = (0, import_react6.useId)();
599
- const trigger = (0, import_react6.cloneElement)(children, { "aria-describedby": visible ? id : void 0, onMouseEnter: (event) => {
812
+ const [visible, setVisible] = (0, import_react7.useState)(false);
813
+ const id = (0, import_react7.useId)();
814
+ const trigger = (0, import_react7.cloneElement)(children, { "aria-describedby": visible ? id : void 0, onMouseEnter: (event) => {
600
815
  children.props.onMouseEnter?.(event);
601
816
  setVisible(true);
602
817
  }, onMouseLeave: (event) => {
@@ -609,46 +824,324 @@ function Tooltip({ content, children, placement = "top", disabled, className, ..
609
824
  children.props.onBlur?.(event);
610
825
  setVisible(false);
611
826
  } });
612
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: ["fe-tooltip", `fe-tooltip--${placement}`, className].filter(Boolean).join(" "), ...props, children: [
827
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: ["fe-tooltip", `fe-tooltip--${placement}`, className].filter(Boolean).join(" "), ...props, children: [
613
828
  trigger,
614
- visible && !disabled && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id, role: "tooltip", className: "fe-tooltip__content", children: content })
829
+ visible && !disabled && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { id, role: "tooltip", className: "fe-tooltip__content", children: content })
615
830
  ] });
616
831
  }
617
832
 
618
833
  // packages/expandable-section/src/index.tsx
619
- var import_react7 = require("react");
620
- var import_jsx_runtime8 = require("react/jsx-runtime");
834
+ var import_react8 = require("react");
835
+ var import_jsx_runtime9 = require("react/jsx-runtime");
621
836
  function ExpandableSection({ title, children, expanded, defaultExpanded = false, onExpandedChange, disabled, className, ...props }) {
622
- const [internalExpanded, setInternalExpanded] = (0, import_react7.useState)(defaultExpanded);
837
+ const [internalExpanded, setInternalExpanded] = (0, import_react8.useState)(defaultExpanded);
623
838
  const isExpanded = expanded ?? internalExpanded;
624
- const id = (0, import_react7.useId)();
839
+ const id = (0, import_react8.useId)();
625
840
  const toggle = () => {
626
841
  if (disabled) return;
627
842
  const next = !isExpanded;
628
843
  if (expanded === void 0) setInternalExpanded(next);
629
844
  onExpandedChange?.(next);
630
845
  };
631
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: ["fe-expandable-section", isExpanded && "fe-expandable-section--expanded", disabled && "fe-expandable-section--disabled", className].filter(Boolean).join(" "), ...props, children: [
632
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { className: "fe-expandable-section__trigger", type: "button", "aria-expanded": isExpanded, "aria-controls": id, disabled, onClick: toggle, children: [
633
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: title }),
634
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "fe-expandable-section__chevron", "aria-hidden": "true" })
846
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: ["fe-expandable-section", isExpanded && "fe-expandable-section--expanded", disabled && "fe-expandable-section--disabled", className].filter(Boolean).join(" "), ...props, children: [
847
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("button", { className: "fe-expandable-section__trigger", type: "button", "aria-expanded": isExpanded, "aria-controls": id, disabled, onClick: toggle, children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: title }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fe-expandable-section__chevron", "aria-hidden": "true" })
850
+ ] }),
851
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { id, className: "fe-expandable-section__content", "aria-hidden": !isExpanded, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "fe-expandable-section__content-inner", children }) })
852
+ ] });
853
+ }
854
+
855
+ // packages/loading/src/index.tsx
856
+ var import_jsx_runtime10 = require("react/jsx-runtime");
857
+ var sizes = {
858
+ small: "16px",
859
+ medium: "24px",
860
+ large: "40px"
861
+ };
862
+ function Loading({ size = "medium", color = "#2878f0", label, className, style, ...props }) {
863
+ const diameter = typeof size === "number" ? `${size}px` : sizes[size];
864
+ const loadingStyle = {
865
+ "--fe-loading-size": diameter,
866
+ "--fe-loading-color": color,
867
+ ...style
868
+ };
869
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: ["fe-loading", className].filter(Boolean).join(" "), style: loadingStyle, role: "status", ...props, children: [
870
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "fe-loading__spinner", "aria-hidden": "true" }),
871
+ label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "fe-loading__label", children: label }),
872
+ !label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "fe-loading__sr-only", children: "Loading" })
873
+ ] });
874
+ }
875
+
876
+ // packages/skeleton/src/index.tsx
877
+ var import_jsx_runtime11 = require("react/jsx-runtime");
878
+ var pixels = (value) => typeof value === "number" ? `${value}px` : value;
879
+ function SkeletonBase({ variant = "box", shape, width, height, count = 1, animated = true, className, style, ...props }) {
880
+ const resolvedVariant = shape ?? variant;
881
+ const resolvedWidth = pixels(width) ?? (resolvedVariant === "circle" ? "40px" : "100%");
882
+ const resolvedHeight = pixels(height) ?? (resolvedVariant === "circle" ? resolvedWidth : resolvedVariant === "line" ? "12px" : "80px");
883
+ const skeletonStyle = {
884
+ "--fe-skeleton-width": resolvedWidth,
885
+ "--fe-skeleton-height": resolvedHeight,
886
+ ...style
887
+ };
888
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_jsx_runtime11.Fragment, { children: Array.from({ length: Math.max(1, count) }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
889
+ "div",
890
+ {
891
+ className: ["fe-skeleton", `fe-skeleton--${resolvedVariant}`, animated && "fe-skeleton--animated", className].filter(Boolean).join(" "),
892
+ style: skeletonStyle,
893
+ "aria-hidden": "true",
894
+ ...props
895
+ },
896
+ index
897
+ )) });
898
+ }
899
+ function SkeletonCircle({ size = 40, ...props }) {
900
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SkeletonBase, { variant: "circle", width: size, height: size, ...props });
901
+ }
902
+ function SkeletonSquare({ size = 80, ...props }) {
903
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SkeletonBase, { variant: "box", width: size, height: size, ...props });
904
+ }
905
+ function SkeletonBox({ width = "100%", height = 80, ...props }) {
906
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SkeletonBase, { variant: "box", width, height, ...props });
907
+ }
908
+ function SkeletonLine({ width = "100%", height = 12, ...props }) {
909
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SkeletonBase, { variant: "line", width, height, ...props });
910
+ }
911
+ var Skeleton = Object.assign(SkeletonBase, {
912
+ Circle: SkeletonCircle,
913
+ Square: SkeletonSquare,
914
+ Box: SkeletonBox,
915
+ Line: SkeletonLine
916
+ });
917
+
918
+ // packages/modal/src/index.tsx
919
+ var import_react9 = require("react");
920
+ var import_react_dom3 = require("react-dom");
921
+ var import_jsx_runtime12 = require("react/jsx-runtime");
922
+ function Modal({
923
+ open,
924
+ onClose,
925
+ title,
926
+ description,
927
+ children,
928
+ footer,
929
+ actions = [],
930
+ size = "medium",
931
+ closeOnBackdropClick = true,
932
+ closeButton = true,
933
+ closeLabel = "Close modal",
934
+ className,
935
+ ...props
936
+ }) {
937
+ const titleId = (0, import_react9.useId)();
938
+ const panelRef = (0, import_react9.useRef)(null);
939
+ (0, import_react9.useEffect)(() => {
940
+ if (!open) return;
941
+ const onKeyDown = (event) => {
942
+ if (event.key === "Escape") onClose();
943
+ };
944
+ document.addEventListener("keydown", onKeyDown);
945
+ panelRef.current?.focus();
946
+ return () => document.removeEventListener("keydown", onKeyDown);
947
+ }, [onClose, open]);
948
+ if (!open || typeof document === "undefined") return null;
949
+ const hasFooter = footer || actions.length > 0;
950
+ return (0, import_react_dom3.createPortal)(
951
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fe-modal-backdrop", role: "presentation", onMouseDown: (event) => {
952
+ if (event.target === event.currentTarget && closeOnBackdropClick) onClose();
953
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
954
+ "div",
955
+ {
956
+ ref: panelRef,
957
+ className: ["fe-modal", `fe-modal--${size}`, className].filter(Boolean).join(" "),
958
+ role: "dialog",
959
+ "aria-modal": "true",
960
+ "aria-labelledby": title ? titleId : void 0,
961
+ "aria-label": title ? void 0 : "Modal",
962
+ tabIndex: -1,
963
+ ...props,
964
+ children: [
965
+ (title || description || closeButton) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("header", { className: "fe-modal__header", children: [
966
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "fe-modal__heading", children: [
967
+ title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { id: titleId, children: title }),
968
+ description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { children: description })
969
+ ] }),
970
+ closeButton && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { hierarchy: "tertiary-gray", size: "32", iconOnly: true, icon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CloseIcon, {}), "aria-label": closeLabel, onClick: onClose })
971
+ ] }),
972
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fe-modal__body", children }),
973
+ hasFooter && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("footer", { className: "fe-modal__footer", children: [
974
+ footer && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fe-modal__footer-content", children: footer }),
975
+ actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fe-modal__actions", children: actions.map(({ label, ...action }, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { ...action, children: label }, index)) })
976
+ ] })
977
+ ]
978
+ }
979
+ ) }),
980
+ document.body
981
+ );
982
+ }
983
+
984
+ // packages/progress/src/index.tsx
985
+ var import_jsx_runtime13 = require("react/jsx-runtime");
986
+ function ProgressBar({ value, max = 100, label, showValue = true, size = "medium", color = "#2878f0", className, style, "aria-label": ariaLabel, ...props }) {
987
+ const indeterminate = value === null || value === void 0;
988
+ const percentage = indeterminate ? 0 : Math.min(100, Math.max(0, Number(value) / Math.max(1, max) * 100));
989
+ const progressStyle = {
990
+ "--fe-progress-value": `${percentage}%`,
991
+ "--fe-progress-color": color,
992
+ ...style
993
+ };
994
+ const visibleValue = `${Math.round(percentage)}%`;
995
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: ["fe-progress", `fe-progress--${size}`, indeterminate && "fe-progress--indeterminate", className].filter(Boolean).join(" "), style: progressStyle, ...props, children: [
996
+ (label || !indeterminate && showValue) && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "fe-progress__meta", children: [
997
+ label && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "fe-progress__label", children: label }),
998
+ !indeterminate && showValue && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("output", { className: "fe-progress__value", children: visibleValue })
635
999
  ] }),
636
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { id, className: "fe-expandable-section__content", "aria-hidden": !isExpanded, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "fe-expandable-section__content-inner", children }) })
1000
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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__ */ (0, import_jsx_runtime13.jsx)("span", { className: "fe-progress__indicator" }) })
1001
+ ] });
1002
+ }
1003
+
1004
+ // packages/chip/src/index.tsx
1005
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1006
+ var statuses = ["default", "info", "success", "warning", "error", "neutral", "pending", "processing"];
1007
+ function Chip({ children, status = "default", color, variant = "subtle", dot = false, onRemove, removeLabel = "Remove", className, style, ...props }) {
1008
+ const usesCustomColor = Boolean(color);
1009
+ const chipStyle = {
1010
+ ...usesCustomColor ? { "--fe-chip-custom-color": color } : {},
1011
+ ...style
1012
+ };
1013
+ const resolvedStatus = statuses.includes(status) ? status : "default";
1014
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: ["fe-chip", `fe-chip--${variant}`, `fe-chip--${usesCustomColor ? "custom" : resolvedStatus}`, className].filter(Boolean).join(" "), style: chipStyle, ...props, children: [
1015
+ dot && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "fe-chip__dot", "aria-hidden": "true" }),
1016
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "fe-chip__content", children }),
1017
+ onRemove && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button, { hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CloseIcon, { size: 14 }), "aria-label": removeLabel, onClick: onRemove, className: "fe-chip__remove" })
637
1018
  ] });
638
1019
  }
1020
+
1021
+ // packages/message/src/index.tsx
1022
+ var import_react10 = require("react");
1023
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1024
+ var messageId = 0;
1025
+ var entries = [];
1026
+ var listeners = /* @__PURE__ */ new Set();
1027
+ var timers = /* @__PURE__ */ new Map();
1028
+ var publish = () => listeners.forEach((listener) => listener(entries));
1029
+ function dismiss(id) {
1030
+ const timer = timers.get(id);
1031
+ if (timer) window.clearTimeout(timer);
1032
+ timers.delete(id);
1033
+ const entry = entries.find((item) => item.id === id);
1034
+ entries = entries.filter((item) => item.id !== id);
1035
+ entry?.onClose?.();
1036
+ publish();
1037
+ }
1038
+ function show({ duration = 4500, severity = "info", ...options }) {
1039
+ const id = ++messageId;
1040
+ entries = [...entries, { id, duration, severity, ...options }];
1041
+ publish();
1042
+ if (duration !== null) timers.set(id, window.setTimeout(() => dismiss(id), duration));
1043
+ return id;
1044
+ }
1045
+ var message = {
1046
+ show,
1047
+ success: (content, options = {}) => show({ content, severity: "success", ...options }),
1048
+ info: (content, options = {}) => show({ content, severity: "info", ...options }),
1049
+ warning: (content, options = {}) => show({ content, severity: "warning", ...options }),
1050
+ error: (content, options = {}) => show({ content, severity: "error", ...options }),
1051
+ dismiss,
1052
+ clear: () => [...entries].forEach((entry) => dismiss(entry.id))
1053
+ };
1054
+ var icons = { success: "\u2713", info: "i", warning: "!", error: "\xD7" };
1055
+ function Message({ maxCount = 4, closeLabel = "Close notification", className, ...props }) {
1056
+ const [visibleEntries, setVisibleEntries] = (0, import_react10.useState)(entries);
1057
+ (0, import_react10.useEffect)(() => {
1058
+ const listener = (nextEntries) => setVisibleEntries(nextEntries);
1059
+ listeners.add(listener);
1060
+ return () => {
1061
+ listeners.delete(listener);
1062
+ };
1063
+ }, []);
1064
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: ["fe-message-viewport", className].filter(Boolean).join(" "), "aria-live": "polite", ...props, children: visibleEntries.slice(-Math.max(1, maxCount)).map((entry) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `fe-message fe-message--${entry.severity ?? "info"}`, role: entry.severity === "error" ? "alert" : "status", children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "fe-message__icon", "aria-hidden": "true", children: icons[entry.severity ?? "info"] }),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "fe-message__body", children: [
1067
+ entry.title && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { className: "fe-message__title", children: entry.title }),
1068
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "fe-message__content", children: entry.content })
1069
+ ] }),
1070
+ entry.action && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "fe-message__action", children: entry.action }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { className: "fe-message__close", hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: "\xD7", "aria-label": closeLabel, onClick: () => dismiss(entry.id) })
1072
+ ] }, entry.id)) });
1073
+ }
1074
+ ((Message2) => {
1075
+ Message2.show = message.show;
1076
+ Message2.success = message.success;
1077
+ Message2.info = message.info;
1078
+ Message2.warning = message.warning;
1079
+ Message2.error = message.error;
1080
+ Message2.dismiss = message.dismiss;
1081
+ Message2.clear = message.clear;
1082
+ })(Message || (Message = {}));
1083
+
1084
+ // packages/snackbar/src/index.tsx
1085
+ var import_react11 = require("react");
1086
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1087
+ function Snackbar({
1088
+ open,
1089
+ message: message2,
1090
+ autoHideDuration = 5e3,
1091
+ onClose,
1092
+ action,
1093
+ anchorOrigin = {},
1094
+ closeLabel = "Close notification",
1095
+ className,
1096
+ ...props
1097
+ }) {
1098
+ const vertical = anchorOrigin.vertical ?? "bottom";
1099
+ const horizontal = anchorOrigin.horizontal ?? "center";
1100
+ (0, import_react11.useEffect)(() => {
1101
+ if (!open || autoHideDuration === null) return void 0;
1102
+ const timer = window.setTimeout(() => onClose?.("timeout"), autoHideDuration);
1103
+ return () => window.clearTimeout(timer);
1104
+ }, [open, autoHideDuration, onClose]);
1105
+ (0, import_react11.useEffect)(() => {
1106
+ if (!open) return void 0;
1107
+ const onKeyDown = (event) => {
1108
+ if (event.key === "Escape") onClose?.("escapeKeyDown");
1109
+ };
1110
+ window.addEventListener("keydown", onKeyDown);
1111
+ return () => window.removeEventListener("keydown", onKeyDown);
1112
+ }, [open, onClose]);
1113
+ if (!open) return null;
1114
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: ["fe-snackbar", `fe-snackbar--${vertical}`, `fe-snackbar--${horizontal}`, className].filter(Boolean).join(" "), ...props, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "fe-snackbar__content", role: "status", "aria-live": "polite", children: [
1115
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "fe-snackbar__message", children: message2 }),
1116
+ action && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "fe-snackbar__action", children: action }),
1117
+ onClose && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Button, { className: "fe-snackbar__close", hierarchy: "tertiary-gray", size: "24", iconOnly: true, icon: "\xD7", "aria-label": closeLabel, onClick: () => onClose("closeButton") })
1118
+ ] }) });
1119
+ }
639
1120
  // Annotate the CommonJS export names for ESM import in node:
640
1121
  0 && (module.exports = {
641
1122
  Button,
642
1123
  ChevronDownIcon,
1124
+ Chip,
643
1125
  CloseIcon,
644
1126
  CurrencyInput,
645
1127
  DateTimePicker,
646
1128
  Drawer,
647
1129
  ExpandableSection,
1130
+ Loading,
1131
+ Message,
1132
+ Modal,
648
1133
  NumberInput,
1134
+ Pagination,
649
1135
  PlusIcon,
1136
+ ProgressBar,
650
1137
  SearchIcon,
651
1138
  Selection,
1139
+ Skeleton,
1140
+ SkeletonBox,
1141
+ SkeletonCircle,
1142
+ SkeletonLine,
1143
+ SkeletonSquare,
1144
+ Snackbar,
652
1145
  Table,
653
1146
  TableBody,
654
1147
  TableCaption,
@@ -656,8 +1149,11 @@ function ExpandableSection({ title, children, expanded, defaultExpanded = false,
656
1149
  TableHead,
657
1150
  TableHeader,
658
1151
  TableRow,
1152
+ TextArea,
1153
+ TextInput,
659
1154
  Toggle,
660
1155
  Tooltip,
661
1156
  formatNumber,
662
- getIconSize
1157
+ getIconSize,
1158
+ message
663
1159
  });