added dropdown in header
This commit is contained in:
parent
d839f631f8
commit
e2bd654c9c
@ -1,6 +1,7 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { PmsGrid } from "./index";
|
import { PmsGrid } from "./index";
|
||||||
import { initPopover } from "./GridService";
|
import { initPopover } from "./GridService";
|
||||||
|
import PmsHeaderOption from "./PmsHeaderOption";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CIVIL BOQ / INVENTORY DEMO DATA
|
* CIVIL BOQ / INVENTORY DEMO DATA
|
||||||
@ -719,6 +720,8 @@ export default function DemoBOQGrid() {
|
|||||||
return (
|
return (
|
||||||
<div className="container-fluid py-3">
|
<div className="container-fluid py-3">
|
||||||
<div className="card p-3">
|
<div className="card p-3">
|
||||||
|
|
||||||
|
|
||||||
<PmsGrid
|
<PmsGrid
|
||||||
data={boqData.map((r) => ({
|
data={boqData.map((r) => ({
|
||||||
...r,
|
...r,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { useRef } from "react";
|
|||||||
import { useGridCore } from "./useGridCore";
|
import { useGridCore } from "./useGridCore";
|
||||||
import { exportToCSV } from "./utils";
|
import { exportToCSV } from "./utils";
|
||||||
import "./pms-grid.css";
|
import "./pms-grid.css";
|
||||||
|
import PmsHeaderOption from "./PmsHeaderOption";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Props:
|
Props:
|
||||||
@ -55,10 +56,17 @@ export default function PmsGrid({
|
|||||||
setColState,
|
setColState,
|
||||||
} = grid;
|
} = grid;
|
||||||
|
|
||||||
// simple pin toggle
|
// --- Pin / Unpin helpers ---
|
||||||
const togglePin = (key) => {
|
const pinColumn = (key, side) => {
|
||||||
const col = colState.find((c) => c.key === key);
|
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
|
// resizing via mouse down on handle
|
||||||
@ -199,7 +207,7 @@ export default function PmsGrid({
|
|||||||
style={{ maxHeight: features.maxHeight || "60vh" }}
|
style={{ maxHeight: features.maxHeight || "60vh" }}
|
||||||
>
|
>
|
||||||
<table
|
<table
|
||||||
className="table table-sm rounded mb-0"
|
className="table table-sm rounded mb-0 "
|
||||||
style={{ width: "max-content", minWidth: "100%" }}
|
style={{ width: "max-content", minWidth: "100%" }}
|
||||||
>
|
>
|
||||||
<thead
|
<thead
|
||||||
@ -243,7 +251,7 @@ export default function PmsGrid({
|
|||||||
key={col.key}
|
key={col.key}
|
||||||
draggable={features.reorder}
|
draggable={features.reorder}
|
||||||
onDragStart={(e) => onDragStart(e, col.key)}
|
onDragStart={(e) => onDragStart(e, col.key)}
|
||||||
onDragOver={(e) => e.preventDefault()}
|
onDragOver={(e)=> e.preventDefault()}
|
||||||
onDrop={(e) => onDrop(e, col.key)}
|
onDrop={(e) => onDrop(e, col.key)}
|
||||||
className={`pms-col-header vs-th ${
|
className={`pms-col-header vs-th ${
|
||||||
col.pinned ? `pinned pinned-${col.pinned}` : ""
|
col.pinned ? `pinned pinned-${col.pinned}` : ""
|
||||||
@ -269,17 +277,17 @@ export default function PmsGrid({
|
|||||||
}`}
|
}`}
|
||||||
></i>
|
></i>
|
||||||
)}
|
)}
|
||||||
|
{col.pinned === col.key && <i className="bx bx-x"></i>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="d-flex align-items-center gap-1">
|
<div className="d-flex align-items-center gap-1">
|
||||||
{features.pinning && (
|
{features.pinning && (
|
||||||
<button
|
<PmsHeaderOption
|
||||||
className="btn btn-sm btn-link p-0"
|
pinned={col.pinned}
|
||||||
title="Pin/Unpin"
|
onPinLeft={() => pinColumn(col.key, "left")}
|
||||||
onClick={() => togglePin(col.key)}
|
onPinRight={() => pinColumn(col.key, "right")}
|
||||||
>
|
onUnpin={() => unpinColumn(col.key)}
|
||||||
<i className="bx bx-pin"></i>
|
/>
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
{features.resizing && (
|
{features.resizing && (
|
||||||
<i
|
<i
|
||||||
@ -322,7 +330,7 @@ export default function PmsGrid({
|
|||||||
{!loading && groupBy && groupedRows && groupedRows.length > 0
|
{!loading && groupBy && groupedRows && groupedRows.length > 0
|
||||||
? groupedRows.map((g) => (
|
? groupedRows.map((g) => (
|
||||||
<React.Fragment key={g.key}>
|
<React.Fragment key={g.key}>
|
||||||
<tr className="table-secondary">
|
<tr className="table-secondary border-0">
|
||||||
<td
|
<td
|
||||||
colSpan={
|
colSpan={
|
||||||
visibleColumns.length +
|
visibleColumns.length +
|
||||||
|
|||||||
52
src/services/pmsGrid/PmsHeaderOption.jsx
Normal file
52
src/services/pmsGrid/PmsHeaderOption.jsx
Normal 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;
|
||||||
@ -5,24 +5,25 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ──────────────────────────────
|
/* ──────────────────────────────
|
||||||
SCROLLABLE WRAPPER
|
SCROLLABLE WRAPPER
|
||||||
────────────────────────────── */
|
────────────────────────────── */
|
||||||
.grid-wrapper {
|
.grid-wrapper {
|
||||||
max-height: 60vh;
|
max-height: 50vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #dee2e6;
|
||||||
border-radius: 0.2rem;
|
border-radius: 0.2rem;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
/* ✅ Force horizontal scroll even if few columns */
|
/* Force horizontal scroll even if few columns */
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ✅ Always visible scrollbar (cross browser) */
|
/* Always visible scrollbar (cross browser) */
|
||||||
.grid-wrapper::-webkit-scrollbar {
|
.grid-wrapper::-webkit-scrollbar {
|
||||||
height: 3px;
|
height: 3px;
|
||||||
width: 3px;
|
width: 3px;
|
||||||
@ -39,13 +40,19 @@
|
|||||||
TABLE BASE STYLE
|
TABLE BASE STYLE
|
||||||
────────────────────────────── */
|
────────────────────────────── */
|
||||||
.pms-grid table {
|
.pms-grid table {
|
||||||
width: max-content; /* ✅ allows scrolling horizontally */
|
width: max-content; /* allows scrolling horizontally */
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
border-collapse: separate;
|
border-collapse: separate;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
.pms-col-header {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible !important;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.pms-grid th,
|
.pms-grid th,
|
||||||
.pms-grid td {
|
.pms-grid td {
|
||||||
@ -81,7 +88,7 @@
|
|||||||
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.05);
|
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 th:first-child,
|
||||||
.pms-grid td:first-child {
|
.pms-grid td:first-child {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
@ -94,7 +101,7 @@
|
|||||||
box-shadow: 2px 0 3px rgba(0, 0, 0, 0.08);
|
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 th:last-child,
|
||||||
.pms-grid td:last-child {
|
.pms-grid td:last-child {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
@ -114,7 +121,7 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
color: #0d6efd;
|
color: var(--bs-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pms-col-header:hover .resize-handle {
|
.pms-col-header:hover .resize-handle {
|
||||||
@ -157,6 +164,9 @@
|
|||||||
.z-6 {
|
.z-6 {
|
||||||
z-index: 6 !important;
|
z-index: 6 !important;
|
||||||
}
|
}
|
||||||
|
.z-100{
|
||||||
|
z-index: 100 !important;
|
||||||
|
}
|
||||||
/* ──────────────────────────────
|
/* ──────────────────────────────
|
||||||
PINNED HEADER COLUMNS (Fix for misalignment)
|
PINNED HEADER COLUMNS (Fix for misalignment)
|
||||||
────────────────────────────── */
|
────────────────────────────── */
|
||||||
@ -165,7 +175,7 @@
|
|||||||
.pms-grid th.pinned-left {
|
.pms-grid th.pinned-left {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 9;
|
z-index: 12;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 2px 0 3px rgba(0, 0, 0, 0.08);
|
box-shadow: 2px 0 3px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
@ -174,9 +184,10 @@
|
|||||||
.pms-grid th.pinned-right {
|
.pms-grid th.pinned-right {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 9;
|
z-index: 12;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: -2px 0 3px rgba(0, 0, 0, 0.08);
|
box-shadow: -2px 0 3px rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Match body pinned cell behavior */
|
/* Match body pinned cell behavior */
|
||||||
@ -186,6 +197,7 @@
|
|||||||
z-index: 8;
|
z-index: 8;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 2px 0 3px rgba(0, 0, 0, 0.08);
|
box-shadow: 2px 0 3px rgba(0, 0, 0, 0.08);
|
||||||
|
border-right: 1px solid var(--bs-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pms-grid td.pinned-right {
|
.pms-grid td.pinned-right {
|
||||||
@ -194,4 +206,27 @@
|
|||||||
z-index: 8;
|
z-index: 8;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: -2px 0 3px rgba(0, 0, 0, 0.08);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user