@dragunio/fe-libs 0.1.2 → 0.1.3

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,34 +425,106 @@ 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_jsx_runtime7 = require("react/jsx-runtime");
443
+ function getFieldError(error, errorMessage) {
444
+ if (errorMessage !== void 0 && errorMessage !== null) return errorMessage;
445
+ if (typeof error !== "boolean") return error;
446
+ return error ? "Invalid value" : void 0;
447
+ }
448
+ function getDescribedBy(existing, messageId2) {
449
+ return [existing, messageId2].filter(Boolean).join(" ") || void 0;
450
+ }
451
+ 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) {
452
+ const generatedId = (0, import_react6.useId)();
453
+ const inputId = id ?? `${generatedId}-input`;
454
+ const labelId = `${generatedId}-label`;
455
+ const messageId2 = `${generatedId}-message`;
456
+ const controlled = value !== void 0;
457
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue);
458
+ const currentValue = controlled ? value : internalValue;
459
+ const fieldError = getFieldError(error, errorMessage);
460
+ const hasError = fieldError !== void 0 && fieldError !== false && fieldError !== null;
461
+ const displayedMessage = hasError ? fieldError : helperText;
462
+ const describedBy = getDescribedBy(ariaDescribedBy, displayedMessage ? messageId2 : void 0);
463
+ const clear = () => {
464
+ if (!controlled) setInternalValue("");
465
+ onValueChange?.("");
466
+ onClear?.();
467
+ };
468
+ const labelledBy = getDescribedBy(ariaLabelledBy, label ? labelId : void 0);
469
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: ["fe-text-input", hasError && "fe-text-input--error", className].filter(Boolean).join(" "), children: [
470
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: labelId, className: "fe-text-input__label", children: label }),
471
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "fe-text-input__control", children: [
472
+ /* @__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) => {
473
+ if (!controlled) setInternalValue(event.target.value);
474
+ onValueChange?.(event.target.value);
475
+ onChange?.(event);
476
+ }, ...props }),
477
+ 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" })
478
+ ] }),
479
+ displayedMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: messageId2, className: hasError ? "fe-text-input__error" : "fe-text-input__helper", children: displayedMessage })
480
+ ] });
481
+ });
482
+ TextInput.displayName = "TextInput";
483
+ 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) {
484
+ const generatedId = (0, import_react6.useId)();
485
+ const inputId = id ?? `${generatedId}-input`;
486
+ const labelId = `${generatedId}-label`;
487
+ const messageId2 = `${generatedId}-message`;
488
+ const controlled = value !== void 0;
489
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue);
490
+ const currentValue = controlled ? value : internalValue;
491
+ const fieldError = getFieldError(error, errorMessage);
492
+ const hasError = fieldError !== void 0 && fieldError !== false && fieldError !== null;
493
+ const displayedMessage = hasError ? fieldError : helperText;
494
+ const describedBy = getDescribedBy(ariaDescribedBy, displayedMessage ? messageId2 : void 0);
495
+ const clear = () => {
496
+ if (!controlled) setInternalValue("");
497
+ onValueChange?.("");
498
+ onClear?.();
499
+ };
500
+ const labelledBy = getDescribedBy(ariaLabelledBy, label ? labelId : void 0);
501
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: ["fe-text-area", hasError && "fe-text-area--error", className].filter(Boolean).join(" "), children: [
502
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: labelId, className: "fe-text-area__label", children: label }),
503
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "fe-text-area__control", children: [
504
+ /* @__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) => {
505
+ if (!controlled) setInternalValue(event.target.value);
506
+ onValueChange?.(event.target.value);
507
+ onChange?.(event);
508
+ }, ...props }),
509
+ 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" })
510
+ ] }),
511
+ displayedMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: messageId2, className: hasError ? "fe-text-area__error" : "fe-text-area__helper", children: displayedMessage })
512
+ ] });
513
+ });
514
+ TextArea.displayName = "TextArea";
515
+ 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) {
516
+ const generatedId = (0, import_react6.useId)();
326
517
  const listboxId = id ?? generatedId;
327
518
  const isControlled = value !== void 0;
328
- const [internalValue, setInternalValue] = (0, import_react5.useState)(defaultValue ?? (multiple ? [] : ""));
519
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue ?? (multiple ? [] : ""));
329
520
  const selectedValue = isControlled ? value : internalValue;
330
521
  const selectedValues = Array.isArray(selectedValue) ? selectedValue : selectedValue === "" || selectedValue === null || selectedValue === void 0 ? [] : [selectedValue];
331
522
  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)(() => {
523
+ const [open, setOpen] = (0, import_react6.useState)(false);
524
+ const [query, setQuery] = (0, import_react6.useState)("");
525
+ const [activeIndex, setActiveIndex] = (0, import_react6.useState)(0);
526
+ const rootRef = (0, import_react6.useRef)(null);
527
+ (0, import_react6.useEffect)(() => {
337
528
  const close = (event) => {
338
529
  if (!rootRef.current?.contains(event.target)) setOpen(false);
339
530
  };
@@ -376,39 +567,39 @@ var Selection = (0, import_react5.forwardRef)(function Selection2({ className, l
376
567
  }
377
568
  if (event.key === "Escape") setOpen(false);
378
569
  };
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: [
570
+ 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: [
571
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-selection__label", children: label }),
572
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref: rootRef, className: "fe-selection__control", children: [
573
+ name && selectedValues.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "hidden", name: multiple ? `${name}[]` : name, value: item }, String(item))),
574
+ /* @__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: [
575
+ /* @__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
576
  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: [
577
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": "true", children: "\xD7" })
578
+ ] }, String(option.value))) : displayLabel.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
388
579
  item,
389
580
  index < displayLabel.length - 1 ? ", " : ""
390
581
  ] }, 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" }) })
582
+ /* @__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
583
  ] }),
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) => {
584
+ clearable && selectedOptions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "fe-selection__clear", "aria-label": "Clear selection", onClick: clear }),
585
+ open && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { id: listboxId, className: "fe-selection__menu", role: "listbox", "aria-multiselectable": multiple || void 0, children: [
586
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "fe-selection__search", children: [
587
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": "true", children: "\u2315" }),
588
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { autoFocus: true, value: query, onChange: (event) => {
398
589
  setQuery(event.target.value);
399
590
  setActiveIndex(0);
400
591
  }, placeholder: "Search\u2026", "aria-label": "Search options" })
401
592
  ] }),
402
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fe-selection__options", children: filteredOptions.length ? filteredOptions.map((option, index) => {
593
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "fe-selection__options", children: filteredOptions.length ? filteredOptions.map((option, index) => {
403
594
  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" })
595
+ 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: [
596
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: option.label }),
597
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-selection__check", "aria-hidden": "true", children: "\u2713" })
407
598
  ] }, String(option.value));
408
- }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "fe-selection__empty", children: "No matching options" }) })
599
+ }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "fe-selection__empty", children: "No matching options" }) })
409
600
  ] })
410
601
  ] }),
411
- helperText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-selection__helper", children: helperText })
602
+ helperText && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-selection__helper", children: helperText })
412
603
  ] });
413
604
  });
414
605
  Selection.displayName = "Selection";
@@ -445,13 +636,19 @@ function normalizeNumberInput(raw, options) {
445
636
  if (value !== numericValue) return { display: formatNumber(value, decimals, thousandSeparator, decimalSeparator), value };
446
637
  return { display: `${negative ? "-" : ""}${groupInteger(integer || "0", thousandSeparator)}${hasDecimal ? `${decimalSeparator}${fraction}` : ""}`, value };
447
638
  }
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) {
639
+ 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) {
640
+ const generatedId = (0, import_react6.useId)();
641
+ const messageId2 = `${generatedId}-message`;
449
642
  const isControlled = value !== void 0;
450
- const [internalValue, setInternalValue] = (0, import_react5.useState)(defaultValue);
451
- const [focused, setFocused] = (0, import_react5.useState)(false);
643
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue);
644
+ const [focused, setFocused] = (0, import_react6.useState)(false);
452
645
  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)(() => {
646
+ const [text, setText] = (0, import_react6.useState)(actualValue === null ? "" : formatNumber(actualValue, decimals, thousandSeparator, decimalSeparator));
647
+ const fieldError = getFieldError(error, errorMessage);
648
+ const hasError = fieldError !== void 0 && fieldError !== false && fieldError !== null;
649
+ const displayedMessage = hasError ? fieldError : helperText;
650
+ const describedBy = getDescribedBy(ariaDescribedBy, displayedMessage ? messageId2 : void 0);
651
+ (0, import_react6.useEffect)(() => {
455
652
  if (!focused) setText(actualValue === null ? "" : formatNumber(actualValue, decimals, thousandSeparator, decimalSeparator));
456
653
  }, [actualValue, decimalSeparator, decimals, focused, thousandSeparator]);
457
654
  const updateValue = (nextText) => {
@@ -461,32 +658,32 @@ var NumberInput = (0, import_react5.forwardRef)(function NumberInput2({ value, d
461
658
  if (!isControlled) setInternalValue(nextValue);
462
659
  onValueChange?.(nextValue);
463
660
  };
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) => {
661
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: ["fe-number-input", hasError && "fe-number-input--error", className].filter(Boolean).join(" "), children: [
662
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-number-input__label", children: label }),
663
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { ref, inputMode: allowDecimal ? "decimal" : "numeric", value: text, onFocus: (event) => {
467
664
  setFocused(true);
468
665
  onFocus?.(event);
469
666
  }, onChange: (event) => updateValue(event.target.value), onBlur: (event) => {
470
667
  setFocused(false);
471
668
  onBlur?.(event);
472
- }, ...props }),
473
- helperText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fe-number-input__helper", children: helperText })
669
+ }, ...props, "aria-invalid": hasError || void 0, "aria-describedby": describedBy }),
670
+ displayedMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: messageId2, className: hasError ? "fe-number-input__error" : "fe-number-input__helper", children: displayedMessage })
474
671
  ] });
475
672
  });
476
673
  NumberInput.displayName = "NumberInput";
477
674
  function CurrencyInput({ suffix = "VND", currencies, currency, defaultCurrency, onCurrencyChange, className, ...props }) {
478
- const [internalCurrency, setInternalCurrency] = (0, import_react5.useState)(defaultCurrency ?? currencies?.[0]?.code ?? "");
675
+ const [internalCurrency, setInternalCurrency] = (0, import_react6.useState)(defaultCurrency ?? currencies?.[0]?.code ?? "");
479
676
  const selectedCurrency = currency ?? internalCurrency;
480
677
  const current = currencies?.find((item) => item.code === selectedCurrency);
481
678
  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) => {
679
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: ["fe-currency-input", className].filter(Boolean).join(" "), children: [
680
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NumberInput, { ...props, className: "fe-currency-input__number" }),
681
+ currencies?.length ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("select", { className: "fe-currency-input__currency", "aria-label": "Currency", value: selectedCurrency, onChange: (event) => {
485
682
  const next = currencies.find((item) => item.code === event.target.value);
486
683
  if (!next) return;
487
684
  if (currency === void 0) setInternalCurrency(next.code);
488
685
  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 })
686
+ }, 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
687
  ] });
491
688
  }
492
689
  var pad = (value) => String(value).padStart(2, "0");
@@ -509,15 +706,15 @@ function DateTimePicker(props) {
509
706
  const { label, format = "dd/MM/yyyy HH:mm", disabled, placeholder, locale = "en-US", className, mode = "single" } = props;
510
707
  const isRange = mode === "range";
511
708
  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]);
709
+ const [singleInternal, setSingleInternal] = (0, import_react6.useState)((!isRange ? props.defaultValue : null) ?? null);
710
+ const [rangeInternal, setRangeInternal] = (0, import_react6.useState)((isRange ? props.defaultValue : void 0) ?? [null, null]);
514
711
  const singleValue = !isRange ? props.value ?? singleInternal : null;
515
712
  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)(() => {
713
+ const [open, setOpen] = (0, import_react6.useState)(false);
714
+ const [month, setMonth] = (0, import_react6.useState)(() => new Date((isRange ? rangeValue[0] : singleValue) ?? /* @__PURE__ */ new Date()));
715
+ const [editingEnd, setEditingEnd] = (0, import_react6.useState)(false);
716
+ const rootRef = (0, import_react6.useRef)(null);
717
+ (0, import_react6.useEffect)(() => {
521
718
  const onPointerDown = (event) => {
522
719
  if (!rootRef.current?.contains(event.target)) setOpen(false);
523
720
  };
@@ -559,44 +756,44 @@ function DateTimePicker(props) {
559
756
  if (isRange) setRange(editingEnd ? [rangeValue[0], next] : [next, rangeValue[1]]);
560
757
  else setSingle(next);
561
758
  };
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") })
759
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref: rootRef, className: ["fe-datetime-picker", className].filter(Boolean).join(" "), children: [
760
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-datetime-picker__label", children: label }),
761
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("button", { type: "button", className: "fe-datetime-picker__trigger", disabled, "aria-expanded": open, onClick: () => setOpen((current) => !current), children: [
762
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": "true", children: "\u25F7" }),
763
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: shownValue || placeholder || (isRange ? "Select date range" : "Select date and time") })
567
764
  ] }),
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" })
765
+ open && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "fe-datetime-picker__popper", role: "dialog", "aria-label": "Date and time picker", children: [
766
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "fe-datetime-picker__month", children: [
767
+ /* @__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" }),
768
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: month.toLocaleDateString(locale, { month: "long", year: "numeric" }) }),
769
+ /* @__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
770
  ] }),
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) => {
771
+ /* @__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)) }),
772
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "fe-datetime-picker__days", children: days.map((day) => {
576
773
  const inMonth = day.getMonth() === month.getMonth();
577
774
  const selected = isRange ? sameDay(day, rangeValue[0]) || sameDay(day, rangeValue[1]) : sameDay(day, singleValue);
578
775
  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());
776
+ 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
777
  }) }),
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" })
778
+ isRange && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "fe-datetime-picker__range-tabs", children: [
779
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: !editingEnd ? "is-active" : "", onClick: () => setEditingEnd(false), children: "Start" }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: editingEnd ? "is-active" : "", onClick: () => setEditingEnd(true), children: "End" })
584
781
  ] }),
585
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "fe-datetime-picker__time", children: [
782
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "fe-datetime-picker__time", children: [
586
783
  "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 })
784
+ /* @__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
785
  ] })
589
786
  ] })
590
787
  ] });
591
788
  }
592
789
 
593
790
  // packages/tooltip/src/index.tsx
594
- var import_react6 = require("react");
595
- var import_jsx_runtime7 = require("react/jsx-runtime");
791
+ var import_react7 = require("react");
792
+ var import_jsx_runtime8 = require("react/jsx-runtime");
596
793
  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) => {
794
+ const [visible, setVisible] = (0, import_react7.useState)(false);
795
+ const id = (0, import_react7.useId)();
796
+ const trigger = (0, import_react7.cloneElement)(children, { "aria-describedby": visible ? id : void 0, onMouseEnter: (event) => {
600
797
  children.props.onMouseEnter?.(event);
601
798
  setVisible(true);
602
799
  }, onMouseLeave: (event) => {
@@ -609,46 +806,324 @@ function Tooltip({ content, children, placement = "top", disabled, className, ..
609
806
  children.props.onBlur?.(event);
610
807
  setVisible(false);
611
808
  } });
612
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: ["fe-tooltip", `fe-tooltip--${placement}`, className].filter(Boolean).join(" "), ...props, children: [
809
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: ["fe-tooltip", `fe-tooltip--${placement}`, className].filter(Boolean).join(" "), ...props, children: [
613
810
  trigger,
614
- visible && !disabled && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id, role: "tooltip", className: "fe-tooltip__content", children: content })
811
+ visible && !disabled && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { id, role: "tooltip", className: "fe-tooltip__content", children: content })
615
812
  ] });
616
813
  }
617
814
 
618
815
  // packages/expandable-section/src/index.tsx
619
- var import_react7 = require("react");
620
- var import_jsx_runtime8 = require("react/jsx-runtime");
816
+ var import_react8 = require("react");
817
+ var import_jsx_runtime9 = require("react/jsx-runtime");
621
818
  function ExpandableSection({ title, children, expanded, defaultExpanded = false, onExpandedChange, disabled, className, ...props }) {
622
- const [internalExpanded, setInternalExpanded] = (0, import_react7.useState)(defaultExpanded);
819
+ const [internalExpanded, setInternalExpanded] = (0, import_react8.useState)(defaultExpanded);
623
820
  const isExpanded = expanded ?? internalExpanded;
624
- const id = (0, import_react7.useId)();
821
+ const id = (0, import_react8.useId)();
625
822
  const toggle = () => {
626
823
  if (disabled) return;
627
824
  const next = !isExpanded;
628
825
  if (expanded === void 0) setInternalExpanded(next);
629
826
  onExpandedChange?.(next);
630
827
  };
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" })
828
+ 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: [
829
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("button", { className: "fe-expandable-section__trigger", type: "button", "aria-expanded": isExpanded, "aria-controls": id, disabled, onClick: toggle, children: [
830
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: title }),
831
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fe-expandable-section__chevron", "aria-hidden": "true" })
832
+ ] }),
833
+ /* @__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 }) })
834
+ ] });
835
+ }
836
+
837
+ // packages/loading/src/index.tsx
838
+ var import_jsx_runtime10 = require("react/jsx-runtime");
839
+ var sizes = {
840
+ small: "16px",
841
+ medium: "24px",
842
+ large: "40px"
843
+ };
844
+ function Loading({ size = "medium", color = "#2878f0", label, className, style, ...props }) {
845
+ const diameter = typeof size === "number" ? `${size}px` : sizes[size];
846
+ const loadingStyle = {
847
+ "--fe-loading-size": diameter,
848
+ "--fe-loading-color": color,
849
+ ...style
850
+ };
851
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: ["fe-loading", className].filter(Boolean).join(" "), style: loadingStyle, role: "status", ...props, children: [
852
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "fe-loading__spinner", "aria-hidden": "true" }),
853
+ label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "fe-loading__label", children: label }),
854
+ !label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "fe-loading__sr-only", children: "Loading" })
855
+ ] });
856
+ }
857
+
858
+ // packages/skeleton/src/index.tsx
859
+ var import_jsx_runtime11 = require("react/jsx-runtime");
860
+ var pixels = (value) => typeof value === "number" ? `${value}px` : value;
861
+ function SkeletonBase({ variant = "box", shape, width, height, count = 1, animated = true, className, style, ...props }) {
862
+ const resolvedVariant = shape ?? variant;
863
+ const resolvedWidth = pixels(width) ?? (resolvedVariant === "circle" ? "40px" : "100%");
864
+ const resolvedHeight = pixels(height) ?? (resolvedVariant === "circle" ? resolvedWidth : resolvedVariant === "line" ? "12px" : "80px");
865
+ const skeletonStyle = {
866
+ "--fe-skeleton-width": resolvedWidth,
867
+ "--fe-skeleton-height": resolvedHeight,
868
+ ...style
869
+ };
870
+ 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)(
871
+ "div",
872
+ {
873
+ className: ["fe-skeleton", `fe-skeleton--${resolvedVariant}`, animated && "fe-skeleton--animated", className].filter(Boolean).join(" "),
874
+ style: skeletonStyle,
875
+ "aria-hidden": "true",
876
+ ...props
877
+ },
878
+ index
879
+ )) });
880
+ }
881
+ function SkeletonCircle({ size = 40, ...props }) {
882
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SkeletonBase, { variant: "circle", width: size, height: size, ...props });
883
+ }
884
+ function SkeletonSquare({ size = 80, ...props }) {
885
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SkeletonBase, { variant: "box", width: size, height: size, ...props });
886
+ }
887
+ function SkeletonBox({ width = "100%", height = 80, ...props }) {
888
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SkeletonBase, { variant: "box", width, height, ...props });
889
+ }
890
+ function SkeletonLine({ width = "100%", height = 12, ...props }) {
891
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SkeletonBase, { variant: "line", width, height, ...props });
892
+ }
893
+ var Skeleton = Object.assign(SkeletonBase, {
894
+ Circle: SkeletonCircle,
895
+ Square: SkeletonSquare,
896
+ Box: SkeletonBox,
897
+ Line: SkeletonLine
898
+ });
899
+
900
+ // packages/modal/src/index.tsx
901
+ var import_react9 = require("react");
902
+ var import_react_dom2 = require("react-dom");
903
+ var import_jsx_runtime12 = require("react/jsx-runtime");
904
+ function Modal({
905
+ open,
906
+ onClose,
907
+ title,
908
+ description,
909
+ children,
910
+ footer,
911
+ actions = [],
912
+ size = "medium",
913
+ closeOnBackdropClick = true,
914
+ closeButton = true,
915
+ closeLabel = "Close modal",
916
+ className,
917
+ ...props
918
+ }) {
919
+ const titleId = (0, import_react9.useId)();
920
+ const panelRef = (0, import_react9.useRef)(null);
921
+ (0, import_react9.useEffect)(() => {
922
+ if (!open) return;
923
+ const onKeyDown = (event) => {
924
+ if (event.key === "Escape") onClose();
925
+ };
926
+ document.addEventListener("keydown", onKeyDown);
927
+ panelRef.current?.focus();
928
+ return () => document.removeEventListener("keydown", onKeyDown);
929
+ }, [onClose, open]);
930
+ if (!open || typeof document === "undefined") return null;
931
+ const hasFooter = footer || actions.length > 0;
932
+ return (0, import_react_dom2.createPortal)(
933
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fe-modal-backdrop", role: "presentation", onMouseDown: (event) => {
934
+ if (event.target === event.currentTarget && closeOnBackdropClick) onClose();
935
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
936
+ "div",
937
+ {
938
+ ref: panelRef,
939
+ className: ["fe-modal", `fe-modal--${size}`, className].filter(Boolean).join(" "),
940
+ role: "dialog",
941
+ "aria-modal": "true",
942
+ "aria-labelledby": title ? titleId : void 0,
943
+ "aria-label": title ? void 0 : "Modal",
944
+ tabIndex: -1,
945
+ ...props,
946
+ children: [
947
+ (title || description || closeButton) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("header", { className: "fe-modal__header", children: [
948
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "fe-modal__heading", children: [
949
+ title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { id: titleId, children: title }),
950
+ description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { children: description })
951
+ ] }),
952
+ 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 })
953
+ ] }),
954
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fe-modal__body", children }),
955
+ hasFooter && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("footer", { className: "fe-modal__footer", children: [
956
+ footer && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fe-modal__footer-content", children: footer }),
957
+ 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)) })
958
+ ] })
959
+ ]
960
+ }
961
+ ) }),
962
+ document.body
963
+ );
964
+ }
965
+
966
+ // packages/progress/src/index.tsx
967
+ var import_jsx_runtime13 = require("react/jsx-runtime");
968
+ function ProgressBar({ value, max = 100, label, showValue = true, size = "medium", color = "#2878f0", className, style, "aria-label": ariaLabel, ...props }) {
969
+ const indeterminate = value === null || value === void 0;
970
+ const percentage = indeterminate ? 0 : Math.min(100, Math.max(0, Number(value) / Math.max(1, max) * 100));
971
+ const progressStyle = {
972
+ "--fe-progress-value": `${percentage}%`,
973
+ "--fe-progress-color": color,
974
+ ...style
975
+ };
976
+ const visibleValue = `${Math.round(percentage)}%`;
977
+ 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: [
978
+ (label || !indeterminate && showValue) && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "fe-progress__meta", children: [
979
+ label && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "fe-progress__label", children: label }),
980
+ !indeterminate && showValue && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("output", { className: "fe-progress__value", children: visibleValue })
635
981
  ] }),
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 }) })
982
+ /* @__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" }) })
637
983
  ] });
638
984
  }
985
+
986
+ // packages/chip/src/index.tsx
987
+ var import_jsx_runtime14 = require("react/jsx-runtime");
988
+ var statuses = ["default", "info", "success", "warning", "error", "neutral", "pending", "processing"];
989
+ function Chip({ children, status = "default", color, variant = "subtle", dot = false, onRemove, removeLabel = "Remove", className, style, ...props }) {
990
+ const usesCustomColor = Boolean(color);
991
+ const chipStyle = {
992
+ ...usesCustomColor ? { "--fe-chip-custom-color": color } : {},
993
+ ...style
994
+ };
995
+ const resolvedStatus = statuses.includes(status) ? status : "default";
996
+ 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: [
997
+ dot && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "fe-chip__dot", "aria-hidden": "true" }),
998
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "fe-chip__content", children }),
999
+ 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" })
1000
+ ] });
1001
+ }
1002
+
1003
+ // packages/message/src/index.tsx
1004
+ var import_react10 = require("react");
1005
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1006
+ var messageId = 0;
1007
+ var entries = [];
1008
+ var listeners = /* @__PURE__ */ new Set();
1009
+ var timers = /* @__PURE__ */ new Map();
1010
+ var publish = () => listeners.forEach((listener) => listener(entries));
1011
+ function dismiss(id) {
1012
+ const timer = timers.get(id);
1013
+ if (timer) window.clearTimeout(timer);
1014
+ timers.delete(id);
1015
+ const entry = entries.find((item) => item.id === id);
1016
+ entries = entries.filter((item) => item.id !== id);
1017
+ entry?.onClose?.();
1018
+ publish();
1019
+ }
1020
+ function show({ duration = 4500, severity = "info", ...options }) {
1021
+ const id = ++messageId;
1022
+ entries = [...entries, { id, duration, severity, ...options }];
1023
+ publish();
1024
+ if (duration !== null) timers.set(id, window.setTimeout(() => dismiss(id), duration));
1025
+ return id;
1026
+ }
1027
+ var message = {
1028
+ show,
1029
+ success: (content, options = {}) => show({ content, severity: "success", ...options }),
1030
+ info: (content, options = {}) => show({ content, severity: "info", ...options }),
1031
+ warning: (content, options = {}) => show({ content, severity: "warning", ...options }),
1032
+ error: (content, options = {}) => show({ content, severity: "error", ...options }),
1033
+ dismiss,
1034
+ clear: () => [...entries].forEach((entry) => dismiss(entry.id))
1035
+ };
1036
+ var icons = { success: "\u2713", info: "i", warning: "!", error: "\xD7" };
1037
+ function Message({ maxCount = 4, closeLabel = "Close notification", className, ...props }) {
1038
+ const [visibleEntries, setVisibleEntries] = (0, import_react10.useState)(entries);
1039
+ (0, import_react10.useEffect)(() => {
1040
+ const listener = (nextEntries) => setVisibleEntries(nextEntries);
1041
+ listeners.add(listener);
1042
+ return () => {
1043
+ listeners.delete(listener);
1044
+ };
1045
+ }, []);
1046
+ 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: [
1047
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "fe-message__icon", "aria-hidden": "true", children: icons[entry.severity ?? "info"] }),
1048
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "fe-message__body", children: [
1049
+ entry.title && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { className: "fe-message__title", children: entry.title }),
1050
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "fe-message__content", children: entry.content })
1051
+ ] }),
1052
+ entry.action && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "fe-message__action", children: entry.action }),
1053
+ /* @__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) })
1054
+ ] }, entry.id)) });
1055
+ }
1056
+ ((Message2) => {
1057
+ Message2.show = message.show;
1058
+ Message2.success = message.success;
1059
+ Message2.info = message.info;
1060
+ Message2.warning = message.warning;
1061
+ Message2.error = message.error;
1062
+ Message2.dismiss = message.dismiss;
1063
+ Message2.clear = message.clear;
1064
+ })(Message || (Message = {}));
1065
+
1066
+ // packages/snackbar/src/index.tsx
1067
+ var import_react11 = require("react");
1068
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1069
+ function Snackbar({
1070
+ open,
1071
+ message: message2,
1072
+ autoHideDuration = 5e3,
1073
+ onClose,
1074
+ action,
1075
+ anchorOrigin = {},
1076
+ closeLabel = "Close notification",
1077
+ className,
1078
+ ...props
1079
+ }) {
1080
+ const vertical = anchorOrigin.vertical ?? "bottom";
1081
+ const horizontal = anchorOrigin.horizontal ?? "center";
1082
+ (0, import_react11.useEffect)(() => {
1083
+ if (!open || autoHideDuration === null) return void 0;
1084
+ const timer = window.setTimeout(() => onClose?.("timeout"), autoHideDuration);
1085
+ return () => window.clearTimeout(timer);
1086
+ }, [open, autoHideDuration, onClose]);
1087
+ (0, import_react11.useEffect)(() => {
1088
+ if (!open) return void 0;
1089
+ const onKeyDown = (event) => {
1090
+ if (event.key === "Escape") onClose?.("escapeKeyDown");
1091
+ };
1092
+ window.addEventListener("keydown", onKeyDown);
1093
+ return () => window.removeEventListener("keydown", onKeyDown);
1094
+ }, [open, onClose]);
1095
+ if (!open) return null;
1096
+ 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: [
1097
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "fe-snackbar__message", children: message2 }),
1098
+ action && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "fe-snackbar__action", children: action }),
1099
+ 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") })
1100
+ ] }) });
1101
+ }
639
1102
  // Annotate the CommonJS export names for ESM import in node:
640
1103
  0 && (module.exports = {
641
1104
  Button,
642
1105
  ChevronDownIcon,
1106
+ Chip,
643
1107
  CloseIcon,
644
1108
  CurrencyInput,
645
1109
  DateTimePicker,
646
1110
  Drawer,
647
1111
  ExpandableSection,
1112
+ Loading,
1113
+ Message,
1114
+ Modal,
648
1115
  NumberInput,
1116
+ Pagination,
649
1117
  PlusIcon,
1118
+ ProgressBar,
650
1119
  SearchIcon,
651
1120
  Selection,
1121
+ Skeleton,
1122
+ SkeletonBox,
1123
+ SkeletonCircle,
1124
+ SkeletonLine,
1125
+ SkeletonSquare,
1126
+ Snackbar,
652
1127
  Table,
653
1128
  TableBody,
654
1129
  TableCaption,
@@ -656,8 +1131,11 @@ function ExpandableSection({ title, children, expanded, defaultExpanded = false,
656
1131
  TableHead,
657
1132
  TableHeader,
658
1133
  TableRow,
1134
+ TextArea,
1135
+ TextInput,
659
1136
  Toggle,
660
1137
  Tooltip,
661
1138
  formatNumber,
662
- getIconSize
1139
+ getIconSize,
1140
+ message
663
1141
  });