@dragunio/fe-libs 0.1.1 → 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,9 +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,
27
+ CurrencyInput: () => CurrencyInput,
28
+ DateTimePicker: () => DateTimePicker,
29
+ Drawer: () => Drawer,
30
+ ExpandableSection: () => ExpandableSection,
31
+ Loading: () => Loading,
32
+ Message: () => Message,
33
+ Modal: () => Modal,
34
+ NumberInput: () => NumberInput,
35
+ Pagination: () => Pagination,
26
36
  PlusIcon: () => PlusIcon,
37
+ ProgressBar: () => ProgressBar,
27
38
  SearchIcon: () => SearchIcon,
39
+ Selection: () => Selection,
40
+ Skeleton: () => Skeleton,
41
+ SkeletonBox: () => SkeletonBox,
42
+ SkeletonCircle: () => SkeletonCircle,
43
+ SkeletonLine: () => SkeletonLine,
44
+ SkeletonSquare: () => SkeletonSquare,
45
+ Snackbar: () => Snackbar,
28
46
  Table: () => Table,
29
47
  TableBody: () => TableBody,
30
48
  TableCaption: () => TableCaption,
@@ -32,7 +50,13 @@ __export(index_exports, {
32
50
  TableHead: () => TableHead,
33
51
  TableHeader: () => TableHeader,
34
52
  TableRow: () => TableRow,
35
- getIconSize: () => getIconSize
53
+ TextArea: () => TextArea,
54
+ TextInput: () => TextInput,
55
+ Toggle: () => Toggle,
56
+ Tooltip: () => Tooltip,
57
+ formatNumber: () => formatNumber,
58
+ getIconSize: () => getIconSize,
59
+ message: () => message
36
60
  });
37
61
  module.exports = __toCommonJS(index_exports);
38
62
 
@@ -46,45 +70,60 @@ var hierarchyFromVariant = {
46
70
  danger: "primary"
47
71
  };
48
72
  var getIconSize = (size) => size === "24" ? 16 : 20;
49
- var Button = (0, import_react.forwardRef)(function Button2({
50
- hierarchy,
51
- variant,
52
- size = "32",
53
- color = "normal",
54
- destructive,
55
- iconOnly = false,
56
- icon,
57
- prefix,
58
- suffix,
59
- content,
60
- loading = false,
61
- alignCenter = false,
62
- children,
63
- className,
64
- disabled,
65
- type = "button",
66
- ...props
67
- }, ref) {
68
- const resolvedHierarchy = hierarchy ?? hierarchyFromVariant[variant ?? "primary"];
69
- const resolvedDestructive = destructive || variant === "danger";
70
- const isDisabled = disabled || loading;
71
- const label = loading ? "Loading\u2026" : children ?? content;
72
- const classes = [
73
- "fe-button",
74
- `fe-button--${resolvedHierarchy}`,
75
- `fe-button--${size}`,
76
- `fe-button--${color}`,
77
- iconOnly && "fe-button--icon-only",
78
- resolvedDestructive && "fe-button--destructive",
79
- alignCenter && "fe-button--align-center",
80
- className
81
- ].filter(Boolean).join(" ");
82
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ref, className: classes, type, disabled: isDisabled, "aria-busy": loading || void 0, ...props, children: iconOnly ? icon : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
83
- prefix && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "fe-button__icon", children: prefix }),
84
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "fe-button__label", children: label }),
85
- suffix && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "fe-button__icon", children: suffix })
86
- ] }) });
87
- });
73
+ var Button = (0, import_react.forwardRef)(
74
+ function Button2({
75
+ hierarchy,
76
+ variant,
77
+ size = "32",
78
+ color = "normal",
79
+ destructive,
80
+ iconOnly = false,
81
+ icon,
82
+ prefix,
83
+ suffix,
84
+ content,
85
+ loading = false,
86
+ alignCenter = false,
87
+ children,
88
+ className,
89
+ disabled,
90
+ type = "button",
91
+ ...props
92
+ }, ref) {
93
+ const resolvedHierarchy = hierarchy ?? hierarchyFromVariant[variant ?? "primary"];
94
+ const resolvedDestructive = destructive || variant === "danger";
95
+ const isDisabled = disabled || loading;
96
+ const label = loading ? "Loading\u2026" : children ?? content;
97
+ const hasLabel = label !== null && label !== void 0 && label !== false && label !== "";
98
+ const resolvedIconOnly = iconOnly || icon !== null && icon !== void 0 && !hasLabel;
99
+ const classes = [
100
+ "fe-button",
101
+ `fe-button--${resolvedHierarchy}`,
102
+ `fe-button--${size}`,
103
+ `fe-button--${color}`,
104
+ resolvedIconOnly && "fe-button--icon-only",
105
+ resolvedDestructive && "fe-button--destructive",
106
+ alignCenter && "fe-button--align-center",
107
+ className
108
+ ].filter(Boolean).join(" ");
109
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
110
+ "button",
111
+ {
112
+ ref,
113
+ className: classes,
114
+ type,
115
+ disabled: isDisabled,
116
+ "aria-busy": loading || void 0,
117
+ ...props,
118
+ children: resolvedIconOnly ? icon : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
119
+ prefix && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "fe-button__icon", children: prefix }),
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "fe-button__label", children: label }),
121
+ suffix && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "fe-button__icon", children: suffix })
122
+ ] })
123
+ }
124
+ );
125
+ }
126
+ );
88
127
  Button.displayName = "Button";
89
128
 
90
129
  // packages/icons/src/index.tsx
@@ -114,9 +153,76 @@ function SearchIcon(props) {
114
153
  }
115
154
 
116
155
  // packages/table/src/index.tsx
156
+ var import_react3 = require("react");
157
+
158
+ // packages/pagination/src/index.tsx
117
159
  var import_react2 = require("react");
118
160
  var import_jsx_runtime3 = require("react/jsx-runtime");
119
- 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 });
120
226
  var MIN_COLUMN_WIDTH = 96;
121
227
  var MAX_COLUMN_WIDTH = 1200;
122
228
  function getColumnCount(headers, data) {
@@ -125,7 +231,7 @@ function getColumnCount(headers, data) {
125
231
  function getText(value) {
126
232
  if (typeof value === "string" || typeof value === "number") return String(value);
127
233
  if (Array.isArray(value)) return value.map(getText).join("");
128
- if ((0, import_react2.isValidElement)(value)) return getText(value.props.children);
234
+ if ((0, import_react3.isValidElement)(value)) return getText(value.props.children);
129
235
  return "";
130
236
  }
131
237
  function autoColumnSize(columnIndex, headers, data) {
@@ -133,9 +239,9 @@ function autoColumnSize(columnIndex, headers, data) {
133
239
  const longestText = values.reduce((longest, value) => Math.max(longest, getText(value).length), 0);
134
240
  return Math.min(MAX_COLUMN_WIDTH, Math.max(MIN_COLUMN_WIDTH, longestText * 8 + 40));
135
241
  }
136
- function getInitialColumnSizes(headers, data, sizes, minColumnWidth = MIN_COLUMN_WIDTH) {
242
+ function getInitialColumnSizes(headers, data, sizes2, minColumnWidth = MIN_COLUMN_WIDTH) {
137
243
  return Array.from({ length: getColumnCount(headers, data) }, (_, columnIndex) => {
138
- const suppliedSize = sizes?.[columnIndex];
244
+ const suppliedSize = sizes2?.[columnIndex];
139
245
  return typeof suppliedSize === "number" && Number.isFinite(suppliedSize) ? Math.min(MAX_COLUMN_WIDTH, Math.max(minColumnWidth, suppliedSize)) : autoColumnSize(columnIndex, headers, data);
140
246
  });
141
247
  }
@@ -150,33 +256,48 @@ function DataTable({
150
256
  columnSizes,
151
257
  onColumnResize,
152
258
  minColumnWidth = MIN_COLUMN_WIDTH,
259
+ pagination,
260
+ pageSize,
261
+ page,
262
+ defaultPage = 1,
263
+ onPageChange,
264
+ paginationPosition = "bottom-right",
153
265
  className,
154
266
  ...props
155
267
  }) {
156
268
  const columnCount = getColumnCount(headers, data);
157
269
  const safeMinWidth = Math.max(1, minColumnWidth);
158
- const generatedSizes = (0, import_react2.useMemo)(
270
+ const generatedSizes = (0, import_react3.useMemo)(
159
271
  () => getInitialColumnSizes(headers, data, defaultColumnSizes, safeMinWidth),
160
272
  [headers, data, defaultColumnSizes, safeMinWidth]
161
273
  );
162
- const [uncontrolledSizes, setUncontrolledSizes] = (0, import_react2.useState)(generatedSizes);
163
- const resizeState = (0, import_react2.useRef)();
164
- const [isResizing, setIsResizing] = (0, import_react2.useState)(false);
165
- const sizes = columnSizes ? getInitialColumnSizes(headers, data, columnSizes, safeMinWidth).map((size) => Math.max(safeMinWidth, size)) : uncontrolledSizes.map((size) => Math.max(safeMinWidth, size));
166
- (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)(() => {
167
285
  if (columnSizes) return;
168
286
  setUncontrolledSizes((currentSizes) => {
169
287
  const nextSizes = generatedSizes.map((generatedSize, index) => currentSizes[index] ?? generatedSize);
170
288
  return sameSizes(currentSizes, nextSizes) ? currentSizes : nextSizes;
171
289
  });
172
290
  }, [columnSizes, generatedSizes]);
173
- 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) => {
174
295
  const clampedSize = Math.min(MAX_COLUMN_WIDTH, Math.max(safeMinWidth, Math.round(nextSize)));
175
- const nextSizes = sizes.map((size, index) => index === columnIndex ? clampedSize : size);
296
+ const nextSizes = sizes2.map((size, index) => index === columnIndex ? clampedSize : size);
176
297
  if (!columnSizes) setUncontrolledSizes(nextSizes);
177
298
  onColumnResize?.(nextSizes, columnIndex);
178
- }, [columnSizes, onColumnResize, safeMinWidth, sizes]);
179
- (0, import_react2.useEffect)(() => {
299
+ }, [columnSizes, onColumnResize, safeMinWidth, sizes2]);
300
+ (0, import_react3.useEffect)(() => {
180
301
  const onPointerMove = (event) => {
181
302
  const activeResize = resizeState.current;
182
303
  if (!activeResize) return;
@@ -195,67 +316,814 @@ function DataTable({
195
316
  window.removeEventListener("pointercancel", stopResizing);
196
317
  };
197
318
  }, [updateColumnSize]);
198
- 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: [
199
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("colgroup", { children: sizes.map((size, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("col", { style: { width: `${size}px` } }, index)) }),
200
- /* @__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: [
201
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-table__header-content", children: headers[columnIndex] ?? `Column ${columnIndex + 1}` }),
202
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
203
- "span",
204
- {
205
- className: "fe-table__resize-handle",
206
- role: "separator",
207
- "aria-label": `Resize ${getText(headers[columnIndex]) || `column ${columnIndex + 1}`}`,
208
- "aria-orientation": "vertical",
209
- "aria-valuemin": safeMinWidth,
210
- "aria-valuemax": MAX_COLUMN_WIDTH,
211
- "aria-valuenow": sizes[columnIndex] ?? safeMinWidth,
212
- tabIndex: 0,
213
- onPointerDown: (event) => {
214
- event.preventDefault();
215
- resizeState.current = { columnIndex, startX: event.clientX, startWidth: sizes[columnIndex] ?? safeMinWidth };
216
- setIsResizing(true);
217
- event.currentTarget.setPointerCapture(event.pointerId);
218
- },
219
- onKeyDown: (event) => {
220
- if (event.key === "ArrowLeft") updateColumnSize(columnIndex, (sizes[columnIndex] ?? safeMinWidth) - 16);
221
- 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
+ }
222
352
  }
223
- }
224
- )
225
- ] }, columnIndex)) }) }),
226
- /* @__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)) })
227
- ] }) });
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
+ ] });
228
359
  }
229
- function Table({ headers, data, striped, children, ...props }) {
230
- if (headers || data) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DataTable, { headers: headers ?? [], data: data ?? [], striped: striped ?? true, ...props });
231
- 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 }) });
232
375
  }
233
376
  function TableHead(props) {
234
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { ...props });
377
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("thead", { ...props });
235
378
  }
236
379
  function TableBody(props) {
237
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { ...props });
380
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { ...props });
238
381
  }
239
382
  function TableRow({ children, ...props }) {
240
- const { striped } = (0, import_react2.useContext)(TableContext);
241
- 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 });
242
385
  }
243
386
  function TableCell({ children, ...props }) {
244
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { ...props, children });
387
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { ...props, children });
245
388
  }
246
389
  function TableHeader({ children, ...props }) {
247
- 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 });
248
391
  }
249
392
  function TableCaption(props) {
250
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("caption", { ...props });
393
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("caption", { ...props });
394
+ }
395
+
396
+ // packages/toggle/src/index.tsx
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) {
400
+ const classes = ["fe-toggle", className].filter(Boolean).join(" ");
401
+ const toggleStyle = {
402
+ "--fe-toggle-color": color,
403
+ ...style
404
+ };
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 })
409
+ ] });
410
+ });
411
+ Toggle.displayName = "Toggle";
412
+
413
+ // packages/drawer/src/index.tsx
414
+ var import_react5 = require("react");
415
+ var import_react_dom = require("react-dom");
416
+ var import_jsx_runtime6 = require("react/jsx-runtime");
417
+ function Drawer({ open, onClose, title, footer, side = "right", width = 400, fullScreen = false, children, closeOnBackdropClick = true, className, style, ...props }) {
418
+ (0, import_react5.useEffect)(() => {
419
+ if (!open) return;
420
+ const onKeyDown = (event) => {
421
+ if (event.key === "Escape") onClose();
422
+ };
423
+ document.addEventListener("keydown", onKeyDown);
424
+ return () => document.removeEventListener("keydown", onKeyDown);
425
+ }, [onClose, open]);
426
+ if (!open || typeof document === "undefined") return null;
427
+ const panelStyle = !fullScreen && (side === "left" || side === "right") ? { ...style, width: typeof width === "number" ? `${width}px` : width } : style;
428
+ return (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fe-drawer-backdrop", role: "presentation", onMouseDown: (event) => {
429
+ if (event.target === event.currentTarget && closeOnBackdropClick) onClose();
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" })
434
+ ] }),
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 })
437
+ ] }) }), document.body);
438
+ }
439
+
440
+ // packages/form-controls/src/index.tsx
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)();
517
+ const listboxId = id ?? generatedId;
518
+ const isControlled = value !== void 0;
519
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue ?? (multiple ? [] : ""));
520
+ const selectedValue = isControlled ? value : internalValue;
521
+ const selectedValues = Array.isArray(selectedValue) ? selectedValue : selectedValue === "" || selectedValue === null || selectedValue === void 0 ? [] : [selectedValue];
522
+ const selectedOptions = options.filter((option) => selectedValues.some((item) => String(item) === String(option.value)));
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)(() => {
528
+ const close = (event) => {
529
+ if (!rootRef.current?.contains(event.target)) setOpen(false);
530
+ };
531
+ document.addEventListener("mousedown", close);
532
+ return () => document.removeEventListener("mousedown", close);
533
+ }, []);
534
+ const filteredOptions = options.filter((option) => !query || (option.searchText ?? (typeof option.label === "string" ? option.label : String(option.value))).toLowerCase().includes(query.toLowerCase()));
535
+ const commit = (next) => {
536
+ if (!isControlled) setInternalValue(next);
537
+ const selected = options.filter((option) => (Array.isArray(next) ? next : [next]).some((item) => String(item) === String(option.value)));
538
+ onChange?.(next, multiple ? selected : selected[0]);
539
+ };
540
+ const choose = (option) => {
541
+ if (option.disabled) return;
542
+ const isSelected = selectedValues.some((item) => String(item) === String(option.value));
543
+ if (multiple) commit(isSelected ? selectedValues.filter((item) => String(item) !== String(option.value)) : [...selectedValues, option.value]);
544
+ else {
545
+ commit(option.value);
546
+ setOpen(false);
547
+ setQuery("");
548
+ }
549
+ };
550
+ const clear = () => {
551
+ commit(multiple ? [] : "");
552
+ setQuery("");
553
+ };
554
+ const displayLabel = selectedOptions.length ? selectedOptions.map((option) => option.label) : null;
555
+ const onTriggerKeyDown = (event) => {
556
+ onKeyDown?.(event);
557
+ if (disabled) return;
558
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
559
+ event.preventDefault();
560
+ setOpen(true);
561
+ setActiveIndex((current) => Math.max(0, Math.min(filteredOptions.length - 1, current + (event.key === "ArrowDown" ? 1 : -1))));
562
+ }
563
+ if (event.key === "Enter" || event.key === " ") {
564
+ event.preventDefault();
565
+ if (!open) setOpen(true);
566
+ else if (filteredOptions[activeIndex]) choose(filteredOptions[activeIndex]);
567
+ }
568
+ if (event.key === "Escape") setOpen(false);
569
+ };
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: [
576
+ option.label,
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: [
579
+ item,
580
+ index < displayLabel.length - 1 ? ", " : ""
581
+ ] }, index)) : placeholder }),
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" }) })
583
+ ] }),
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) => {
589
+ setQuery(event.target.value);
590
+ setActiveIndex(0);
591
+ }, placeholder: "Search\u2026", "aria-label": "Search options" })
592
+ ] }),
593
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "fe-selection__options", children: filteredOptions.length ? filteredOptions.map((option, index) => {
594
+ const isSelected = selectedValues.some((item) => String(item) === String(option.value));
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" })
598
+ ] }, String(option.value));
599
+ }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "fe-selection__empty", children: "No matching options" }) })
600
+ ] })
601
+ ] }),
602
+ helperText && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fe-selection__helper", children: helperText })
603
+ ] });
604
+ });
605
+ Selection.displayName = "Selection";
606
+ function formatNumber(value, decimals = 2, thousandSeparator = ",", decimalSeparator = ".") {
607
+ return new Intl.NumberFormat("en-US", {
608
+ minimumFractionDigits: 0,
609
+ maximumFractionDigits: decimals,
610
+ useGrouping: true
611
+ }).format(value).replace(/,/g, thousandSeparator).replace(/\./g, decimalSeparator);
612
+ }
613
+ function escapeRegExp(value) {
614
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
615
+ }
616
+ function groupInteger(value, separator) {
617
+ return value.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
618
+ }
619
+ function normalizeNumberInput(raw, options) {
620
+ const { allowDecimal, decimalSeparator, thousandSeparator, min, max } = options;
621
+ const decimals = Math.max(0, options.decimals);
622
+ const negative = raw.trimStart().startsWith("-") && (min === void 0 || min < 0);
623
+ const decimalExpression = new RegExp(`[^0-9${allowDecimal ? escapeRegExp(decimalSeparator) : ""}]`, "g");
624
+ const cleaned = raw.replace(decimalExpression, "");
625
+ const decimalIndex = allowDecimal ? cleaned.indexOf(decimalSeparator) : -1;
626
+ const hasDecimal = decimalIndex >= 0;
627
+ const rawInteger = hasDecimal ? cleaned.slice(0, decimalIndex) : cleaned;
628
+ const rawFraction = hasDecimal ? cleaned.slice(decimalIndex + decimalSeparator.length).replace(new RegExp(escapeRegExp(decimalSeparator), "g"), "") : "";
629
+ const integer = rawInteger.replace(/^0+(?=\d)/, "") || (rawInteger ? "0" : "");
630
+ const fraction = rawFraction.slice(0, decimals);
631
+ if (!integer && !fraction) return { display: negative ? "-" : "", value: null };
632
+ const numericValue = Number(`${negative ? "-" : ""}${integer || "0"}${hasDecimal ? `.${fraction || "0"}` : ""}`);
633
+ if (!Number.isFinite(numericValue)) return { display: "", value: null };
634
+ const lowerBound = min === void 0 ? numericValue : Math.max(numericValue, min);
635
+ const value = max === void 0 ? lowerBound : Math.min(lowerBound, max);
636
+ if (value !== numericValue) return { display: formatNumber(value, decimals, thousandSeparator, decimalSeparator), value };
637
+ return { display: `${negative ? "-" : ""}${groupInteger(integer || "0", thousandSeparator)}${hasDecimal ? `${decimalSeparator}${fraction}` : ""}`, value };
638
+ }
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`;
642
+ const isControlled = value !== void 0;
643
+ const [internalValue, setInternalValue] = (0, import_react6.useState)(defaultValue);
644
+ const [focused, setFocused] = (0, import_react6.useState)(false);
645
+ const actualValue = isControlled ? value ?? null : internalValue;
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)(() => {
652
+ if (!focused) setText(actualValue === null ? "" : formatNumber(actualValue, decimals, thousandSeparator, decimalSeparator));
653
+ }, [actualValue, decimalSeparator, decimals, focused, thousandSeparator]);
654
+ const updateValue = (nextText) => {
655
+ const next = normalizeNumberInput(nextText, { allowDecimal, decimals, decimalSeparator, thousandSeparator, min, max });
656
+ setText(next.display);
657
+ const nextValue = next.value;
658
+ if (!isControlled) setInternalValue(nextValue);
659
+ onValueChange?.(nextValue);
660
+ };
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) => {
664
+ setFocused(true);
665
+ onFocus?.(event);
666
+ }, onChange: (event) => updateValue(event.target.value), onBlur: (event) => {
667
+ setFocused(false);
668
+ onBlur?.(event);
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 })
671
+ ] });
672
+ });
673
+ NumberInput.displayName = "NumberInput";
674
+ function CurrencyInput({ suffix = "VND", currencies, currency, defaultCurrency, onCurrencyChange, className, ...props }) {
675
+ const [internalCurrency, setInternalCurrency] = (0, import_react6.useState)(defaultCurrency ?? currencies?.[0]?.code ?? "");
676
+ const selectedCurrency = currency ?? internalCurrency;
677
+ const current = currencies?.find((item) => item.code === selectedCurrency);
678
+ const currencySuffix = current?.suffix ?? current?.code ?? suffix;
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) => {
682
+ const next = currencies.find((item) => item.code === event.target.value);
683
+ if (!next) return;
684
+ if (currency === void 0) setInternalCurrency(next.code);
685
+ onCurrencyChange?.(next);
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 })
687
+ ] });
688
+ }
689
+ var pad = (value) => String(value).padStart(2, "0");
690
+ function formatDate(date, format) {
691
+ return format.replace(/yyyy/g, String(date.getFullYear())).replace(/MM/g, pad(date.getMonth() + 1)).replace(/dd/g, pad(date.getDate())).replace(/HH/g, pad(date.getHours())).replace(/mm/g, pad(date.getMinutes()));
692
+ }
693
+ function startOfDay(date) {
694
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
695
+ }
696
+ function sameDay(first, second) {
697
+ return !!first && !!second && startOfDay(first).getTime() === startOfDay(second).getTime();
698
+ }
699
+ function mergeDateTime(day, time) {
700
+ const [hours, minutes] = time.split(":").map(Number);
701
+ const next = new Date(day);
702
+ next.setHours(hours || 0, minutes || 0, 0, 0);
703
+ return next;
704
+ }
705
+ function DateTimePicker(props) {
706
+ const { label, format = "dd/MM/yyyy HH:mm", disabled, placeholder, locale = "en-US", className, mode = "single" } = props;
707
+ const isRange = mode === "range";
708
+ const controlled = props.value !== void 0;
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]);
711
+ const singleValue = !isRange ? props.value ?? singleInternal : null;
712
+ const rangeValue = isRange ? props.value ?? rangeInternal : [null, null];
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)(() => {
718
+ const onPointerDown = (event) => {
719
+ if (!rootRef.current?.contains(event.target)) setOpen(false);
720
+ };
721
+ document.addEventListener("mousedown", onPointerDown);
722
+ return () => document.removeEventListener("mousedown", onPointerDown);
723
+ }, []);
724
+ const setSingle = (next) => {
725
+ if (!controlled) setSingleInternal(next);
726
+ props.onChange?.(next);
727
+ };
728
+ const setRange = (next) => {
729
+ if (!controlled) setRangeInternal(next);
730
+ props.onChange?.(next);
731
+ };
732
+ const selectDay = (day) => {
733
+ if (!isRange) {
734
+ setSingle(mergeDateTime(day, singleValue ? `${pad(singleValue.getHours())}:${pad(singleValue.getMinutes())}` : "00:00"));
735
+ setOpen(false);
736
+ return;
737
+ }
738
+ const [start, end] = rangeValue;
739
+ if (!start || end || editingEnd) {
740
+ setRange([mergeDateTime(day, start ? `${pad(start.getHours())}:${pad(start.getMinutes())}` : "00:00"), null]);
741
+ setEditingEnd(false);
742
+ return;
743
+ }
744
+ const nextEnd = mergeDateTime(day, "00:00");
745
+ setRange(nextEnd < start ? [nextEnd, start] : [start, nextEnd]);
746
+ setEditingEnd(false);
747
+ };
748
+ const shownValue = isRange ? [rangeValue[0] && formatDate(rangeValue[0], format), rangeValue[1] && formatDate(rangeValue[1], format)].filter(Boolean).join(" \u2014 ") : singleValue && formatDate(singleValue, format);
749
+ const firstDay = new Date(month.getFullYear(), month.getMonth(), 1);
750
+ const offset = (firstDay.getDay() + 6) % 7;
751
+ const days = Array.from({ length: 42 }, (_, index) => new Date(month.getFullYear(), month.getMonth(), index - offset + 1));
752
+ const selectedForTime = isRange ? editingEnd ? rangeValue[1] : rangeValue[0] : singleValue;
753
+ const setTime = (time) => {
754
+ if (!selectedForTime) return;
755
+ const next = mergeDateTime(selectedForTime, time);
756
+ if (isRange) setRange(editingEnd ? [rangeValue[0], next] : [next, rangeValue[1]]);
757
+ else setSingle(next);
758
+ };
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") })
764
+ ] }),
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" })
770
+ ] }),
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) => {
773
+ const inMonth = day.getMonth() === month.getMonth();
774
+ const selected = isRange ? sameDay(day, rangeValue[0]) || sameDay(day, rangeValue[1]) : sameDay(day, singleValue);
775
+ const inRange = isRange && rangeValue[0] && rangeValue[1] && day > startOfDay(rangeValue[0]) && day < startOfDay(rangeValue[1]);
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());
777
+ }) }),
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" })
781
+ ] }),
782
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "fe-datetime-picker__time", children: [
783
+ "Time",
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 })
785
+ ] })
786
+ ] })
787
+ ] });
788
+ }
789
+
790
+ // packages/tooltip/src/index.tsx
791
+ var import_react7 = require("react");
792
+ var import_jsx_runtime8 = require("react/jsx-runtime");
793
+ function Tooltip({ content, children, placement = "top", disabled, className, ...props }) {
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) => {
797
+ children.props.onMouseEnter?.(event);
798
+ setVisible(true);
799
+ }, onMouseLeave: (event) => {
800
+ children.props.onMouseLeave?.(event);
801
+ setVisible(false);
802
+ }, onFocus: (event) => {
803
+ children.props.onFocus?.(event);
804
+ setVisible(true);
805
+ }, onBlur: (event) => {
806
+ children.props.onBlur?.(event);
807
+ setVisible(false);
808
+ } });
809
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: ["fe-tooltip", `fe-tooltip--${placement}`, className].filter(Boolean).join(" "), ...props, children: [
810
+ trigger,
811
+ visible && !disabled && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { id, role: "tooltip", className: "fe-tooltip__content", children: content })
812
+ ] });
813
+ }
814
+
815
+ // packages/expandable-section/src/index.tsx
816
+ var import_react8 = require("react");
817
+ var import_jsx_runtime9 = require("react/jsx-runtime");
818
+ function ExpandableSection({ title, children, expanded, defaultExpanded = false, onExpandedChange, disabled, className, ...props }) {
819
+ const [internalExpanded, setInternalExpanded] = (0, import_react8.useState)(defaultExpanded);
820
+ const isExpanded = expanded ?? internalExpanded;
821
+ const id = (0, import_react8.useId)();
822
+ const toggle = () => {
823
+ if (disabled) return;
824
+ const next = !isExpanded;
825
+ if (expanded === void 0) setInternalExpanded(next);
826
+ onExpandedChange?.(next);
827
+ };
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 })
981
+ ] }),
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" }) })
983
+ ] });
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
+ ] }) });
251
1101
  }
252
1102
  // Annotate the CommonJS export names for ESM import in node:
253
1103
  0 && (module.exports = {
254
1104
  Button,
255
1105
  ChevronDownIcon,
1106
+ Chip,
256
1107
  CloseIcon,
1108
+ CurrencyInput,
1109
+ DateTimePicker,
1110
+ Drawer,
1111
+ ExpandableSection,
1112
+ Loading,
1113
+ Message,
1114
+ Modal,
1115
+ NumberInput,
1116
+ Pagination,
257
1117
  PlusIcon,
1118
+ ProgressBar,
258
1119
  SearchIcon,
1120
+ Selection,
1121
+ Skeleton,
1122
+ SkeletonBox,
1123
+ SkeletonCircle,
1124
+ SkeletonLine,
1125
+ SkeletonSquare,
1126
+ Snackbar,
259
1127
  Table,
260
1128
  TableBody,
261
1129
  TableCaption,
@@ -263,5 +1131,11 @@ function TableCaption(props) {
263
1131
  TableHead,
264
1132
  TableHeader,
265
1133
  TableRow,
266
- getIconSize
1134
+ TextArea,
1135
+ TextInput,
1136
+ Toggle,
1137
+ Tooltip,
1138
+ formatNumber,
1139
+ getIconSize,
1140
+ message
267
1141
  });