Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions shesha-reactjs/src/components/dataTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ export interface IIndexTableProps extends IShaDataTableProps, TableProps {
bodyFontWeight?: string;
bodyFontColor?: string;

// Action column icon styling
actionIconSize?: string | number;
actionIconColor?: string;

// Cell styling
cellTextColor?: string;
cellBackgroundColor?: string;
Expand Down Expand Up @@ -200,6 +204,8 @@ export const DataTable: FC<Partial<IIndexTableProps>> = ({
bodyFontSize,
bodyFontWeight,
bodyFontColor,
actionIconSize,
actionIconColor,
columnsMismatch,
...props
}) => {
Expand Down Expand Up @@ -1020,6 +1026,8 @@ export const DataTable: FC<Partial<IIndexTableProps>> = ({
bodyFontSize,
bodyFontWeight,
bodyFontColor,
actionIconSize,
actionIconColor,
};

// Always render ReactTable - it handles empty columns gracefully
Expand Down
4 changes: 4 additions & 0 deletions shesha-reactjs/src/components/reactTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ export const ReactTable: FC<IReactTableProps> = ({
bodyFontSize,
bodyFontWeight,
bodyFontColor,
actionIconSize,
actionIconColor,
}) => {
const [componentState, setComponentState] = useState<IReactTableState>({
allRows: data,
Expand Down Expand Up @@ -181,6 +183,8 @@ export const ReactTable: FC<IReactTableProps> = ({
bodyFontWeight,
bodyFontColor,
freezeHeaders,
actionIconSize,
actionIconColor,
});

const { setDragState } = useDataTableStore();
Expand Down
4 changes: 4 additions & 0 deletions shesha-reactjs/src/components/reactTable/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ export interface IReactTableProps extends ITableRowDragProps {
bodyFontWeight?: string;
bodyFontColor?: string;

// Action column icon styling
actionIconSize?: string | number;
actionIconColor?: string;

// Overall table styling
borderRadius?: string;
border?: IBorderValue;
Expand Down
64 changes: 50 additions & 14 deletions shesha-reactjs/src/components/reactTable/styles/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export const useMainStyles = createStyles(({ css, cx, token, prefixCls, iconPref
bodyFontWeight,
bodyFontColor,
freezeHeaders,
actionIconSize,
actionIconColor,
}: {
rowBackgroundColor?: string;
rowAlternateBackgroundColor?: string;
Expand Down Expand Up @@ -107,6 +109,8 @@ export const useMainStyles = createStyles(({ css, cx, token, prefixCls, iconPref
bodyFontWeight?: string;
bodyFontColor?: string;
freezeHeaders?: boolean;
actionIconSize?: string | number;
actionIconColor?: string;
}) => {
const {
shaTable,
Expand Down Expand Up @@ -322,6 +326,10 @@ export const useMainStyles = createStyles(({ css, cx, token, prefixCls, iconPref

&.${trBody} {
${rowHeight ? `height: ${rowHeight};` : 'height: auto;'}
/* Row must be positioned and use flex for separators to work */
position: relative;
display: flex;
align-items: stretch;
${(() => {
// Prefer rowBorderStyle over rowBorder for full border control
if (rowBorderStyle) {
Expand Down Expand Up @@ -433,6 +441,18 @@ export const useMainStyles = createStyles(({ css, cx, token, prefixCls, iconPref
vertical-align: middle;
${cellBorders && cellBorderColor ? `border: 1px solid ${cellBorderColor};` : ''}
${Object.entries(cellBorderStyles).map(([key, value]) => `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`).join(' ')}

/* Action icons styling for all table cells */
.sha-link {
.${iconPrefixCls} {
font-size: ${actionIconSize || bodyFontSize || '16px'};
width: ${actionIconSize || bodyFontSize || '16px'};
height: ${actionIconSize || bodyFontSize || '16px'};
min-width: ${actionIconSize || bodyFontSize || '16px'};
min-height: ${actionIconSize || bodyFontSize || '16px'};
${actionIconColor ? `color: ${actionIconColor};` : ''}
}
}
}

.${th} {
Expand All @@ -459,11 +479,12 @@ export const useMainStyles = createStyles(({ css, cx, token, prefixCls, iconPref
height: auto;

.${iconPrefixCls} {
font-size: ${bodyFontSize || '16px'};
width: ${bodyFontSize || '16px'};
height: ${bodyFontSize || '16px'};
min-width: ${bodyFontSize || '16px'};
min-height: ${bodyFontSize || '16px'};
font-size: ${actionIconSize || bodyFontSize || '16px'};
width: ${actionIconSize || bodyFontSize || '16px'};
height: ${actionIconSize || bodyFontSize || '16px'};
min-width: ${actionIconSize || bodyFontSize || '16px'};
min-height: ${actionIconSize || bodyFontSize || '16px'};
${actionIconColor ? `color: ${actionIconColor};` : ''}
display: inline-flex;
align-items: center;
justify-content: center;
Expand All @@ -476,11 +497,12 @@ export const useMainStyles = createStyles(({ css, cx, token, prefixCls, iconPref
align-items: center;

.${iconPrefixCls} {
font-size: ${bodyFontSize || '16px'};
width: ${bodyFontSize || '16px'};
height: ${bodyFontSize || '16px'};
min-width: ${bodyFontSize || '16px'};
min-height: ${bodyFontSize || '16px'};
font-size: ${actionIconSize || bodyFontSize || '16px'};
width: ${actionIconSize || bodyFontSize || '16px'};
height: ${actionIconSize || bodyFontSize || '16px'};
min-width: ${actionIconSize || bodyFontSize || '16px'};
min-height: ${actionIconSize || bodyFontSize || '16px'};
${actionIconColor ? `color: ${actionIconColor};` : ''}
display: inline-flex;
align-items: center;
justify-content: center;
Expand Down Expand Up @@ -712,14 +734,28 @@ export const useMainStyles = createStyles(({ css, cx, token, prefixCls, iconPref
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
border-right: 1px solid rgba(0, 0, 0, 0.05);
padding: 0.5rem; /* Default padding for all cells */

/* Add vertical separator using pseudo-element that stretches full height */
&::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0px;
width: 1px;
background-color: rgba(0, 0, 0, 0.05);
pointer-events: none;
}

/* Only apply minimum height when rowHeight is auto to prevent empty cell collapse */
${!rowHeight || rowHeight === 'auto' ? `
height: 44px;
vertical-align: middle;
line-height: normal;
/* Force cells to stretch to row height - !important overrides React Table inline styles */
min-height: 44px !important;
height: auto !important;
align-self: stretch !important;
display: flex !important;
justify-content: center !important;

/* Force empty cells to maintain height */
&:empty::before {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ export interface ITableComponentBaseProps extends IShaDataTableInlineEditablePro
rowDividers?: boolean; // Horizontal dividers between rows
responsiveMode?: 'scroll' | 'stack' | 'collapse';

actionIconSize?: string | number; // Icon size for action columns (inherits from bodyFontSize if not set)
actionIconColor?: string; // Icon color for action columns

// Table settings nested structure for form binding
tableSettings?: {
rowHeight?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,12 @@ const TableComponent: TableComponentDefinition = {
rowDimensions: updateRowHeight(prev.desktop?.rowDimensions),
},
};
}),
})
.add<ITableComponentProps>(29, (prev) => ({
...prev,
// Set default actionIconSize for existing tables
actionIconSize: prev.actionIconSize ?? '14px',
})),
actualModelPropertyFilter: (name, value) => {
// Allow all styling properties through to the settings form
const allowedStyleProperties = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,43 @@ export const getSettings: SettingsFormMarkupFactory = ({ fbf }) => {
],
},
})
.addCollapsiblePanel({
id: nanoid(),
propertyName: 'actionIconStyling',
label: 'Action Column Icons',
labelAlign: 'right',
ghost: true,
parentId: appearanceTabId,
collapsible: 'header',
className: 'ant-collapse-ghost',
content: {
id: nanoid(),
components: [...fbf()
.addSettingsInputRow({
id: nanoid(),
inputs: [
{
id: nanoid(),
propertyName: 'actionIconSize',
label: 'Icon Size',
type: 'textField',
tooltip: 'Size of action column icons (e.g., 16px, 1.2em). Inherits from table font size if not specified.',
jsSetting: true,
},
{
id: nanoid(),
propertyName: 'actionIconColor',
label: 'Icon Color',
type: 'colorPicker',
tooltip: 'Color of action column icons',
jsSetting: true,
},
],
})
.toJson(),
],
},
})
.toJson(),
...fbf()
.addPropertyRouter({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,38 @@ export const TableWrapper: FC<TableWrapperProps> = (props) => {
// Convert background object to CSS string
const effectiveBackground = useMemo(() => {
const bgStyles = getBackgroundStyle(props?.background, undefined);
// Combine all background properties into a single string

// Build complete background CSS string with all properties
const parts: string[] = [];

// Add image or color
if (bgStyles.backgroundImage) {
return bgStyles.backgroundImage;
parts.push(bgStyles.backgroundImage);
} else if (bgStyles.backgroundColor) {
return bgStyles.backgroundColor; // Color doesn't need size/position
}
if (bgStyles.backgroundColor) {
return bgStyles.backgroundColor;

// Add position if present
if (bgStyles.backgroundPosition) {
parts.push(bgStyles.backgroundPosition);
}

// Add size if present (must come after position with / separator)
if (bgStyles.backgroundSize) {
// If position exists, add size with / separator, otherwise add it separately
if (bgStyles.backgroundPosition) {
parts[parts.length - 1] = `${parts[parts.length - 1]} / ${bgStyles.backgroundSize}`;
} else {
parts.push(`/ ${bgStyles.backgroundSize}`);
}
}

// Add repeat if present
if (bgStyles.backgroundRepeat) {
parts.push(bgStyles.backgroundRepeat);
}
return undefined;

return parts.length > 0 ? parts.join(' ') : undefined;
}, [props?.background]);

const { styles } = useStyles({
Expand Down Expand Up @@ -204,17 +228,18 @@ export const TableWrapper: FC<TableWrapperProps> = (props) => {
baseStyle = props.allStyles.fullStyle;
}

if (props.border && baseStyle) {
if (baseStyle) {
const {
border,
// Remove border properties if border prop is set
border: borderProp,
borderTop,
borderRight,
borderBottom,
borderLeft,
borderWidth,
borderStyle,
borderStyle: borderStyleProp,
borderColor,
borderRadius,
borderRadius: borderRadiusProp,
borderTopWidth,
borderRightWidth,
borderBottomWidth,
Expand All @@ -231,9 +256,53 @@ export const TableWrapper: FC<TableWrapperProps> = (props) => {
borderTopRightRadius,
borderBottomLeftRadius,
borderBottomRightRadius,
...styleWithoutBorder
// Remove background properties to prevent duplicate application
background,
backgroundColor,
backgroundImage,
backgroundSize,
backgroundPosition,
backgroundRepeat,
backgroundAttachment,
backgroundOrigin,
backgroundClip,
...styleWithoutBorderAndBackground
} = baseStyle;
return styleWithoutBorder;

// Only remove border properties if props.border is set
if (props.border) {
return styleWithoutBorderAndBackground;
}

// Remove background properties but keep border properties
return {
borderProp,
borderTop,
borderRight,
borderBottom,
borderLeft,
borderWidth,
borderStyle: borderStyleProp,
borderColor,
borderRadius: borderRadiusProp,
borderTopWidth,
borderRightWidth,
borderBottomWidth,
borderLeftWidth,
borderTopStyle,
borderRightStyle,
borderBottomStyle,
borderLeftStyle,
borderTopColor,
borderRightColor,
borderBottomColor,
borderLeftColor,
borderTopLeftRadius,
borderTopRightRadius,
borderBottomLeftRadius,
borderBottomRightRadius,
...styleWithoutBorderAndBackground,
};
Comment on lines +281 to +308
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bug: Property renamed incorrectly when re-spreading border properties.

Line 279 uses borderProp, which creates a property named borderProp instead of restoring the original border property. The destructuring at line 234 renames border to borderProp, but when re-adding it to the object, it should be border: borderProp,.

This causes the border CSS property to be lost when props.border is falsy.

🐛 Proposed fix
         // Remove background properties but keep border properties
         return {
-          borderProp,
+          border: borderProp,
           borderTop,
           borderRight,
           borderBottom,
           borderLeft,
           borderWidth,
           borderStyle: borderStyleProp,
           borderColor,
           borderRadius: borderRadiusProp,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
borderProp,
borderTop,
borderRight,
borderBottom,
borderLeft,
borderWidth,
borderStyle: borderStyleProp,
borderColor,
borderRadius: borderRadiusProp,
borderTopWidth,
borderRightWidth,
borderBottomWidth,
borderLeftWidth,
borderTopStyle,
borderRightStyle,
borderBottomStyle,
borderLeftStyle,
borderTopColor,
borderRightColor,
borderBottomColor,
borderLeftColor,
borderTopLeftRadius,
borderTopRightRadius,
borderBottomLeftRadius,
borderBottomRightRadius,
...styleWithoutBorderAndBackground,
};
return {
border: borderProp,
borderTop,
borderRight,
borderBottom,
borderLeft,
borderWidth,
borderStyle: borderStyleProp,
borderColor,
borderRadius: borderRadiusProp,
borderTopWidth,
borderRightWidth,
borderBottomWidth,
borderLeftWidth,
borderTopStyle,
borderRightStyle,
borderBottomStyle,
borderLeftStyle,
borderTopColor,
borderRightColor,
borderBottomColor,
borderLeftColor,
borderTopLeftRadius,
borderTopRightRadius,
borderBottomLeftRadius,
borderBottomRightRadius,
...styleWithoutBorderAndBackground,
};
🤖 Prompt for AI Agents
In `@shesha-reactjs/src/designer-components/dataTable/table/tableWrapper.tsx`
around lines 278 - 305, The return object mistakenly emits a property named
borderProp instead of restoring the original CSS property; change the returned
key from borderProp to border: borderProp so the original `border` value
(renamed to `borderProp` during destructuring) is reattached; update the object
in tableWrapper.tsx where `borderProp` is spread alongside other border fields
(the same place returning borderTop/Right/Bottom/Left etc.) so the `border` CSS
prop is preserved when props.border was falsy.

}
return baseStyle;
}
Expand Down Expand Up @@ -453,6 +522,8 @@ export const TableWrapper: FC<TableWrapperProps> = (props) => {
bodyFontSize={props?.font?.size ? `${props.font.size}px` : undefined}
bodyFontWeight={props?.font?.weight}
bodyFontColor={props?.font?.color}
actionIconSize={props.actionIconSize}
actionIconColor={props.actionIconColor}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export const getTableDefaults = (): {
hoverHighlight: boolean;
headerBackgroundColor: string;
headerFontFamily: string;
actionIconSize: string;
} => {
return {
rowHeight: 'auto',
Expand All @@ -148,6 +149,7 @@ export const getTableDefaults = (): {
rowAlternateBackgroundColor: '#f5f5f5',
striped: true,
hoverHighlight: true,
actionIconSize: '14px',
};
};

Expand Down
Loading