uibee 3.1.4 → 3.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/components/index.js +163 -180
- package/dist/style.css +32 -14
- package/package.json +1 -1
- package/src/components/table/body.tsx +28 -31
- package/src/components/table/constants.ts +16 -1
- package/src/components/table/empty.tsx +6 -6
- package/src/components/table/header.tsx +15 -12
- package/src/components/table/skeleton.tsx +13 -16
- package/src/components/table/table.tsx +2 -2
|
@@ -2731,7 +2731,19 @@ const VARIANT_CONTAINER = {
|
|
|
2731
2731
|
};
|
|
2732
2732
|
const VARIANT_THEAD = {
|
|
2733
2733
|
original: "bg-login-700",
|
|
2734
|
-
modern: "bg-transparent
|
|
2734
|
+
modern: "bg-transparent"
|
|
2735
|
+
};
|
|
2736
|
+
const VARIANT_HEAD_BG = {
|
|
2737
|
+
original: "bg-login-700",
|
|
2738
|
+
modern: ""
|
|
2739
|
+
};
|
|
2740
|
+
const VARIANT_HEAD_BORDER = {
|
|
2741
|
+
original: "border-b border-login-600",
|
|
2742
|
+
modern: "border-b border-login-500/40"
|
|
2743
|
+
};
|
|
2744
|
+
const VARIANT_ROW_BORDER = {
|
|
2745
|
+
original: "border-b border-login-600",
|
|
2746
|
+
modern: "border-b border-login-600/15"
|
|
2735
2747
|
};
|
|
2736
2748
|
const VARIANT_THEAD_TH = {
|
|
2737
2749
|
original: "text-login-200",
|
|
@@ -2764,17 +2776,16 @@ function Header({ columns, sort, onSort, hasMenu, hasSelect, hasExpand, allSelec
|
|
|
2764
2776
|
});
|
|
2765
2777
|
}
|
|
2766
2778
|
return /* @__PURE__ */ jsx("thead", {
|
|
2767
|
-
className:
|
|
2768
|
-
children: /* @__PURE__ */ jsxs("tr", {
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2779
|
+
className: VARIANT_THEAD[variant],
|
|
2780
|
+
children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
2781
|
+
hasSelect && /* @__PURE__ */ jsx("th", {
|
|
2782
|
+
className: `sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`,
|
|
2783
|
+
style: {
|
|
2784
|
+
width: "3rem",
|
|
2785
|
+
minWidth: "3rem"
|
|
2786
|
+
},
|
|
2787
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2772
2788
|
className: "flex items-center justify-center",
|
|
2773
|
-
style: {
|
|
2774
|
-
width: "3rem",
|
|
2775
|
-
minWidth: "3rem",
|
|
2776
|
-
flexShrink: 0
|
|
2777
|
-
},
|
|
2778
2789
|
children: /* @__PURE__ */ jsxs("button", {
|
|
2779
2790
|
type: "button",
|
|
2780
2791
|
"aria-label": allSelected ? "Deselect all" : "Select all",
|
|
@@ -2796,62 +2807,58 @@ function Header({ columns, sort, onSort, hasMenu, hasSelect, hasExpand, allSelec
|
|
|
2796
2807
|
})
|
|
2797
2808
|
})]
|
|
2798
2809
|
})
|
|
2799
|
-
})
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
className: `
|
|
2812
|
-
${col.width ? "" : "flex-1"}
|
|
2810
|
+
})
|
|
2811
|
+
}),
|
|
2812
|
+
columns.map((col) => {
|
|
2813
|
+
const sortable = col.sortable !== false;
|
|
2814
|
+
const isActive = sort?.column === col.key;
|
|
2815
|
+
const ariaSort = isActive ? sort.order === "asc" ? "ascending" : "descending" : "none";
|
|
2816
|
+
const alignClass = col.align === "right" ? "justify-end" : col.align === "center" ? "justify-center" : "";
|
|
2817
|
+
return /* @__PURE__ */ jsx("th", {
|
|
2818
|
+
"aria-sort": sortable ? ariaSort : void 0,
|
|
2819
|
+
style: col.width ? { width: col.width } : void 0,
|
|
2820
|
+
className: `
|
|
2821
|
+
sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}
|
|
2813
2822
|
text-xs font-medium uppercase tracking-wider
|
|
2814
2823
|
${DENSITY_TH[density]} ${VARIANT_THEAD_TH[variant]}
|
|
2815
2824
|
`,
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
children: formatLabel(col.key, col.label)
|
|
2823
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
2824
|
-
className: "shrink-0 text-current",
|
|
2825
|
-
children: isActive ? sort.order === "asc" ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "h-3.5 w-3.5 opacity-0 group-hover:opacity-35 transition-opacity" })
|
|
2826
|
-
})]
|
|
2827
|
-
}) : /* @__PURE__ */ jsx("span", {
|
|
2828
|
-
className: `flex w-full ${alignClass}`,
|
|
2825
|
+
children: sortable ? /* @__PURE__ */ jsxs("button", {
|
|
2826
|
+
type: "button",
|
|
2827
|
+
className: `group inline-flex items-center gap-1.5 w-full cursor-pointer ${alignClass}`,
|
|
2828
|
+
onClick: () => handleSort(col.key),
|
|
2829
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2830
|
+
className: "whitespace-nowrap",
|
|
2829
2831
|
children: formatLabel(col.key, col.label)
|
|
2830
|
-
})
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
minWidth: "2.5rem",
|
|
2838
|
-
flexShrink: 0
|
|
2839
|
-
},
|
|
2840
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
2841
|
-
className: `text-xs font-medium tracking-wider uppercase opacity-50 ${VARIANT_THEAD_TH[variant]}`,
|
|
2842
|
-
children: "+"
|
|
2832
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2833
|
+
className: "shrink-0 text-current",
|
|
2834
|
+
children: isActive ? sort.order === "asc" ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "h-3.5 w-3.5 opacity-0 group-hover:opacity-35 transition-opacity" })
|
|
2835
|
+
})]
|
|
2836
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
2837
|
+
className: `flex w-full whitespace-nowrap ${alignClass}`,
|
|
2838
|
+
children: formatLabel(col.key, col.label)
|
|
2843
2839
|
})
|
|
2844
|
-
})
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2840
|
+
}, col.key);
|
|
2841
|
+
}),
|
|
2842
|
+
hasExpand && /* @__PURE__ */ jsx("th", {
|
|
2843
|
+
className: `sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`,
|
|
2844
|
+
style: {
|
|
2845
|
+
width: "2.5rem",
|
|
2846
|
+
minWidth: "2.5rem"
|
|
2847
|
+
},
|
|
2848
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
2849
|
+
className: `flex items-center justify-center text-xs font-medium tracking-wider uppercase opacity-50 ${VARIANT_THEAD_TH[variant]}`,
|
|
2850
|
+
children: "+"
|
|
2852
2851
|
})
|
|
2853
|
-
|
|
2854
|
-
|
|
2852
|
+
}),
|
|
2853
|
+
hasMenu && /* @__PURE__ */ jsx("th", {
|
|
2854
|
+
"aria-hidden": "true",
|
|
2855
|
+
className: `sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`,
|
|
2856
|
+
style: {
|
|
2857
|
+
width: "3.5rem",
|
|
2858
|
+
minWidth: "3.5rem"
|
|
2859
|
+
}
|
|
2860
|
+
})
|
|
2861
|
+
] })
|
|
2855
2862
|
});
|
|
2856
2863
|
}
|
|
2857
2864
|
//#endregion
|
|
@@ -2997,7 +3004,7 @@ function resolveRedirectUrl(redirectPath, row, id) {
|
|
|
2997
3004
|
const rid = cfg.key ? String(row[cfg.key] ?? id) : id;
|
|
2998
3005
|
return cfg.path.includes("?") ? `${cfg.path}${rid}` : `${cfg.path}/${rid}`;
|
|
2999
3006
|
}
|
|
3000
|
-
function Cell({ col, row, density }) {
|
|
3007
|
+
function Cell({ col, row, density, variant }) {
|
|
3001
3008
|
const value = row[col.key];
|
|
3002
3009
|
const shouldTruncate = col.truncate === true;
|
|
3003
3010
|
const align = col.align ?? "left";
|
|
@@ -3025,16 +3032,13 @@ function Cell({ col, row, density }) {
|
|
|
3025
3032
|
return /* @__PURE__ */ jsx("td", {
|
|
3026
3033
|
style: col.width ? {
|
|
3027
3034
|
width: col.width,
|
|
3028
|
-
|
|
3035
|
+
maxWidth: col.width
|
|
3029
3036
|
} : void 0,
|
|
3030
3037
|
className: `
|
|
3031
|
-
|
|
3032
|
-
${DENSITY_TD[density]}
|
|
3038
|
+
align-middle text-sm text-login-75 ${wrapperAlign}
|
|
3039
|
+
${DENSITY_TD[density]} ${VARIANT_ROW_BORDER[variant]}
|
|
3033
3040
|
`,
|
|
3034
|
-
children:
|
|
3035
|
-
className: `min-w-0 w-full ${wrapperAlign}`,
|
|
3036
|
-
children: content
|
|
3037
|
-
})
|
|
3041
|
+
children: content
|
|
3038
3042
|
});
|
|
3039
3043
|
}
|
|
3040
3044
|
function Body({ data, columns, idKey, variant, density, striped, redirectPath, onRowClick, renderExpandedRow, selectable, selectedIds, onSelectionChange, menuItems }) {
|
|
@@ -3043,16 +3047,14 @@ function Body({ data, columns, idKey, variant, density, striped, redirectPath, o
|
|
|
3043
3047
|
const [anchor, setAnchor] = useState(null);
|
|
3044
3048
|
const [expandedId, setExpandedId] = useState(null);
|
|
3045
3049
|
const menuRef = useRef(null);
|
|
3046
|
-
const tbodyRef = useRef(null);
|
|
3047
3050
|
const menuWasOpenOnMouseDown = useRef(false);
|
|
3048
3051
|
const closeMenu = useCallback(() => setOpenMenuId(null), []);
|
|
3049
3052
|
useClickOutside(menuRef, closeMenu, openMenuId !== null);
|
|
3050
3053
|
useEffect(() => {
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
}, [closeMenu]);
|
|
3054
|
+
if (openMenuId === null) return;
|
|
3055
|
+
document.addEventListener("scroll", closeMenu, true);
|
|
3056
|
+
return () => document.removeEventListener("scroll", closeMenu, true);
|
|
3057
|
+
}, [closeMenu, openMenuId]);
|
|
3056
3058
|
function openMenu(id, coords) {
|
|
3057
3059
|
setAnchor(coords);
|
|
3058
3060
|
setOpenMenuId(id);
|
|
@@ -3064,8 +3066,7 @@ function Body({ data, columns, idKey, variant, density, striped, redirectPath, o
|
|
|
3064
3066
|
const hasMenu = Boolean(menuItems);
|
|
3065
3067
|
const hasExpand = Boolean(renderExpandedRow);
|
|
3066
3068
|
return /* @__PURE__ */ jsx("tbody", {
|
|
3067
|
-
|
|
3068
|
-
className: `block overflow-y-auto flex-1 min-h-0 divide-y ${VARIANT_TBODY[variant]}`,
|
|
3069
|
+
className: VARIANT_TBODY[variant],
|
|
3069
3070
|
children: data.map((row, rowIdx) => {
|
|
3070
3071
|
const id = resolveId(row, idKey, columns);
|
|
3071
3072
|
const url = resolveRedirectUrl(redirectPath, row, id);
|
|
@@ -3075,7 +3076,7 @@ function Body({ data, columns, idKey, variant, density, striped, redirectPath, o
|
|
|
3075
3076
|
const isSelected = selectable && selectedSet.has(id);
|
|
3076
3077
|
const expandedContent = renderExpandedRow?.(row);
|
|
3077
3078
|
const rowClass = [
|
|
3078
|
-
"
|
|
3079
|
+
"transition-colors duration-100",
|
|
3079
3080
|
isClickable ? "cursor-pointer" : "",
|
|
3080
3081
|
VARIANT_ROW_HOVER[variant],
|
|
3081
3082
|
striped ? VARIANT_ROW_STRIPED[variant] : "",
|
|
@@ -3114,35 +3115,37 @@ function Body({ data, columns, idKey, variant, density, striped, redirectPath, o
|
|
|
3114
3115
|
},
|
|
3115
3116
|
children: [
|
|
3116
3117
|
selectable && /* @__PURE__ */ jsx("td", {
|
|
3117
|
-
className:
|
|
3118
|
+
className: `align-middle ${VARIANT_ROW_BORDER[variant]}`,
|
|
3118
3119
|
style: {
|
|
3119
3120
|
width: "3rem",
|
|
3120
|
-
minWidth: "3rem"
|
|
3121
|
-
flexShrink: 0
|
|
3121
|
+
minWidth: "3rem"
|
|
3122
3122
|
},
|
|
3123
|
-
children: /* @__PURE__ */ jsx("
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3123
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
3124
|
+
className: "flex items-center justify-center",
|
|
3125
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
3126
|
+
type: "button",
|
|
3127
|
+
"aria-label": isSelected ? "Deselect row" : "Select row",
|
|
3128
|
+
"aria-checked": isSelected,
|
|
3129
|
+
role: "checkbox",
|
|
3130
|
+
onClick: (e) => {
|
|
3131
|
+
e.stopPropagation();
|
|
3132
|
+
toggleSelect(id);
|
|
3133
|
+
},
|
|
3134
|
+
className: `
|
|
3133
3135
|
h-4 w-4 rounded border flex items-center justify-center transition-colors cursor-pointer
|
|
3134
3136
|
${isSelected ? "bg-login border-login text-white" : "border-login-400 bg-transparent hover:border-login-100"}
|
|
3135
3137
|
`,
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3138
|
+
children: isSelected && /* @__PURE__ */ jsx("svg", {
|
|
3139
|
+
className: "h-3 w-3",
|
|
3140
|
+
viewBox: "0 0 12 12",
|
|
3141
|
+
fill: "none",
|
|
3142
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
3143
|
+
d: "M2 6l3 3 5-5",
|
|
3144
|
+
stroke: "currentColor",
|
|
3145
|
+
strokeWidth: "1.5",
|
|
3146
|
+
strokeLinecap: "round",
|
|
3147
|
+
strokeLinejoin: "round"
|
|
3148
|
+
})
|
|
3146
3149
|
})
|
|
3147
3150
|
})
|
|
3148
3151
|
})
|
|
@@ -3150,29 +3153,31 @@ function Body({ data, columns, idKey, variant, density, striped, redirectPath, o
|
|
|
3150
3153
|
columns.map((col) => /* @__PURE__ */ jsx(Cell, {
|
|
3151
3154
|
col,
|
|
3152
3155
|
row,
|
|
3153
|
-
density
|
|
3156
|
+
density,
|
|
3157
|
+
variant
|
|
3154
3158
|
}, col.key)),
|
|
3155
3159
|
hasExpand && /* @__PURE__ */ jsx("td", {
|
|
3156
|
-
className:
|
|
3160
|
+
className: `align-middle ${VARIANT_ROW_BORDER[variant]}`,
|
|
3157
3161
|
style: {
|
|
3158
3162
|
width: "2.5rem",
|
|
3159
|
-
minWidth: "2.5rem"
|
|
3160
|
-
flexShrink: 0
|
|
3163
|
+
minWidth: "2.5rem"
|
|
3161
3164
|
},
|
|
3162
|
-
children: /* @__PURE__ */ jsx(
|
|
3165
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
3166
|
+
className: "flex items-center justify-center",
|
|
3167
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: `
|
|
3163
3168
|
h-4 w-4 transition-transform duration-200
|
|
3164
3169
|
${isExpanded ? "rotate-180 text-login" : "text-login-400"}
|
|
3165
3170
|
` })
|
|
3171
|
+
})
|
|
3166
3172
|
}),
|
|
3167
3173
|
hasMenu && /* @__PURE__ */ jsx("td", {
|
|
3168
|
-
className:
|
|
3174
|
+
className: `align-middle pr-3 ${VARIANT_ROW_BORDER[variant]}`,
|
|
3169
3175
|
style: {
|
|
3170
3176
|
width: "3.5rem",
|
|
3171
|
-
minWidth: "3.5rem"
|
|
3172
|
-
flexShrink: 0
|
|
3177
|
+
minWidth: "3.5rem"
|
|
3173
3178
|
},
|
|
3174
3179
|
children: /* @__PURE__ */ jsxs("div", {
|
|
3175
|
-
className: "relative",
|
|
3180
|
+
className: "relative flex items-center justify-end",
|
|
3176
3181
|
children: [/* @__PURE__ */ jsx("button", {
|
|
3177
3182
|
type: "button",
|
|
3178
3183
|
"aria-label": "Row actions",
|
|
@@ -3205,9 +3210,10 @@ function Body({ data, columns, idKey, variant, density, striped, redirectPath, o
|
|
|
3205
3210
|
})
|
|
3206
3211
|
]
|
|
3207
3212
|
}), hasExpand && isExpanded && /* @__PURE__ */ jsx("tr", {
|
|
3208
|
-
className: "
|
|
3213
|
+
className: "bg-login-700/25",
|
|
3209
3214
|
children: /* @__PURE__ */ jsx("td", {
|
|
3210
|
-
|
|
3215
|
+
colSpan: 999,
|
|
3216
|
+
className: "px-6 py-4 border-b border-login-600/20",
|
|
3211
3217
|
onClick: (e) => e.stopPropagation(),
|
|
3212
3218
|
children: expandedContent
|
|
3213
3219
|
})
|
|
@@ -3219,81 +3225,58 @@ function Body({ data, columns, idKey, variant, density, striped, redirectPath, o
|
|
|
3219
3225
|
//#region src/components/table/skeleton.tsx
|
|
3220
3226
|
function Skeleton({ columns, rows, variant, density, hasMenu, hasSelect }) {
|
|
3221
3227
|
return /* @__PURE__ */ jsx("tbody", {
|
|
3222
|
-
className:
|
|
3223
|
-
children: Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsxs("tr", {
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
}
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
width: col.width,
|
|
3238
|
-
flexShrink: 0
|
|
3239
|
-
} : void 0,
|
|
3240
|
-
className: `
|
|
3241
|
-
flex-1 min-w-0 flex items-center
|
|
3242
|
-
${DENSITY_TD[density]}
|
|
3243
|
-
`,
|
|
3244
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
3245
|
-
className: "block h-4 rounded animate-shimmer",
|
|
3246
|
-
style: {
|
|
3247
|
-
width: colIdx === 0 ? `${55 + (rowIdx * 17 + colIdx * 31) % 30}%` : `${40 + (rowIdx * 13 + colIdx * 23) % 40}%`,
|
|
3248
|
-
animationDelay: `${(rowIdx * columns.length + colIdx) * 40}ms`
|
|
3249
|
-
}
|
|
3250
|
-
})
|
|
3251
|
-
}, col.key)),
|
|
3252
|
-
hasMenu && /* @__PURE__ */ jsx("td", {
|
|
3253
|
-
className: "flex items-center justify-end pr-3",
|
|
3228
|
+
className: VARIANT_TBODY[variant],
|
|
3229
|
+
children: Array.from({ length: rows }).map((_, rowIdx) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
3230
|
+
hasSelect && /* @__PURE__ */ jsx("td", {
|
|
3231
|
+
className: `align-middle ${VARIANT_ROW_BORDER[variant]}`,
|
|
3232
|
+
style: {
|
|
3233
|
+
width: "3rem",
|
|
3234
|
+
minWidth: "3rem"
|
|
3235
|
+
},
|
|
3236
|
+
children: /* @__PURE__ */ jsx("span", { className: "block h-4 w-4 mx-auto rounded animate-shimmer" })
|
|
3237
|
+
}),
|
|
3238
|
+
columns.map((col, colIdx) => /* @__PURE__ */ jsx("td", {
|
|
3239
|
+
style: col.width ? { width: col.width } : void 0,
|
|
3240
|
+
className: `align-middle ${DENSITY_TD[density]} ${VARIANT_ROW_BORDER[variant]}`,
|
|
3241
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
3242
|
+
className: "block h-4 rounded animate-shimmer",
|
|
3254
3243
|
style: {
|
|
3255
|
-
width:
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
},
|
|
3259
|
-
children: /* @__PURE__ */ jsx("span", { className: "block h-5 w-5 rounded animate-shimmer" })
|
|
3244
|
+
width: colIdx === 0 ? `${8 + (rowIdx * 17 + colIdx * 31) % 5}rem` : `${5 + (rowIdx * 13 + colIdx * 23) % 5}rem`,
|
|
3245
|
+
animationDelay: `${(rowIdx * columns.length + colIdx) * 40}ms`
|
|
3246
|
+
}
|
|
3260
3247
|
})
|
|
3261
|
-
|
|
3262
|
-
|
|
3248
|
+
}, col.key)),
|
|
3249
|
+
hasMenu && /* @__PURE__ */ jsx("td", {
|
|
3250
|
+
className: `align-middle pr-3 ${VARIANT_ROW_BORDER[variant]}`,
|
|
3251
|
+
style: {
|
|
3252
|
+
width: "3.5rem",
|
|
3253
|
+
minWidth: "3.5rem"
|
|
3254
|
+
},
|
|
3255
|
+
children: /* @__PURE__ */ jsx("span", { className: "block h-5 w-5 ml-auto rounded animate-shimmer" })
|
|
3256
|
+
})
|
|
3257
|
+
] }, rowIdx))
|
|
3263
3258
|
});
|
|
3264
3259
|
}
|
|
3265
3260
|
//#endregion
|
|
3266
3261
|
//#region src/components/table/empty.tsx
|
|
3267
3262
|
function Empty({ emptyState }) {
|
|
3268
|
-
if (emptyState) return /* @__PURE__ */ jsx("tbody", {
|
|
3269
|
-
|
|
3270
|
-
children: /* @__PURE__ */ jsx("
|
|
3271
|
-
className: "flex
|
|
3272
|
-
children:
|
|
3273
|
-
className: "w-full",
|
|
3274
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
3275
|
-
className: "flex items-center justify-center min-h-[160px] py-8 px-6",
|
|
3276
|
-
children: emptyState
|
|
3277
|
-
})
|
|
3278
|
-
})
|
|
3263
|
+
if (emptyState) return /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", {
|
|
3264
|
+
colSpan: 999,
|
|
3265
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
3266
|
+
className: "flex items-center justify-center min-h-[160px] py-8 px-6",
|
|
3267
|
+
children: emptyState
|
|
3279
3268
|
})
|
|
3280
|
-
});
|
|
3281
|
-
return /* @__PURE__ */ jsx("tbody", {
|
|
3282
|
-
|
|
3283
|
-
children: /* @__PURE__ */
|
|
3284
|
-
className: "flex
|
|
3285
|
-
children: /* @__PURE__ */ jsx("
|
|
3286
|
-
className: "
|
|
3287
|
-
children:
|
|
3288
|
-
|
|
3289
|
-
children: [/* @__PURE__ */ jsx(TableIcon, { className: "h-10 w-10 opacity-30" }), /* @__PURE__ */ jsx("span", {
|
|
3290
|
-
className: "text-sm",
|
|
3291
|
-
children: "No data found"
|
|
3292
|
-
})]
|
|
3293
|
-
})
|
|
3294
|
-
})
|
|
3269
|
+
}) }) });
|
|
3270
|
+
return /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", {
|
|
3271
|
+
colSpan: 999,
|
|
3272
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
3273
|
+
className: "flex flex-col items-center justify-center gap-3 min-h-[160px] py-8 text-login-300",
|
|
3274
|
+
children: [/* @__PURE__ */ jsx(TableIcon, { className: "h-10 w-10 opacity-30" }), /* @__PURE__ */ jsx("span", {
|
|
3275
|
+
className: "text-sm",
|
|
3276
|
+
children: "No data found"
|
|
3277
|
+
})]
|
|
3295
3278
|
})
|
|
3296
|
-
});
|
|
3279
|
+
}) }) });
|
|
3297
3280
|
}
|
|
3298
3281
|
//#endregion
|
|
3299
3282
|
//#region src/components/table/pagination.tsx
|
|
@@ -3443,9 +3426,9 @@ function TableShell({ data, columns, idKey, variant, density, striped, loading,
|
|
|
3443
3426
|
className
|
|
3444
3427
|
].filter(Boolean).join(" "),
|
|
3445
3428
|
children: [/* @__PURE__ */ jsx("div", {
|
|
3446
|
-
className: "flex-1 min-h-0 overflow-
|
|
3429
|
+
className: "flex-1 min-h-0 overflow-auto",
|
|
3447
3430
|
children: /* @__PURE__ */ jsxs("table", {
|
|
3448
|
-
className: "min-w-full
|
|
3431
|
+
className: "min-w-full table-auto border-separate border-spacing-0",
|
|
3449
3432
|
children: [/* @__PURE__ */ jsx(Header, {
|
|
3450
3433
|
columns,
|
|
3451
3434
|
sort,
|
package/dist/style.css
CHANGED
|
@@ -1580,6 +1580,9 @@
|
|
|
1580
1580
|
.ml-1 {
|
|
1581
1581
|
margin-left: var(--spacing);
|
|
1582
1582
|
}
|
|
1583
|
+
.ml-auto {
|
|
1584
|
+
margin-left: auto;
|
|
1585
|
+
}
|
|
1583
1586
|
.noscroll {
|
|
1584
1587
|
-ms-overflow-style: none;
|
|
1585
1588
|
scrollbar-width: none;
|
|
@@ -1777,9 +1780,6 @@
|
|
|
1777
1780
|
.w-full {
|
|
1778
1781
|
width: 100%;
|
|
1779
1782
|
}
|
|
1780
|
-
.w-max {
|
|
1781
|
-
width: max-content;
|
|
1782
|
-
}
|
|
1783
1783
|
.max-w-2xl {
|
|
1784
1784
|
max-width: var(--container-2xl);
|
|
1785
1785
|
}
|
|
@@ -1837,6 +1837,17 @@
|
|
|
1837
1837
|
.shrink-0 {
|
|
1838
1838
|
flex-shrink: 0;
|
|
1839
1839
|
}
|
|
1840
|
+
.table-auto {
|
|
1841
|
+
table-layout: auto;
|
|
1842
|
+
}
|
|
1843
|
+
.border-separate {
|
|
1844
|
+
border-collapse: separate;
|
|
1845
|
+
}
|
|
1846
|
+
.border-spacing-0 {
|
|
1847
|
+
--tw-border-spacing-x: 0;
|
|
1848
|
+
--tw-border-spacing-y: 0;
|
|
1849
|
+
border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y);
|
|
1850
|
+
}
|
|
1840
1851
|
.origin-center {
|
|
1841
1852
|
transform-origin: center;
|
|
1842
1853
|
}
|
|
@@ -2013,14 +2024,6 @@
|
|
|
2013
2024
|
}
|
|
2014
2025
|
}
|
|
2015
2026
|
}
|
|
2016
|
-
.divide-login-600\/20 {
|
|
2017
|
-
:where(& > :not(:last-child)) {
|
|
2018
|
-
border-color: color-mix(in srgb, #212121 20%, transparent);
|
|
2019
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
2020
|
-
border-color: color-mix(in oklab, var(--color-login-600) 20%, transparent);
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
2027
|
.truncate {
|
|
2025
2028
|
overflow: hidden;
|
|
2026
2029
|
text-overflow: ellipsis;
|
|
@@ -2032,9 +2035,6 @@
|
|
|
2032
2035
|
.overflow-hidden {
|
|
2033
2036
|
overflow: hidden;
|
|
2034
2037
|
}
|
|
2035
|
-
.overflow-x-auto {
|
|
2036
|
-
overflow-x: auto;
|
|
2037
|
-
}
|
|
2038
2038
|
.overflow-y-auto {
|
|
2039
2039
|
overflow-y: auto;
|
|
2040
2040
|
}
|
|
@@ -2143,6 +2143,12 @@
|
|
|
2143
2143
|
.border-login-600 {
|
|
2144
2144
|
border-color: var(--color-login-600);
|
|
2145
2145
|
}
|
|
2146
|
+
.border-login-600\/15 {
|
|
2147
|
+
border-color: color-mix(in srgb, #212121 15%, transparent);
|
|
2148
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
2149
|
+
border-color: color-mix(in oklab, var(--color-login-600) 15%, transparent);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2146
2152
|
.border-login-600\/20 {
|
|
2147
2153
|
border-color: color-mix(in srgb, #212121 20%, transparent);
|
|
2148
2154
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -3826,6 +3832,16 @@ input::-ms-reveal,
|
|
|
3826
3832
|
input::-ms-clear {
|
|
3827
3833
|
display: none !important;
|
|
3828
3834
|
}
|
|
3835
|
+
@property --tw-border-spacing-x {
|
|
3836
|
+
syntax: "<length>";
|
|
3837
|
+
inherits: false;
|
|
3838
|
+
initial-value: 0;
|
|
3839
|
+
}
|
|
3840
|
+
@property --tw-border-spacing-y {
|
|
3841
|
+
syntax: "<length>";
|
|
3842
|
+
inherits: false;
|
|
3843
|
+
initial-value: 0;
|
|
3844
|
+
}
|
|
3829
3845
|
@property --tw-translate-x {
|
|
3830
3846
|
syntax: "*";
|
|
3831
3847
|
inherits: false;
|
|
@@ -4137,6 +4153,8 @@ input::-ms-clear {
|
|
|
4137
4153
|
@layer properties {
|
|
4138
4154
|
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
|
4139
4155
|
*, ::before, ::after, ::backdrop {
|
|
4156
|
+
--tw-border-spacing-x: 0;
|
|
4157
|
+
--tw-border-spacing-y: 0;
|
|
4140
4158
|
--tw-translate-x: 0;
|
|
4141
4159
|
--tw-translate-y: 0;
|
|
4142
4160
|
--tw-translate-z: 0;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import React, { useState, useRef, useEffect, useCallback, useMemo, type RefObjec
|
|
|
4
4
|
import { useRouter } from 'next/navigation'
|
|
5
5
|
import { EllipsisVertical, ChevronDown } from 'lucide-react'
|
|
6
6
|
import type { Column, Density, TableVariant, MenuAnchor, TableShellProps } from './types'
|
|
7
|
-
import { HIGHLIGHT, DENSITY_TD, VARIANT_TBODY, VARIANT_ROW_HOVER, VARIANT_ROW_STRIPED } from './constants'
|
|
7
|
+
import { HIGHLIGHT, DENSITY_TD, VARIANT_TBODY, VARIANT_ROW_HOVER, VARIANT_ROW_STRIPED, VARIANT_ROW_BORDER } from './constants'
|
|
8
8
|
import { formatValue } from './format'
|
|
9
9
|
import { resolveId } from './utils'
|
|
10
10
|
import Menu from './menu'
|
|
@@ -38,13 +38,14 @@ function Cell<T extends Record<string, unknown>>({
|
|
|
38
38
|
col,
|
|
39
39
|
row,
|
|
40
40
|
density,
|
|
41
|
+
variant,
|
|
41
42
|
}: {
|
|
42
43
|
col: Column<T>
|
|
43
44
|
row: T
|
|
44
45
|
density: Density
|
|
46
|
+
variant: TableVariant
|
|
45
47
|
}) {
|
|
46
48
|
const value = row[col.key]
|
|
47
|
-
// Truncate: opt-in, only applied when col.truncate === true
|
|
48
49
|
const shouldTruncate = col.truncate === true
|
|
49
50
|
const align = col.align ?? 'left'
|
|
50
51
|
|
|
@@ -55,7 +56,6 @@ function Cell<T extends Record<string, unknown>>({
|
|
|
55
56
|
let content: React.ReactNode
|
|
56
57
|
|
|
57
58
|
if (col.render) {
|
|
58
|
-
// Custom renderer: caller is responsible for alignment
|
|
59
59
|
content = col.render(value, row)
|
|
60
60
|
} else if (col.highlight) {
|
|
61
61
|
const colorName = col.highlight[String(value)] ?? col.highlight['default']
|
|
@@ -84,16 +84,13 @@ function Cell<T extends Record<string, unknown>>({
|
|
|
84
84
|
|
|
85
85
|
return (
|
|
86
86
|
<td
|
|
87
|
-
style={col.width ? { width: col.width,
|
|
87
|
+
style={col.width ? { width: col.width, maxWidth: col.width } : undefined}
|
|
88
88
|
className={`
|
|
89
|
-
|
|
90
|
-
${DENSITY_TD[density]}
|
|
89
|
+
align-middle text-sm text-login-75 ${wrapperAlign}
|
|
90
|
+
${DENSITY_TD[density]} ${VARIANT_ROW_BORDER[variant]}
|
|
91
91
|
`}
|
|
92
92
|
>
|
|
93
|
-
{
|
|
94
|
-
<div className={`min-w-0 w-full ${wrapperAlign}`}>
|
|
95
|
-
{content}
|
|
96
|
-
</div>
|
|
93
|
+
{content}
|
|
97
94
|
</td>
|
|
98
95
|
)
|
|
99
96
|
}
|
|
@@ -118,18 +115,16 @@ export default function Body<T extends Record<string, unknown>>({
|
|
|
118
115
|
const [expandedId, setExpandedId] = useState<string | null>(null)
|
|
119
116
|
|
|
120
117
|
const menuRef = useRef<HTMLDivElement>(null)
|
|
121
|
-
const tbodyRef = useRef<HTMLTableSectionElement>(null)
|
|
122
118
|
const menuWasOpenOnMouseDown = useRef(false)
|
|
123
119
|
|
|
124
120
|
const closeMenu = useCallback(() => setOpenMenuId(null), [])
|
|
125
121
|
useClickOutside(menuRef as RefObject<HTMLElement>, closeMenu, openMenuId !== null)
|
|
126
122
|
|
|
127
123
|
useEffect(() => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}, [closeMenu])
|
|
124
|
+
if (openMenuId === null) return
|
|
125
|
+
document.addEventListener('scroll', closeMenu, true)
|
|
126
|
+
return () => document.removeEventListener('scroll', closeMenu, true)
|
|
127
|
+
}, [closeMenu, openMenuId])
|
|
133
128
|
|
|
134
129
|
function openMenu(id: string, coords: MenuAnchor) {
|
|
135
130
|
setAnchor(coords)
|
|
@@ -149,10 +144,7 @@ export default function Body<T extends Record<string, unknown>>({
|
|
|
149
144
|
const hasExpand = Boolean(renderExpandedRow)
|
|
150
145
|
|
|
151
146
|
return (
|
|
152
|
-
<tbody
|
|
153
|
-
ref={tbodyRef}
|
|
154
|
-
className={`block overflow-y-auto flex-1 min-h-0 divide-y ${VARIANT_TBODY[variant]}`}
|
|
155
|
-
>
|
|
147
|
+
<tbody className={VARIANT_TBODY[variant]}>
|
|
156
148
|
{data.map((row, rowIdx) => {
|
|
157
149
|
const id = resolveId(row as Record<string, unknown>, idKey as string | undefined, columns as Column<Record<string, unknown>>[])
|
|
158
150
|
const url = resolveRedirectUrl(redirectPath, row as Record<string, unknown>, id)
|
|
@@ -163,7 +155,7 @@ export default function Body<T extends Record<string, unknown>>({
|
|
|
163
155
|
const expandedContent = renderExpandedRow?.(row)
|
|
164
156
|
|
|
165
157
|
const rowClass = [
|
|
166
|
-
'
|
|
158
|
+
'transition-colors duration-100',
|
|
167
159
|
isClickable ? 'cursor-pointer' : '',
|
|
168
160
|
VARIANT_ROW_HOVER[variant],
|
|
169
161
|
striped ? VARIANT_ROW_STRIPED[variant] : '',
|
|
@@ -197,8 +189,9 @@ export default function Body<T extends Record<string, unknown>>({
|
|
|
197
189
|
}}
|
|
198
190
|
>
|
|
199
191
|
{selectable && (
|
|
200
|
-
<td className=
|
|
201
|
-
style={{ width: '3rem', minWidth: '3rem'
|
|
192
|
+
<td className={`align-middle ${VARIANT_ROW_BORDER[variant]}`}
|
|
193
|
+
style={{ width: '3rem', minWidth: '3rem' }}>
|
|
194
|
+
<div className='flex items-center justify-center'>
|
|
202
195
|
<button
|
|
203
196
|
type='button'
|
|
204
197
|
aria-label={isSelected ? 'Deselect row' : 'Select row'}
|
|
@@ -219,27 +212,30 @@ export default function Body<T extends Record<string, unknown>>({
|
|
|
219
212
|
</svg>
|
|
220
213
|
)}
|
|
221
214
|
</button>
|
|
215
|
+
</div>
|
|
222
216
|
</td>
|
|
223
217
|
)}
|
|
224
218
|
|
|
225
219
|
{columns.map((col) => (
|
|
226
|
-
<Cell key={col.key} col={col} row={row} density={density} />
|
|
220
|
+
<Cell key={col.key} col={col} row={row} density={density} variant={variant} />
|
|
227
221
|
))}
|
|
228
222
|
|
|
229
223
|
{hasExpand && (
|
|
230
|
-
<td className=
|
|
231
|
-
style={{ width: '2.5rem', minWidth: '2.5rem'
|
|
224
|
+
<td className={`align-middle ${VARIANT_ROW_BORDER[variant]}`}
|
|
225
|
+
style={{ width: '2.5rem', minWidth: '2.5rem' }}>
|
|
226
|
+
<div className='flex items-center justify-center'>
|
|
232
227
|
<ChevronDown className={`
|
|
233
228
|
h-4 w-4 transition-transform duration-200
|
|
234
229
|
${isExpanded ? 'rotate-180 text-login' : 'text-login-400'}
|
|
235
230
|
`} />
|
|
231
|
+
</div>
|
|
236
232
|
</td>
|
|
237
233
|
)}
|
|
238
234
|
|
|
239
235
|
{hasMenu && (
|
|
240
|
-
<td className=
|
|
241
|
-
style={{ width: '3.5rem', minWidth: '3.5rem'
|
|
242
|
-
<div className='relative'>
|
|
236
|
+
<td className={`align-middle pr-3 ${VARIANT_ROW_BORDER[variant]}`}
|
|
237
|
+
style={{ width: '3.5rem', minWidth: '3.5rem' }}>
|
|
238
|
+
<div className='relative flex items-center justify-end'>
|
|
243
239
|
<button
|
|
244
240
|
type='button'
|
|
245
241
|
aria-label='Row actions'
|
|
@@ -276,9 +272,10 @@ export default function Body<T extends Record<string, unknown>>({
|
|
|
276
272
|
</tr>
|
|
277
273
|
|
|
278
274
|
{hasExpand && isExpanded && (
|
|
279
|
-
<tr className='
|
|
275
|
+
<tr className='bg-login-700/25'>
|
|
280
276
|
<td
|
|
281
|
-
|
|
277
|
+
colSpan={999}
|
|
278
|
+
className='px-6 py-4 border-b border-login-600/20'
|
|
282
279
|
onClick={(e) => e.stopPropagation()}
|
|
283
280
|
>
|
|
284
281
|
{expandedContent}
|
|
@@ -29,7 +29,22 @@ export const VARIANT_CONTAINER: Record<TableVariant, string> = {
|
|
|
29
29
|
|
|
30
30
|
export const VARIANT_THEAD: Record<TableVariant, string> = {
|
|
31
31
|
original: 'bg-login-700',
|
|
32
|
-
modern: 'bg-transparent
|
|
32
|
+
modern: 'bg-transparent',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const VARIANT_HEAD_BG: Record<TableVariant, string> = {
|
|
36
|
+
original: 'bg-login-700',
|
|
37
|
+
modern: '',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const VARIANT_HEAD_BORDER: Record<TableVariant, string> = {
|
|
41
|
+
original: 'border-b border-login-600',
|
|
42
|
+
modern: 'border-b border-login-500/40',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const VARIANT_ROW_BORDER: Record<TableVariant, string> = {
|
|
46
|
+
original: 'border-b border-login-600',
|
|
47
|
+
modern: 'border-b border-login-600/15',
|
|
33
48
|
}
|
|
34
49
|
|
|
35
50
|
export const VARIANT_THEAD_TH: Record<TableVariant, string> = {
|
|
@@ -4,9 +4,9 @@ import type { ReactNode } from 'react'
|
|
|
4
4
|
export default function Empty({ emptyState }: { emptyState?: ReactNode }) {
|
|
5
5
|
if (emptyState) {
|
|
6
6
|
return (
|
|
7
|
-
<tbody
|
|
8
|
-
<tr
|
|
9
|
-
<td
|
|
7
|
+
<tbody>
|
|
8
|
+
<tr>
|
|
9
|
+
<td colSpan={999}>
|
|
10
10
|
<div className='flex items-center justify-center min-h-[160px] py-8 px-6'>
|
|
11
11
|
{emptyState}
|
|
12
12
|
</div>
|
|
@@ -17,9 +17,9 @@ export default function Empty({ emptyState }: { emptyState?: ReactNode }) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
|
-
<tbody
|
|
21
|
-
<tr
|
|
22
|
-
<td
|
|
20
|
+
<tbody>
|
|
21
|
+
<tr>
|
|
22
|
+
<td colSpan={999}>
|
|
23
23
|
<div className='flex flex-col items-center justify-center gap-3 min-h-[160px] py-8 text-login-300'>
|
|
24
24
|
<TableIcon className='h-10 w-10 opacity-30' />
|
|
25
25
|
<span className='text-sm'>No data found</span>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { ChevronUp, ChevronDown, ChevronsUpDown } from 'lucide-react'
|
|
4
4
|
import type { Column, SortState, Density, TableVariant } from './types'
|
|
5
|
-
import { DENSITY_TH, VARIANT_THEAD, VARIANT_THEAD_TH } from './constants'
|
|
5
|
+
import { DENSITY_TH, VARIANT_THEAD, VARIANT_THEAD_TH, VARIANT_HEAD_BORDER, VARIANT_HEAD_BG } from './constants'
|
|
6
6
|
|
|
7
7
|
type HeaderProps<T extends Record<string, unknown>> = {
|
|
8
8
|
columns: Column<T>[]
|
|
@@ -43,12 +43,13 @@ export default function Header<T extends Record<string, unknown>>({
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
|
-
<thead className={
|
|
47
|
-
<tr
|
|
46
|
+
<thead className={VARIANT_THEAD[variant]}>
|
|
47
|
+
<tr>
|
|
48
48
|
|
|
49
49
|
{hasSelect && (
|
|
50
|
-
<th className=
|
|
51
|
-
style={{ width: '3rem', minWidth: '3rem'
|
|
50
|
+
<th className={`sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`}
|
|
51
|
+
style={{ width: '3rem', minWidth: '3rem' }}>
|
|
52
|
+
<div className='flex items-center justify-center'>
|
|
52
53
|
<button
|
|
53
54
|
type='button'
|
|
54
55
|
aria-label={allSelected ? 'Deselect all' : 'Select all'}
|
|
@@ -70,6 +71,7 @@ export default function Header<T extends Record<string, unknown>>({
|
|
|
70
71
|
</svg>
|
|
71
72
|
)}
|
|
72
73
|
</button>
|
|
74
|
+
</div>
|
|
73
75
|
</th>
|
|
74
76
|
)}
|
|
75
77
|
|
|
@@ -88,9 +90,9 @@ export default function Header<T extends Record<string, unknown>>({
|
|
|
88
90
|
<th
|
|
89
91
|
key={col.key}
|
|
90
92
|
aria-sort={sortable ? ariaSort : undefined}
|
|
91
|
-
style={col.width ? { width: col.width
|
|
93
|
+
style={col.width ? { width: col.width } : undefined}
|
|
92
94
|
className={`
|
|
93
|
-
|
|
95
|
+
sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}
|
|
94
96
|
text-xs font-medium uppercase tracking-wider
|
|
95
97
|
${DENSITY_TH[density]} ${VARIANT_THEAD_TH[variant]}
|
|
96
98
|
`}
|
|
@@ -113,7 +115,7 @@ export default function Header<T extends Record<string, unknown>>({
|
|
|
113
115
|
</span>
|
|
114
116
|
</button>
|
|
115
117
|
) : (
|
|
116
|
-
<span className={`flex w-full ${alignClass}`}>
|
|
118
|
+
<span className={`flex w-full whitespace-nowrap ${alignClass}`}>
|
|
117
119
|
{formatLabel(col.key, col.label)}
|
|
118
120
|
</span>
|
|
119
121
|
)}
|
|
@@ -122,9 +124,9 @@ export default function Header<T extends Record<string, unknown>>({
|
|
|
122
124
|
})}
|
|
123
125
|
|
|
124
126
|
{hasExpand && (
|
|
125
|
-
<th className=
|
|
126
|
-
style={{ width: '2.5rem', minWidth: '2.5rem'
|
|
127
|
-
<span className={`text-xs font-medium tracking-wider uppercase opacity-50 ${VARIANT_THEAD_TH[variant]}`}>
|
|
127
|
+
<th className={`sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`}
|
|
128
|
+
style={{ width: '2.5rem', minWidth: '2.5rem' }}>
|
|
129
|
+
<span className={`flex items-center justify-center text-xs font-medium tracking-wider uppercase opacity-50 ${VARIANT_THEAD_TH[variant]}`}>
|
|
128
130
|
+
|
|
129
131
|
</span>
|
|
130
132
|
</th>
|
|
@@ -132,7 +134,8 @@ export default function Header<T extends Record<string, unknown>>({
|
|
|
132
134
|
|
|
133
135
|
{hasMenu && (
|
|
134
136
|
<th aria-hidden='true'
|
|
135
|
-
|
|
137
|
+
className={`sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`}
|
|
138
|
+
style={{ width: '3.5rem', minWidth: '3.5rem' }} />
|
|
136
139
|
)}
|
|
137
140
|
</tr>
|
|
138
141
|
</thead>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Density, TableVariant } from './types'
|
|
2
2
|
|
|
3
3
|
type Column = { key: string; width?: string }
|
|
4
|
-
import { DENSITY_TD,
|
|
4
|
+
import { DENSITY_TD, VARIANT_TBODY, VARIANT_ROW_BORDER } from './constants'
|
|
5
5
|
|
|
6
6
|
type SkeletonProps = {
|
|
7
7
|
columns: Column[]
|
|
@@ -14,39 +14,36 @@ type SkeletonProps = {
|
|
|
14
14
|
|
|
15
15
|
export default function Skeleton({ columns, rows, variant, density, hasMenu, hasSelect }: SkeletonProps) {
|
|
16
16
|
return (
|
|
17
|
-
<tbody className={
|
|
17
|
+
<tbody className={VARIANT_TBODY[variant]}>
|
|
18
18
|
{Array.from({ length: rows }).map((_, rowIdx) => (
|
|
19
|
-
<tr key={rowIdx}
|
|
19
|
+
<tr key={rowIdx}>
|
|
20
20
|
{hasSelect && (
|
|
21
|
-
<td className=
|
|
22
|
-
style={{ width: '3rem', minWidth: '3rem'
|
|
23
|
-
<span className='block h-4 w-4 rounded animate-shimmer' />
|
|
21
|
+
<td className={`align-middle ${VARIANT_ROW_BORDER[variant]}`}
|
|
22
|
+
style={{ width: '3rem', minWidth: '3rem' }}>
|
|
23
|
+
<span className='block h-4 w-4 mx-auto rounded animate-shimmer' />
|
|
24
24
|
</td>
|
|
25
25
|
)}
|
|
26
26
|
{columns.map((col, colIdx) => (
|
|
27
27
|
<td
|
|
28
28
|
key={col.key}
|
|
29
|
-
style={col.width ? { width: col.width
|
|
30
|
-
className={`
|
|
31
|
-
flex-1 min-w-0 flex items-center
|
|
32
|
-
${DENSITY_TD[density]}
|
|
33
|
-
`}
|
|
29
|
+
style={col.width ? { width: col.width } : undefined}
|
|
30
|
+
className={`align-middle ${DENSITY_TD[density]} ${VARIANT_ROW_BORDER[variant]}`}
|
|
34
31
|
>
|
|
35
32
|
<span
|
|
36
33
|
className='block h-4 rounded animate-shimmer'
|
|
37
34
|
style={{
|
|
38
35
|
width: colIdx === 0
|
|
39
|
-
? `${
|
|
40
|
-
: `${
|
|
36
|
+
? `${8 + ((rowIdx * 17 + colIdx * 31) % 5)}rem`
|
|
37
|
+
: `${5 + ((rowIdx * 13 + colIdx * 23) % 5)}rem`,
|
|
41
38
|
animationDelay: `${(rowIdx * columns.length + colIdx) * 40}ms`,
|
|
42
39
|
}}
|
|
43
40
|
/>
|
|
44
41
|
</td>
|
|
45
42
|
))}
|
|
46
43
|
{hasMenu && (
|
|
47
|
-
<td className=
|
|
48
|
-
style={{ width: '3.5rem', minWidth: '3.5rem'
|
|
49
|
-
<span className='block h-5 w-5 rounded animate-shimmer' />
|
|
44
|
+
<td className={`align-middle pr-3 ${VARIANT_ROW_BORDER[variant]}`}
|
|
45
|
+
style={{ width: '3.5rem', minWidth: '3.5rem' }}>
|
|
46
|
+
<span className='block h-5 w-5 ml-auto rounded animate-shimmer' />
|
|
50
47
|
</td>
|
|
51
48
|
)}
|
|
52
49
|
</tr>
|
|
@@ -45,8 +45,8 @@ function TableShell<T extends Record<string, unknown>>({
|
|
|
45
45
|
|
|
46
46
|
return (
|
|
47
47
|
<div className={['flex flex-col min-h-0 w-full', VARIANT_CONTAINER[variant], className].filter(Boolean).join(' ')}>
|
|
48
|
-
<div className='flex-1 min-h-0 overflow-
|
|
49
|
-
<table className='min-w-full
|
|
48
|
+
<div className='flex-1 min-h-0 overflow-auto'>
|
|
49
|
+
<table className='min-w-full table-auto border-separate border-spacing-0'>
|
|
50
50
|
<Header
|
|
51
51
|
columns={columns}
|
|
52
52
|
sort={sort}
|