added dropdown in header

This commit is contained in:
pramod.mahajan 2025-11-09 01:07:42 +05:30
parent d839f631f8
commit e2bd654c9c
4 changed files with 120 additions and 22 deletions

View File

@ -1,6 +1,7 @@
import React, { useEffect, useRef } from "react";
import { PmsGrid } from "./index";
import { initPopover } from "./GridService";
import PmsHeaderOption from "./PmsHeaderOption";
/**
* CIVIL BOQ / INVENTORY DEMO DATA
@ -719,6 +720,8 @@ export default function DemoBOQGrid() {
return (
<div className="container-fluid py-3">
<div className="card p-3">
<PmsGrid
data={boqData.map((r) => ({
...r,

View File

@ -2,6 +2,7 @@ import React, { useRef } from "react";
import { useGridCore } from "./useGridCore";
import { exportToCSV } from "./utils";
import "./pms-grid.css";
import PmsHeaderOption from "./PmsHeaderOption";
/*
Props:
@ -55,10 +56,17 @@ export default function PmsGrid({
setColState,
} = grid;
// simple pin toggle
const togglePin = (key) => {
// --- Pin / Unpin helpers ---
const pinColumn = (key, side) => {
const col = colState.find((c) => c.key === key);
updateColumn(key, { pinned: col.pinned === "left" ? null : "left" });
if (!col) return;
// if already pinned to that side unpin
const newPinned = col.pinned === side || side === "none" ? null : side;
updateColumn(key, { pinned: newPinned });
};
const unpinColumn = (key) => {
updateColumn(key, { pinned: null });
};
// resizing via mouse down on handle
@ -269,17 +277,17 @@ export default function PmsGrid({
}`}
></i>
)}
{col.pinned === col.key && <i className="bx bx-x"></i>}
</div>
<div className="d-flex align-items-center gap-1">
{features.pinning && (
<button
className="btn btn-sm btn-link p-0"
title="Pin/Unpin"
onClick={() => togglePin(col.key)}
>
<i className="bx bx-pin"></i>
</button>
<PmsHeaderOption
pinned={col.pinned}
onPinLeft={() => pinColumn(col.key, "left")}
onPinRight={() => pinColumn(col.key, "right")}
onUnpin={() => unpinColumn(col.key)}
/>
)}
{features.resizing && (
<i
@ -322,7 +330,7 @@ export default function PmsGrid({
{!loading && groupBy && groupedRows && groupedRows.length > 0
? groupedRows.map((g) => (
<React.Fragment key={g.key}>
<tr className="table-secondary">
<tr className="table-secondary border-0">
<td
colSpan={
visibleColumns.length +

View File

@ -0,0 +1,52 @@
const PmsHeaderOption = ({ pinned, onPinLeft, onPinRight, onUnpin }) => {
return (
<div className="dropdown z-100">
<button
type="button"
className="btn btn-icon btn-text-secondary rounded-pill p-0"
data-bs-toggle="dropdown"
data-bs-auto-close="outside"
aria-expanded="false"
>
<i className="bx bx-dots-vertical-rounded bx-sm text-muted"></i>
</button>
<ul className="dropdown-menu dropdown-menu-end shadow-sm border-0 rounded-3 py-2">
<li>
<button
className={`dropdown-item d-flex align-items-center ${
pinned === "left" ? "active" : ""
}`}
onClick={onPinLeft}
>
<i className="bx bx-pin me-2"></i> Pin Left
</button>
</li>
<li>
<button
className={`dropdown-item d-flex align-items-center ${
pinned === "right" ? "active" : ""
}`}
onClick={onPinRight}
>
<i className="bx bx-pin me-2"></i> Pin Right
</button>
</li>
{pinned && (
<>
<li><hr className="dropdown-divider" /></li>
<li>
<button
className="dropdown-item text-danger d-flex align-items-center"
onClick={onUnpin}
>
<i className="bx bx-x me-2"></i> Unpin Column
</button>
</li>
</>
)}
</ul>
</div>
);
};
export default PmsHeaderOption;

View File

@ -5,24 +5,25 @@
width: 100%;
position: relative;
font-size: 0.875rem;
}
/*
SCROLLABLE WRAPPER
*/
.grid-wrapper {
max-height: 60vh;
max-height: 50vh;
overflow-y: auto;
overflow-x: auto;
position: relative;
border: 1px solid #dee2e6;
border-radius: 0.2rem;
background: #fff;
/* Force horizontal scroll even if few columns */
/* Force horizontal scroll even if few columns */
white-space: nowrap;
}
/* Always visible scrollbar (cross browser) */
/* Always visible scrollbar (cross browser) */
.grid-wrapper::-webkit-scrollbar {
height: 3px;
width: 3px;
@ -39,13 +40,19 @@
TABLE BASE STYLE
*/
.pms-grid table {
width: max-content; /* allows scrolling horizontally */
width: max-content; /* allows scrolling horizontally */
min-width: 100%;
border-collapse: separate;
border-spacing: 0;
table-layout: fixed;
background: #fff;
}
.pms-col-header {
position: relative;
overflow: visible !important;
z-index: 20;
}
.pms-grid th,
.pms-grid td {
@ -81,7 +88,7 @@
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.05);
}
/* Always sticky first column (checkbox) */
/* Always sticky first column (checkbox) */
.pms-grid th:first-child,
.pms-grid td:first-child {
position: sticky;
@ -94,7 +101,7 @@
box-shadow: 2px 0 3px rgba(0, 0, 0, 0.08);
}
/* Always sticky last column (Actions) */
/* Always sticky last column (Actions) */
.pms-grid th:last-child,
.pms-grid td:last-child {
position: sticky;
@ -114,7 +121,7 @@
display: inline-block;
opacity: 0;
transition: opacity 0.2s;
color: #0d6efd;
color: var(--bs-primary);
}
.pms-col-header:hover .resize-handle {
@ -157,6 +164,9 @@
.z-6 {
z-index: 6 !important;
}
.z-100{
z-index: 100 !important;
}
/*
PINNED HEADER COLUMNS (Fix for misalignment)
*/
@ -165,7 +175,7 @@
.pms-grid th.pinned-left {
position: sticky;
left: 0;
z-index: 9;
z-index: 12;
background: #fff;
box-shadow: 2px 0 3px rgba(0, 0, 0, 0.08);
}
@ -174,9 +184,10 @@
.pms-grid th.pinned-right {
position: sticky;
right: 0;
z-index: 9;
z-index: 12;
background: #fff;
box-shadow: -2px 0 3px rgba(0, 0, 0, 0.08);
}
/* Match body pinned cell behavior */
@ -186,6 +197,7 @@
z-index: 8;
background: #fff;
box-shadow: 2px 0 3px rgba(0, 0, 0, 0.08);
border-right: 1px solid var(--bs-primary);
}
.pms-grid td.pinned-right {
@ -194,4 +206,27 @@
z-index: 8;
background: #fff;
box-shadow: -2px 0 3px rgba(0, 0, 0, 0.08);
}
.dropend:hover > .dropdown-menu {
display: block;
}
/* Hover color consistency with Sneat variables */
.dropdown-item:focus,
.dropdown-item:hover {
background-color: rgba(var(--bs-primary-rgb), 0.08);
color: var(--bs-primary);
}
/* Responsive fallback: stack submenu below parent on small screens */
@media (max-width: 768px) {
.dropend .dropdown-menu {
position: relative;
left: 0;
top: 0;
margin-left: 0;
}
}