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