From 3e873cdd5e962a17daf57055f831a246f2face03 Mon Sep 17 00:00:00 2001 From: Patrick Sullivan Date: Thu, 28 Nov 2024 19:32:29 +0000 Subject: [PATCH] feat(data-table): Resolve issues with table footer controls --- components/button/src/Button.tsx | 16 +- components/data-table/package.json | 3 +- components/data-table/src/DataTable.tsx | 160 +++++++++++++----- components/field/src/Field.tsx | 12 +- components/input-field/src/InputField.tsx | 8 +- components/next-button/src/NextButton.tsx | 26 +-- components/pagination/src/Pagination.tsx | 56 ++++-- .../previous-button/src/PreviousButton.tsx | 17 +- components/select-field/src/SelectField.tsx | 21 ++- components/select/src/Select.tsx | 101 ++++++----- components/select/src/SelectItems.tsx | 39 ++++- components/select/src/SelectTextBox.tsx | 34 +++- components/select/src/SelectValue.tsx | 19 ++- components/select/src/types.ts | 17 ++ components/select/src/utilities.ts | 4 +- components/table/src/Table.tsx | 6 +- .../form-state/src/hooks/use-field-actions.ts | 25 ++- .../src/providers/FieldStoreProvider.tsx | 10 +- packages/form-state/src/stores/field-store.ts | 9 +- packages/form-state/src/stores/form-store.ts | 3 + packages/form-state/src/types.ts | 18 +- pnpm-lock.yaml | 15 +- 22 files changed, 462 insertions(+), 157 deletions(-) diff --git a/components/button/src/Button.tsx b/components/button/src/Button.tsx index f31c5247..2790e0e0 100644 --- a/components/button/src/Button.tsx +++ b/components/button/src/Button.tsx @@ -46,7 +46,7 @@ import { } from "@tamagui/core"; import { withStaticProperties } from "@tamagui/helpers"; import { LinearGradient } from "@tamagui/linear-gradient"; -import { ThemeableStack, XStack } from "@tamagui/stacks"; +import { ThemeableStack } from "@tamagui/stacks"; import type { TextContextStyles, TextParentStyles } from "@tamagui/text"; import { useCallback, useMemo, type FunctionComponent } from "react"; import { GestureResponderEvent } from "react-native"; @@ -173,7 +173,7 @@ export const ButtonContext = createStyledContext({ animate: true }); -const ButtonFrame = styled(XStack, { +const ButtonFrame = styled(View, { name: "Button", context: ButtonContext, @@ -189,8 +189,9 @@ const ButtonFrame = styled(XStack, { borderColor: "$borderColor", borderWidth: 1, flexWrap: "nowrap", + flexDirection: "row", flex: 1, - flexShrink: 1, + // flexShrink: 1, overflow: "hidden", hoverStyle: { @@ -388,7 +389,6 @@ const ButtonTextFrame = styled(LabelText, { textTransform: "capitalize", whiteSpace: "nowrap", textOverflow: "ellipsis", - display: "inline-flex", // flexGrow 1 leads to inconsistent native style where text pushes to start of view flexGrow: 0, @@ -585,7 +585,13 @@ const ButtonIcon = View.styleable( : ColorThemeName.BASE; return ( - + ({ {pageCount > 1 && ( - + + + )} @@ -207,7 +212,7 @@ export const DataTableCell = ( const value = props.value ? props.value : props.renderValue(); return ( @@ -261,18 +266,18 @@ export const DataTableHeader = ( borderRightWidth={1}> - {titleCase(id.replaceAll("_", " "))} + {titleCase(id.replaceAll("_", " "))} {isSorted && !desc && ( ( = Pick< > & Pick & { pageCount: number; + rowCount: number; }; export function DataTablePagination({ @@ -383,36 +389,114 @@ export function DataTablePagination({ previousPage, firstPage, lastPage, + rowCount, pageIndex, pageSize, - pageCount + pageCount, + ...props }: DataTablePaginationProps) { + const pageSizes = useMemo(() => { + const result = [] as SelectOption[]; + if (rowCount >= 5) { + result.push({ + index: result.length, + name: "5", + value: 5, + selected: false, + disabled: false + }); + } + if (rowCount >= 10) { + result.push({ + index: result.length, + name: "10", + value: 10, + selected: false, + disabled: false + }); + } + if (rowCount >= 25) { + result.push({ + index: result.length, + name: "25", + value: 25, + selected: false, + disabled: false + }); + } + if (rowCount >= 50) { + result.push({ + index: result.length, + name: "50", + value: 50, + selected: false, + disabled: false + }); + } + if (rowCount >= 100) { + result.push({ + index: result.length, + name: "100", + value: 100, + selected: false, + disabled: false + }); + } + if (rowCount >= 500) { + result.push({ + index: result.length, + name: "500", + value: 500, + selected: false, + disabled: false + }); + } + if (rowCount >= 1000) { + result.push({ + index: result.length, + name: "1000", + value: 1000, + selected: false, + disabled: false + }); + } + + result.push({ + index: result.length, + name: String(rowCount), + value: rowCount, + selected: false, + disabled: false + }); + + result.sort((a, b) => a.value - b.value); + + return result; + }, []); + return ( -
- - - Items per page - - - -
+ alignItems="center" + paddingHorizontal="$1"> + +
+ + + + Per page + + + + +
+
Boolean(fieldDisabled || props.disabled), @@ -368,6 +375,7 @@ const FieldLabelTextImpl = FieldLabelText.styleable<{ {children} diff --git a/components/input-field/src/InputField.tsx b/components/input-field/src/InputField.tsx index 49637e47..fea822e8 100644 --- a/components/input-field/src/InputField.tsx +++ b/components/input-field/src/InputField.tsx @@ -19,7 +19,7 @@ import { Field } from "@cyclone-ui/field"; import { useFieldActions, useFieldStore } from "@cyclone-ui/form-state"; import { Input } from "@cyclone-ui/input"; import { Theme, withStaticProperties } from "@tamagui/core"; -import { useCallback } from "react"; +import { useCallback, useLayoutEffect } from "react"; const InputFieldGroup = Field.styleable((props, forwardedRef) => { const { children, ...rest } = props; @@ -83,11 +83,15 @@ const InputFieldControlTextBoxValue = Input.TextBox.Value.styleable( const initialValue = store.get.initialValue(); const options = store.get.options(); - const { change } = useFieldActions(); + const { change, mount } = useFieldActions(); const handleClear = useCallback(() => { change(options?.defaultValue); }, [change, options?.defaultValue]); + useLayoutEffect(() => { + mount(forwardedRef); + }, [mount]); + return ( { +export const NextButton = Button.styleable<{ + hideText?: boolean; +}>( + ({ children, hideText = false, ...props }: NextButtonProps, forwardedRef) => { const [hoverRef, hovering] = useHover(); const ref = useComposedRefs(forwardedRef, hoverRef); return ( ); } diff --git a/components/pagination/src/Pagination.tsx b/components/pagination/src/Pagination.tsx index dfb1582a..2436e5f3 100644 --- a/components/pagination/src/Pagination.tsx +++ b/components/pagination/src/Pagination.tsx @@ -1,11 +1,12 @@ -import { useCallback } from "react"; import { Button } from "@cyclone-ui/button"; import { NextButton } from "@cyclone-ui/next-button"; import { PreviousButton } from "@cyclone-ui/previous-button"; import { XStack, XStackProps } from "@tamagui/stacks"; import { SizableText } from "@tamagui/text"; +import { useCallback } from "react"; type ExtraPaginationProps = { + hideText?: boolean; pageCount: number; pageIndex: number; setPageIndex: (pageIndex: number) => void; @@ -22,6 +23,7 @@ export const Pagination = XStack.styleable( pageCount, pageIndex, theme, + hideText = false, setPageIndex, onFirst, onLast, @@ -72,32 +74,40 @@ export const Pagination = XStack.styleable( }, [setPageIndex, currentPage, pageCount]); return ( - + {currentPage > 3 && pageCount > 5 && ( - + . . . )} {pageCount > 1 && ( )} diff --git a/components/previous-button/src/PreviousButton.tsx b/components/previous-button/src/PreviousButton.tsx index 7c636ec7..4f286942 100644 --- a/components/previous-button/src/PreviousButton.tsx +++ b/components/previous-button/src/PreviousButton.tsx @@ -21,10 +21,17 @@ import { useHover } from "@storm-stack/hooks"; import { useComposedRefs } from "@tamagui/core"; import { XStack } from "@tamagui/stacks"; -export type PreviousButtonProps = ButtonProps; - -export const PreviousButton = Button.styleable( - ({ children, ...props }: ButtonProps, forwardedRef) => { +export type PreviousButtonProps = ButtonProps & { + hideText?: boolean; +}; + +export const PreviousButton = Button.styleable<{ + hideText?: boolean; +}>( + ( + { children, hideText = false, ...props }: PreviousButtonProps, + forwardedRef + ) => { const [hoverRef, hovering] = useHover(); const ref = useComposedRefs(forwardedRef, hoverRef); @@ -38,7 +45,7 @@ export const PreviousButton = Button.styleable( - {children || "Previous"} + {!hideText && {children || "Previous"}} ); diff --git a/components/select-field/src/SelectField.tsx b/components/select-field/src/SelectField.tsx index c357f726..24da4f25 100644 --- a/components/select-field/src/SelectField.tsx +++ b/components/select-field/src/SelectField.tsx @@ -19,9 +19,13 @@ import { Field } from "@cyclone-ui/field"; import { useFieldActions, useFieldStore } from "@cyclone-ui/form-state"; import { Select } from "@cyclone-ui/select"; import { SelectOption } from "@storm-stack/types/utility-types/form"; -import { GetProps, withStaticProperties } from "@tamagui/core"; +import { + GetProps, + TamaguiTextElement, + withStaticProperties +} from "@tamagui/core"; import { Atom, useAtomValue } from "jotai"; -import { PropsWithChildren, useCallback } from "react"; +import { PropsWithChildren, useCallback, useLayoutEffect, useRef } from "react"; const SelectFieldGroup = Field.styleable((props, forwardedRef) => { const { children, ...rest } = props; @@ -49,7 +53,7 @@ const SelectFieldItem = ( const SelectFieldControl = Select.styleable< Pick, "placeholder"> >(({ placeholder, children, ...props }, forwardedRef) => { - const { focus, blur, change } = useFieldActions(); + const { focus, blur, change, mount } = useFieldActions(); const handleChange = useCallback( (event: CustomEvent) => { change(event.detail); @@ -66,6 +70,11 @@ const SelectFieldControl = Select.styleable< const formattedValue = store.get.formattedValue(); const initialValue = store.get.initialValue(); + const selectRef = useRef(null); + useLayoutEffect(() => { + mount(selectRef); + }, [mount]); + return (