@dragunio/fe-libs 0.1.0 → 0.1.1

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 CHANGED
@@ -61,4 +61,26 @@ import { Button, PlusIcon, Table } from '@dragunio/fe-libs';
61
61
  import '@dragunio/fe-libs/styles.css';
62
62
  ```
63
63
 
64
+ ### Resizable data table
65
+
66
+ Pass `headers` and `data` to have `Table` derive the maximum column count and an initial width for each column. The resize callback emits pixel widths, ready to persist in user preferences.
67
+
68
+ ```tsx
69
+ const [columnSizes, setColumnSizes] = useState<number[]>();
70
+
71
+ <Table
72
+ headers={['Name', 'Email']}
73
+ data={[
74
+ ['Ada Lovelace', 'ada@example.com'],
75
+ ['Grace Hopper', 'grace@example.com'],
76
+ ]}
77
+ striped
78
+ defaultColumnSizes={[180, 280]}
79
+ columnSizes={columnSizes}
80
+ onColumnResize={(nextSizes) => setColumnSizes(nextSizes)}
81
+ />
82
+ ```
83
+
84
+ Use `minColumnWidth` to change the minimum resize limit. The header resize handles support pointer dragging and left/right arrow keys.
85
+
64
86
  The library exposes ESM, CommonJS, TypeScript declarations, and keeps React as a peer dependency.
package/dist/index.d.mts CHANGED
@@ -51,10 +51,29 @@ declare function ChevronDownIcon(props: IconProps): react.JSX.Element;
51
51
  declare function CloseIcon(props: IconProps): react.JSX.Element;
52
52
  declare function SearchIcon(props: IconProps): react.JSX.Element;
53
53
 
54
+ type TableData = readonly (readonly ReactNode[])[];
55
+ type ColumnResizeHandler = (columnSizes: number[], columnIndex: number) => void;
54
56
  type TableProps = TableHTMLAttributes<HTMLTableElement> & {
57
+ /** Enables alternating background colors for body rows. */
55
58
  striped?: boolean;
59
+ /** Column labels. Passing headers or data enables the data-driven table API. */
60
+ headers?: readonly ReactNode[];
61
+ /** Rows of cells. A row can contain fewer or more cells than headers. */
62
+ data?: TableData;
63
+ /** Initial widths, in pixels, for the matching column indexes. */
64
+ defaultColumnSizes?: readonly number[];
65
+ /** Controlled column widths, in pixels. */
66
+ columnSizes?: readonly number[];
67
+ /** Called while a user resizes a column, with the complete current widths. */
68
+ onColumnResize?: ColumnResizeHandler;
69
+ /** Smallest width, in pixels, that a user can resize a column to. */
70
+ minColumnWidth?: number;
56
71
  };
57
- declare function Table({ striped, children, ...props }: TableProps): react.JSX.Element;
72
+ /**
73
+ * A semantic table. Use its child primitives for custom markup, or pass headers
74
+ * and data for an automatically sized, striped and resizable table.
75
+ */
76
+ declare function Table({ headers, data, striped, children, ...props }: TableProps): react.JSX.Element;
58
77
  declare function TableHead(props: HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
59
78
  declare function TableBody(props: HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
60
79
  declare function TableRow({ children, ...props }: HTMLAttributes<HTMLTableRowElement>): react.JSX.Element;
@@ -62,4 +81,4 @@ declare function TableCell({ children, ...props }: HTMLAttributes<HTMLTableCellE
62
81
  declare function TableHeader({ children, ...props }: HTMLAttributes<HTMLTableCellElement>): react.JSX.Element;
63
82
  declare function TableCaption(props: HTMLAttributes<HTMLTableCaptionElement>): react.JSX.Element;
64
83
 
65
- export { Button, type ButtonColor, type ButtonHierarchy, type ButtonProps, type ButtonSize, type ButtonVariant, ChevronDownIcon, CloseIcon, type IconProps, PlusIcon, SearchIcon, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, type TableProps, TableRow, getIconSize };
84
+ export { Button, type ButtonColor, type ButtonHierarchy, type ButtonProps, type ButtonSize, type ButtonVariant, ChevronDownIcon, CloseIcon, type ColumnResizeHandler, type IconProps, PlusIcon, SearchIcon, Table, TableBody, TableCaption, TableCell, type TableData, TableHead, TableHeader, type TableProps, TableRow, getIconSize };
package/dist/index.d.ts CHANGED
@@ -51,10 +51,29 @@ declare function ChevronDownIcon(props: IconProps): react.JSX.Element;
51
51
  declare function CloseIcon(props: IconProps): react.JSX.Element;
52
52
  declare function SearchIcon(props: IconProps): react.JSX.Element;
53
53
 
54
+ type TableData = readonly (readonly ReactNode[])[];
55
+ type ColumnResizeHandler = (columnSizes: number[], columnIndex: number) => void;
54
56
  type TableProps = TableHTMLAttributes<HTMLTableElement> & {
57
+ /** Enables alternating background colors for body rows. */
55
58
  striped?: boolean;
59
+ /** Column labels. Passing headers or data enables the data-driven table API. */
60
+ headers?: readonly ReactNode[];
61
+ /** Rows of cells. A row can contain fewer or more cells than headers. */
62
+ data?: TableData;
63
+ /** Initial widths, in pixels, for the matching column indexes. */
64
+ defaultColumnSizes?: readonly number[];
65
+ /** Controlled column widths, in pixels. */
66
+ columnSizes?: readonly number[];
67
+ /** Called while a user resizes a column, with the complete current widths. */
68
+ onColumnResize?: ColumnResizeHandler;
69
+ /** Smallest width, in pixels, that a user can resize a column to. */
70
+ minColumnWidth?: number;
56
71
  };
57
- declare function Table({ striped, children, ...props }: TableProps): react.JSX.Element;
72
+ /**
73
+ * A semantic table. Use its child primitives for custom markup, or pass headers
74
+ * and data for an automatically sized, striped and resizable table.
75
+ */
76
+ declare function Table({ headers, data, striped, children, ...props }: TableProps): react.JSX.Element;
58
77
  declare function TableHead(props: HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
59
78
  declare function TableBody(props: HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
60
79
  declare function TableRow({ children, ...props }: HTMLAttributes<HTMLTableRowElement>): react.JSX.Element;
@@ -62,4 +81,4 @@ declare function TableCell({ children, ...props }: HTMLAttributes<HTMLTableCellE
62
81
  declare function TableHeader({ children, ...props }: HTMLAttributes<HTMLTableCellElement>): react.JSX.Element;
63
82
  declare function TableCaption(props: HTMLAttributes<HTMLTableCaptionElement>): react.JSX.Element;
64
83
 
65
- export { Button, type ButtonColor, type ButtonHierarchy, type ButtonProps, type ButtonSize, type ButtonVariant, ChevronDownIcon, CloseIcon, type IconProps, PlusIcon, SearchIcon, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, type TableProps, TableRow, getIconSize };
84
+ export { Button, type ButtonColor, type ButtonHierarchy, type ButtonProps, type ButtonSize, type ButtonVariant, ChevronDownIcon, CloseIcon, type ColumnResizeHandler, type IconProps, PlusIcon, SearchIcon, Table, TableBody, TableCaption, TableCell, type TableData, TableHead, TableHeader, type TableProps, TableRow, getIconSize };
package/dist/index.js CHANGED
@@ -117,8 +117,118 @@ function SearchIcon(props) {
117
117
  var import_react2 = require("react");
118
118
  var import_jsx_runtime3 = require("react/jsx-runtime");
119
119
  var TableContext = (0, import_react2.createContext)({ striped: false });
120
- function Table({ striped = false, children, ...props }) {
121
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TableContext.Provider, { value: { striped }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("table", { ...props, children }) });
120
+ var MIN_COLUMN_WIDTH = 96;
121
+ var MAX_COLUMN_WIDTH = 1200;
122
+ function getColumnCount(headers, data) {
123
+ return Math.max(headers.length, ...data.map((row) => row.length), 0);
124
+ }
125
+ function getText(value) {
126
+ if (typeof value === "string" || typeof value === "number") return String(value);
127
+ if (Array.isArray(value)) return value.map(getText).join("");
128
+ if ((0, import_react2.isValidElement)(value)) return getText(value.props.children);
129
+ return "";
130
+ }
131
+ function autoColumnSize(columnIndex, headers, data) {
132
+ const values = [headers[columnIndex], ...data.map((row) => row[columnIndex])];
133
+ const longestText = values.reduce((longest, value) => Math.max(longest, getText(value).length), 0);
134
+ return Math.min(MAX_COLUMN_WIDTH, Math.max(MIN_COLUMN_WIDTH, longestText * 8 + 40));
135
+ }
136
+ function getInitialColumnSizes(headers, data, sizes, minColumnWidth = MIN_COLUMN_WIDTH) {
137
+ return Array.from({ length: getColumnCount(headers, data) }, (_, columnIndex) => {
138
+ const suppliedSize = sizes?.[columnIndex];
139
+ return typeof suppliedSize === "number" && Number.isFinite(suppliedSize) ? Math.min(MAX_COLUMN_WIDTH, Math.max(minColumnWidth, suppliedSize)) : autoColumnSize(columnIndex, headers, data);
140
+ });
141
+ }
142
+ function sameSizes(left, right) {
143
+ return left.length === right.length && left.every((size, index) => size === right[index]);
144
+ }
145
+ function DataTable({
146
+ headers,
147
+ data,
148
+ striped = true,
149
+ defaultColumnSizes,
150
+ columnSizes,
151
+ onColumnResize,
152
+ minColumnWidth = MIN_COLUMN_WIDTH,
153
+ className,
154
+ ...props
155
+ }) {
156
+ const columnCount = getColumnCount(headers, data);
157
+ const safeMinWidth = Math.max(1, minColumnWidth);
158
+ const generatedSizes = (0, import_react2.useMemo)(
159
+ () => getInitialColumnSizes(headers, data, defaultColumnSizes, safeMinWidth),
160
+ [headers, data, defaultColumnSizes, safeMinWidth]
161
+ );
162
+ const [uncontrolledSizes, setUncontrolledSizes] = (0, import_react2.useState)(generatedSizes);
163
+ const resizeState = (0, import_react2.useRef)();
164
+ const [isResizing, setIsResizing] = (0, import_react2.useState)(false);
165
+ const sizes = columnSizes ? getInitialColumnSizes(headers, data, columnSizes, safeMinWidth).map((size) => Math.max(safeMinWidth, size)) : uncontrolledSizes.map((size) => Math.max(safeMinWidth, size));
166
+ (0, import_react2.useEffect)(() => {
167
+ if (columnSizes) return;
168
+ setUncontrolledSizes((currentSizes) => {
169
+ const nextSizes = generatedSizes.map((generatedSize, index) => currentSizes[index] ?? generatedSize);
170
+ return sameSizes(currentSizes, nextSizes) ? currentSizes : nextSizes;
171
+ });
172
+ }, [columnSizes, generatedSizes]);
173
+ const updateColumnSize = (0, import_react2.useCallback)((columnIndex, nextSize) => {
174
+ const clampedSize = Math.min(MAX_COLUMN_WIDTH, Math.max(safeMinWidth, Math.round(nextSize)));
175
+ const nextSizes = sizes.map((size, index) => index === columnIndex ? clampedSize : size);
176
+ if (!columnSizes) setUncontrolledSizes(nextSizes);
177
+ onColumnResize?.(nextSizes, columnIndex);
178
+ }, [columnSizes, onColumnResize, safeMinWidth, sizes]);
179
+ (0, import_react2.useEffect)(() => {
180
+ const onPointerMove = (event) => {
181
+ const activeResize = resizeState.current;
182
+ if (!activeResize) return;
183
+ updateColumnSize(activeResize.columnIndex, activeResize.startWidth + event.clientX - activeResize.startX);
184
+ };
185
+ const stopResizing = () => {
186
+ resizeState.current = void 0;
187
+ setIsResizing(false);
188
+ };
189
+ window.addEventListener("pointermove", onPointerMove);
190
+ window.addEventListener("pointerup", stopResizing);
191
+ window.addEventListener("pointercancel", stopResizing);
192
+ return () => {
193
+ window.removeEventListener("pointermove", onPointerMove);
194
+ window.removeEventListener("pointerup", stopResizing);
195
+ window.removeEventListener("pointercancel", stopResizing);
196
+ };
197
+ }, [updateColumnSize]);
198
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fe-table", "data-resizing": isResizing || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { className: ["fe-table__table", className].filter(Boolean).join(" "), "data-fe-table": true, "data-striped": striped || void 0, ...props, children: [
199
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("colgroup", { children: sizes.map((size, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("col", { style: { width: `${size}px` } }, index)) }),
200
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("th", { scope: "col", children: [
201
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-table__header-content", children: headers[columnIndex] ?? `Column ${columnIndex + 1}` }),
202
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
203
+ "span",
204
+ {
205
+ className: "fe-table__resize-handle",
206
+ role: "separator",
207
+ "aria-label": `Resize ${getText(headers[columnIndex]) || `column ${columnIndex + 1}`}`,
208
+ "aria-orientation": "vertical",
209
+ "aria-valuemin": safeMinWidth,
210
+ "aria-valuemax": MAX_COLUMN_WIDTH,
211
+ "aria-valuenow": sizes[columnIndex] ?? safeMinWidth,
212
+ tabIndex: 0,
213
+ onPointerDown: (event) => {
214
+ event.preventDefault();
215
+ resizeState.current = { columnIndex, startX: event.clientX, startWidth: sizes[columnIndex] ?? safeMinWidth };
216
+ setIsResizing(true);
217
+ event.currentTarget.setPointerCapture(event.pointerId);
218
+ },
219
+ onKeyDown: (event) => {
220
+ if (event.key === "ArrowLeft") updateColumnSize(columnIndex, (sizes[columnIndex] ?? safeMinWidth) - 16);
221
+ if (event.key === "ArrowRight") updateColumnSize(columnIndex, (sizes[columnIndex] ?? safeMinWidth) + 16);
222
+ }
223
+ }
224
+ )
225
+ ] }, columnIndex)) }) }),
226
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tr", { children: Array.from({ length: columnCount }, (_, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { children: row[columnIndex] }, columnIndex)) }, rowIndex)) })
227
+ ] }) });
228
+ }
229
+ function Table({ headers, data, striped, children, ...props }) {
230
+ if (headers || data) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DataTable, { headers: headers ?? [], data: data ?? [], striped: striped ?? true, ...props });
231
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TableContext.Provider, { value: { striped: striped ?? false }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("table", { "data-fe-table": true, "data-striped": striped || void 0, ...props, children }) });
122
232
  }
123
233
  function TableHead(props) {
124
234
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { ...props });
package/dist/index.mjs CHANGED
@@ -76,11 +76,130 @@ function SearchIcon(props) {
76
76
  }
77
77
 
78
78
  // packages/table/src/index.tsx
79
- import { createContext, useContext } from "react";
80
- import { jsx as jsx3 } from "react/jsx-runtime";
79
+ import {
80
+ createContext,
81
+ isValidElement,
82
+ useCallback,
83
+ useContext,
84
+ useEffect,
85
+ useMemo,
86
+ useRef,
87
+ useState
88
+ } from "react";
89
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
81
90
  var TableContext = createContext({ striped: false });
82
- function Table({ striped = false, children, ...props }) {
83
- return /* @__PURE__ */ jsx3(TableContext.Provider, { value: { striped }, children: /* @__PURE__ */ jsx3("table", { ...props, children }) });
91
+ var MIN_COLUMN_WIDTH = 96;
92
+ var MAX_COLUMN_WIDTH = 1200;
93
+ function getColumnCount(headers, data) {
94
+ return Math.max(headers.length, ...data.map((row) => row.length), 0);
95
+ }
96
+ function getText(value) {
97
+ if (typeof value === "string" || typeof value === "number") return String(value);
98
+ if (Array.isArray(value)) return value.map(getText).join("");
99
+ if (isValidElement(value)) return getText(value.props.children);
100
+ return "";
101
+ }
102
+ function autoColumnSize(columnIndex, headers, data) {
103
+ const values = [headers[columnIndex], ...data.map((row) => row[columnIndex])];
104
+ const longestText = values.reduce((longest, value) => Math.max(longest, getText(value).length), 0);
105
+ return Math.min(MAX_COLUMN_WIDTH, Math.max(MIN_COLUMN_WIDTH, longestText * 8 + 40));
106
+ }
107
+ function getInitialColumnSizes(headers, data, sizes, minColumnWidth = MIN_COLUMN_WIDTH) {
108
+ return Array.from({ length: getColumnCount(headers, data) }, (_, columnIndex) => {
109
+ const suppliedSize = sizes?.[columnIndex];
110
+ return typeof suppliedSize === "number" && Number.isFinite(suppliedSize) ? Math.min(MAX_COLUMN_WIDTH, Math.max(minColumnWidth, suppliedSize)) : autoColumnSize(columnIndex, headers, data);
111
+ });
112
+ }
113
+ function sameSizes(left, right) {
114
+ return left.length === right.length && left.every((size, index) => size === right[index]);
115
+ }
116
+ function DataTable({
117
+ headers,
118
+ data,
119
+ striped = true,
120
+ defaultColumnSizes,
121
+ columnSizes,
122
+ onColumnResize,
123
+ minColumnWidth = MIN_COLUMN_WIDTH,
124
+ className,
125
+ ...props
126
+ }) {
127
+ const columnCount = getColumnCount(headers, data);
128
+ const safeMinWidth = Math.max(1, minColumnWidth);
129
+ const generatedSizes = useMemo(
130
+ () => getInitialColumnSizes(headers, data, defaultColumnSizes, safeMinWidth),
131
+ [headers, data, defaultColumnSizes, safeMinWidth]
132
+ );
133
+ const [uncontrolledSizes, setUncontrolledSizes] = useState(generatedSizes);
134
+ 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));
137
+ useEffect(() => {
138
+ if (columnSizes) return;
139
+ setUncontrolledSizes((currentSizes) => {
140
+ const nextSizes = generatedSizes.map((generatedSize, index) => currentSizes[index] ?? generatedSize);
141
+ return sameSizes(currentSizes, nextSizes) ? currentSizes : nextSizes;
142
+ });
143
+ }, [columnSizes, generatedSizes]);
144
+ const updateColumnSize = useCallback((columnIndex, nextSize) => {
145
+ const clampedSize = Math.min(MAX_COLUMN_WIDTH, Math.max(safeMinWidth, Math.round(nextSize)));
146
+ const nextSizes = sizes.map((size, index) => index === columnIndex ? clampedSize : size);
147
+ if (!columnSizes) setUncontrolledSizes(nextSizes);
148
+ onColumnResize?.(nextSizes, columnIndex);
149
+ }, [columnSizes, onColumnResize, safeMinWidth, sizes]);
150
+ useEffect(() => {
151
+ const onPointerMove = (event) => {
152
+ const activeResize = resizeState.current;
153
+ if (!activeResize) return;
154
+ updateColumnSize(activeResize.columnIndex, activeResize.startWidth + event.clientX - activeResize.startX);
155
+ };
156
+ const stopResizing = () => {
157
+ resizeState.current = void 0;
158
+ setIsResizing(false);
159
+ };
160
+ window.addEventListener("pointermove", onPointerMove);
161
+ window.addEventListener("pointerup", stopResizing);
162
+ window.addEventListener("pointercancel", stopResizing);
163
+ return () => {
164
+ window.removeEventListener("pointermove", onPointerMove);
165
+ window.removeEventListener("pointerup", stopResizing);
166
+ window.removeEventListener("pointercancel", stopResizing);
167
+ };
168
+ }, [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);
193
+ }
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
+ ] }) });
199
+ }
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 }) });
84
203
  }
85
204
  function TableHead(props) {
86
205
  return /* @__PURE__ */ jsx3("thead", { ...props });
package/dist/styles.css CHANGED
@@ -212,4 +212,88 @@
212
212
  outline-offset: 2px;
213
213
  }
214
214
 
215
+ /* packages/table/src/styles.css */
216
+ .fe-table {
217
+ width: 100%;
218
+ overflow-x: auto;
219
+ border: 1px solid var(--fe-table-border, #e3e7ee);
220
+ border-radius: var(--fe-table-radius, 10px);
221
+ background: var(--fe-table-background, #fff);
222
+ }
223
+ .fe-table__table,
224
+ table[data-fe-table] {
225
+ width: max-content;
226
+ min-width: 100%;
227
+ margin: 0;
228
+ border-collapse: collapse;
229
+ table-layout: fixed;
230
+ color: var(--fe-table-text, #242a35);
231
+ text-align: left;
232
+ }
233
+ .fe-table__table th,
234
+ .fe-table__table td,
235
+ table[data-fe-table] th,
236
+ table[data-fe-table] td {
237
+ min-width: 0;
238
+ padding: var(--fe-table-cell-padding, 12px 16px);
239
+ border-bottom: 1px solid var(--fe-table-border, #e3e7ee);
240
+ overflow-wrap: anywhere;
241
+ vertical-align: middle;
242
+ }
243
+ .fe-table__table th,
244
+ table[data-fe-table] th {
245
+ position: relative;
246
+ color: var(--fe-table-header-text, #596273);
247
+ background: var(--fe-table-header-background, #f8fafc);
248
+ font-size: var(--fe-table-header-font-size, 12px);
249
+ font-weight: 700;
250
+ letter-spacing: .04em;
251
+ text-transform: uppercase;
252
+ }
253
+ .fe-table__table tbody tr:last-child td,
254
+ table[data-fe-table] tbody tr:last-child td {
255
+ border-bottom: 0;
256
+ }
257
+ .fe-table__table[data-striped] tbody tr:nth-child(even),
258
+ table[data-fe-table][data-striped] tbody tr:nth-child(even) {
259
+ background: var(--fe-table-stripe-background, #f8fafc);
260
+ }
261
+ .fe-table__table tbody tr:hover,
262
+ table[data-fe-table] tbody tr:hover {
263
+ background: var(--fe-table-hover-background, #edf5ff);
264
+ }
265
+ .fe-table__header-content {
266
+ display: block;
267
+ padding-right: 16px;
268
+ }
269
+ .fe-table__resize-handle {
270
+ position: absolute;
271
+ z-index: 1;
272
+ top: 7px;
273
+ right: 4px;
274
+ width: 4px;
275
+ height: calc(100% - 14px);
276
+ border-radius: 4px;
277
+ background: var(--fe-table-resize-handle-background, #d6deea);
278
+ cursor: col-resize;
279
+ touch-action: none;
280
+ outline: none;
281
+ }
282
+ .fe-table__resize-handle::after {
283
+ }
284
+ .fe-table__resize-handle:hover,
285
+ .fe-table__resize-handle:focus-visible,
286
+ .fe-table[data-resizing] .fe-table__resize-handle {
287
+ background: var(--fe-table-resize-handle-active-background, #bfdbfe);
288
+ }
289
+ .fe-table__resize-handle:hover::after,
290
+ .fe-table__resize-handle:focus-visible::after,
291
+ .fe-table[data-resizing] .fe-table__resize-handle::after {
292
+ background: var(--fe-table-resize-handle, #1d4ed8);
293
+ box-shadow: -3px 0 var(--fe-table-resize-handle, #1d4ed8), 3px 0 var(--fe-table-resize-handle, #1d4ed8);
294
+ }
295
+ .fe-table__resize-handle:focus-visible {
296
+ box-shadow: 0 0 0 2px var(--fe-table-focus-ring, #2563eb);
297
+ }
298
+
215
299
  /* src/styles.css */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dragunio/fe-libs",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Reusable React components, icons, and table primitives.",
5
5
  "license": "MIT",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/kdragon113/fe-libs.git" },