diff --git a/packages/graphic-walker/package.json b/packages/graphic-walker/package.json index cb0be39c..e66303b7 100644 --- a/packages/graphic-walker/package.json +++ b/packages/graphic-walker/package.json @@ -58,6 +58,7 @@ "@radix-ui/react-context-menu": "^2.1.5", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-hover-card": "^1.0.7", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "^1.0.7", diff --git a/packages/graphic-walker/src/components/dataTable/index.tsx b/packages/graphic-walker/src/components/dataTable/index.tsx index eb81e81f..f0224a47 100644 --- a/packages/graphic-walker/src/components/dataTable/index.tsx +++ b/packages/graphic-walker/src/components/dataTable/index.tsx @@ -11,11 +11,12 @@ import { PureFilterEditDialog } from '../../fields/filterField/filterEditDialog' import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon } from '@heroicons/react/24/outline'; import { ComputationContext } from '../../store'; import { parsedOffsetDate } from '../../lib/op/offset'; -import { formatDate } from '../../utils'; +import { cn, formatDate } from '../../utils'; import { FieldProfiling } from './profiling'; import { addFilterForQuery, createFilter } from '../../utils/workflow'; import { Button, buttonVariants } from '../ui/button'; import { Badge } from '../ui/badge'; +import { HoverCard, HoverCardContent, HoverCardTrigger } from '../ui/hover-card'; interface DataTableProps { /** page limit */ @@ -164,12 +165,66 @@ function useFilters(metas: IMutField[]) { return { filters, options, editingFilterIdx, onSelectFilter, onDeleteFilter, onWriteFilter, onClose }; } -function FieldValue(props: { field: IMutField; item: IRow; displayOffset?: number }) { +function fieldValue(props: { field: IMutField; item: IRow; displayOffset?: number }) { const { field, item } = props; if (field.semanticType === 'temporal') { - return <>{formatDate(parsedOffsetDate(props.displayOffset, field.offset)(item[field.fid]))}>; + return formatDate(parsedOffsetDate(props.displayOffset, field.offset)(item[field.fid])); } - return <>{`${item[field.fid]}`}>; + return `${item[field.fid]}`; +} + +function CopyButton(props: { value: string }) { + const [copied, setCopied] = useState(false); + useEffect(() => { + if (copied) { + const timer = setTimeout(() => { + setCopied(false); + }, 2000); + return () => { + clearTimeout(timer); + }; + } + }, [copied]); + return ( + { + try { + navigator.clipboard.writeText(props.value); + setCopied(true); + } catch (e) { + console.error(e); + } + }} + disabled={copied} + > + {copied ? 'Copied' : 'Copy'} + + ); +} + +function TruncateDector(props: { value: string }) { + const ref = useRef(null); + const [isTruncate, setIsTruncate] = useState(false); + const [open, setOpen] = useState(false); + useEffect(() => { + if (ref.current) { + setIsTruncate(ref.current.offsetWidth < ref.current.scrollWidth); + } + }, [ref.current]); + return ( + + + {props.value} + + + {props.value} + + + + ); } const DataTable: React.FC = (props) => { @@ -442,11 +497,17 @@ const DataTable: React.FC = (props) => { {rows.map((row, index) => ( - {metas.map((field) => ( - - - - ))} + {metas.map((field) => { + const value = fieldValue({ field, item: row, displayOffset }); + return ( + + + + ); + })} ))} diff --git a/packages/graphic-walker/src/components/dataTable/profiling.tsx b/packages/graphic-walker/src/components/dataTable/profiling.tsx index 547d9e1f..5b9578d2 100644 --- a/packages/graphic-walker/src/components/dataTable/profiling.tsx +++ b/packages/graphic-walker/src/components/dataTable/profiling.tsx @@ -52,7 +52,7 @@ function NominalProfiling({ computation, field, valueRenderer = (s) => `${s}` }: return ( - {displayValue} + {displayValue} {Math.floor((100 * count) / meta.total)}% diff --git a/packages/graphic-walker/src/components/ui/hover-card.tsx b/packages/graphic-walker/src/components/ui/hover-card.tsx new file mode 100644 index 00000000..6da1c7cd --- /dev/null +++ b/packages/graphic-walker/src/components/ui/hover-card.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import * as HoverCardPrimitive from '@radix-ui/react-hover-card'; + +import { cn } from '@/utils'; +import { portalContainerContext } from '@/store/theme'; + +const HoverCard = HoverCardPrimitive.Root; + +const HoverCardTrigger = HoverCardPrimitive.Trigger; + +const HoverCardContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( + + + +)); +HoverCardContent.displayName = HoverCardPrimitive.Content.displayName; + +export { HoverCard, HoverCardTrigger, HoverCardContent }; diff --git a/yarn.lock b/yarn.lock index 6f44f48c..c6ece5c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1297,6 +1297,22 @@ "@radix-ui/react-primitive" "1.0.3" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-hover-card@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-hover-card/-/react-hover-card-1.0.7.tgz#684bca2504432566357e7157e087051aa3577948" + integrity sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-dismissable-layer" "1.0.5" + "@radix-ui/react-popper" "1.1.3" + "@radix-ui/react-portal" "1.0.4" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-controllable-state" "1.0.1" + "@radix-ui/react-icons@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.0.tgz#c61af8f323d87682c5ca76b856d60c2312dbcb69" @@ -2060,7 +2076,7 @@ "@types/react" "*" "@types/reactcss" "*" -"@types/react-dom@*", "@types/react-dom@^18.2.15", "@types/react-dom@^18.x": +"@types/react-dom@*", "@types/react-dom@^18.2.15": version "18.3.0" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== @@ -2074,7 +2090,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.2.37", "@types/react@^18.x": +"@types/react@*", "@types/react@^18.2.37": version "18.3.2" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.2.tgz#462ae4904973bc212fa910424d901e3d137dbfcd" integrity sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==
{props.value}