diff --git a/package.json b/package.json
index 185a4c88..7fb663ab 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@qoretechnologies/reqore",
- "version": "0.36.1",
+ "version": "0.36.2",
"description": "ReQore is a highly theme-able and modular UI library for React",
"main": "dist/index.js",
"types": "dist/index.d.ts",
diff --git a/src/components/InternalPopover/index.tsx b/src/components/InternalPopover/index.tsx
index 41f4a633..aed794e5 100644
--- a/src/components/InternalPopover/index.tsx
+++ b/src/components/InternalPopover/index.tsx
@@ -59,8 +59,8 @@ const StyledPopoverArrow = styled.div<{ theme: IReqoreTheme }>`
const StyledPopoverWrapper = styled.div<{ theme: IReqoreTheme }>`
animation: 0.2s ${fadeIn} ease-out;
- max-width: ${({ maxWidth = '80vw' }) => maxWidth};
- max-height: ${({ maxHeight = '80vh' }) => maxHeight};
+ max-width: ${({ maxWidth }) => maxWidth};
+ max-height: ${({ maxHeight }) => maxHeight};
z-index: 999999;
border-radius: ${RADIUS_FROM_SIZE.normal}px;
border: ${({ flat, noWrapper, ...rest }: any) =>
diff --git a/src/components/KeyValueTable/index.tsx b/src/components/KeyValueTable/index.tsx
index 8f36a60a..409848d5 100644
--- a/src/components/KeyValueTable/index.tsx
+++ b/src/components/KeyValueTable/index.tsx
@@ -1,7 +1,9 @@
import { keys } from 'lodash';
import { useMemo } from 'react';
+import { SIZE_TO_PX } from '../../constants/sizes';
import { TReqoreIntent } from '../../constants/theme';
import { IReqoreIconName } from '../../types/icons';
+import { IReqoreButtonProps } from '../Button';
import { IReqorePanelProps } from '../Panel';
import ReqoreTable, { IReqoreTableColumn, IReqoreTableProps, IReqoreTableRowData } from '../Table';
import { IReqoreTableValueProps, ReqoreTableValue } from '../Table/value';
@@ -21,6 +23,7 @@ export interface IReqoreKeyValueTableProps
| 'height'
| 'fill'
| 'filterable'
+ | 'exportable'
| 'filter'
| 'onFilterChange'
| 'filterProps'
@@ -36,6 +39,10 @@ export interface IReqoreKeyValueTableProps
keyAlign?: IReqoreTableColumn['align'];
maxKeyWidth?: number;
+ sortable?: boolean;
+
+ rowActions?: (key: string, value: TReqoreKeyValueTableValue) => IReqoreButtonProps[];
+
valueLabel?: string;
valueIcon?: IReqoreIconName;
valueAlign?: IReqoreTableColumn['align'];
@@ -61,6 +68,8 @@ export const ReqoreKeyValueTable = ({
keyAlign = 'left',
valueAlign = 'left',
defaultValueFilter,
+ sortable,
+ rowActions,
...rest
}: IReqoreKeyValueTableProps) => {
const { columns, items } = useMemo(() => {
@@ -68,6 +77,7 @@ export const ReqoreKeyValueTable = ({
{
dataId: 'tableKey',
grow: 1,
+ sortable,
width: maxKeyWidth < 150 ? maxKeyWidth : 150,
pin: 'left',
hideable: false,
@@ -87,6 +97,7 @@ export const ReqoreKeyValueTable = ({
{
dataId: 'value',
grow: 3,
+ sortable,
hideable: false,
filterable: true,
pinnable: false,
@@ -100,12 +111,34 @@ export const ReqoreKeyValueTable = ({
},
cell: {
+ tooltip: (value) => ({
+ content: JSON.stringify(value),
+ noArrow: true,
+ useTargetWidth: true,
+ }),
content: (data) =>
valueRenderer?.(data, ReqoreTableValue) || ,
},
},
];
+ if (rowActions) {
+ columns.push({
+ dataId: 'actions',
+ hideable: false,
+ pin: 'right',
+ header: {
+ icon: 'SettingsLine',
+ },
+ width: SIZE_TO_PX[rest.size || 'normal'] * 2,
+ align: 'center',
+ cell: {
+ padded: 'none',
+ actions: (data) => rowActions(data.tableKey, data.value),
+ },
+ });
+ }
+
let items: IReqoreTableRowData[] = [];
keys(data).map((key) => {
@@ -123,6 +156,15 @@ export const ReqoreKeyValueTable = ({
columns={columns}
data={items}
{...rest}
+ sort={
+ sortable
+ ? {
+ by: 'tableKey',
+ thenBy: 'value',
+ direction: 'asc',
+ }
+ : undefined
+ }
className={`${rest.className || ''} reqore-key-value-table`}
/>
);
diff --git a/src/components/Message/index.tsx b/src/components/Message/index.tsx
index 67f69faf..991a6999 100644
--- a/src/components/Message/index.tsx
+++ b/src/components/Message/index.tsx
@@ -52,6 +52,7 @@ export interface IReqoreMessageProps
onFinish?: () => any;
flat?: boolean;
hasShadow?: boolean;
+ margin?: 'top' | 'bottom' | 'both' | 'none';
}
export interface IReqoreNotificationStyle extends IReqoreMessageProps {
diff --git a/src/components/Notifications/notification.tsx b/src/components/Notifications/notification.tsx
index a7d5b17c..e42b30fa 100644
--- a/src/components/Notifications/notification.tsx
+++ b/src/components/Notifications/notification.tsx
@@ -61,6 +61,7 @@ export interface IReqoreNotificationStyle extends IWithReqoreOpaque {
minimal?: boolean;
size?: TSizes;
asMessage?: boolean;
+ margin?: 'top' | 'bottom' | 'both' | 'none';
}
const timeoutAnimation = keyframes`
@@ -84,6 +85,15 @@ export const StyledReqoreNotification = styled(StyledEffect) css`
+ margin-top: ${margin === 'top' || margin === 'both'
+ ? `${PADDING_FROM_SIZE[size]}px`
+ : undefined};
+ margin-bottom: ${margin === 'bottom' || margin === 'both'
+ ? `${PADDING_FROM_SIZE[size]}px`
+ : undefined};
+ `};
+
// Do not fade in the component if it's a message
${({ asMessage }) => {
if (asMessage) {
@@ -95,7 +105,8 @@ export const StyledReqoreNotification = styled(StyledEffect) (asMessage ? undefined : `10px`)};
+ margin-top: ${({ asMessage, size }) =>
+ asMessage ? undefined : `${PADDING_FROM_SIZE[size]}px`};
}
${({
diff --git a/src/components/Popover/index.tsx b/src/components/Popover/index.tsx
index a0de0ca1..ba5913ea 100644
--- a/src/components/Popover/index.tsx
+++ b/src/components/Popover/index.tsx
@@ -31,6 +31,8 @@ const Popover = memo(
}: IReqorePopoverProps) => {
const [ref, setRef] = useState(undefined);
+ console.log(rest);
+
const popoverData: IPopoverControls = usePopover({ targetElement: ref, ...rest });
useEffect(() => {
@@ -38,6 +40,7 @@ const Popover = memo(
}, [popoverData]);
if (isReqoreComponent) {
+ console.log(Component);
return (
useMount(() => {
targetRef.current?.addEventListener('wheel', (e) => {
- if (e.deltaY) {
- e.preventDefault();
+ // Only scroll if the element is scrollable
+ if (refs[type].current?.scrollHeight > refs[type].current?.clientHeight) {
+ if (e.deltaY) {
+ e.preventDefault();
- const currentScroll = refs[type].current?.scrollTop + e.deltaY;
+ const currentScroll = refs[type].current?.scrollTop + e.deltaY;
- onScrollChange?.(currentScroll > 0);
+ onScrollChange?.(currentScroll > 0);
- refs[type].current?.scrollTo({ top: currentScroll });
+ refs[type].current?.scrollTo({ top: currentScroll });
- if (type === 'left') {
- refs.main.current?.scrollTo({ top: refs.main.current?.scrollTop + e.deltaY });
- refs.right.current?.scrollTo({ top: refs.right.current?.scrollTop + e.deltaY });
- } else if (type === 'main') {
- refs.left.current?.scrollTo({ top: refs.left.current?.scrollTop + e.deltaY });
- refs.right.current?.scrollTo({ top: refs.right.current?.scrollTop + e.deltaY });
- } else if (type === 'right') {
- refs.main.current?.scrollTo({ top: refs.main.current?.scrollTop + e.deltaY });
- refs.left.current?.scrollTo({ top: refs.left.current?.scrollTop + e.deltaY });
+ if (type === 'left') {
+ refs.main.current?.scrollTo({ top: refs.main.current?.scrollTop + e.deltaY });
+ refs.right.current?.scrollTo({ top: refs.right.current?.scrollTop + e.deltaY });
+ } else if (type === 'main') {
+ refs.left.current?.scrollTo({ top: refs.left.current?.scrollTop + e.deltaY });
+ refs.right.current?.scrollTo({ top: refs.right.current?.scrollTop + e.deltaY });
+ } else if (type === 'right') {
+ refs.main.current?.scrollTo({ top: refs.main.current?.scrollTop + e.deltaY });
+ refs.left.current?.scrollTo({ top: refs.left.current?.scrollTop + e.deltaY });
+ }
}
- }
- if (e.deltaX && type === 'main') {
- refs.header.current?.scrollTo({ left: refs.main.current?.scrollLeft + e.deltaX });
+ if (e.deltaX && type === 'main') {
+ refs.header.current?.scrollTo({ left: refs.main.current?.scrollLeft + e.deltaX });
+ }
}
});
});
diff --git a/src/components/Table/cell.tsx b/src/components/Table/cell.tsx
index 2833e723..8344da5b 100644
--- a/src/components/Table/cell.tsx
+++ b/src/components/Table/cell.tsx
@@ -1,9 +1,13 @@
import { lighten, rgba } from 'polished';
+import { forwardRef } from 'react';
import styled, { css } from 'styled-components';
import { IReqoreTableColumn } from '.';
import { TEXT_FROM_SIZE } from '../../constants/sizes';
import { changeLightness, getReadableColorFrom } from '../../helpers/colors';
import { alignToFlexAlign } from '../../helpers/utils';
+import { useCombinedRefs } from '../../hooks/useCombinedRefs';
+import { useTooltip } from '../../hooks/useTooltip';
+import { IWithReqoreTooltip } from '../../types/global';
import { TReqoreColor } from '../Effect';
import { IReqoreTableCellStyle } from './row';
@@ -11,7 +15,8 @@ export interface IReqoreCustomTableBodyCellProps extends IReqoreTableBodyCellPro
export interface IReqoreCustomTableBodyCell extends React.FC {}
export interface IReqoreTableBodyCellProps
extends Partial,
- React.HTMLAttributes {
+ React.HTMLAttributes,
+ IWithReqoreTooltip {
children?: React.ReactNode;
padded?: IReqoreTableColumn['cell']['padded'];
}
@@ -118,6 +123,12 @@ export const StyledTableCell = styled.div`
}}
`;
-export const ReqoreTableBodyCell = (props: IReqoreTableBodyCellProps) => {
- return ;
-};
+export const ReqoreTableBodyCell = forwardRef(
+ (props: IReqoreTableBodyCellProps, ref) => {
+ const { targetRef } = useCombinedRefs(ref);
+
+ useTooltip(targetRef.current, props.tooltip);
+
+ return ;
+ }
+);
diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx
index 530e876e..61b127ec 100644
--- a/src/components/Table/index.tsx
+++ b/src/components/Table/index.tsx
@@ -3,10 +3,17 @@ import { size as count, isArray } from 'lodash';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { useMeasure, useUpdateEffect } from 'react-use';
import styled, { css } from 'styled-components';
-import { ReqoreMessage, ReqorePaginationContainer, ReqorePanel, ReqoreVerticalSpacer } from '../..';
+import {
+ ReqoreMessage,
+ ReqorePaginationContainer,
+ ReqorePanel,
+ ReqoreVerticalSpacer,
+ useReqoreTheme,
+} from '../..';
import { TReqorePaginationType, getPagingObjectFromType } from '../../constants/paging';
import { TABLE_SIZE_TO_PX, TSizes } from '../../constants/sizes';
import { IReqoreTheme, TReqoreIntent } from '../../constants/theme';
+import ReqoreThemeProvider from '../../containers/ThemeProvider';
import { useQueryWithDelay } from '../../hooks/useQueryWithDelay';
import { IReqoreIntent, IReqoreTooltip } from '../../types/global';
import { IReqoreButtonProps, TReqoreBadge } from '../Button';
@@ -183,7 +190,6 @@ const ReqoreTable = ({
selected,
onSelectedChange,
selectToggleTooltip,
- customTheme,
onRowClick,
striped,
selectedRowIntent = 'info',
@@ -222,6 +228,7 @@ const ReqoreTable = ({
const [_internalColumns, setColumns] = useState(columns);
const [zoom, setZoom] = useState(sizeToZoom[size]);
const [showExportModal, setShowExportModal] = useState<'full' | 'current' | undefined>(undefined);
+ const theme = useReqoreTheme('main', rest.customTheme, intent);
const [wrapperRef, sizes] = useMeasure();
const { query, preQuery, setQuery, setPreQuery } = useQueryWithDelay(
@@ -554,6 +561,7 @@ const ReqoreTable = ({
if (exportable) {
moreActions = [
+ ...moreActions,
...getExportActions((type) => setShowExportModal(type)),
{ divider: true, line: true },
];
@@ -561,6 +569,7 @@ const ReqoreTable = ({
if (zoomable) {
moreActions = [
+ ...moreActions,
...getZoomActions('reqore-table', zoom, setZoom, true),
{ divider: true, line: true },
];
@@ -703,47 +712,49 @@ const ReqoreTable = ({
getContentRef={wrapperRef}
badge={badge}
>
-
- items={transformedData}
- type={
- pagingOptions
- ? {
- ...pagingOptions,
- onPageChange: () => {
- if (!pagingOptions.infinite) {
- handleScrollToTop();
- }
- },
- }
- : undefined
- }
- >
- {(pagedData) => (
- <>
- {showExportModal && (
- setShowExportModal(undefined)}
- />
- )}
-
- {renderTable('left', pagedData)}
- {renderTable('main', pagedData)}
- {renderTable('right', pagedData)}
-
- {count(pagedData) === 0 ? (
- <>
-
-
- {emptyMessage}
-
- >
- ) : null}
- >
- )}
-
+
+
+ items={transformedData}
+ type={
+ pagingOptions
+ ? {
+ ...pagingOptions,
+ onPageChange: () => {
+ if (!pagingOptions.infinite) {
+ handleScrollToTop();
+ }
+ },
+ }
+ : undefined
+ }
+ >
+ {(pagedData) => (
+ <>
+ {showExportModal && (
+ setShowExportModal(undefined)}
+ />
+ )}
+
+ {renderTable('left', pagedData)}
+ {renderTable('main', pagedData)}
+ {renderTable('right', pagedData)}
+
+ {count(pagedData) === 0 ? (
+ <>
+
+
+ {emptyMessage}
+
+ >
+ ) : null}
+ >
+ )}
+
+
>
);
diff --git a/src/components/Table/row.tsx b/src/components/Table/row.tsx
index c9b53f50..2e8d55e1 100644
--- a/src/components/Table/row.tsx
+++ b/src/components/Table/row.tsx
@@ -3,7 +3,7 @@ import { isFunction, isString } from 'lodash';
import React, { ReactElement, useState } from 'react';
import styled, { css } from 'styled-components';
import { IReqoreTableColumn, IReqoreTableData, IReqoreTableRowClick } from '.';
-import { ReqoreButton, ReqoreControlGroup, ReqorePopover } from '../..';
+import { ReqoreButton, ReqoreControlGroup } from '../..';
import { SIZE_TO_PX, TSizes } from '../../constants/sizes';
import { IReqoreTheme, TReqoreIntent } from '../../constants/theme';
import { IReqoreTooltip } from '../../types/global';
@@ -200,48 +200,44 @@ const ReqoreTableRow = ({
: {};
return (
- ) => {
- if (cell?.onClick) {
- e.stopPropagation();
- cell.onClick(data[index]);
- } else if (onRowClick) {
- e.stopPropagation();
- onRowClick(data[index]);
- } else if (selectable && data[index]._selectId) {
- // Otherwise select the row if selectable
- onSelectClick(data[index]._selectId!);
- }
- },
- className: 'reqore-table-cell',
- } as IReqoreTableCellStyle
- }
- {...tooltip}
+ {...({
+ width: resizedWidth || width,
+ minWidth,
+ maxWidth,
+ grow,
+ align,
+ size,
+ striped,
+ tooltip,
+ padded: cell?.padded,
+ disabled: data[index]._disabled,
+ selected: !!isSelected,
+ selectedIntent: selectedRowIntent,
+ flat,
+ even: index % 2 === 0 ? true : false,
+ intent: cell?.intent || data[index]._intent || intent,
+ hovered: isHovered,
+ interactive: !!cell?.onClick || !!onRowClick,
+ interactiveCell: !!cell?.onClick,
+ onClick: (e: React.MouseEvent) => {
+ if (cell?.onClick) {
+ e.stopPropagation();
+ cell.onClick(data[index]);
+ } else if (onRowClick) {
+ e.stopPropagation();
+ onRowClick(data[index]);
+ } else if (selectable && data[index]._selectId) {
+ // Otherwise select the row if selectable
+ onSelectClick(data[index]._selectId!);
+ }
+ },
+ className: 'reqore-table-cell',
+ } as IReqoreTableCellStyle)}
>
{renderContent(cell, data[index], dataId, align)}
-
+
);
}
);
diff --git a/src/constants/sizes.ts b/src/constants/sizes.ts
index 6fc33782..3e1f869f 100644
--- a/src/constants/sizes.ts
+++ b/src/constants/sizes.ts
@@ -110,11 +110,11 @@ export const TEXT_FROM_SIZE = {
};
export const ICON_FROM_SIZE = {
- tiny: 11,
- small: 14,
- normal: 17,
- big: 20,
- huge: 23,
+ tiny: 13,
+ small: 17,
+ normal: 20,
+ big: 26,
+ huge: 33,
};
export const PADDING_FROM_SIZE = {
diff --git a/src/stories/KeyValueTable/KeyValueTable.stories.tsx b/src/stories/KeyValueTable/KeyValueTable.stories.tsx
index 583be62a..cec51c20 100644
--- a/src/stories/KeyValueTable/KeyValueTable.stories.tsx
+++ b/src/stories/KeyValueTable/KeyValueTable.stories.tsx
@@ -3,6 +3,7 @@ import ReqoreIcon from '../../components/Icon';
import { IReqoreKeyValueTableProps, ReqoreKeyValueTable } from '../../components/KeyValueTable';
import { IReqoreTableRowData, TReqoreTableColumnContent } from '../../components/Table';
import { TReqorePaginationType } from '../../constants/paging';
+import { Exportable as ExportableTable } from '../Table/Table.stories';
import { StoryMeta } from '../utils';
import { CustomIntentArg, FlatArg, IntentArg, SizeArg, argManager } from '../utils/args';
@@ -112,6 +113,23 @@ export const Striped: Story = {
},
};
+export const Exportable: Story = {
+ args: {
+ exportable: true,
+ zoomable: true,
+ },
+ play: async ({ canvasElement, ...rest }) => {
+ // @ts-ignore
+ await ExportableTable.play({ canvasElement, ...rest });
+ },
+};
+
+export const Sortable: Story = {
+ args: {
+ sortable: true,
+ },
+};
+
export const Filterable: Story = {
args: {
filterable: true,
@@ -139,6 +157,29 @@ export const AllFiltersActive: Story = {
},
};
+export const WithActions: Story = {
+ args: {
+ rowActions: () => [
+ {
+ icon: 'EditLine',
+ },
+ {
+ icon: 'DeleteBinLine',
+ intent: 'danger',
+ },
+ ],
+ zoomable: true,
+ },
+};
+
+export const CustomTheme: Story = {
+ args: {
+ customTheme: { main: '#ff0000' },
+ flat: false,
+ transparent: false,
+ },
+};
+
export const NoDataMessage: Story = {
args: {
filterable: true,
diff --git a/src/stories/Message/Message.stories.tsx b/src/stories/Message/Message.stories.tsx
index f9dd36c4..ccf945ad 100644
--- a/src/stories/Message/Message.stories.tsx
+++ b/src/stories/Message/Message.stories.tsx
@@ -109,6 +109,31 @@ export const CustomTheme: Story = {
},
};
+export const WithMargin: Story = {
+ render: () => (
+ <>
+
+ In to am attended desirous raptures declared diverted confined at. Collected instantly
+ remaining up certainly to necessary as. Over walk dull into son boy door went new. At or
+ happiness commanded daughters as.
+
+
+ In to am attended desirous raptures declared diverted confined at.
+
+
+ In to am attended desirous raptures declared diverted confined at. Collected instantly
+ remaining up certainly to necessary as. Over walk dull into son boy door went new. At or
+ happiness commanded daughters as.
+
+
+ In to am attended desirous raptures declared diverted confined at. Collected instantly
+ remaining up certainly to necessary as. Over walk dull into son boy door went new. At or
+ happiness commanded daughters as.
+
+ >
+ ),
+};
+
export const Effect: Story = {
render: Template,