@dreamtree-org/twreact-ui 1.1.73 → 1.2.0
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/README.md +4 -1
- package/dist/components/core/Table/Table.d.ts +2 -53
- package/dist/components/core/Table/Table.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +138 -60
- package/dist/index.js +138 -60
- package/mcp/catalog.snapshot.json +9 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5729,7 +5729,7 @@ function FieldInput(_ref) {
|
|
|
5729
5729
|
});
|
|
5730
5730
|
}
|
|
5731
5731
|
|
|
5732
|
-
var _excluded$x = ["data", "columns", "sortable", "filterable", "selectable", "pagination", "pageSize", "onSort", "onFilter", "onFetch", "onFilterChange", "onSelectionChange", "onRowClick", "hasDetails", "DetailsComponent", "className", "withAction", "onAction", "actions", "replaceDefaultActions", "showSerial", "cellClass", "rowClass", "globalSearch", "limitOptions", "showLimitSelector", "onLimitChange", "showReloadButton", "renderReloadButton", "onReload", "stripedRows", "theme", "responsiveBreakpoint", "serverSide", "totalRecords", "pageNumber", "onPageChange", "TheadComponent", "TfootComponent", "interactive", "onSave", "onEdit", "onRowAdd", "onRowDelete"],
|
|
5732
|
+
var _excluded$x = ["data", "columns", "sortable", "filterable", "selectable", "pagination", "pageSize", "onSort", "onFilter", "onFetch", "onFilterChange", "onSelectionChange", "onRowClick", "hasDetails", "DetailsComponent", "className", "withAction", "onAction", "actions", "replaceDefaultActions", "showSerial", "cellClass", "rowClass", "globalSearch", "limitOptions", "showLimitSelector", "onLimitChange", "showReloadButton", "renderReloadButton", "onReload", "stripedRows", "theme", "responsiveBreakpoint", "serverSide", "totalRecords", "pageNumber", "onPageChange", "TheadComponent", "TfootComponent", "interactive", "onSave", "onEdit", "onRowAdd", "onRowDelete", "editKeyBinding", "newRowPosition", "addRowRef", "onInitAddRow", "renderAddRowButton", "renderExternalAddRow"],
|
|
5733
5733
|
_excluded2$4 = ["_key", "_isNew"];
|
|
5734
5734
|
function ownKeys$B(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5735
5735
|
function _objectSpread$B(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$B(Object(t), true).forEach(function (r) { _defineProperty$4(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$B(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -5742,7 +5742,7 @@ var DEFAULT_THEME = {
|
|
|
5742
5742
|
menuBg: "bg-white",
|
|
5743
5743
|
menuItemHover: "hover:bg-gray-50"
|
|
5744
5744
|
};
|
|
5745
|
-
var Table = function Table(_ref) {
|
|
5745
|
+
var Table = /*#__PURE__*/React.forwardRef(function Table(_ref, ref) {
|
|
5746
5746
|
var _validationErrors$;
|
|
5747
5747
|
var _ref$data = _ref.data,
|
|
5748
5748
|
initialData = _ref$data === void 0 ? DEFAULT_DATA : _ref$data,
|
|
@@ -5812,6 +5812,14 @@ var Table = function Table(_ref) {
|
|
|
5812
5812
|
onEdit = _ref.onEdit,
|
|
5813
5813
|
onRowAdd = _ref.onRowAdd,
|
|
5814
5814
|
onRowDelete = _ref.onRowDelete,
|
|
5815
|
+
_ref$editKeyBinding = _ref.editKeyBinding,
|
|
5816
|
+
editKeyBinding = _ref$editKeyBinding === void 0 ? "e" : _ref$editKeyBinding,
|
|
5817
|
+
_ref$newRowPosition = _ref.newRowPosition,
|
|
5818
|
+
newRowPosition = _ref$newRowPosition === void 0 ? "bottom" : _ref$newRowPosition,
|
|
5819
|
+
addRowRef = _ref.addRowRef,
|
|
5820
|
+
onInitAddRow = _ref.onInitAddRow,
|
|
5821
|
+
renderAddRowButton = _ref.renderAddRowButton,
|
|
5822
|
+
renderExternalAddRow = _ref.renderExternalAddRow,
|
|
5815
5823
|
props = _objectWithoutProperties$1(_ref, _excluded$x);
|
|
5816
5824
|
theme = _objectSpread$B(_objectSpread$B({}, DEFAULT_THEME), theme);
|
|
5817
5825
|
// State
|
|
@@ -6089,9 +6097,10 @@ var Table = function Table(_ref) {
|
|
|
6089
6097
|
return _objectSpread$B(_objectSpread$B({}, prev), {}, _defineProperty$4({}, rowKey, nextRowEdits));
|
|
6090
6098
|
});
|
|
6091
6099
|
}, [newRows, tableData, onEdit]);
|
|
6092
|
-
var
|
|
6100
|
+
var _handleAddRow = React.useCallback(function (positionOverride) {
|
|
6093
6101
|
var _columnsState$find, _columnsState$;
|
|
6094
|
-
var
|
|
6102
|
+
var targetPos = typeof positionOverride === "string" && (positionOverride === "top" || positionOverride === "bottom") ? positionOverride : newRowPosition || "bottom";
|
|
6103
|
+
var newKey = "new-".concat(Date.now(), "-").concat(Math.random().toString(36).substring(2, 6));
|
|
6095
6104
|
var newRowObj = {
|
|
6096
6105
|
_key: newKey,
|
|
6097
6106
|
_isNew: true
|
|
@@ -6101,23 +6110,51 @@ var Table = function Table(_ref) {
|
|
|
6101
6110
|
newRowObj[col.key] = (_col$defaultValue = col.defaultValue) !== null && _col$defaultValue !== void 0 ? _col$defaultValue : "";
|
|
6102
6111
|
});
|
|
6103
6112
|
setNewRows(function (prev) {
|
|
6113
|
+
if (targetPos === "top") {
|
|
6114
|
+
return [newRowObj].concat(_toConsumableArray$1(prev));
|
|
6115
|
+
}
|
|
6104
6116
|
return [].concat(_toConsumableArray$1(prev), [newRowObj]);
|
|
6105
6117
|
});
|
|
6106
6118
|
var firstColKey = ((_columnsState$find = columnsState.find(function (c) {
|
|
6107
6119
|
return c.isVisible !== false;
|
|
6108
6120
|
})) === null || _columnsState$find === void 0 ? void 0 : _columnsState$find.key) || ((_columnsState$ = columnsState[0]) === null || _columnsState$ === void 0 ? void 0 : _columnsState$.key);
|
|
6121
|
+
var initialRowIndex = targetPos === "top" ? 0 : tableData.length + newRows.length;
|
|
6109
6122
|
setActiveCell({
|
|
6110
6123
|
rowKey: newKey,
|
|
6111
6124
|
colKey: firstColKey,
|
|
6112
|
-
rowIndex:
|
|
6125
|
+
rowIndex: initialRowIndex
|
|
6113
6126
|
});
|
|
6114
6127
|
setEditingCell({
|
|
6115
6128
|
rowKey: newKey,
|
|
6116
6129
|
colKey: firstColKey,
|
|
6117
|
-
rowIndex:
|
|
6130
|
+
rowIndex: initialRowIndex
|
|
6131
|
+
});
|
|
6132
|
+
onRowAdd === null || onRowAdd === void 0 || onRowAdd(newRowObj, targetPos);
|
|
6133
|
+
}, [columnsState, tableData.length, newRows.length, onRowAdd, newRowPosition]);
|
|
6134
|
+
React.useImperativeHandle(ref, function () {
|
|
6135
|
+
return {
|
|
6136
|
+
addRow: function addRow(pos) {
|
|
6137
|
+
return _handleAddRow(pos);
|
|
6138
|
+
},
|
|
6139
|
+
addNewRow: function addNewRow(pos) {
|
|
6140
|
+
return _handleAddRow(pos);
|
|
6141
|
+
},
|
|
6142
|
+
handleAddRow: function handleAddRow(pos) {
|
|
6143
|
+
return _handleAddRow(pos);
|
|
6144
|
+
},
|
|
6145
|
+
tableContainerRef: tableContainerRef.current
|
|
6146
|
+
};
|
|
6147
|
+
}, [_handleAddRow]);
|
|
6148
|
+
React.useEffect(function () {
|
|
6149
|
+
if (addRowRef) {
|
|
6150
|
+
addRowRef.current = function (pos) {
|
|
6151
|
+
return _handleAddRow(pos);
|
|
6152
|
+
};
|
|
6153
|
+
}
|
|
6154
|
+
onInitAddRow === null || onInitAddRow === void 0 || onInitAddRow(function (pos) {
|
|
6155
|
+
return _handleAddRow(pos);
|
|
6118
6156
|
});
|
|
6119
|
-
|
|
6120
|
-
}, [columnsState, tableData.length, newRows.length, onRowAdd]);
|
|
6157
|
+
}, [addRowRef, onInitAddRow, _handleAddRow]);
|
|
6121
6158
|
var handleDeleteNewRow = React.useCallback(function (rowKey) {
|
|
6122
6159
|
setNewRows(function (prev) {
|
|
6123
6160
|
return prev.filter(function (r) {
|
|
@@ -6381,6 +6418,15 @@ var Table = function Table(_ref) {
|
|
|
6381
6418
|
};
|
|
6382
6419
|
});
|
|
6383
6420
|
}, [limitOptions]);
|
|
6421
|
+
var isEditKey = React.useCallback(function (key, e) {
|
|
6422
|
+
if (typeof editKeyBinding === "function") {
|
|
6423
|
+
return editKeyBinding(e);
|
|
6424
|
+
}
|
|
6425
|
+
if (typeof editKeyBinding === "string" && editKeyBinding.trim() !== "") {
|
|
6426
|
+
return key.toLowerCase() === editKeyBinding.toLowerCase();
|
|
6427
|
+
}
|
|
6428
|
+
return key.toLowerCase() === "e" || key === "Insert";
|
|
6429
|
+
}, [editKeyBinding]);
|
|
6384
6430
|
var handleTableKeyDown = React.useCallback(function (e) {
|
|
6385
6431
|
if (!interactive) return;
|
|
6386
6432
|
var key = e.key,
|
|
@@ -6393,24 +6439,33 @@ var Table = function Table(_ref) {
|
|
|
6393
6439
|
}
|
|
6394
6440
|
return;
|
|
6395
6441
|
}
|
|
6396
|
-
// Enter: Save changes
|
|
6397
|
-
if (key === "Enter") {
|
|
6442
|
+
// Enter: Save changes when there are modified changes (isDirty)
|
|
6443
|
+
if (key === "Enter" && isDirty) {
|
|
6398
6444
|
var _e$target;
|
|
6399
6445
|
if (((_e$target = e.target) === null || _e$target === void 0 ? void 0 : _e$target.tagName) === "TEXTAREA" && shiftKey) {
|
|
6400
6446
|
return;
|
|
6401
6447
|
}
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
handleSave();
|
|
6407
|
-
}
|
|
6448
|
+
e.preventDefault();
|
|
6449
|
+
setEditingCell(null);
|
|
6450
|
+
if (isValid && !isSaving) {
|
|
6451
|
+
handleSave();
|
|
6408
6452
|
}
|
|
6409
6453
|
return;
|
|
6410
6454
|
}
|
|
6455
|
+
// Standard Edit Key ('e' or custom editKeyBinding) when focused on active cell and not editing: enter edit mode
|
|
6456
|
+
if (isEditKey(key, e) && activeCell && !editingCell) {
|
|
6457
|
+
var col = visibleColumns.find(function (c) {
|
|
6458
|
+
return c.key === activeCell.colKey;
|
|
6459
|
+
});
|
|
6460
|
+
if (col && col.editable !== false) {
|
|
6461
|
+
e.preventDefault();
|
|
6462
|
+
setEditingCell(activeCell);
|
|
6463
|
+
return;
|
|
6464
|
+
}
|
|
6465
|
+
}
|
|
6411
6466
|
var currentCell = activeCell || editingCell;
|
|
6412
6467
|
if (!currentCell) return;
|
|
6413
|
-
var allRows = [].concat(_toConsumableArray$1(paginatedData), _toConsumableArray$1(newRows));
|
|
6468
|
+
var allRows = newRowPosition === "top" ? [].concat(_toConsumableArray$1(newRows), _toConsumableArray$1(paginatedData)) : [].concat(_toConsumableArray$1(paginatedData), _toConsumableArray$1(newRows));
|
|
6414
6469
|
if (allRows.length === 0 || visibleColumns.length === 0) return;
|
|
6415
6470
|
var rIdx = allRows.findIndex(function (r, idx) {
|
|
6416
6471
|
var rKey = r._key || getRowKey(r, startIndex + idx);
|
|
@@ -6729,6 +6784,26 @@ var Table = function Table(_ref) {
|
|
|
6729
6784
|
invalidCellSet: invalidCellSet,
|
|
6730
6785
|
pendingEdits: pendingEdits
|
|
6731
6786
|
};
|
|
6787
|
+
var renderAddRowClickableRow = React.useCallback(function () {
|
|
6788
|
+
return jsxRuntime.jsx("tr", {
|
|
6789
|
+
onClick: function onClick() {
|
|
6790
|
+
return _handleAddRow(newRowPosition);
|
|
6791
|
+
},
|
|
6792
|
+
className: "bg-primary-50/40 hover:bg-primary-100/70 active:bg-primary-100 transition-colors border-t-2 border-b-2 border-dashed border-primary-300 cursor-pointer group",
|
|
6793
|
+
children: jsxRuntime.jsx("td", {
|
|
6794
|
+
colSpan: visibleCount,
|
|
6795
|
+
className: "px-6 py-3.5 text-left text-sm font-semibold text-primary-700 group-hover:text-primary-800",
|
|
6796
|
+
children: jsxRuntime.jsxs("div", {
|
|
6797
|
+
className: "flex items-center justify-start gap-2",
|
|
6798
|
+
children: [jsxRuntime.jsx(Plus, {
|
|
6799
|
+
className: "w-4 h-4 text-primary-600 group-hover:scale-110 transition-transform"
|
|
6800
|
+
}), jsxRuntime.jsx("span", {
|
|
6801
|
+
children: "Click here to add new record instantly...."
|
|
6802
|
+
})]
|
|
6803
|
+
})
|
|
6804
|
+
})
|
|
6805
|
+
});
|
|
6806
|
+
}, [_handleAddRow, newRowPosition, visibleCount]);
|
|
6732
6807
|
return jsxRuntime.jsxs("div", {
|
|
6733
6808
|
ref: tableContainerRef,
|
|
6734
6809
|
tabIndex: interactive ? 0 : undefined,
|
|
@@ -6773,7 +6848,14 @@ var Table = function Table(_ref) {
|
|
|
6773
6848
|
})]
|
|
6774
6849
|
}), jsxRuntime.jsxs("div", {
|
|
6775
6850
|
className: "flex flex-col sm:flex-row items-start sm:items-center gap-3 w-full md:w-auto",
|
|
6776
|
-
children: [interactive &&
|
|
6851
|
+
children: [interactive && (renderAddRowButton || renderExternalAddRow) && jsxRuntime.jsx("div", {
|
|
6852
|
+
className: "flex items-center gap-2",
|
|
6853
|
+
children: typeof (renderAddRowButton || renderExternalAddRow) === "function" ? (renderAddRowButton || renderExternalAddRow)({
|
|
6854
|
+
addRow: _handleAddRow,
|
|
6855
|
+
handleAddRow: _handleAddRow,
|
|
6856
|
+
newRowPosition: newRowPosition
|
|
6857
|
+
}) : renderAddRowButton || renderExternalAddRow
|
|
6858
|
+
}), interactive && isDirty && jsxRuntime.jsxs("div", {
|
|
6777
6859
|
className: "flex items-center gap-2 flex-wrap",
|
|
6778
6860
|
children: [jsxRuntime.jsxs("button", {
|
|
6779
6861
|
type: "button",
|
|
@@ -6969,9 +7051,9 @@ var Table = function Table(_ref) {
|
|
|
6969
7051
|
className: "pb-4"
|
|
6970
7052
|
})]
|
|
6971
7053
|
})]
|
|
6972
|
-
}), jsxRuntime.
|
|
7054
|
+
}), jsxRuntime.jsx("tbody", {
|
|
6973
7055
|
className: "divide-y divide-gray-200",
|
|
6974
|
-
children:
|
|
7056
|
+
children: loading ? jsxRuntime.jsx("tr", {
|
|
6975
7057
|
children: jsxRuntime.jsx("td", {
|
|
6976
7058
|
colSpan: visibleCount,
|
|
6977
7059
|
className: "px-6 py-8 text-center",
|
|
@@ -6985,32 +7067,43 @@ var Table = function Table(_ref) {
|
|
|
6985
7067
|
})]
|
|
6986
7068
|
})
|
|
6987
7069
|
})
|
|
6988
|
-
}) : paginatedData.length === 0 && newRows.length === 0 ? jsxRuntime.
|
|
6989
|
-
children: jsxRuntime.jsx("
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7070
|
+
}) : paginatedData.length === 0 && newRows.length === 0 ? jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
7071
|
+
children: [interactive && newRowPosition === "top" && renderAddRowClickableRow(), jsxRuntime.jsx("tr", {
|
|
7072
|
+
children: jsxRuntime.jsx("td", {
|
|
7073
|
+
colSpan: visibleCount,
|
|
7074
|
+
className: "px-6 py-8 text-center",
|
|
7075
|
+
children: jsxRuntime.jsxs("div", {
|
|
7076
|
+
className: "flex flex-col items-center gap-2",
|
|
7077
|
+
children: [jsxRuntime.jsx("div", {
|
|
7078
|
+
className: "text-primary-400",
|
|
7079
|
+
children: jsxRuntime.jsx(List, {
|
|
7080
|
+
className: "h-8 w-8"
|
|
7081
|
+
})
|
|
7082
|
+
}), jsxRuntime.jsx("span", {
|
|
7083
|
+
className: "text-sm text-primary-500",
|
|
7084
|
+
children: "No results found."
|
|
7085
|
+
})]
|
|
7086
|
+
})
|
|
7003
7087
|
})
|
|
7004
|
-
})
|
|
7088
|
+
}), interactive && newRowPosition !== "top" && renderAddRowClickableRow()]
|
|
7005
7089
|
}) : jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
7006
|
-
children: [
|
|
7090
|
+
children: [interactive && newRowPosition === "top" && renderAddRowClickableRow(), interactive && newRowPosition === "top" && newRows.map(function (newRow, newIdx) {
|
|
7091
|
+
return jsxRuntime.jsx(TableRow, _objectSpread$B({
|
|
7092
|
+
row: newRow,
|
|
7093
|
+
rowIndexInPage: newIdx,
|
|
7094
|
+
startIndex: 0,
|
|
7095
|
+
visibleCount: visibleCount,
|
|
7096
|
+
isNewRow: true,
|
|
7097
|
+
onDeleteNewRow: handleDeleteNewRow
|
|
7098
|
+
}, rowSharedProps), newRow._key);
|
|
7099
|
+
}), paginatedData.map(function (row, rowIndexInPage) {
|
|
7007
7100
|
return jsxRuntime.jsx(TableRow, _objectSpread$B({
|
|
7008
7101
|
row: row,
|
|
7009
7102
|
rowIndexInPage: rowIndexInPage,
|
|
7010
7103
|
startIndex: startIndex,
|
|
7011
7104
|
visibleCount: visibleCount
|
|
7012
7105
|
}, rowSharedProps), getRowKey(row, startIndex + rowIndexInPage));
|
|
7013
|
-
}), interactive && newRows.map(function (newRow, newIdx) {
|
|
7106
|
+
}), interactive && newRowPosition !== "top" && newRows.map(function (newRow, newIdx) {
|
|
7014
7107
|
return jsxRuntime.jsx(TableRow, _objectSpread$B({
|
|
7015
7108
|
row: newRow,
|
|
7016
7109
|
rowIndexInPage: paginatedData.length + newIdx,
|
|
@@ -7019,23 +7112,8 @@ var Table = function Table(_ref) {
|
|
|
7019
7112
|
isNewRow: true,
|
|
7020
7113
|
onDeleteNewRow: handleDeleteNewRow
|
|
7021
7114
|
}, rowSharedProps), newRow._key);
|
|
7022
|
-
})]
|
|
7023
|
-
})
|
|
7024
|
-
onClick: handleAddRow,
|
|
7025
|
-
className: "bg-primary-50/40 hover:bg-primary-100/70 active:bg-primary-100 transition-colors border-t-2 border-dashed border-primary-300 cursor-pointer group",
|
|
7026
|
-
children: jsxRuntime.jsx("td", {
|
|
7027
|
-
colSpan: visibleCount,
|
|
7028
|
-
className: "px-6 py-3.5 text-left text-sm font-semibold text-primary-700 group-hover:text-primary-800",
|
|
7029
|
-
children: jsxRuntime.jsxs("div", {
|
|
7030
|
-
className: "flex items-center justify-start gap-2",
|
|
7031
|
-
children: [jsxRuntime.jsx(Plus, {
|
|
7032
|
-
className: "w-4 h-4 text-primary-600 group-hover:scale-110 transition-transform"
|
|
7033
|
-
}), jsxRuntime.jsx("span", {
|
|
7034
|
-
children: "Click here to add new record instantly...."
|
|
7035
|
-
})]
|
|
7036
|
-
})
|
|
7037
|
-
})
|
|
7038
|
-
})]
|
|
7115
|
+
}), interactive && newRowPosition !== "top" && renderAddRowClickableRow()]
|
|
7116
|
+
})
|
|
7039
7117
|
})]
|
|
7040
7118
|
}))
|
|
7041
7119
|
}), isMobileView && jsxRuntime.jsx("div", {
|
|
@@ -7083,7 +7161,7 @@ var Table = function Table(_ref) {
|
|
|
7083
7161
|
theme: safeTheme
|
|
7084
7162
|
})]
|
|
7085
7163
|
});
|
|
7086
|
-
};
|
|
7164
|
+
});
|
|
7087
7165
|
|
|
7088
7166
|
var isCheckBoxInput = (element) => element.type === 'checkbox';
|
|
7089
7167
|
|
|
@@ -13704,7 +13782,7 @@ var Accordion = /*#__PURE__*/React.forwardRef(function Accordion(_ref, ref) {
|
|
|
13704
13782
|
});
|
|
13705
13783
|
Accordion.displayName = "Accordion";
|
|
13706
13784
|
|
|
13707
|
-
var _excluded$s = ["id", "name", "label", "checked", "defaultChecked", "onChange", "disabled", "required", "className", "size", "labelPosition", "value", "ariaLabel", "variant", "indeterminate", "borderColor", "color", "iconClass", "checkedClass"];
|
|
13785
|
+
var _excluded$s = ["id", "name", "label", "checked", "defaultChecked", "onChange", "disabled", "required", "className", "size", "labelPosition", "value", "ariaLabel", "variant", "indeterminate", "borderColor", "color", "iconClass", "checkedClass", "wrapperProps"];
|
|
13708
13786
|
function ownKeys$w(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
13709
13787
|
function _objectSpread$w(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$w(Object(t), true).forEach(function (r) { _defineProperty$4(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$w(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
13710
13788
|
var SIZE_CLASSES = {
|
|
@@ -13771,7 +13849,6 @@ var COLOR_PRESETS = {
|
|
|
13771
13849
|
}
|
|
13772
13850
|
};
|
|
13773
13851
|
var Checkbox = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
13774
|
-
var _props$wrapperProps;
|
|
13775
13852
|
var id = _ref.id,
|
|
13776
13853
|
name = _ref.name,
|
|
13777
13854
|
label = _ref.label,
|
|
@@ -13799,6 +13876,7 @@ var Checkbox = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
|
13799
13876
|
color = _ref$color === void 0 ? "primary" : _ref$color,
|
|
13800
13877
|
iconClass = _ref.iconClass,
|
|
13801
13878
|
checkedClass = _ref.checkedClass,
|
|
13879
|
+
wrapperProps = _ref.wrapperProps,
|
|
13802
13880
|
props = _objectWithoutProperties$1(_ref, _excluded$s);
|
|
13803
13881
|
var autoId = React.useId();
|
|
13804
13882
|
var inputId = id || "".concat(autoId, "-checkbox");
|
|
@@ -13839,7 +13917,7 @@ var Checkbox = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
|
13839
13917
|
} : {};
|
|
13840
13918
|
return jsxRuntime.jsxs("div", _objectSpread$w(_objectSpread$w({
|
|
13841
13919
|
className: wrapperCls
|
|
13842
|
-
},
|
|
13920
|
+
}, wrapperProps !== null && wrapperProps !== void 0 ? wrapperProps : {}), {}, {
|
|
13843
13921
|
children: [label && labelPosition === "left" && jsxRuntime.jsx("label", {
|
|
13844
13922
|
htmlFor: inputId,
|
|
13845
13923
|
className: "".concat(sizeClass.label, " select-none mr-2"),
|
|
@@ -13858,7 +13936,6 @@ var Checkbox = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
|
13858
13936
|
className: "peer absolute inset-0 m-0 p-0 opacity-0 z-10",
|
|
13859
13937
|
// ensure we set checked only from the boolean (avoid undefined)
|
|
13860
13938
|
checked: Boolean(currentChecked),
|
|
13861
|
-
defaultChecked: defaultChecked,
|
|
13862
13939
|
onChange: handleChange,
|
|
13863
13940
|
disabled: disabled,
|
|
13864
13941
|
required: required,
|
|
@@ -13885,6 +13962,7 @@ var Checkbox = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
|
13885
13962
|
})]
|
|
13886
13963
|
}));
|
|
13887
13964
|
});
|
|
13965
|
+
Checkbox.displayName = "Checkbox";
|
|
13888
13966
|
|
|
13889
13967
|
var _excluded$r = ["value", "defaultValue", "onChange", "swatches", "showAlpha", "size", "disabled", "className", "label", "id"];
|
|
13890
13968
|
function ownKeys$v(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
@@ -575,7 +575,7 @@
|
|
|
575
575
|
"description": "px breakpoint for mobile cards"
|
|
576
576
|
}
|
|
577
577
|
],
|
|
578
|
-
"examples": "### Basic table\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name' },\n { key: 'email', label: 'Email' },\n];\nconst data = [\n { id: 1, name: 'Alice', email: 'alice@example.com' },\n { id: 2, name: 'Bob', email: 'bob@example.com' },\n];\n<Table data={data} columns={columns} />\n```\n\n### With pagination and selection\n\n```jsx\n<Table\n data={data}\n columns={columns}\n pagination\n pageSize={10}\n selectable\n onSelectionChange={(selected) => console.log(selected)}\n/>\n```\n\n### Server-side data\n\n```jsx\n<Table\n data={data}\n columns={columns}\n serverSide\n pagination\n totalRecords={total}\n onFetch={async ({ setData, setLoading, page, limit, sort }) => {\n setLoading(true);\n const res = await fetch(`/api/users?page=${page}&limit=${limit}`);\n const json = await res.json();\n setData(json.rows);\n setLoading(false);\n }}\n/>\n```\n\n### Conditional row details\n\n`hasDetails` accepts a predicate so only rows that actually have something to\nshow get an expand toggle. Rows that don't qualify still render the (empty)\ntoggle cell, so the columns stay aligned.\n\n```jsx\nconst ErrorDetails = ({ row }) => <pre>{row.stackTrace}</pre>;\n\n<Table\n data={jobs}\n columns={columns}\n hasDetails={(row) => row.status === 'failed'}\n DetailsComponent={ErrorDetails}\n/>\n```\n\n### Conditional and inline actions\n\n`hidden` and `disabled` accept a `(row, index)` predicate, `label` and `icon`\naccept a `(row, index)` producer, and `inline: true` promotes an action out of\nthe kebab menu into the row itself. When no menu actions remain for a row, the\nkebab button is not rendered at all.\n\n```jsx\n<Table\n data={jobs}\n columns={columns}\n replaceDefaultActions\n actions={[\n { name: 'view', label: 'View', icon: <Eye size={16} />, inline: true },\n { name: 'edit', label: 'Edit', icon: <Edit size={16} />,\n disabled: (row) => row.locked },\n { name: 'retry', label: (row) => `Retry (${row.tries})`,\n hidden: (row) => row.status !== 'failed' },\n ]}\n onAction={({ action, row }) => handle(action.name, row)}\n/>\n```\n\nFor a large table, wrap a function-valued `actions` in `useCallback` — a fresh\narrow on every render defeats the per-row memoization.\n\n```jsx\nconst actions = useCallback(\n (row) => (row.role === 'admin' ? adminActions : userActions),\n []\n);\n```\n\nNote that because `icon` may be a `(row, index) => node` producer, passing a\nbare component reference (`icon: Edit`) calls it rather than rendering it. Pass\nan element instead: `icon: <Edit size={16} />`.\n\n### Patching the default actions\n\nEntries you supply are merged onto the built-in Edit/Delete/View set by `name`,\nso you can swap one handler without restating the rest. Unmatched names are\nappended after the defaults, and `hidden: true` removes a default outright.\n\n```jsx\n<Table\n data={users}\n columns={columns}\n actions={[\n { name: 'edit', onClick: ({ row }) => edit(row) }, // keeps default label + icon\n { name: 'delete', hidden: (row) => !row.canDelete },\n { name: 'invite', label: 'Invite', icon: <Mail size={16} /> }, // appended\n ]}\n/>\n```\n\nPass `replaceDefaultActions` to skip merging and use only your own set:\n\n```jsx\n<Table\n data={users}\n columns={columns}\n replaceDefaultActions\n actions={[{ name: 'approve', label: 'Approve' }]}\n/>\n```\n\n### Interactive Table Mode\n\nPass `interactive={true}` to enable inline cell editing (double-click a cell), instant record addition, and constraint validation before saving.\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name', type: 'text', required: true },\n { key: 'role', label: 'Role', type: 'select', options: ['Admin', 'User'], required: true },\n { key: 'notes', label: 'Notes', type: 'textarea' },\n];\n\n<Table\n
|
|
578
|
+
"examples": "### Basic table\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name' },\n { key: 'email', label: 'Email' },\n];\nconst data = [\n { id: 1, name: 'Alice', email: 'alice@example.com' },\n { id: 2, name: 'Bob', email: 'bob@example.com' },\n];\n<Table data={data} columns={columns} />\n```\n\n### With pagination and selection\n\n```jsx\n<Table\n data={data}\n columns={columns}\n pagination\n pageSize={10}\n selectable\n onSelectionChange={(selected) => console.log(selected)}\n/>\n```\n\n### Server-side data\n\n```jsx\n<Table\n data={data}\n columns={columns}\n serverSide\n pagination\n totalRecords={total}\n onFetch={async ({ setData, setLoading, page, limit, sort }) => {\n setLoading(true);\n const res = await fetch(`/api/users?page=${page}&limit=${limit}`);\n const json = await res.json();\n setData(json.rows);\n setLoading(false);\n }}\n/>\n```\n\n### Conditional row details\n\n`hasDetails` accepts a predicate so only rows that actually have something to\nshow get an expand toggle. Rows that don't qualify still render the (empty)\ntoggle cell, so the columns stay aligned.\n\n```jsx\nconst ErrorDetails = ({ row }) => <pre>{row.stackTrace}</pre>;\n\n<Table\n data={jobs}\n columns={columns}\n hasDetails={(row) => row.status === 'failed'}\n DetailsComponent={ErrorDetails}\n/>\n```\n\n### Conditional and inline actions\n\n`hidden` and `disabled` accept a `(row, index)` predicate, `label` and `icon`\naccept a `(row, index)` producer, and `inline: true` promotes an action out of\nthe kebab menu into the row itself. When no menu actions remain for a row, the\nkebab button is not rendered at all.\n\n```jsx\n<Table\n data={jobs}\n columns={columns}\n replaceDefaultActions\n actions={[\n { name: 'view', label: 'View', icon: <Eye size={16} />, inline: true },\n { name: 'edit', label: 'Edit', icon: <Edit size={16} />,\n disabled: (row) => row.locked },\n { name: 'retry', label: (row) => `Retry (${row.tries})`,\n hidden: (row) => row.status !== 'failed' },\n ]}\n onAction={({ action, row }) => handle(action.name, row)}\n/>\n```\n\nFor a large table, wrap a function-valued `actions` in `useCallback` — a fresh\narrow on every render defeats the per-row memoization.\n\n```jsx\nconst actions = useCallback(\n (row) => (row.role === 'admin' ? adminActions : userActions),\n []\n);\n```\n\nNote that because `icon` may be a `(row, index) => node` producer, passing a\nbare component reference (`icon: Edit`) calls it rather than rendering it. Pass\nan element instead: `icon: <Edit size={16} />`.\n\n### Patching the default actions\n\nEntries you supply are merged onto the built-in Edit/Delete/View set by `name`,\nso you can swap one handler without restating the rest. Unmatched names are\nappended after the defaults, and `hidden: true` removes a default outright.\n\n```jsx\n<Table\n data={users}\n columns={columns}\n actions={[\n { name: 'edit', onClick: ({ row }) => edit(row) }, // keeps default label + icon\n { name: 'delete', hidden: (row) => !row.canDelete },\n { name: 'invite', label: 'Invite', icon: <Mail size={16} /> }, // appended\n ]}\n/>\n```\n\nPass `replaceDefaultActions` to skip merging and use only your own set:\n\n```jsx\n<Table\n data={users}\n columns={columns}\n replaceDefaultActions\n actions={[{ name: 'approve', label: 'Approve' }]}\n/>\n```\n\n### Interactive Table Mode\n\nPass `interactive={true}` to enable inline cell editing (double-click a cell or press standard `'e'` key on a focused active cell to enter edit mode), instant record addition (at top or bottom), keyboard navigation (`Tab`/`Shift+Tab` to move between cells, `Escape` to exit edit mode, `Enter` to save modified changes), and constraint validation before saving.\n\n#### Interactive Props & Options\n\n- `interactive`: `boolean` (default: `false`) — Enables interactive mode.\n- `editKeyBinding`: `string | ((e) => boolean)` (default: `\"e\"`) — Standard key binding to enter cell edit mode on active focused cell.\n- `newRowPosition`: `\"bottom\" | \"top\"` (default: `\"bottom\"`) — Position where empty new rows are inserted (at the bottom or top of table dataset).\n- `addRowRef`: `React.MutableRefObject` — External ref populated with `(position?: \"top\" | \"bottom\") => void` to trigger adding a new row from external buttons.\n- `onInitAddRow`: `(addRowFn: (position?: \"top\" | \"bottom\") => void) => void` — Callback receiving the add-row function on component initialization.\n- `renderAddRowButton`: `({ addRow, newRowPosition }) => ReactNode` — Render prop for rendering a custom external add-row button in the table toolbar.\n\n#### Interactive Usage Example\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name', type: 'text', required: true },\n { key: 'role', label: 'Role', type: 'select', options: ['Admin', 'User'], required: true },\n { key: 'notes', label: 'Notes', type: 'textarea' },\n];\n\nconst addRowRef = useRef();\n\nreturn (\n <div>\n <button onClick={() => addRowRef.current?.(\"top\")}>\n Add Row at Top\n </button>\n\n <Table\n interactive\n editKeyBinding=\"e\"\n newRowPosition=\"top\"\n addRowRef={addRowRef}\n columns={columns}\n data={users}\n onSave={async ({ updatedRows, newRows, allData }) => {\n await api.saveUsers({ updatedRows, newRows });\n }}\n />\n </div>\n);\n```\n\n[← Component overview](README.md)"
|
|
579
579
|
},
|
|
580
580
|
{
|
|
581
581
|
"name": "Form",
|
|
@@ -849,6 +849,12 @@
|
|
|
849
849
|
"type": "`string`",
|
|
850
850
|
"default": "",
|
|
851
851
|
"description": "Extra classes applied when checked"
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
"prop": "wrapperProps",
|
|
855
|
+
"type": "`object`",
|
|
856
|
+
"default": "",
|
|
857
|
+
"description": "Extra props spread onto the outer wrapper element"
|
|
852
858
|
}
|
|
853
859
|
],
|
|
854
860
|
"examples": "```jsx\n<Checkbox label=\"Accept terms\" checked={checked} onChange={e => setChecked(e.target.checked)} />\n<Checkbox label=\"Remember me\" defaultChecked />\n```\n\n[← Component overview](README.md)"
|
|
@@ -3202,7 +3208,7 @@
|
|
|
3202
3208
|
"Button": "# Button\n\nButton with variants, sizes, loading state, and optional left/right icons.\n\n## Import\n\n```jsx\nimport { Button } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `variant` | `string` | `\"primary\"` | `primary`, `secondary`, `outline`, `ghost`, `destructive`, `success`, `warning` |\n| `size` | `string` | `\"md\"` | `sm`, `md`, `lg` |\n| `disabled` | `boolean` | — | Disable button |\n| `loading` | `boolean` | — | Show spinner and disable |\n| `leftIcon` | `ReactNode` | — | Icon before children |\n| `rightIcon` | `ReactNode` | — | Icon after children |\n| `fullWidth` | `boolean` | — | Full width |\n| `className` | `string` | — | Extra CSS classes |\n| ...rest | — | — | Passed to native `<button>` |\n\n## Examples\n\n### Basic\n\n```jsx\n<Button variant=\"primary\">Save</Button>\n<Button variant=\"outline\">Cancel</Button>\n```\n\n### Sizes and loading\n\n```jsx\n<Button size=\"sm\">Small</Button>\n<Button size=\"lg\">Large</Button>\n<Button loading>Saving...</Button>\n```\n\n### With icons\n\n```jsx\nimport { Download } from 'lucide-react';\n\n<Button leftIcon={<Download className=\"h-4 w-4\" />}>Download</Button>\n```\n\n[← Component overview](README.md)\n",
|
|
3203
3209
|
"Card": "# Card\n\nCard container with optional hover shadow. Subcomponents: `Card.Header`, `Card.Title`, `Card.Description`, `Card.Content`, `Card.Footer` for structure.\n\n## Import\n\n```jsx\nimport { Card } from '@dreamtree-org/twreact-ui';\n```\n\n## Props (Card root)\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `children` | `ReactNode` | — | Card content. |\n| `className` | `string` | — | Wrapper CSS classes. |\n| `hover` | `boolean` | `false` | If true, add hover:shadow-md transition. |\n| ...rest | — | — | Passed to root `<div>`. |\n\n## Subcomponents\n\n- **Card.Header**: Wrapper for header (border-bottom, padding). Accepts `children`, `className`.\n- **Card.Title**: `<h3>` for title. Accepts `children`, `className`.\n- **Card.Description**: `<p>` for description. Accepts `children`, `className`.\n- **Card.Content**: Main body (padding). Accepts `children`, `className`.\n- **Card.Footer**: Footer (border-top, padding). Accepts `children`, `className`.\n\n## Example\n\n```jsx\n<Card hover>\n <Card.Header>\n <Card.Title>Card title</Card.Title>\n <Card.Description>Optional description text.</Card.Description>\n </Card.Header>\n <Card.Content>\n Main content here.\n </Card.Content>\n <Card.Footer>\n <Button>Action</Button>\n </Card.Footer>\n</Card>\n```\n\n[← Component overview](README.md)\n",
|
|
3204
3210
|
"Carousel": "# Carousel\n\nHorizontal carousel of slides. Renders `children` as slides (one per child). Optional auto-play, previous/next buttons (shown on hover), and dot indicators. Pauses auto-play on hover.\n\n## Import\n\n```jsx\nimport { Carousel } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `children` | `ReactNode` | — | Each child is one slide. |\n| `autoPlay` | `boolean` | `false` | Auto-advance slides. |\n| `interval` | `number` | `5000` | Auto-play interval in ms. |\n| `showDots` | `boolean` | `true` | Show dot indicators at bottom. |\n| `className` | `string` | — | Wrapper CSS classes. |\n| `itemClassName` | `string` | — | Each slide wrapper CSS classes. |\n\n## Behavior\n\n- Slides are laid out in a row; current slide is translated into view.\n- Previous/Next buttons appear on hover (positioned left/right center).\n- Dots are clickable to jump to a slide.\n- On mouse enter, auto-play pauses; on mouse leave, it resumes (if `autoPlay` is true).\n\n## Example\n\n```jsx\n<Carousel autoPlay interval={5000} showDots>\n <div className=\"h-64 bg-gray-100 flex items-center justify-center\">Slide 1</div>\n <div className=\"h-64 bg-gray-200 flex items-center justify-center\">Slide 2</div>\n <div className=\"h-64 bg-gray-300 flex items-center justify-center\">Slide 3</div>\n</Carousel>\n```\n\n[← Component overview](README.md)\n",
|
|
3205
|
-
"Checkbox": "# Checkbox\n\nCheckbox with label, sizes, and optional indeterminate state.\n\n## Import\n\n```jsx\nimport { Checkbox } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `id` | `string` | — | Input id |\n| `name` | `string` | — | Form name |\n| `label` | `string` | — | Label text |\n| `checked` | `boolean` | — | Controlled checked |\n| `defaultChecked` | `boolean` | `false` | Uncontrolled default |\n| `onChange` | `function` | — | Change handler |\n| `disabled` | `boolean` | `false` | Disable |\n| `required` | `boolean` | `false` | Required |\n| `size` | `string` | `\"md\"` | `sm`, `md`, `lg` |\n| `labelPosition` | `string` | `\"right\"` | `left`, `right` |\n| `value` | `any` | — | Value when checked |\n| `indeterminate` | `boolean` | `false` | Indeterminate state |\n| `borderColor` | `string` | `\"#ccc\"` | Box border color |\n| `color` | `string` | `\"primary\"` | Color preset (`primary`, `secondary`, `success`, `error`, `warning`, `info`, etc.) or custom hex/RGB color string |\n| `iconClass` | `string` | — | Extra classes for the check icon |\n| `checkedClass` | `string` | — | Extra classes applied when checked |\n\n## Example\n\n```jsx\n<Checkbox label=\"Accept terms\" checked={checked} onChange={e => setChecked(e.target.checked)} />\n<Checkbox label=\"Remember me\" defaultChecked />\n```\n\n[← Component overview](README.md)\n",
|
|
3211
|
+
"Checkbox": "# Checkbox\n\nCheckbox with label, sizes, and optional indeterminate state.\n\n## Import\n\n```jsx\nimport { Checkbox } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `id` | `string` | — | Input id |\n| `name` | `string` | — | Form name |\n| `label` | `string` | — | Label text |\n| `checked` | `boolean` | — | Controlled checked |\n| `defaultChecked` | `boolean` | `false` | Uncontrolled default |\n| `onChange` | `function` | — | Change handler |\n| `disabled` | `boolean` | `false` | Disable |\n| `required` | `boolean` | `false` | Required |\n| `size` | `string` | `\"md\"` | `sm`, `md`, `lg` |\n| `labelPosition` | `string` | `\"right\"` | `left`, `right` |\n| `value` | `any` | — | Value when checked |\n| `indeterminate` | `boolean` | `false` | Indeterminate state |\n| `borderColor` | `string` | `\"#ccc\"` | Box border color |\n| `color` | `string` | `\"primary\"` | Color preset (`primary`, `secondary`, `success`, `error`, `warning`, `info`, etc.) or custom hex/RGB color string |\n| `iconClass` | `string` | — | Extra classes for the check icon |\n| `checkedClass` | `string` | — | Extra classes applied when checked |\n| `wrapperProps` | `object` | — | Extra props spread onto the outer wrapper element |\n\n## Example\n\n```jsx\n<Checkbox label=\"Accept terms\" checked={checked} onChange={e => setChecked(e.target.checked)} />\n<Checkbox label=\"Remember me\" defaultChecked />\n```\n\n[← Component overview](README.md)\n",
|
|
3206
3212
|
"ColorPicker": "# ColorPicker\n\nColor picker with optional swatches, hex input, HSL sliders, and alpha. Value can be hex or rgba string; `onChange` receives normalized `rgba(r,g,b,a)` string.\n\n## Import\n\n```jsx\nimport { ColorPicker } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `value` | `string` | — | Controlled value (hex or rgba string). |\n| `defaultValue` | `string` | — | Uncontrolled initial value. |\n| `onChange` | `(rgbaString: string) => void` | — | Called with normalized `rgba(r,g,b,a)` string. |\n| `swatches` | `string[]` | — | Array of hex/rgba strings for preset swatches. |\n| `showAlpha` | `boolean` | `false` | Show alpha slider. When off (or when the alpha channel is fully opaque), the displayed hex is the 6-char `#rrggbb` form; the 8-char `#rrggbbff` form is shown only when `showAlpha` is on **and** alpha < 1. |\n| `size` | `\"sm\"` \\| `\"md\"` \\| `\"lg\"` | — | Picker size. |\n| `disabled` | `boolean` | — | Disable the picker. |\n| `className` | `string` | — | Wrapper CSS classes. |\n| `label` | `string` | — | Label text. |\n| `id` | `string` | — | Input id. |\n\n## Example\n\n```jsx\nconst [color, setColor] = useState('#3b82f6');\n<ColorPicker\n value={color}\n onChange={setColor}\n swatches={['#ef4444', '#22c55e', '#3b82f6']}\n showAlpha\n label=\"Background\"\n/>\n```\n\n## Notes\n\n- Forwards `ref` to the root wrapper element and spreads unknown props (`...rest`,\n e.g. `data-*`) onto it. `className` is merged via `cn()` so caller classes win\n on Tailwind conflicts.\n- The default render (no `showAlpha`) displays the 6-char `#rrggbb` value.\n\n[← Component overview](README.md)\n",
|
|
3207
3213
|
"Condition": "# Condition\n\nRenders `children` when a condition is true, otherwise renders `fallback`. Simple conditional wrapper with no extra DOM when not needed.\n\n## Import\n\n```jsx\nimport { Condition } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `condition` | `boolean` | — | When true, render `children`; when false, render `fallback`. |\n| `children` | `ReactNode` | — | Content when condition is true. |\n| `fallback` | `ReactNode` | `null` | Content when condition is false. |\n\n## Example\n\n```jsx\n<Condition condition={isLoggedIn}>\n <Dashboard />\n</Condition>\n\n<Condition condition={loading} fallback={<Loader />}>\n <Content />\n</Condition>\n\n<Condition condition={hasError} fallback={<Alert variant=\"error\">Error</Alert>}>\n <Data />\n</Condition>\n```\n\n[← Component overview](README.md)\n",
|
|
3208
3214
|
"DatePicker": "# DatePicker\n\nSingle date picker with calendar popup. Accepts `Date` or ISO date string; supports min/max, clear button, and optional portal rendering.\n\n## Import\n\n```jsx\nimport { DatePicker } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `value` | `Date` \\| `string` | — | Selected date (Date or ISO string). |\n| `onChange` | `(date: Date \\| null) => void` | — | Called when date changes. |\n| `placeholder` | `string` | `\"Select date...\"` | Input placeholder. |\n| `label` | `string` | — | Label text. |\n| `error` | `string` | — | Error message. |\n| `disabled` | `boolean` | — | Disable the picker. |\n| `required` | `boolean` | — | Required field. |\n| `minDate` | `Date` | — | Minimum selectable date. |\n| `maxDate` | `Date` | — | Maximum selectable date. |\n| `className` | `string` | — | Wrapper CSS classes. |\n| `weekStartsOn` | `0` \\| `1` | `0` | 0 = Sunday, 1 = Monday. |\n| `portal` | `boolean` | `false` | Render calendar in `document.body`. |\n| `displayFormat` | `string` | `\"MMM dd, yyyy\"` | date-fns format for display. |\n| `locale` | `Locale` | — | date-fns locale. |\n| `showClear` | `boolean` | `true` | Show clear button. |\n| `closeOnSelect` | `boolean` | `true` | Close popup on date select. |\n\n## Example\n\n```jsx\nconst [date, setDate] = useState(null);\n<DatePicker\n value={date}\n onChange={setDate}\n placeholder=\"Select date\"\n minDate={new Date()}\n label=\"Start date\"\n/>\n```\n\n[← Component overview](README.md)\n",
|
|
@@ -3227,7 +3233,7 @@
|
|
|
3227
3233
|
"SpeechToText": "# SpeechToText\n\nVoice-to-text using the Web Speech API. Headless by default — render a custom\ncontrol via `renderButton`, or drive it imperatively through the ref. The\nbuilt-in fallback button is `className`-extensible and forwards arbitrary props.\n\n## Import\n\n```jsx\nimport { SpeechToText } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `lang` | `string` | `\"en-US\"` | Recognition language |\n| `continuous` | `boolean` | `true` | Keep listening until stopped (forced `false` on mobile) |\n| `interimResults` | `boolean` | `true` | Return interim results |\n| `onSpeechComplete` | `function` | — | Final transcript chunk callback `(text) => void` |\n| `onSpeaking` | `function` | — | Interim transcript callback `(text) => void` |\n| `onError` | `function` | — | Fatal error callback `(Error) => void`. Routine events (`no-speech`, `aborted`) are **not** surfaced |\n| `onStart` | `function` | — | Start callback |\n| `onStop` | `function` | — | Stop callback |\n| `renderButton` | `function` | — | Custom control render-prop (see signature below) |\n| `autoStart` | `boolean` | `false` | Start on mount (once, after support is detected) |\n| `disabled` | `boolean` | `false` | Disable |\n| `resetOnStart` | `boolean` | `false` | Clear the accumulated transcript when a new session starts |\n| `className` | `string` | — | Merged (via `cn`) onto the default button |\n| `...rest` | — | — | Forwarded to the default `<button>` |\n\nThe default button is keyboard-accessible (`aria-pressed`, `aria-label`) and\ntoken-driven. When `renderButton` is supplied, `className`/`...rest` are not\napplied (you own the rendered control).\n\n## `renderButton` signature\n\n```js\nrenderButton({ isListening, isSupported, error, start, stop, toggle, disabled })\n```\n\n## Ref methods\n\n`start()`, `stop()`, `toggle()`, `isListening()`, `isSupported()`,\n`getTranscript()`, `clearTranscript()`, `getError()`.\n\n## Example\n\n```jsx\nconst [transcript, setTranscript] = useState('');\n<SpeechToText\n onSpeechComplete={(chunk) => setTranscript((t) => `${t} ${chunk}`.trim())}\n onError={(e) => console.error(e)}\n/>\n<div>Transcript: {transcript}</div>\n```\n\n[← Component overview](README.md)\n",
|
|
3228
3234
|
"Stepper": "# Stepper\n\nHorizontal or vertical step indicator. Each step has circle (with check for completed), optional line, and label. Optional `onStepClick` for clickable steps.\n\n## Import\n\n```jsx\nimport { Stepper } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `steps` | `Array` | `[]` | Each step: `{ id?, label?, description? }`. |\n| `currentStep` | `number` | `0` | Zero-based index of current step. |\n| `orientation` | `\"horizontal\"` \\| `\"vertical\"` | `\"horizontal\"` | Layout. |\n| `variant` | `string` | `\"default\"` | Visual variant. |\n| `onStepClick` | `(step, stepIndex) => void` | — | Called when a step is clicked (e.g. for completed/current). |\n| `className` | `string` | — | Nav wrapper CSS classes. |\n| ...rest | — | — | Passed to `<nav>`. |\n\n## Step status\n\n- **completed**: `stepIndex < currentStep` (check icon, primary color).\n- **current**: `stepIndex === currentStep`.\n- **upcoming**: `stepIndex > currentStep` (gray).\n\n## Example\n\n```jsx\n<Stepper\n steps={[\n { id: '1', label: 'Details' },\n { id: '2', label: 'Payment' },\n { id: '3', label: 'Confirm' },\n ]}\n currentStep={1}\n orientation=\"horizontal\"\n onStepClick={(step, idx) => setStep(idx)}\n/>\n```\n\n[← Component overview](README.md)\n",
|
|
3229
3235
|
"Switch": "# Switch\n\nToggle switch (on/off). Controlled or uncontrolled.\n\n## Import\n\n```jsx\nimport { Switch } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `checked` | `boolean` | — | Controlled checked |\n| `defaultChecked` | `boolean` | `false` | Uncontrolled default |\n| `onChange` | `(checked: boolean) => void` | — | Receives the new boolean state (not a DOM event) |\n| `disabled` | `boolean` | `false` | Disable |\n| `size` | `string` | `\"md\"` | `sm`, `md`, `lg` |\n| `color` | `string` | `\"primary\"` | Color preset (`primary`, `secondary`, `success`, `error`, `warning`, `info`, etc.) or custom hex/RGB color string |\n| `name` | `string` | — | Form name |\n| `topLabel` | `string` | — | Label above |\n| `bottomLabel` | `string` | — | Label below |\n| `leftLabel` | `string` | — | Label left |\n| `rightLabel` | `string` | — | Label right |\n\n## Example\n\n```jsx\n<Switch checked={enabled} onChange={checked => setEnabled(checked)} />\n<Switch defaultChecked rightLabel=\"Enable notifications\" />\n```\n\n[← Component overview](README.md)\n",
|
|
3230
|
-
"Table": "# Table\n\nData table with sorting, filtering, pagination, row selection, expandable details, and responsive mobile card view.\n\n## Import\n\n```jsx\nimport { Table } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `data` | `Array` | `[]` | Row data (array of objects) |\n| `columns` | `Array` | `[]` | Column definitions `[{ key, label, sortable?, isVisible?, editable?, type?, options?, required?, validate?, onRender?, render? }]` |\n| `interactive` | `boolean` | `false` | Enable interactive mode with inline cell editing (double-click cell), keyboard shortcuts (`Tab`/`Shift+Tab` cell navigation, `Enter` to save modified changes, `Arrows`, `Escape`), primary Save button, outline Clear Changes button, constraint validation before save, and instant new row addition. |\n| `onSave` | `function` | — | Save callback in interactive mode `({ updatedRows, newRows, allData, tableData }) => Promise<allData | { data: allData } | void> | void`. Returning a dataset array or `{ data }`/`{ allData }` updates internal state with returned records. |\n| `onEdit` | `function` | — | Live edit callback fired when a cell value changes or row edit action is clicked `({ rowKey, colKey, newValue, row, action, isNew }) => void` |\n| `sortable` | `boolean` | `true` | Enable column sort |\n| `filterable` | `boolean` | `false` | Enable column filters |\n| `selectable` | `boolean` | `false` | Row selection checkboxes |\n| `pagination` | `boolean` | `false` | Enable pagination |\n| `pageSize` | `number` | `25` | Rows per page |\n| `onSort` | `function` | — | Sort callback |\n| `onFilter` | `function` | — | Filter callback |\n| `onFetch` | `function` | — | Server-side fetch (setData, setLoading, filters, page, limit, sort) |\n| `onFilterChange` | `function` | — | Filter change callback |\n| `onSelectionChange` | `function` | — | Selected rows callback |\n| `onRowClick` | `function` | — | Row click callback |\n| `hasDetails` | `boolean` or `(row, index) => boolean` | `false` | Expandable row details. Pass a predicate to give only matching rows an expand toggle; non-matching rows render an empty placeholder cell so columns stay aligned. |\n| `DetailsComponent` | `Component` | — | Component for expanded content |\n| `withAction` | `boolean` | `true` | Show actions column |\n| `onAction` | `function` | — | Action menu callback |\n| `actions` | `Array` or `(row, index) => Array` | — | Row actions. Entries are `{ name, label, icon, render?, onClick?, hidden?, disabled?, inline? }`; `label`, `icon`, `hidden` and `disabled` each accept a value or `(row, index) => value`. `inline: true` renders the action as an icon button in the actions cell instead of inside the kebab menu; the kebab is hidden when no menu actions remain. Entries are merged onto the built-in Edit/Delete/View set by `name` (so `{ name: 'edit', onClick }` keeps the default label and icon, and `{ name: 'delete', hidden: true }` removes a default); unmatched entries are appended. Set `replaceDefaultActions` to opt out. |\n| `replaceDefaultActions` | `boolean` | `false` | Replace the built-in Edit/Delete/View actions entirely instead of patching them by `name`. |\n| `showSerial` | `boolean` | `true` | Show row number column |\n| `cellClass` | `string` \\| `function` | — | Cell className |\n| `rowClass` | `string` \\| `function` | — | Row className |\n| `globalSearch` | `boolean` | `false` | Global search box |\n| `limitOptions` | `number[]` | `[10,25,50,100]` | Page size options |\n| `showLimitSelector` | `boolean` | `true` | Show page size selector |\n| `showReloadButton` | `boolean` | `true` | Show reload button |\n| `onReload` | `function` | — | Reload click handler |\n| `stripedRows` | `boolean` | `true` | Striped row background |\n| `theme` | `object` | — | `stripedColors`, `rowHover`, `accentColor` |\n| `serverSide` | `boolean` | `false` | Server-side pagination/sort/filter |\n| `totalRecords` | `number` | `0` | Total count (serverSide) |\n| `pageNumber` | `number` | — | Controlled page |\n| `onPageChange` | `(page: number) => void` | — | Called with the new page number when the page changes. Consumed by the table — it is **not** forwarded onto the `<table>`. |\n| `responsiveBreakpoint` | `number` | `768` | px breakpoint for mobile cards |\n\nOnly DOM-valid attributes in `...rest` (e.g. `id`, `aria-*`, `data-*`) are\nforwarded to the root `<table>`; the table's own control props (`onPageChange`,\n`serverSide`, `pagination`, the `on*` callbacks, …) are consumed and never leak\nto the DOM as unknown attributes.\n\n## Examples\n\n### Basic table\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name' },\n { key: 'email', label: 'Email' },\n];\nconst data = [\n { id: 1, name: 'Alice', email: 'alice@example.com' },\n { id: 2, name: 'Bob', email: 'bob@example.com' },\n];\n<Table data={data} columns={columns} />\n```\n\n### With pagination and selection\n\n```jsx\n<Table\n data={data}\n columns={columns}\n pagination\n pageSize={10}\n selectable\n onSelectionChange={(selected) => console.log(selected)}\n/>\n```\n\n### Server-side data\n\n```jsx\n<Table\n data={data}\n columns={columns}\n serverSide\n pagination\n totalRecords={total}\n onFetch={async ({ setData, setLoading, page, limit, sort }) => {\n setLoading(true);\n const res = await fetch(`/api/users?page=${page}&limit=${limit}`);\n const json = await res.json();\n setData(json.rows);\n setLoading(false);\n }}\n/>\n```\n\n### Conditional row details\n\n`hasDetails` accepts a predicate so only rows that actually have something to\nshow get an expand toggle. Rows that don't qualify still render the (empty)\ntoggle cell, so the columns stay aligned.\n\n```jsx\nconst ErrorDetails = ({ row }) => <pre>{row.stackTrace}</pre>;\n\n<Table\n data={jobs}\n columns={columns}\n hasDetails={(row) => row.status === 'failed'}\n DetailsComponent={ErrorDetails}\n/>\n```\n\n### Conditional and inline actions\n\n`hidden` and `disabled` accept a `(row, index)` predicate, `label` and `icon`\naccept a `(row, index)` producer, and `inline: true` promotes an action out of\nthe kebab menu into the row itself. When no menu actions remain for a row, the\nkebab button is not rendered at all.\n\n```jsx\n<Table\n data={jobs}\n columns={columns}\n replaceDefaultActions\n actions={[\n { name: 'view', label: 'View', icon: <Eye size={16} />, inline: true },\n { name: 'edit', label: 'Edit', icon: <Edit size={16} />,\n disabled: (row) => row.locked },\n { name: 'retry', label: (row) => `Retry (${row.tries})`,\n hidden: (row) => row.status !== 'failed' },\n ]}\n onAction={({ action, row }) => handle(action.name, row)}\n/>\n```\n\nFor a large table, wrap a function-valued `actions` in `useCallback` — a fresh\narrow on every render defeats the per-row memoization.\n\n```jsx\nconst actions = useCallback(\n (row) => (row.role === 'admin' ? adminActions : userActions),\n []\n);\n```\n\nNote that because `icon` may be a `(row, index) => node` producer, passing a\nbare component reference (`icon: Edit`) calls it rather than rendering it. Pass\nan element instead: `icon: <Edit size={16} />`.\n\n### Patching the default actions\n\nEntries you supply are merged onto the built-in Edit/Delete/View set by `name`,\nso you can swap one handler without restating the rest. Unmatched names are\nappended after the defaults, and `hidden: true` removes a default outright.\n\n```jsx\n<Table\n data={users}\n columns={columns}\n actions={[\n { name: 'edit', onClick: ({ row }) => edit(row) }, // keeps default label + icon\n { name: 'delete', hidden: (row) => !row.canDelete },\n { name: 'invite', label: 'Invite', icon: <Mail size={16} /> }, // appended\n ]}\n/>\n```\n\nPass `replaceDefaultActions` to skip merging and use only your own set:\n\n```jsx\n<Table\n data={users}\n columns={columns}\n replaceDefaultActions\n actions={[{ name: 'approve', label: 'Approve' }]}\n/>\n```\n\n### Interactive Table Mode\n\nPass `interactive={true}` to enable inline cell editing (double-click a cell), instant record addition, and constraint validation before saving.\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name', type: 'text', required: true },\n { key: 'role', label: 'Role', type: 'select', options: ['Admin', 'User'], required: true },\n { key: 'notes', label: 'Notes', type: 'textarea' },\n];\n\n<Table\n interactive\n columns={columns}\n data={users}\n onSave={async ({ updatedRows, newRows, allData }) => {\n await api.saveUsers({ updatedRows, newRows });\n }}\n/>\n```\n\n[← Component overview](README.md)\n",
|
|
3236
|
+
"Table": "# Table\n\nData table with sorting, filtering, pagination, row selection, expandable details, and responsive mobile card view.\n\n## Import\n\n```jsx\nimport { Table } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `data` | `Array` | `[]` | Row data (array of objects) |\n| `columns` | `Array` | `[]` | Column definitions `[{ key, label, sortable?, isVisible?, editable?, type?, options?, required?, validate?, onRender?, render? }]` |\n| `interactive` | `boolean` | `false` | Enable interactive mode with inline cell editing (double-click cell), keyboard shortcuts (`Tab`/`Shift+Tab` cell navigation, `Enter` to save modified changes, `Arrows`, `Escape`), primary Save button, outline Clear Changes button, constraint validation before save, and instant new row addition. |\n| `onSave` | `function` | — | Save callback in interactive mode `({ updatedRows, newRows, allData, tableData }) => Promise<allData | { data: allData } | void> | void`. Returning a dataset array or `{ data }`/`{ allData }` updates internal state with returned records. |\n| `onEdit` | `function` | — | Live edit callback fired when a cell value changes or row edit action is clicked `({ rowKey, colKey, newValue, row, action, isNew }) => void` |\n| `sortable` | `boolean` | `true` | Enable column sort |\n| `filterable` | `boolean` | `false` | Enable column filters |\n| `selectable` | `boolean` | `false` | Row selection checkboxes |\n| `pagination` | `boolean` | `false` | Enable pagination |\n| `pageSize` | `number` | `25` | Rows per page |\n| `onSort` | `function` | — | Sort callback |\n| `onFilter` | `function` | — | Filter callback |\n| `onFetch` | `function` | — | Server-side fetch (setData, setLoading, filters, page, limit, sort) |\n| `onFilterChange` | `function` | — | Filter change callback |\n| `onSelectionChange` | `function` | — | Selected rows callback |\n| `onRowClick` | `function` | — | Row click callback |\n| `hasDetails` | `boolean` or `(row, index) => boolean` | `false` | Expandable row details. Pass a predicate to give only matching rows an expand toggle; non-matching rows render an empty placeholder cell so columns stay aligned. |\n| `DetailsComponent` | `Component` | — | Component for expanded content |\n| `withAction` | `boolean` | `true` | Show actions column |\n| `onAction` | `function` | — | Action menu callback |\n| `actions` | `Array` or `(row, index) => Array` | — | Row actions. Entries are `{ name, label, icon, render?, onClick?, hidden?, disabled?, inline? }`; `label`, `icon`, `hidden` and `disabled` each accept a value or `(row, index) => value`. `inline: true` renders the action as an icon button in the actions cell instead of inside the kebab menu; the kebab is hidden when no menu actions remain. Entries are merged onto the built-in Edit/Delete/View set by `name` (so `{ name: 'edit', onClick }` keeps the default label and icon, and `{ name: 'delete', hidden: true }` removes a default); unmatched entries are appended. Set `replaceDefaultActions` to opt out. |\n| `replaceDefaultActions` | `boolean` | `false` | Replace the built-in Edit/Delete/View actions entirely instead of patching them by `name`. |\n| `showSerial` | `boolean` | `true` | Show row number column |\n| `cellClass` | `string` \\| `function` | — | Cell className |\n| `rowClass` | `string` \\| `function` | — | Row className |\n| `globalSearch` | `boolean` | `false` | Global search box |\n| `limitOptions` | `number[]` | `[10,25,50,100]` | Page size options |\n| `showLimitSelector` | `boolean` | `true` | Show page size selector |\n| `showReloadButton` | `boolean` | `true` | Show reload button |\n| `onReload` | `function` | — | Reload click handler |\n| `stripedRows` | `boolean` | `true` | Striped row background |\n| `theme` | `object` | — | `stripedColors`, `rowHover`, `accentColor` |\n| `serverSide` | `boolean` | `false` | Server-side pagination/sort/filter |\n| `totalRecords` | `number` | `0` | Total count (serverSide) |\n| `pageNumber` | `number` | — | Controlled page |\n| `onPageChange` | `(page: number) => void` | — | Called with the new page number when the page changes. Consumed by the table — it is **not** forwarded onto the `<table>`. |\n| `responsiveBreakpoint` | `number` | `768` | px breakpoint for mobile cards |\n\nOnly DOM-valid attributes in `...rest` (e.g. `id`, `aria-*`, `data-*`) are\nforwarded to the root `<table>`; the table's own control props (`onPageChange`,\n`serverSide`, `pagination`, the `on*` callbacks, …) are consumed and never leak\nto the DOM as unknown attributes.\n\n## Examples\n\n### Basic table\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name' },\n { key: 'email', label: 'Email' },\n];\nconst data = [\n { id: 1, name: 'Alice', email: 'alice@example.com' },\n { id: 2, name: 'Bob', email: 'bob@example.com' },\n];\n<Table data={data} columns={columns} />\n```\n\n### With pagination and selection\n\n```jsx\n<Table\n data={data}\n columns={columns}\n pagination\n pageSize={10}\n selectable\n onSelectionChange={(selected) => console.log(selected)}\n/>\n```\n\n### Server-side data\n\n```jsx\n<Table\n data={data}\n columns={columns}\n serverSide\n pagination\n totalRecords={total}\n onFetch={async ({ setData, setLoading, page, limit, sort }) => {\n setLoading(true);\n const res = await fetch(`/api/users?page=${page}&limit=${limit}`);\n const json = await res.json();\n setData(json.rows);\n setLoading(false);\n }}\n/>\n```\n\n### Conditional row details\n\n`hasDetails` accepts a predicate so only rows that actually have something to\nshow get an expand toggle. Rows that don't qualify still render the (empty)\ntoggle cell, so the columns stay aligned.\n\n```jsx\nconst ErrorDetails = ({ row }) => <pre>{row.stackTrace}</pre>;\n\n<Table\n data={jobs}\n columns={columns}\n hasDetails={(row) => row.status === 'failed'}\n DetailsComponent={ErrorDetails}\n/>\n```\n\n### Conditional and inline actions\n\n`hidden` and `disabled` accept a `(row, index)` predicate, `label` and `icon`\naccept a `(row, index)` producer, and `inline: true` promotes an action out of\nthe kebab menu into the row itself. When no menu actions remain for a row, the\nkebab button is not rendered at all.\n\n```jsx\n<Table\n data={jobs}\n columns={columns}\n replaceDefaultActions\n actions={[\n { name: 'view', label: 'View', icon: <Eye size={16} />, inline: true },\n { name: 'edit', label: 'Edit', icon: <Edit size={16} />,\n disabled: (row) => row.locked },\n { name: 'retry', label: (row) => `Retry (${row.tries})`,\n hidden: (row) => row.status !== 'failed' },\n ]}\n onAction={({ action, row }) => handle(action.name, row)}\n/>\n```\n\nFor a large table, wrap a function-valued `actions` in `useCallback` — a fresh\narrow on every render defeats the per-row memoization.\n\n```jsx\nconst actions = useCallback(\n (row) => (row.role === 'admin' ? adminActions : userActions),\n []\n);\n```\n\nNote that because `icon` may be a `(row, index) => node` producer, passing a\nbare component reference (`icon: Edit`) calls it rather than rendering it. Pass\nan element instead: `icon: <Edit size={16} />`.\n\n### Patching the default actions\n\nEntries you supply are merged onto the built-in Edit/Delete/View set by `name`,\nso you can swap one handler without restating the rest. Unmatched names are\nappended after the defaults, and `hidden: true` removes a default outright.\n\n```jsx\n<Table\n data={users}\n columns={columns}\n actions={[\n { name: 'edit', onClick: ({ row }) => edit(row) }, // keeps default label + icon\n { name: 'delete', hidden: (row) => !row.canDelete },\n { name: 'invite', label: 'Invite', icon: <Mail size={16} /> }, // appended\n ]}\n/>\n```\n\nPass `replaceDefaultActions` to skip merging and use only your own set:\n\n```jsx\n<Table\n data={users}\n columns={columns}\n replaceDefaultActions\n actions={[{ name: 'approve', label: 'Approve' }]}\n/>\n```\n\n### Interactive Table Mode\n\nPass `interactive={true}` to enable inline cell editing (double-click a cell or press standard `'e'` key on a focused active cell to enter edit mode), instant record addition (at top or bottom), keyboard navigation (`Tab`/`Shift+Tab` to move between cells, `Escape` to exit edit mode, `Enter` to save modified changes), and constraint validation before saving.\n\n#### Interactive Props & Options\n\n- `interactive`: `boolean` (default: `false`) — Enables interactive mode.\n- `editKeyBinding`: `string | ((e) => boolean)` (default: `\"e\"`) — Standard key binding to enter cell edit mode on active focused cell.\n- `newRowPosition`: `\"bottom\" | \"top\"` (default: `\"bottom\"`) — Position where empty new rows are inserted (at the bottom or top of table dataset).\n- `addRowRef`: `React.MutableRefObject` — External ref populated with `(position?: \"top\" | \"bottom\") => void` to trigger adding a new row from external buttons.\n- `onInitAddRow`: `(addRowFn: (position?: \"top\" | \"bottom\") => void) => void` — Callback receiving the add-row function on component initialization.\n- `renderAddRowButton`: `({ addRow, newRowPosition }) => ReactNode` — Render prop for rendering a custom external add-row button in the table toolbar.\n\n#### Interactive Usage Example\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name', type: 'text', required: true },\n { key: 'role', label: 'Role', type: 'select', options: ['Admin', 'User'], required: true },\n { key: 'notes', label: 'Notes', type: 'textarea' },\n];\n\nconst addRowRef = useRef();\n\nreturn (\n <div>\n <button onClick={() => addRowRef.current?.(\"top\")}>\n Add Row at Top\n </button>\n\n <Table\n interactive\n editKeyBinding=\"e\"\n newRowPosition=\"top\"\n addRowRef={addRowRef}\n columns={columns}\n data={users}\n onSave={async ({ updatedRows, newRows, allData }) => {\n await api.saveUsers({ updatedRows, newRows });\n }}\n />\n </div>\n);\n```\n\n[← Component overview](README.md)\n",
|
|
3231
3237
|
"Tabs": "# Tabs\n\nTabbed content with list and panels. Use subcomponents: `Tabs.List`, `Tabs.Tab`, `Tabs.Panels`, `Tabs.Panel`. Supports controlled (`index` + `onChange`) or uncontrolled (`defaultIndex`), keyboard navigation, and variants.\n\n## Import\n\n```jsx\nimport { Tabs } from '@dreamtree-org/twreact-ui';\n```\n\n## Props (Tabs root)\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `children` | `ReactNode` | — | Must include `Tabs.List` and `Tabs.Panels`. |\n| `defaultIndex` | `number` | `0` | Uncontrolled initial active tab index. |\n| `index` | `number` | — | Controlled active index. |\n| `onChange` | `(index: number) => void` | — | Called when tab changes. |\n| `orientation` | `\"horizontal\"` \\| `\"vertical\"` | `\"horizontal\"` | Layout direction. |\n| `size` | `\"sm\"` \\| `\"md\"` \\| `\"lg\"` | `\"md\"` | Tab size. |\n| `variant` | `\"line\"` \\| `\"pills\"` \\| `\"unstyled\"` | `\"line\"` | Tab style. |\n| `animated` | `boolean` | `true` | Fade animation on panel change. |\n| `className` | `string` | `\"\"` | Wrapper CSS classes. |\n\n## Usage\n\n```jsx\n<Tabs defaultIndex={0} variant=\"pills\" size=\"md\">\n <Tabs.List>\n <Tabs.Tab>Home</Tabs.Tab>\n <Tabs.Tab>Profile</Tabs.Tab>\n <Tabs.Tab>Settings</Tabs.Tab>\n </Tabs.List>\n <Tabs.Panels>\n <Tabs.Panel>Home content</Tabs.Panel>\n <Tabs.Panel>Profile content</Tabs.Panel>\n <Tabs.Panel>Settings content</Tabs.Panel>\n </Tabs.Panels>\n</Tabs>\n```\n\nControlled:\n\n```jsx\nconst [index, setIndex] = useState(0);\n<Tabs index={index} onChange={setIndex}>\n ...\n</Tabs>\n```\n\n[← Component overview](README.md)\n",
|
|
3232
3238
|
"TextToSpeech": "# TextToSpeech\n\nText-to-speech using the browser’s Speech Synthesis API. Renders a default \"Speak\" / \"Stop\" button or a custom button via `renderButton`. Toggling speak while playing stops playback.\n\n## Import\n\n```jsx\nimport { TextToSpeech } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `text` | `string` | `\"\"` | Text to speak (used as initial input if component manages input). |\n| `rate` | `number` | `1` | Speech rate (0.1–10). |\n| `pitch` | `number` | `0.5` | Pitch (0–2). |\n| `onSpeak` | `(text: string) => void` | `() => {}` | Called when speech starts (with the text spoken). |\n| `renderButton` | `(props: { onClick, isSpeaking }) => ReactNode` | — | Custom button; receives `onClick` and `isSpeaking`. |\n\n## Behavior\n\n- Component keeps internal `inputText` state initialized from `text`; speaking uses that.\n- Clicking the button toggles: if not speaking, starts `SpeechSynthesisUtterance` with `inputText`; if speaking, calls `speechSynthesis.cancel()` and sets speaking to false.\n- `onSpeak` is called when speech starts.\n\n## Example\n\n```jsx\n<TextToSpeech text=\"Hello, world!\" rate={1} onSpeak={(t) => console.log('Speaking:', t)} />\n\n<TextToSpeech\n text={content}\n renderButton={({ onClick, isSpeaking }) => (\n <Button onClick={onClick}>{isSpeaking ? 'Stop' : 'Play'} audio</Button>\n )}\n/>\n```\n\n[← Component overview](README.md)\n",
|
|
3233
3239
|
"ThreeDotPopover": "# ThreeDotPopover\n\nDropdown menu triggered by a three-dot (kebab) button. Use for row actions (Edit, Delete, etc.). Closes on outside click and Escape; optional close on item select.\n\n## Import\n\n```jsx\nimport { ThreeDotPopover } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `items` | `Array` | `[]` | Menu items: `{ key, label, icon?, onClick?, disabled?, destructive? }`. `icon` can be component (e.g. `Edit2`). |\n| `trigger` | `ReactNode` | — | Custom trigger; default is three-dot button. |\n| `className` | `string` | `\"\"` | CSS classes for the **default** trigger button (merged via `cn()`). |\n| `wrapperClassName` | `string` | `\"\"` | CSS classes merged (via `cn()`) onto the root container element. |\n| `menuClass` | `string` | `\"\"` | Menu container CSS classes. |\n| `menuItemClass` | `string` | `\"\"` | Each menu item CSS classes. |\n| `closeOnSelect` | `boolean` | `true` | Close menu when an item is selected. |\n| `ariaLabel` | `string` | `\"More options\"` | Trigger button aria-label. |\n\n## Item shape\n\n- `key` (string): Unique key.\n- `label` (string): Display text.\n- `icon` (optional): React component or node.\n- `onClick(item)` (optional): Called when item is clicked.\n- `disabled` (boolean): Disable the item.\n- `destructive` (boolean): Style as destructive (e.g. red for Delete).\n\n## Example\n\n```jsx\nimport { Edit2, Eye, Trash } from 'lucide-react';\n\n<ThreeDotPopover\n items={[\n { key: 'edit', label: 'Edit', icon: Edit2, onClick: () => edit(row) },\n { key: 'view', label: 'View', icon: Eye, onClick: () => view(row) },\n { key: 'delete', label: 'Delete', destructive: true, onClick: () => remove(row) },\n ]}\n closeOnSelect\n/>\n```\n\n## Notes\n\n- Forwards `ref` to the root container element and spreads unknown props\n (`...rest`) onto it.\n\n[← Component overview](README.md)\n",
|