@sqrzro/ui 4.0.0-alpha.17 → 4.0.0-alpha.19

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.
@@ -1,9 +1,9 @@
1
1
  import { CollectionComponentProps } from '../Collection';
2
2
  import type { TableColumnObject, TableItemObject } from '../interfaces';
3
3
  export type { TableClassNames } from '../TableClientComponent';
4
- export interface TableProps<Item, Filters = null> extends CollectionComponentProps<Item, TableItemObject, Filters> {
4
+ export interface TableProps<Item, Filters = null, Data extends object | null = null> extends CollectionComponentProps<Item, TableItemObject<Data>, Filters> {
5
5
  readonly columns: TableColumnObject[];
6
6
  readonly isSelectable?: boolean;
7
7
  }
8
- declare function Table<Item extends object, Filters>({ columns, isSelectable, ...props }: TableProps<Item, Filters>): React.ReactElement;
8
+ declare function Table<Item extends object, Filters = null, Data extends object | null = null>({ columns, isSelectable, ...props }: TableProps<Item, Filters, Data>): React.ReactElement;
9
9
  export default Table;
@@ -4,7 +4,7 @@ import TableClientComponent from '../TableClientComponent';
4
4
  function defaultTransformer(item) {
5
5
  return {
6
6
  id: 'id' in item ? String(item.id) : JSON.stringify(item),
7
- ...item,
7
+ row: { ...item },
8
8
  };
9
9
  }
10
10
  function Table({ columns, isSelectable, ...props }) {
@@ -5,10 +5,10 @@ export interface TableClassNames {
5
5
  row: SelectableClassName;
6
6
  cell: string;
7
7
  }
8
- interface TableClientComponentProps extends ClassNameProps<TableClassNames> {
8
+ interface TableClientComponentProps<Data extends object | null = null> extends ClassNameProps<TableClassNames> {
9
9
  readonly columns: TableColumnObject[];
10
- readonly data: TableItemObject[];
10
+ readonly data: TableItemObject<Data>[];
11
11
  readonly isSelectable?: boolean;
12
12
  }
13
- declare function TableClientComponent({ classNameProps, classNames, columns, data, isSelectable, }: Readonly<TableClientComponentProps>): React.ReactElement;
13
+ declare function TableClientComponent<Data extends object | null = null>({ classNameProps, classNames, columns, data, isSelectable, }: Readonly<TableClientComponentProps<Data>>): React.ReactElement;
14
14
  export default TableClientComponent;
@@ -1,10 +1,9 @@
1
1
  'use client';
2
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { toggleArrayItem } from '@sqrzro/utility';
4
4
  import { useSearchParams } from 'next/navigation';
5
5
  import useSearchParamsHref from '../../../hooks/useSearchParamsHref';
6
6
  import { getClassNames } from '../../../styles/classnames';
7
- import filterColumns from '../utility/filter-columns';
8
7
  import getSelectedFromSearchParams from '../utility/get-selected-from-search-params';
9
8
  import setSelectedToSearchParams from '../utility/set-selected-to-search-params';
10
9
  import TableRow from '../TableRow';
@@ -12,7 +11,6 @@ function TableClientComponent({ classNameProps, classNames, columns, data, isSel
12
11
  const componentClassNames = getClassNames('table', { props: classNameProps }, classNames);
13
12
  const { setSearchParamsHref } = useSearchParamsHref();
14
13
  const searchParams = useSearchParams();
15
- const filteredColumns = filterColumns(columns, isSelectable);
16
14
  const selected = getSelectedFromSearchParams(searchParams);
17
15
  function handleSelect(event) {
18
16
  const value = toggleArrayItem(selected, event.target.name);
@@ -22,6 +20,6 @@ function TableClientComponent({ classNameProps, classNames, columns, data, isSel
22
20
  const value = selected.length === data.length ? [] : data.map((item) => item.id);
23
21
  setSearchParamsHref({ selected: setSelectedToSearchParams(value) });
24
22
  }
25
- return (_jsx("div", { className: componentClassNames?.root, children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { children: _jsx("tr", { children: filteredColumns.map((column) => (_jsx("th", { children: column.type === 'selectable' ? (_jsxs("button", { name: column.id, onClick: handleSelectAll, children: ["Select All ", selected.includes(column.id) ? '✓' : ''] })) : (column.title) }, column.id))) }) }), _jsx("tbody", { children: data.map((item) => (_jsx(TableRow, { columns: filteredColumns, data: item, isSelected: selected.includes(item.id), onSelect: handleSelect }, item.id))) })] }) }));
23
+ return (_jsx("div", { className: componentClassNames?.root, children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { children: _jsxs("tr", { children: [data[0].isSelectable ? (_jsx("th", { children: _jsx("button", { onClick: handleSelectAll, children: "Select All" }) })) : null, columns.map((column) => (_jsx("th", { children: column.title }, column.id)))] }) }), _jsx("tbody", { children: data.map((item) => (_jsx(TableRow, { columns: columns, data: item, isSelected: selected.includes(item.id), onSelect: handleSelect }, item.id))) })] }) }));
26
24
  }
27
25
  export default TableClientComponent;
@@ -1,12 +1,13 @@
1
1
  import type { InputEvent } from '../../../forms/interfaces';
2
2
  import type { ClassNameProps } from '../../../styles/classnames/interfaces';
3
- import { TableClassNames } from '../TableClientComponent';
3
+ import type { TableClassNames } from '../TableClientComponent';
4
4
  import type { TableColumnObject, TableItemObject } from '../interfaces';
5
- interface TableRowProps extends ClassNameProps<TableClassNames> {
5
+ interface TableRowProps<Data extends object | null = null> extends ClassNameProps<TableClassNames> {
6
6
  columns: TableColumnObject[];
7
- data: TableItemObject;
7
+ data: TableItemObject<Data>;
8
+ isSelectable?: boolean;
8
9
  isSelected: boolean;
9
10
  onSelect: (event: InputEvent<boolean>) => void;
10
11
  }
11
- declare function TableRow({ classNameProps, classNames, columns, data, isSelected, onSelect, }: TableRowProps): React.ReactElement;
12
+ declare function TableRow<Data extends object | null = null>({ classNameProps, classNames, columns, data, isSelected, onSelect, }: TableRowProps<Data>): React.ReactElement;
12
13
  export default TableRow;
@@ -1,9 +1,9 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Switch from '../../../forms/components/Switch';
3
3
  import { getClassNames } from '../../../styles/classnames';
4
- import tw from '../../../styles/classnames/tw';
4
+ import ListItemMenu from '../ListItemMenu';
5
5
  function TableRow({ classNameProps, classNames, columns, data, isSelected, onSelect, }) {
6
6
  const componentClassNames = getClassNames('table', { props: classNameProps, states: { isSelected } }, classNames);
7
- return (_jsx("tr", { className: componentClassNames?.row, children: columns.map((column) => (_jsx("td", { className: tw(componentClassNames?.cell, column.type === 'selectable' ? 'w-1' : null), children: column.type === 'selectable' ? (_jsx(Switch, { name: data.id, onChange: onSelect, value: isSelected })) : (data[column.id]) }, column.id))) }));
7
+ return (_jsxs("tr", { className: componentClassNames?.row, children: [data.isSelectable ? (_jsx("td", { children: _jsx(Switch, { name: data.id, onChange: onSelect, value: isSelected }) })) : null, columns.map((column) => (_jsx("td", { className: componentClassNames?.cell, children: data.row[column.id] }, column.id))), data.actions ? (_jsx("td", { children: _jsx(ListItemMenu, { actions: data.actions, data: data.$data }) })) : null] }));
8
8
  }
9
9
  export default TableRow;
@@ -29,11 +29,16 @@ export interface ListItemObject<Data extends object | null = null> {
29
29
  variant?: StyleVariant | null;
30
30
  }
31
31
  export type TableFunctionConfig<Filters = null> = CollectionFunctionConfig<Filters>;
32
- export interface TableItemObject {
33
- [key: string]: React.ReactNode;
32
+ export interface TableItemObject<Data extends object | null = null> {
33
+ $data?: Data;
34
+ actions?: ActionObject<Data>[];
34
35
  id: string;
36
+ isSelectable?: boolean;
37
+ row: {
38
+ [key: string]: React.ReactNode;
39
+ };
35
40
  }
36
- export type TableColumnType = 'date' | 'decimal' | 'id' | 'link' | 'money' | 'number' | 'percentage' | 'reference' | 'selectable' | 'string';
41
+ export type TableColumnType = 'date' | 'decimal' | 'link' | 'money' | 'number' | 'percentage' | 'points' | 'raw' | 'reference' | 'string';
37
42
  export interface TableColumnObject {
38
43
  isSortable?: boolean;
39
44
  id: string;
@@ -4,7 +4,7 @@ export interface ActionProps<Data extends object | null = null> extends Omit<Act
4
4
  readonly label?: React.ReactNode;
5
5
  readonly render: (props: ActionComponentProps<Data>) => React.ReactElement;
6
6
  }
7
- export interface ActionComponentProps<Data extends object | null = null> extends Pick<ActionProps<Data>, 'href' | 'icon' | 'isDisabled' | 'isLoading' | 'isSubmittable' | 'onClick' | 'variant'> {
7
+ export interface ActionComponentProps<Data extends object | null = null> extends Pick<ActionProps<Data>, 'href' | 'icon' | 'isDisabled' | 'isLoading' | 'isSubmittable' | 'onClick' | 'searchParams' | 'variant'> {
8
8
  children: React.ReactNode;
9
9
  }
10
10
  declare function Action<Data extends object | null = null>(props: ActionProps<Data>): React.ReactElement;
@@ -7,6 +7,7 @@ function Confirmable({ confirmable, renderProps, render, }) {
7
7
  const { handle, isOpen, modalProps } = useConfirmable({
8
8
  isLoading: renderProps.isLoading,
9
9
  onConfirm: (event) => renderProps.onClick?.(event),
10
+ refreshOnSuccess: true,
10
11
  });
11
12
  return (_jsxs(Fragment, { children: [render({ ...renderProps, isLoading: renderProps.isLoading, onClick: handle }), isOpen ? _jsx(ConfirmModal, { confirmable: confirmable, ...modalProps }) : null] }));
12
13
  }
@@ -1,4 +1,4 @@
1
1
  import type { ActionComponentProps } from '../Action';
2
2
  type MenuItemProps<Data extends object | null = null> = ActionComponentProps<Data>;
3
- declare function MenuItem<Data extends object | null = null>({ children, onClick, }: MenuItemProps<Data>): React.ReactElement;
3
+ declare function MenuItem<Data extends object | null = null>({ children, href, onClick, }: MenuItemProps<Data>): React.ReactElement;
4
4
  export default MenuItem;
@@ -1,5 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- function MenuItem({ children, onClick, }) {
2
+ import Link from '../Link';
3
+ function MenuItem({ children, href, onClick, }) {
4
+ if (href) {
5
+ return (_jsx(Link, { href: href, onClick: onClick, children: children }));
6
+ }
3
7
  return (_jsx("button", { onClick: onClick, type: "button", children: children }));
4
8
  }
5
9
  export default MenuItem;
@@ -3,6 +3,9 @@ interface UseConfirmableArgs {
3
3
  isLoading?: boolean;
4
4
  onBeforeConfirm?: () => void;
5
5
  onConfirm: (event: ActionEvent, currentRef: HTMLButtonElement | null) => void;
6
+ onSuccess?: () => Promise<void> | void;
7
+ redirectOnSuccess?: string | false | ((response: Response) => string | false);
8
+ refreshOnSuccess?: boolean;
6
9
  }
7
10
  interface UseConfirmableReturn {
8
11
  handle: (event?: ActionEvent) => void;
@@ -14,5 +17,5 @@ interface UseConfirmableReturn {
14
17
  };
15
18
  ref: React.RefObject<HTMLButtonElement | null>;
16
19
  }
17
- declare function useConfirmable({ isLoading, onBeforeConfirm, onConfirm, }: Readonly<UseConfirmableArgs>): UseConfirmableReturn;
20
+ declare function useConfirmable({ isLoading, onBeforeConfirm, onConfirm, onSuccess, redirectOnSuccess, refreshOnSuccess, }: Readonly<UseConfirmableArgs>): UseConfirmableReturn;
18
21
  export default useConfirmable;
@@ -1,7 +1,9 @@
1
1
  'use client';
2
2
  import { useEffect, useRef, useState } from 'react';
3
- function useConfirmable({ isLoading, onBeforeConfirm, onConfirm, }) {
3
+ import { useRouter } from 'next/navigation';
4
+ function useConfirmable({ isLoading, onBeforeConfirm, onConfirm, onSuccess, redirectOnSuccess, refreshOnSuccess, }) {
4
5
  const ref = useRef(null);
6
+ const router = useRouter();
5
7
  const [isModalOpen, setIsModalOpen] = useState(false);
6
8
  const [isConfirmed, setIsConfirmed] = useState(false);
7
9
  const [refEvent, setRefEvent] = useState();
@@ -32,10 +34,14 @@ function useConfirmable({ isLoading, onBeforeConfirm, onConfirm, }) {
32
34
  function handleCancel() {
33
35
  setIsModalOpen(false);
34
36
  }
37
+ function handleSuccess() {
38
+ setIsConfirmed(false);
39
+ setIsModalOpen(false);
40
+ router.refresh();
41
+ }
35
42
  useEffect(() => {
36
43
  if (!isLoading && isConfirmed) {
37
- setIsConfirmed(false);
38
- setIsModalOpen(false);
44
+ handleSuccess();
39
45
  }
40
46
  }, [isLoading]);
41
47
  return {
@@ -0,0 +1,11 @@
1
+ interface UsePopoverReturn<T extends HTMLElement = HTMLElement> {
2
+ controlProps: {
3
+ onClick: () => void;
4
+ };
5
+ ref: React.RefObject<T | null>;
6
+ targetProps: {
7
+ isOpen: boolean;
8
+ };
9
+ }
10
+ declare function usePopover<T extends HTMLElement = HTMLElement>(): UsePopoverReturn<T>;
11
+ export default usePopover;
@@ -0,0 +1,17 @@
1
+ import useClickOutside from './useClickOutside';
2
+ function usePopover() {
3
+ const [isOpen, setIsOpen, ref] = useClickOutside();
4
+ function toggleIsOpen() {
5
+ setIsOpen(!isOpen);
6
+ }
7
+ return {
8
+ controlProps: {
9
+ onClick: toggleIsOpen,
10
+ },
11
+ ref,
12
+ targetProps: {
13
+ isOpen,
14
+ },
15
+ };
16
+ }
17
+ export default usePopover;
@@ -7,6 +7,7 @@ export interface AppNavigationClassNames {
7
7
  link: HighlightableClassName;
8
8
  }
9
9
  export interface AppNavigationProps extends ClassNameProps<AppNavigationClassNames> {
10
+ classNameKey?: 'appNavigation' | 'tabs';
10
11
  data: NavigationObject[];
11
12
  }
12
13
  declare function AppNavigation(props: Readonly<AppNavigationProps>): React.ReactElement;
@@ -4,10 +4,10 @@ import { Suspense } from 'react';
4
4
  import { getClassNames } from '../../../styles/classnames';
5
5
  import useNavigation from '../../hooks/useNavigation';
6
6
  import AppNavigationItem from '../AppNavigationItem';
7
- function AppNavigationComponent({ classNameProps, classNames, data, }) {
8
- const componentClassNames = getClassNames('appNavigation', { props: classNameProps }, classNames);
7
+ function AppNavigationComponent({ classNameKey, classNameProps, classNames, data, }) {
8
+ const componentClassNames = getClassNames(classNameKey ?? 'appNavigation', { props: classNameProps }, classNames);
9
9
  const navigation = useNavigation(data);
10
- return (_jsx("nav", { className: componentClassNames?.root, children: _jsx("ul", { className: componentClassNames?.list, children: navigation.map((item, index) => (_jsx(AppNavigationItem, { classNames: classNames, ...item }, index))) }) }));
10
+ return (_jsx("nav", { className: componentClassNames?.root, children: _jsx("ul", { className: componentClassNames?.list, children: navigation.map((item, index) => (_jsx(AppNavigationItem, { classNameKey: classNameKey, classNames: classNames, ...item }, index))) }) }));
11
11
  }
12
12
  function AppNavigation(props) {
13
13
  return (_jsx(Suspense, { children: _jsx(AppNavigationComponent, { ...props }) }));
@@ -1,6 +1,8 @@
1
1
  import type { NavigationObject } from '../../../navigation/interfaces';
2
2
  import type { ClassNameProps } from '../../../styles/classnames/interfaces';
3
3
  import type { AppNavigationClassNames } from '../AppNavigation';
4
- export type AppNavigationItemProps = NavigationObject & ClassNameProps<AppNavigationClassNames>;
5
- declare function AppNavigationItem({ classNameProps, classNames, href, isActive, label, }: Readonly<AppNavigationItemProps>): React.ReactElement;
4
+ export interface AppNavigationItemProps extends NavigationObject, ClassNameProps<AppNavigationClassNames> {
5
+ classNameKey?: 'appNavigation' | 'tabs';
6
+ }
7
+ declare function AppNavigationItem({ children, classNameKey, classNameProps, classNames, href, isActive, label, }: Readonly<AppNavigationItemProps>): React.ReactElement;
6
8
  export default AppNavigationItem;
@@ -1,8 +1,16 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Action from '../../../components/utility/Action';
2
3
  import Link from '../../../components/utility/Link';
4
+ import MenuItem from '../../../components/utility/MenuItem';
5
+ import Popover from '../../../components/utility/Popover';
6
+ import usePopover from '../../../hooks/usePopover';
3
7
  import { getClassNames } from '../../../styles/classnames';
4
- function AppNavigationItem({ classNameProps, classNames, href, isActive, label, }) {
5
- const componentClassNames = getClassNames('appNavigation', { props: classNameProps, states: { isHighlighted: isActive ?? false } }, classNames);
8
+ function AppNavigationItem({ children, classNameKey, classNameProps, classNames, href, isActive, label, }) {
9
+ const componentClassNames = getClassNames(classNameKey ?? 'appNavigation', { props: classNameProps, states: { isHighlighted: isActive ?? false } }, classNames);
10
+ const { controlProps, ref, targetProps } = usePopover();
11
+ if (children?.length) {
12
+ return (_jsxs("li", { className: componentClassNames?.item, ref: ref, children: [_jsx("button", { className: componentClassNames?.link, type: "button", ...controlProps, children: label }), _jsx(Popover, { align: "left", vAlign: "bottom", ...targetProps, children: _jsx("ul", { children: children.map((item, index) => (_jsx("li", { children: _jsx(Action, { ...item, render: MenuItem }) }, index))) }) })] }));
13
+ }
6
14
  return (_jsx("li", { className: componentClassNames?.item, children: _jsx(Link, { className: componentClassNames?.link, href: href, children: label }) }));
7
15
  }
8
16
  export default AppNavigationItem;
@@ -1,9 +1,6 @@
1
- 'use client';
2
1
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { getClassNames } from '../../../styles/classnames';
4
2
  import AppNavigation from '../AppNavigation';
5
3
  function Tabs({ classNameProps, classNames, ...props }) {
6
- const componentClassNames = getClassNames('tabs', { props: classNameProps }, classNames);
7
- return _jsx(AppNavigation, { classNames: componentClassNames ?? {}, ...props });
4
+ return _jsx(AppNavigation, { classNameKey: "tabs", ...props });
8
5
  }
9
6
  export default Tabs;
@@ -7,12 +7,15 @@ export interface ConfirmableObject {
7
7
  modalIcon?: React.ReactNode;
8
8
  onBeforeConfirm?: () => void;
9
9
  onConfirm?: () => void;
10
+ onSuccess?: () => Promise<void> | void;
11
+ redirectOnSuccess?: string | false | ((response: Response) => string | false);
12
+ refreshOnSuccess?: boolean;
10
13
  submitLabel?: string;
11
14
  title?: React.ReactNode;
12
15
  }
13
16
  export type ActionEvent = React.MouseEvent<HTMLAnchorElement> | React.MouseEvent<HTMLButtonElement>;
14
17
  export interface ActionObject<Data extends object | null = null> {
15
- action?: (data?: Data) => Promise<void>;
18
+ action?: (data: Data) => Promise<void>;
16
19
  confirmable?: ConfirmableObject;
17
20
  data?: Data;
18
21
  details?: React.ReactNode;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sqrzro/ui",
3
3
  "type": "module",
4
- "version": "4.0.0-alpha.17",
4
+ "version": "4.0.0-alpha.19",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "ISC",
@@ -48,7 +48,7 @@
48
48
  "react-dom": "^19.2.1",
49
49
  "tailwind-merge": "^3.4.0",
50
50
  "use-deep-compare-effect": "^1.8.1",
51
- "@sqrzro/utility": "^4.0.0-alpha.6"
51
+ "@sqrzro/utility": "^4.0.0-alpha.7"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@storybook/addon-a11y": "^10.1.7",
@@ -1,3 +0,0 @@
1
- import type { TableColumnObject } from '../interfaces';
2
- declare function filterColumns(columns: TableColumnObject[], isSelectable?: boolean): TableColumnObject[];
3
- export default filterColumns;
@@ -1,8 +0,0 @@
1
- function filterColumns(columns, isSelectable) {
2
- const filtered = columns.filter((column) => column.type !== 'id');
3
- if (isSelectable) {
4
- return [{ id: 'select', type: 'selectable' }, ...filtered];
5
- }
6
- return filtered;
7
- }
8
- export default filterColumns;