@umodeapporg/ui 0.27.0 → 0.29.0
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.
- package/dist/assets/umode-ui.css +0 -5
- package/dist/components/DateTime/DatePicker.d.ts +2 -0
- package/dist/components/DateTime/DatePicker.js +5 -2
- package/dist/components/DateTime/DatePicker.js.map +1 -1
- package/dist/components/DateTime/DateRangePicker.d.ts +2 -0
- package/dist/components/DateTime/DateRangePicker.js +3 -0
- package/dist/components/DateTime/DateRangePicker.js.map +1 -1
- package/dist/components/Form/ControlledButton.d.ts +1 -1
- package/dist/components/Form/ControlledButton.js +2 -2
- package/dist/components/Form/ControlledButton.js.map +1 -1
- package/dist/components/Form/ControlledCheckbox.d.ts +5 -1
- package/dist/components/Form/ControlledCheckbox.js +53 -39
- package/dist/components/Form/ControlledCheckbox.js.map +1 -1
- package/dist/components/Form/ControlledDialogPrimitives.js +17 -5
- package/dist/components/Form/ControlledDialogPrimitives.js.map +1 -1
- package/dist/components/Form/ControlledDrawer.js +1 -0
- package/dist/components/Form/ControlledDrawer.js.map +1 -1
- package/dist/components/Form/ControlledImage/index.js +2 -0
- package/dist/components/Form/ControlledImage/index.js.map +1 -1
- package/dist/components/Form/ControlledInput.d.ts +1 -1
- package/dist/components/Form/ControlledInput.js +4 -4
- package/dist/components/Form/ControlledInput.js.map +1 -1
- package/dist/components/Form/ControlledSelect.js +1 -1
- package/dist/components/Form/ControlledSelect.js.map +1 -1
- package/dist/components/Form/ControlledTabs.js +17 -3
- package/dist/components/Form/ControlledTabs.js.map +1 -1
- package/dist/components/Form/ControlledTabsPrimitives.js +6 -6
- package/dist/components/Form/ControlledTabsPrimitives.js.map +1 -1
- package/dist/components/Form/ControlledTextarea.js +2 -2
- package/dist/components/Form/ControlledTextarea.js.map +1 -1
- package/dist/components/Form/ControlledTimeline.js +11 -8
- package/dist/components/Form/ControlledTimeline.js.map +1 -1
- package/dist/components/Form/ControlledVoiceTextarea.d.ts +2 -0
- package/dist/components/Form/ControlledVoiceTextarea.js +3 -0
- package/dist/components/Form/ControlledVoiceTextarea.js.map +1 -1
- package/dist/components/Form/FieldLabel.d.ts +16 -0
- package/dist/components/Form/FieldLabel.js +7 -0
- package/dist/components/Form/FieldLabel.js.map +1 -0
- package/dist/components/Form/RichTextEditor.js +15 -1
- package/dist/components/Form/RichTextEditor.js.map +1 -1
- package/dist/components/Form/attachments/AttachmentPreviewList.js +3 -0
- package/dist/components/Form/attachments/AttachmentPreviewList.js.map +1 -1
- package/dist/components/Form/attachments/FileDropzone.js +3 -3
- package/dist/components/Form/attachments/FileDropzone.js.map +1 -1
- package/dist/components/Navigation/PageTitle.d.ts +6 -0
- package/dist/components/Navigation/PageTitle.js +6 -4
- package/dist/components/Navigation/PageTitle.js.map +1 -1
- package/dist/components/Search/filters/FiltersPanel.js +1 -0
- package/dist/components/Search/filters/FiltersPanel.js.map +1 -1
- package/dist/components/Search/filters/FiltersSummaryTooltip.js +2 -2
- package/dist/components/Search/filters/FiltersSummaryTooltip.js.map +1 -1
- package/dist/components/Shell/LanguageToggle.js +1 -1
- package/dist/components/Shell/LanguageToggle.js.map +1 -1
- package/dist/components/Shell/SidebarMenu.js +9 -1
- package/dist/components/Shell/SidebarMenu.js.map +1 -1
- package/dist/components/Shell/ThemeToggle.js +2 -1
- package/dist/components/Shell/ThemeToggle.js.map +1 -1
- package/dist/components/UI/BaseDataCard.d.ts +2 -0
- package/dist/components/UI/BaseDataCard.js +6 -0
- package/dist/components/UI/BaseDataCard.js.map +1 -1
- package/dist/components/UI/BulkActionBar.js +2 -0
- package/dist/components/UI/BulkActionBar.js.map +1 -1
- package/dist/components/UI/Chips/OutlinedChip.js +13 -6
- package/dist/components/UI/Chips/OutlinedChip.js.map +1 -1
- package/dist/components/UI/ColorSwatch.js +2 -2
- package/dist/components/UI/ColorSwatch.js.map +1 -1
- package/dist/components/UI/ControlledAccordion.js +10 -4
- package/dist/components/UI/ControlledAccordion.js.map +1 -1
- package/dist/components/UI/ControlledCollapse.js +2 -2
- package/dist/components/UI/ControlledCollapse.js.map +1 -1
- package/dist/components/UI/ControlledMenu.js +3 -3
- package/dist/components/UI/ControlledMenu.js.map +1 -1
- package/dist/components/UI/ControlledPopover.d.ts +12 -0
- package/dist/components/UI/ControlledPopover.js +35 -5
- package/dist/components/UI/ControlledPopover.js.map +1 -1
- package/dist/components/UI/ControlledProgress.js +3 -3
- package/dist/components/UI/ControlledProgress.js.map +1 -1
- package/dist/components/UI/ControlledSlider.d.ts +2 -0
- package/dist/components/UI/ControlledSlider.js +8 -3
- package/dist/components/UI/ControlledSlider.js.map +1 -1
- package/dist/components/UI/ControlledTooltip.js +7 -3
- package/dist/components/UI/ControlledTooltip.js.map +1 -1
- package/dist/components/UI/DataTable/DataTable.js +1 -1
- package/dist/components/UI/DataTable/DataTableBody.js +4 -0
- package/dist/components/UI/DataTable/DataTableBody.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableBulkEditDialog.js +4 -2
- package/dist/components/UI/DataTable/DataTableBulkEditDialog.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js +1 -1
- package/dist/components/UI/DataTable/DataTableBulkEditValueEditor.d.ts +5 -1
- package/dist/components/UI/DataTable/DataTableBulkEditValueEditor.js +19 -4
- package/dist/components/UI/DataTable/DataTableBulkEditValueEditor.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableColumnToggle.js +3 -1
- package/dist/components/UI/DataTable/DataTableColumnToggle.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableFilters.js +1 -1
- package/dist/components/UI/DataTable/DataTablePagination.js +4 -0
- package/dist/components/UI/DataTable/DataTablePagination.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableStates.js +2 -2
- package/dist/components/UI/DataTable/DataTableStates.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableToolbar.js +1 -1
- package/dist/components/UI/DataTable/applyDataTableFilters.js +1 -1
- package/dist/components/UI/DataTable/cells/DataTableEditableCell.js +2 -1
- package/dist/components/UI/DataTable/cells/DataTableEditableCell.js.map +1 -1
- package/dist/components/UI/DataTable/types.d.ts +4 -0
- package/dist/components/UI/DataTable/types.js.map +1 -1
- package/dist/components/UI/Flow/FlowBlockNode.js +1 -0
- package/dist/components/UI/Flow/FlowBlockNode.js.map +1 -1
- package/dist/components/UI/Flow/FlowCanvas.js +2 -2
- package/dist/components/UI/Flow/FlowCanvas.js.map +1 -1
- package/dist/components/UI/Flow/FlowConnectionEdge.js +1 -0
- package/dist/components/UI/Flow/FlowConnectionEdge.js.map +1 -1
- package/dist/components/UI/Flow/FlowInspector.js +3 -2
- package/dist/components/UI/Flow/FlowInspector.js.map +1 -1
- package/dist/components/UI/Flow/FlowIssueList.js +2 -2
- package/dist/components/UI/Flow/FlowIssueList.js.map +1 -1
- package/dist/components/UI/Flow/FlowLaneMap.js +2 -2
- package/dist/components/UI/Flow/FlowLaneMap.js.map +1 -1
- package/dist/components/UI/Flow/FlowResizablePanel.js +3 -2
- package/dist/components/UI/Flow/FlowResizablePanel.js.map +1 -1
- package/dist/components/UI/Flow/FlowZoomControls.js +1 -0
- package/dist/components/UI/Flow/FlowZoomControls.js.map +1 -1
- package/dist/components/UI/HorizontalScrollArea.d.ts +1 -1
- package/dist/components/UI/HorizontalScrollArea.js +1 -0
- package/dist/components/UI/HorizontalScrollArea.js.map +1 -1
- package/dist/components/UI/ImageAnnotator/components/AnnotationColorPopover.js +1 -0
- package/dist/components/UI/ImageAnnotator/components/AnnotationColorPopover.js.map +1 -1
- package/dist/components/UI/ImagePlaceholder.js +5 -2
- package/dist/components/UI/ImagePlaceholder.js.map +1 -1
- package/dist/components/UI/ImageZoomOverlay.js +1 -0
- package/dist/components/UI/ImageZoomOverlay.js.map +1 -1
- package/dist/components/UI/MaterialSymbol/index.js +5 -0
- package/dist/components/UI/MaterialSymbol/index.js.map +1 -1
- package/dist/components/UI/NoDataFound/index.js +3 -3
- package/dist/components/UI/NoDataFound/index.js.map +1 -1
- package/dist/components/UI/RichTextContent.js +3 -3
- package/dist/components/UI/RichTextContent.js.map +1 -1
- package/dist/components/UI/ThreeDots.js +15 -11
- package/dist/components/UI/ThreeDots.js.map +1 -1
- package/dist/helpers/mergeClasses.d.ts +7 -3
- package/dist/helpers/mergeClasses.js +23 -1
- package/dist/helpers/mergeClasses.js.map +1 -1
- package/dist/hooks/useVoiceTranscription.js +5 -2
- package/dist/hooks/useVoiceTranscription.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +58 -54
- package/dist/index.js.map +1 -1
- package/dist/locales/en/ui.json.d.ts +8 -3
- package/dist/locales/en/ui.json.js +3 -3
- package/dist/locales/index.d.ts +10 -0
- package/dist/locales/pt/ui.json.d.ts +8 -3
- package/dist/locales/pt/ui.json.js +3 -3
- package/package.json +1 -1
- package/src/assets/styles/base.css +7 -1
- package/tailwind-plugins/glass.js +23 -28
|
@@ -2,8 +2,8 @@ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState, useRef, useCallback, useEffect, isValidElement, cloneElement } from "react";
|
|
3
3
|
import { AnimatePresence, motion } from "motion/react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
-
import { twMerge } from "tailwind-merge";
|
|
6
5
|
import { composeRefs } from "../../helpers/composeRefs.js";
|
|
6
|
+
import { mergeClasses } from "../../helpers/mergeClasses.js";
|
|
7
7
|
import { getPortalHost } from "../../helpers/portalHost.js";
|
|
8
8
|
import { useAnchoredFloating } from "../../hooks/useAnchoredFloating.js";
|
|
9
9
|
import { tooltipVariants } from "../../motion/motionPresets.js";
|
|
@@ -101,6 +101,10 @@ const ControlledTooltip = ({
|
|
|
101
101
|
trigger.props.onPointerEnter?.(event);
|
|
102
102
|
if (!isControlledByHost) openTooltip();
|
|
103
103
|
},
|
|
104
|
+
onPointerDown: (event) => {
|
|
105
|
+
trigger.props.onPointerDown?.(event);
|
|
106
|
+
if (!isControlledByHost && opensOnHover) closeTooltip();
|
|
107
|
+
},
|
|
104
108
|
onMouseEnter: (event) => {
|
|
105
109
|
triggerRef.current = event.currentTarget;
|
|
106
110
|
pointerPositionRef.current = { x: event.clientX, y: event.clientY };
|
|
@@ -124,8 +128,8 @@ const ControlledTooltip = ({
|
|
|
124
128
|
animate: "visible",
|
|
125
129
|
exit: "exit",
|
|
126
130
|
role: "tooltip",
|
|
127
|
-
className:
|
|
128
|
-
"fixed left-0 top-0 z-[
|
|
131
|
+
className: mergeClasses(
|
|
132
|
+
"fixed left-0 top-0 z-[9999] max-w-xs rounded-lg bg-ink px-3 py-2 text-xs font-normal text-ink-fg shadow-lg",
|
|
129
133
|
className
|
|
130
134
|
),
|
|
131
135
|
children: content
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlledTooltip.js","sources":["../../../src/components/UI/ControlledTooltip.tsx"],"sourcesContent":["import { cloneElement, isValidElement, useCallback, useEffect, useRef, useState } from 'react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport type { FocusEvent, MouseEvent, PointerEvent, ReactElement, ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport { twMerge } from 'tailwind-merge';\n\nimport { composeRefs } from '@/helpers/composeRefs';\nimport { getPortalHost } from '@/helpers/portalHost';\nimport { TFloatingPlacement, useAnchoredFloating } from '@/hooks/useAnchoredFloating';\nimport { tooltipVariants } from '@/motion';\n\nexport interface IControlledTooltipProps {\n content: ReactNode;\n children: ReactNode;\n className?: string;\n placement?: TFloatingPlacement;\n offset?: number;\n /** Lifts the touch block: a click-to-open tooltip is a deliberate mobile pattern. */\n interactive?: boolean;\n open?: boolean;\n handler?: (open: boolean) => void;\n}\n\ntype TTooltipTriggerProps = {\n onPointerEnter?: (event: PointerEvent<HTMLElement>) => void;\n onMouseEnter?: (event: MouseEvent<HTMLElement>) => void;\n onFocus?: (event: FocusEvent<HTMLElement>) => void;\n ref?: React.Ref<HTMLElement>;\n};\n\nconst ORPHAN_TOOLTIP_CHECK_INTERVAL_MS = 250;\n\nconst isTouchFirstDevice = (): boolean => window.matchMedia('(hover: none), (pointer: coarse)').matches;\n\n/**\n * Owns the open state instead of delegating to the positioning library, because a raw\n * hover interaction opens on touch (the browser's compatibility `mouseenter`) and never\n * closes when the trigger stops receiving `mouseleave`: detached from the DOM, covered by\n * a dialog, or re-rendered. Both leave a bubble parked on the screen.\n */\nconst ControlledTooltip: React.FC<IControlledTooltipProps> = ({\n content,\n children,\n className,\n placement = 'top',\n offset = 6,\n interactive,\n open,\n handler,\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const triggerRef = useRef<HTMLElement | null>(null);\n const triggerPointerTypeRef = useRef<string | null>(null);\n const pointerPositionRef = useRef<{ x: number; y: number } | null>(null);\n\n const isControlledByHost = open !== undefined;\n const opensOnHover = !interactive;\n const resolvedOpen = isControlledByHost ? Boolean(open) : isOpen;\n\n const { setReference, setFloating } = useAnchoredFloating({\n open: resolvedOpen,\n placement,\n offsetPx: offset,\n constrainToViewport: false,\n });\n\n const closeTooltip = useCallback(() => {\n setIsOpen(false);\n handler?.(false);\n }, [handler]);\n\n const openTooltip = useCallback(() => {\n const pointerType = triggerPointerTypeRef.current;\n const openedByTouch = pointerType === null ? isTouchFirstDevice() : pointerType !== 'mouse';\n if (opensOnHover && openedByTouch) return;\n\n setIsOpen(true);\n handler?.(true);\n }, [handler, opensOnHover]);\n\n useEffect(() => {\n if (isControlledByHost || !resolvedOpen || !opensOnHover) return undefined;\n\n const belongsToTooltip = (element: Node | null): boolean => {\n const trigger = triggerRef.current;\n if (!element || !trigger) return false;\n if (trigger.contains(element)) return true;\n return element instanceof Element && element.closest('[role=\"tooltip\"]') !== null;\n };\n\n const closeWhenPointerLeftTrigger = (event: globalThis.PointerEvent) => {\n pointerPositionRef.current = { x: event.clientX, y: event.clientY };\n if (!triggerRef.current) return;\n if (belongsToTooltip(event.target instanceof Node ? event.target : null)) return;\n closeTooltip();\n };\n\n const closeWhenPageIsHidden = () => {\n if (document.hidden) closeTooltip();\n };\n\n const orphanTooltipCheck = window.setInterval(() => {\n const trigger = triggerRef.current;\n if (!trigger) return;\n if (!trigger.isConnected) {\n closeTooltip();\n return;\n }\n\n const pointerPosition = pointerPositionRef.current;\n if (!pointerPosition) return;\n if (!belongsToTooltip(document.elementFromPoint(pointerPosition.x, pointerPosition.y))) closeTooltip();\n }, ORPHAN_TOOLTIP_CHECK_INTERVAL_MS);\n\n document.addEventListener('pointermove', closeWhenPointerLeftTrigger, { capture: true, passive: true });\n document.addEventListener('pointerdown', closeTooltip, { capture: true, passive: true });\n document.addEventListener('visibilitychange', closeWhenPageIsHidden);\n window.addEventListener('blur', closeTooltip);\n\n return () => {\n window.clearInterval(orphanTooltipCheck);\n document.removeEventListener('pointermove', closeWhenPointerLeftTrigger, { capture: true });\n document.removeEventListener('pointerdown', closeTooltip, { capture: true });\n document.removeEventListener('visibilitychange', closeWhenPageIsHidden);\n window.removeEventListener('blur', closeTooltip);\n };\n }, [closeTooltip, isControlledByHost, resolvedOpen, opensOnHover]);\n\n if (!isValidElement(children)) return <>{children}</>;\n\n const trigger = children as ReactElement<TTooltipTriggerProps>;\n\n const triggerWithTracking = cloneElement(trigger, {\n ref: composeRefs<HTMLElement>(\n (node) => {\n triggerRef.current = node;\n },\n setReference,\n (trigger as { ref?: React.Ref<HTMLElement> }).ref,\n ),\n onPointerEnter: (event: PointerEvent<HTMLElement>) => {\n triggerPointerTypeRef.current = event.pointerType || null;\n triggerRef.current = event.currentTarget;\n pointerPositionRef.current = { x: event.clientX, y: event.clientY };\n trigger.props.onPointerEnter?.(event);\n if (!isControlledByHost) openTooltip();\n },\n onMouseEnter: (event: MouseEvent<HTMLElement>) => {\n triggerRef.current = event.currentTarget;\n pointerPositionRef.current = { x: event.clientX, y: event.clientY };\n trigger.props.onMouseEnter?.(event);\n },\n onFocus: (event: FocusEvent<HTMLElement>) => {\n triggerRef.current = event.currentTarget;\n trigger.props.onFocus?.(event);\n },\n } as TTooltipTriggerProps);\n\n const portalHost = getPortalHost();\n\n return (\n <>\n {triggerWithTracking}\n\n {portalHost !== null &&\n createPortal(\n <AnimatePresence>\n {resolvedOpen && (\n <motion.div\n key=\"tooltip-bubble\"\n ref={setFloating}\n variants={tooltipVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n role=\"tooltip\"\n className={twMerge(\n 'fixed left-0 top-0 z-[999] max-w-xs rounded-lg bg-ink px-3 py-2 text-xs font-normal text-ink-fg shadow-lg',\n className,\n )}\n >\n {content}\n </motion.div>\n )}\n </AnimatePresence>,\n portalHost,\n )}\n </>\n );\n};\n\nexport default ControlledTooltip;\n"],"names":["trigger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ControlledTooltip.js","sources":["../../../src/components/UI/ControlledTooltip.tsx"],"sourcesContent":["import { cloneElement, isValidElement, useCallback, useEffect, useRef, useState } from 'react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport type { FocusEvent, MouseEvent, PointerEvent, ReactElement, ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { composeRefs } from '@/helpers/composeRefs';\nimport { mergeClasses } from '@/helpers/mergeClasses';\nimport { getPortalHost } from '@/helpers/portalHost';\nimport { TFloatingPlacement, useAnchoredFloating } from '@/hooks/useAnchoredFloating';\nimport { tooltipVariants } from '@/motion';\n\nexport interface IControlledTooltipProps {\n content: ReactNode;\n children: ReactNode;\n className?: string;\n placement?: TFloatingPlacement;\n offset?: number;\n /** Lifts the touch block: a click-to-open tooltip is a deliberate mobile pattern. */\n interactive?: boolean;\n open?: boolean;\n handler?: (open: boolean) => void;\n}\n\ntype TTooltipTriggerProps = {\n onPointerEnter?: (event: PointerEvent<HTMLElement>) => void;\n onPointerDown?: (event: PointerEvent<HTMLElement>) => void;\n onMouseEnter?: (event: MouseEvent<HTMLElement>) => void;\n onFocus?: (event: FocusEvent<HTMLElement>) => void;\n ref?: React.Ref<HTMLElement>;\n};\n\nconst ORPHAN_TOOLTIP_CHECK_INTERVAL_MS = 250;\n\nconst isTouchFirstDevice = (): boolean => window.matchMedia('(hover: none), (pointer: coarse)').matches;\n\n/**\n * Owns the open state instead of delegating to the positioning library, because a raw\n * hover interaction opens on touch (the browser's compatibility `mouseenter`) and never\n * closes when the trigger stops receiving `mouseleave`: detached from the DOM, covered by\n * a dialog, or re-rendered. Both leave a bubble parked on the screen.\n */\nconst ControlledTooltip: React.FC<IControlledTooltipProps> = ({\n content,\n children,\n className,\n placement = 'top',\n offset = 6,\n interactive,\n open,\n handler,\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const triggerRef = useRef<HTMLElement | null>(null);\n const triggerPointerTypeRef = useRef<string | null>(null);\n const pointerPositionRef = useRef<{ x: number; y: number } | null>(null);\n\n const isControlledByHost = open !== undefined;\n const opensOnHover = !interactive;\n const resolvedOpen = isControlledByHost ? Boolean(open) : isOpen;\n\n const { setReference, setFloating } = useAnchoredFloating({\n open: resolvedOpen,\n placement,\n offsetPx: offset,\n constrainToViewport: false,\n });\n\n const closeTooltip = useCallback(() => {\n setIsOpen(false);\n handler?.(false);\n }, [handler]);\n\n const openTooltip = useCallback(() => {\n const pointerType = triggerPointerTypeRef.current;\n const openedByTouch = pointerType === null ? isTouchFirstDevice() : pointerType !== 'mouse';\n if (opensOnHover && openedByTouch) return;\n\n setIsOpen(true);\n handler?.(true);\n }, [handler, opensOnHover]);\n\n useEffect(() => {\n if (isControlledByHost || !resolvedOpen || !opensOnHover) return undefined;\n\n const belongsToTooltip = (element: Node | null): boolean => {\n const trigger = triggerRef.current;\n if (!element || !trigger) return false;\n if (trigger.contains(element)) return true;\n return element instanceof Element && element.closest('[role=\"tooltip\"]') !== null;\n };\n\n const closeWhenPointerLeftTrigger = (event: globalThis.PointerEvent) => {\n pointerPositionRef.current = { x: event.clientX, y: event.clientY };\n if (!triggerRef.current) return;\n if (belongsToTooltip(event.target instanceof Node ? event.target : null)) return;\n closeTooltip();\n };\n\n const closeWhenPageIsHidden = () => {\n if (document.hidden) closeTooltip();\n };\n\n const orphanTooltipCheck = window.setInterval(() => {\n const trigger = triggerRef.current;\n if (!trigger) return;\n if (!trigger.isConnected) {\n closeTooltip();\n return;\n }\n\n const pointerPosition = pointerPositionRef.current;\n if (!pointerPosition) return;\n if (!belongsToTooltip(document.elementFromPoint(pointerPosition.x, pointerPosition.y))) closeTooltip();\n }, ORPHAN_TOOLTIP_CHECK_INTERVAL_MS);\n\n document.addEventListener('pointermove', closeWhenPointerLeftTrigger, { capture: true, passive: true });\n document.addEventListener('pointerdown', closeTooltip, { capture: true, passive: true });\n document.addEventListener('visibilitychange', closeWhenPageIsHidden);\n window.addEventListener('blur', closeTooltip);\n\n return () => {\n window.clearInterval(orphanTooltipCheck);\n document.removeEventListener('pointermove', closeWhenPointerLeftTrigger, { capture: true });\n document.removeEventListener('pointerdown', closeTooltip, { capture: true });\n document.removeEventListener('visibilitychange', closeWhenPageIsHidden);\n window.removeEventListener('blur', closeTooltip);\n };\n }, [closeTooltip, isControlledByHost, resolvedOpen, opensOnHover]);\n\n if (!isValidElement(children)) return <>{children}</>;\n\n const trigger = children as ReactElement<TTooltipTriggerProps>;\n\n const triggerWithTracking = cloneElement(trigger, {\n ref: composeRefs<HTMLElement>(\n (node) => {\n triggerRef.current = node;\n },\n setReference,\n (trigger as { ref?: React.Ref<HTMLElement> }).ref,\n ),\n onPointerEnter: (event: PointerEvent<HTMLElement>) => {\n triggerPointerTypeRef.current = event.pointerType || null;\n triggerRef.current = event.currentTarget;\n pointerPositionRef.current = { x: event.clientX, y: event.clientY };\n trigger.props.onPointerEnter?.(event);\n if (!isControlledByHost) openTooltip();\n },\n onPointerDown: (event: PointerEvent<HTMLElement>) => {\n trigger.props.onPointerDown?.(event);\n // Not left to the document listener above, which only exists after the effect of the render\n // that opened the bubble: a press right behind the hover finds nobody listening, and the\n // bubble stays on the screen under whatever the press opened.\n if (!isControlledByHost && opensOnHover) closeTooltip();\n },\n onMouseEnter: (event: MouseEvent<HTMLElement>) => {\n triggerRef.current = event.currentTarget;\n pointerPositionRef.current = { x: event.clientX, y: event.clientY };\n trigger.props.onMouseEnter?.(event);\n },\n onFocus: (event: FocusEvent<HTMLElement>) => {\n triggerRef.current = event.currentTarget;\n trigger.props.onFocus?.(event);\n },\n } as TTooltipTriggerProps);\n\n const portalHost = getPortalHost();\n\n return (\n <>\n {triggerWithTracking}\n\n {portalHost !== null &&\n createPortal(\n <AnimatePresence>\n {resolvedOpen && (\n <motion.div\n key=\"tooltip-bubble\"\n ref={setFloating}\n variants={tooltipVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n role=\"tooltip\"\n className={mergeClasses(\n 'fixed left-0 top-0 z-[9999] max-w-xs rounded-lg bg-ink px-3 py-2 text-xs font-normal text-ink-fg shadow-lg',\n className,\n )}\n >\n {content}\n </motion.div>\n )}\n </AnimatePresence>,\n portalHost,\n )}\n </>\n );\n};\n\nexport default ControlledTooltip;\n"],"names":["trigger"],"mappings":"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"}
|
|
@@ -11,10 +11,10 @@ import { buildEmptyFilterValues, emptyValueForKind } from "../../Search/filters/
|
|
|
11
11
|
import "date-fns";
|
|
12
12
|
import "date-fns/locale";
|
|
13
13
|
import "react-day-picker";
|
|
14
|
-
import "tailwind-merge";
|
|
15
14
|
import "../ControlledPopover.js";
|
|
16
15
|
import "motion/react";
|
|
17
16
|
import "react-dom";
|
|
17
|
+
import "../../../helpers/mergeClasses.js";
|
|
18
18
|
import "@floating-ui/dom";
|
|
19
19
|
import "../../../motion/motionPresets.js";
|
|
20
20
|
import "../../../motion/motionTokens.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment as Fragment$1 } from "react/jsx-runtime";
|
|
2
2
|
import { Fragment } from "react";
|
|
3
3
|
import classNames from "classnames";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
4
5
|
import ControlledButton from "../../Form/ControlledButton.js";
|
|
5
6
|
import LoadingSpinner from "../LoadingSpinner.js";
|
|
6
7
|
import { MaterialSymbol } from "../MaterialSymbol/index.js";
|
|
@@ -32,6 +33,7 @@ const DataTableBody = ({
|
|
|
32
33
|
bodyTopRow,
|
|
33
34
|
bodyTopRowColSpan
|
|
34
35
|
}) => {
|
|
36
|
+
const { t } = useTranslation("ui");
|
|
35
37
|
const expansion = config.expansion;
|
|
36
38
|
const panelExpansionConfig = expansion && isDataTablePanelExpansion(expansion) ? expansion : null;
|
|
37
39
|
const subRowsExpansionConfig = expansion && isDataTableSubRowsExpansion(expansion) ? expansion : null;
|
|
@@ -70,6 +72,8 @@ const DataTableBody = ({
|
|
|
70
72
|
return /* @__PURE__ */ jsx(
|
|
71
73
|
ControlledButton,
|
|
72
74
|
{
|
|
75
|
+
"aria-label": t(row.getIsExpanded() ? "dataTable.collapseRow" : "dataTable.expandRow"),
|
|
76
|
+
"aria-expanded": row.getIsExpanded(),
|
|
73
77
|
defaultHtmlButton: true,
|
|
74
78
|
onClick: (event) => {
|
|
75
79
|
event.stopPropagation();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTableBody.js","sources":["../../../../src/components/UI/DataTable/DataTableBody.tsx"],"sourcesContent":["import React, { Fragment } from 'react';\nimport classNames from 'classnames';\n\nimport ControlledButton from '@/components/Form/ControlledButton';\nimport LoadingSpinner from '@/components/UI/LoadingSpinner';\nimport { MaterialSymbol } from '@/components/UI/MaterialSymbol';\nimport { flexRender, type Row } from '@tanstack/react-table';\n\nimport { GRID_COLUMN_DATA_ATTR, GRID_ROW_DATA_ATTR, type IGridCellCoord } from './grid/gridTypes';\nimport DataTable from './DataTable';\nimport DataTableBodyCell from './DataTableBodyCell';\nimport { alignClass, FROZEN_COLUMN_SHADOW, FROZEN_LEFT_COLUMN_SHADOW } from './dataTableClasses';\nimport { resolveColumnMinWidthPx } from './dataTableColumnWidths';\nimport DataTableDetailPanel from './DataTableDetailPanel';\nimport { type IDataTableDetailColumnsState } from './DataTableDetailTable';\nimport { dataTableOpacityTransitionClass } from './dataTableMotion';\nimport {\n type IDataTableColumnConfig,\n type IDataTableConfig,\n type IDataTableRow,\n isDataTableDetailTableExpansion,\n isDataTablePanelExpansion,\n isDataTableSubRowsExpansion,\n isDataTableTableExpansion,\n} from './types';\n\ninterface IDataTableBodyProps<T extends IDataTableRow, C extends IDataTableRow = IDataTableRow> {\n config: IDataTableConfig<T, C>;\n visibleRows: Array<Row<T>>;\n columnConfigById: Map<string, IDataTableColumnConfig<T>>;\n /** Row-derived widths for actions/badge columns; other columns fall back to the seed. */\n columnContentWidths: Map<string, number>;\n registerRow: (id: string) => (element: HTMLElement | null) => void;\n onCollapseStart: (childRowIds: Array<string>) => void;\n detailColumnsState?: IDataTableDetailColumnsState;\n detailSlotWidth?: number;\n showActionsShadow?: boolean;\n /** Denser cell padding for embedded/editor (fit) layouts. */\n dense?: boolean;\n /** Roomier cell padding, for tables that need to read bigger. */\n spacious?: boolean;\n /** Whether editable columns show the drag-to-fill handle. */\n fillEnabled?: boolean;\n /** While true the cells own the pointer and the row click is suspended. */\n editMode?: boolean;\n /** Whether a click on the cell opens its editor, which then does not also open the row. */\n canEditCell?: (coord: IGridCellCoord) => boolean;\n /** Full-width band rendered as the first body row, spanning every column. */\n bodyTopRow?: React.ReactNode;\n /** Number of visible leaf columns the `bodyTopRow` cell must span. */\n bodyTopRowColSpan?: number;\n}\n\nconst INDENT_PER_LEVEL_PX = 24;\n\nconst DataTableBody = <T extends IDataTableRow, C extends IDataTableRow = IDataTableRow>({\n config,\n visibleRows,\n columnConfigById,\n columnContentWidths,\n registerRow,\n onCollapseStart,\n detailColumnsState,\n detailSlotWidth,\n showActionsShadow = false,\n dense = false,\n spacious = false,\n fillEnabled = false,\n editMode = false,\n canEditCell,\n bodyTopRow,\n bodyTopRowColSpan,\n}: IDataTableBodyProps<T, C>) => {\n const expansion = config.expansion;\n const panelExpansionConfig = expansion && isDataTablePanelExpansion(expansion) ? expansion : null;\n const subRowsExpansionConfig = expansion && isDataTableSubRowsExpansion(expansion) ? expansion : null;\n const detailTableExpansionConfig = expansion && isDataTableDetailTableExpansion(expansion) ? expansion : null;\n const tableExpansionConfig = expansion && isDataTableTableExpansion(expansion) ? expansion : null;\n\n const rowClickEnabled = !!config.onRowClick;\n\n // In a spreadsheet-enabled table an editable cell owns its click (it opens the editor),\n // so the row only opens the record from the other cells. A click that bubbled from a\n // portal (a select's listbox) is not inside the row in the DOM and never opens it either.\n const handleRowClick = (event: React.MouseEvent<HTMLTableRowElement>, row: Row<T>) => {\n const target = event.target;\n if (!(target instanceof Node) || !event.currentTarget.contains(target)) return;\n\n if (editMode && canEditCell && target instanceof Element) {\n const cellElement = target.closest<HTMLElement>(`[${GRID_ROW_DATA_ATTR}]`);\n const cellRowId = cellElement?.getAttribute(GRID_ROW_DATA_ATTR);\n const cellColumnId = cellElement?.getAttribute(GRID_COLUMN_DATA_ATTR);\n if (cellRowId && cellColumnId && canEditCell({ rowId: cellRowId, columnId: cellColumnId })) return;\n }\n\n config.onRowClick?.(row.original);\n };\n\n const summaryCells = config.summaryRow?.(visibleRows.map((row) => row.original)) ?? null;\n const summaryContentById = new Map((summaryCells ?? []).map((cell) => [cell.columnId, cell.content]));\n\n const expandTriggerColumnId = expansion?.triggerColumnId ?? null;\n\n const toggleRowExpansion = (row: Row<T>) => {\n if (!expansion || !row.getCanExpand()) return;\n\n const willExpand = !row.getIsExpanded();\n if (!willExpand && subRowsExpansionConfig) {\n onCollapseStart(row.subRows.map((subRow) => subRow.id));\n }\n if (willExpand && tableExpansionConfig && tableExpansionConfig.getChildRows(row.original) === undefined) {\n tableExpansionConfig.loadChildren?.(row.original);\n }\n row.toggleExpanded();\n expansion.onExpandChange?.(row.original, willExpand);\n };\n\n const renderExpandControl = (row: Row<T>) => {\n if (!expansion) return null;\n\n if (!row.getCanExpand()) return <span className=\"inline-block size-8 shrink-0\" />;\n\n return (\n <ControlledButton\n defaultHtmlButton\n onClick={(event) => {\n event.stopPropagation();\n toggleRowExpansion(row);\n }}\n className=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-foreground-subtle transition-colors hover:bg-muted-hover hover:text-foreground-muted\"\n >\n <MaterialSymbol icon={row.getIsExpanded() ? 'expand_more' : 'chevron_right'} size={20} />\n </ControlledButton>\n );\n };\n\n const tableBody = (\n <tbody\n className={classNames(\n dataTableOpacityTransitionClass,\n '[&_tr:last-child_td:first-child]:rounded-bl-md [&_tr:last-child_td:last-child]:rounded-br-md [&_tr:last-child_td]:border-b-0',\n )}\n >\n {bodyTopRow && (\n <tr>\n <td colSpan={bodyTopRowColSpan} className=\"border-b border-border bg-surface p-0\">\n {/* Pin the band to the scroller's visible width so it never widens with\n the (possibly overflowing) table and its trailing controls stay in view. */}\n <div className=\"sticky left-0\" style={detailSlotWidth ? { width: detailSlotWidth } : undefined}>\n <div className={dense ? 'px-2 py-2' : 'px-4 py-2.5'}>{bodyTopRow}</div>\n </div>\n </td>\n </tr>\n )}\n\n {visibleRows.map((row) => {\n const isDimmed = config.rowIsDimmed?.(row.original) ?? false;\n const isChild = row.depth > 0;\n const isExpanded = row.getIsExpanded();\n const columnCount = row.getVisibleCells().length;\n\n const showSubRowsLoading =\n !!subRowsExpansionConfig &&\n isExpanded &&\n row.subRows.length === 0 &&\n !!subRowsExpansionConfig.isLoadingChildren?.(row.original);\n\n const showPanelContent = !!panelExpansionConfig && isExpanded;\n const showPanelLoading = showPanelContent && !!panelExpansionConfig.isLoadingContent?.(row.original);\n\n const showDetailTable = !!detailTableExpansionConfig && isExpanded;\n const detailRows = showDetailTable ? detailTableExpansionConfig.getDetailRows(row.original) : undefined;\n const showDetailLoading =\n showDetailTable &&\n (detailRows === undefined || !!detailTableExpansionConfig.isLoadingChildren?.(row.original));\n\n const showNestedTable = !!tableExpansionConfig && isExpanded;\n const nestedRows = showNestedTable ? tableExpansionConfig.getChildRows(row.original) : undefined;\n const showNestedLoading =\n showNestedTable && (nestedRows === undefined || !!tableExpansionConfig.isLoadingChildren?.(row.original));\n\n const visibleCells = row.getVisibleCells();\n const lastCellIndex = visibleCells.length - 1;\n\n return (\n <Fragment key={row.id}>\n <tr\n ref={registerRow(row.id)}\n onClick={rowClickEnabled ? (event) => handleRowClick(event, row) : undefined}\n // A altura na linha vale como MÍNIMO numa tabela: linhas de texto puro e\n // linhas com miniatura ou chip param de alternar de altura, e o olho\n // percorre a coluna sem tropeço. Conteúdo mais alto ainda expande.\n className={classNames('transition-colors', dense ? 'h-9' : 'h-12', {\n 'cursor-pointer': rowClickEnabled,\n 'bg-surface': !isChild,\n 'hover:bg-muted': rowClickEnabled && !isChild,\n 'bg-muted-hover': isChild,\n 'hover:bg-muted-active': isChild && rowClickEnabled,\n })}\n >\n {visibleCells.map((cell, cellIndex) => {\n const columnConfig = columnConfigById.get(cell.column.id);\n const cellContent = flexRender(cell.column.columnDef.cell, cell.getContext());\n const isFirstCell = cellIndex === 0;\n const isActionsCell = cellIndex === lastCellIndex && columnConfig?.type === 'actions';\n const isTriggerColumnCell = !!expandTriggerColumnId && cell.column.id === expandTriggerColumnId;\n const isExpandCell = !!expansion && (expandTriggerColumnId ? isTriggerColumnCell : isFirstCell);\n\n return (\n <DataTableBodyCell\n key={cell.id}\n rowId={row.id}\n columnId={cell.column.id}\n navigable={editMode && columnConfig?.type !== 'actions'}\n fillEnabled={fillEnabled && !!columnConfig?.editable && columnConfig.editable.fillable !== false}\n className={classNames(\n 'border-b border-border-subtle align-middle',\n dense ? 'px-3 py-1.5' : spacious ? 'px-5 py-4' : 'px-4 py-2.5',\n !isFirstCell && !isActionsCell && 'overflow-hidden',\n alignClass(columnConfig?.cellAlign),\n { 'opacity-60': isDimmed && columnConfig?.type !== 'actions' },\n isFirstCell && 'sticky left-0 z-10 bg-inherit',\n isFirstCell && showActionsShadow && FROZEN_LEFT_COLUMN_SHADOW,\n isActionsCell && 'sticky right-0 z-10 bg-inherit',\n isActionsCell && showActionsShadow && FROZEN_COLUMN_SHADOW,\n )}\n style={{\n minWidth:\n columnContentWidths.get(cell.column.id) ??\n resolveColumnMinWidthPx(columnConfig, {\n isFirstColumn: isFirstCell,\n hasColumnToggle: isFirstCell,\n }),\n }}\n >\n {isExpandCell ? (\n <div\n role={isTriggerColumnCell && row.getCanExpand() ? 'button' : undefined}\n tabIndex={isTriggerColumnCell && row.getCanExpand() ? 0 : undefined}\n onClick={\n isTriggerColumnCell && row.getCanExpand()\n ? (event) => {\n event.stopPropagation();\n toggleRowExpansion(row);\n }\n : undefined\n }\n onKeyDown={\n isTriggerColumnCell && row.getCanExpand()\n ? (event) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n event.stopPropagation();\n toggleRowExpansion(row);\n }\n : undefined\n }\n className={classNames('flex min-w-0 items-center gap-1.5', {\n 'cursor-pointer rounded-md': isTriggerColumnCell && row.getCanExpand(),\n })}\n style={{ paddingLeft: isFirstCell ? row.depth * INDENT_PER_LEVEL_PX : undefined }}\n >\n {renderExpandControl(row)}\n <div className=\"min-w-0 flex-1\">{cellContent}</div>\n </div>\n ) : (\n cellContent\n )}\n </DataTableBodyCell>\n );\n })}\n </tr>\n\n {showSubRowsLoading && (\n <tr>\n <td colSpan={columnCount} className=\"border-b border-border px-4 py-3\">\n <LoadingSpinner size={5} color=\"foreground-faint\" />\n </td>\n </tr>\n )}\n\n {showPanelContent && panelExpansionConfig && (\n <tr>\n <td colSpan={columnCount} className=\"border-b border-border p-0\">\n <div className=\"w-full bg-surface py-3\">\n {showPanelLoading ? (\n <div className=\"flex items-center justify-center py-2\">\n <LoadingSpinner size={5} color=\"foreground-faint\" />\n </div>\n ) : (\n panelExpansionConfig.renderExpandedContent(row.original, {\n collapse: () => {\n if (!row.getIsExpanded()) return;\n row.toggleExpanded(false);\n panelExpansionConfig.onExpandChange?.(row.original, false);\n },\n })\n )}\n </div>\n </td>\n </tr>\n )}\n\n {showDetailTable && detailTableExpansionConfig && (\n <tr>\n <td colSpan={columnCount} className=\"border-b border-border bg-muted/70 p-0\">\n <DataTableDetailPanel\n columns={\n typeof detailTableExpansionConfig.columns === 'function'\n ? detailTableExpansionConfig.columns(row.original)\n : detailTableExpansionConfig.columns\n }\n rows={detailRows ?? []}\n loading={showDetailLoading}\n onRowClick={\n detailTableExpansionConfig.onDetailRowClick\n ? (detailRow) => detailTableExpansionConfig.onDetailRowClick?.(detailRow, row.original)\n : undefined\n }\n rowIsDimmed={detailTableExpansionConfig.detailRowIsDimmed}\n emptyMessage={detailTableExpansionConfig.emptyMessage}\n columnsState={detailColumnsState}\n slotWidth={detailSlotWidth}\n />\n </td>\n </tr>\n )}\n\n {showNestedTable && tableExpansionConfig && (\n <tr>\n <td colSpan={columnCount} className=\"border-b border-border bg-muted/70 p-0\">\n <div className=\"sticky left-0\" style={detailSlotWidth ? { width: detailSlotWidth } : undefined}>\n <div className=\"px-3 py-3\">\n <DataTable\n config={tableExpansionConfig.childConfig}\n rows={nestedRows ?? []}\n loading={showNestedLoading}\n />\n </div>\n </div>\n </td>\n </tr>\n )}\n </Fragment>\n );\n })}\n </tbody>\n );\n\n const summaryFooter =\n summaryCells && summaryCells.length > 0 && visibleRows.length > 0 ? (\n <tfoot>\n <tr className=\"bg-muted font-semibold text-foreground-muted\">\n {visibleRows[0]?.getVisibleCells().map((cell, cellIndex) => {\n const columnConfig = columnConfigById.get(cell.column.id);\n const isFirstCell = cellIndex === 0;\n const isActionsCell =\n cellIndex === (visibleRows[0]?.getVisibleCells().length ?? 0) - 1 && columnConfig?.type === 'actions';\n const content = summaryContentById.get(cell.column.id) ?? null;\n\n return (\n <td\n key={cell.column.id}\n className={classNames(\n 'sticky bottom-0 z-10 border-t-2 border-border bg-muted align-middle',\n dense ? 'px-3 py-1.5' : 'px-4 py-2.5',\n alignClass(columnConfig?.cellAlign),\n isFirstCell && 'left-0 z-20',\n isFirstCell && showActionsShadow && FROZEN_LEFT_COLUMN_SHADOW,\n isActionsCell && 'right-0 z-20',\n isActionsCell && showActionsShadow && FROZEN_COLUMN_SHADOW,\n )}\n style={{\n minWidth: resolveColumnMinWidthPx(columnConfig, {\n isFirstColumn: isFirstCell,\n hasColumnToggle: isFirstCell,\n }),\n }}\n >\n {isFirstCell && expansion ? (\n <div className=\"flex min-w-0 items-center gap-1.5\">\n <span className=\"inline-block size-8 shrink-0\" />\n <div className=\"min-w-0 flex-1\">{content}</div>\n </div>\n ) : (\n content\n )}\n </td>\n );\n })}\n </tr>\n </tfoot>\n ) : null;\n\n return (\n <>\n {tableBody}\n {summaryFooter}\n </>\n );\n};\n\nexport default DataTableBody;\n"],"names":["Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataTableBody.js","sources":["../../../../src/components/UI/DataTable/DataTableBody.tsx"],"sourcesContent":["import React, { Fragment } from 'react';\nimport classNames from 'classnames';\nimport { useTranslation } from 'react-i18next';\n\nimport ControlledButton from '@/components/Form/ControlledButton';\nimport LoadingSpinner from '@/components/UI/LoadingSpinner';\nimport { MaterialSymbol } from '@/components/UI/MaterialSymbol';\nimport { flexRender, type Row } from '@tanstack/react-table';\n\nimport { GRID_COLUMN_DATA_ATTR, GRID_ROW_DATA_ATTR, type IGridCellCoord } from './grid/gridTypes';\nimport DataTable from './DataTable';\nimport DataTableBodyCell from './DataTableBodyCell';\nimport { alignClass, FROZEN_COLUMN_SHADOW, FROZEN_LEFT_COLUMN_SHADOW } from './dataTableClasses';\nimport { resolveColumnMinWidthPx } from './dataTableColumnWidths';\nimport DataTableDetailPanel from './DataTableDetailPanel';\nimport { type IDataTableDetailColumnsState } from './DataTableDetailTable';\nimport { dataTableOpacityTransitionClass } from './dataTableMotion';\nimport {\n type IDataTableColumnConfig,\n type IDataTableConfig,\n type IDataTableRow,\n isDataTableDetailTableExpansion,\n isDataTablePanelExpansion,\n isDataTableSubRowsExpansion,\n isDataTableTableExpansion,\n} from './types';\n\ninterface IDataTableBodyProps<T extends IDataTableRow, C extends IDataTableRow = IDataTableRow> {\n config: IDataTableConfig<T, C>;\n visibleRows: Array<Row<T>>;\n columnConfigById: Map<string, IDataTableColumnConfig<T>>;\n /** Row-derived widths for actions/badge columns; other columns fall back to the seed. */\n columnContentWidths: Map<string, number>;\n registerRow: (id: string) => (element: HTMLElement | null) => void;\n onCollapseStart: (childRowIds: Array<string>) => void;\n detailColumnsState?: IDataTableDetailColumnsState;\n detailSlotWidth?: number;\n showActionsShadow?: boolean;\n /** Denser cell padding for embedded/editor (fit) layouts. */\n dense?: boolean;\n /** Roomier cell padding, for tables that need to read bigger. */\n spacious?: boolean;\n /** Whether editable columns show the drag-to-fill handle. */\n fillEnabled?: boolean;\n /** While true the cells own the pointer and the row click is suspended. */\n editMode?: boolean;\n /** Whether a click on the cell opens its editor, which then does not also open the row. */\n canEditCell?: (coord: IGridCellCoord) => boolean;\n /** Full-width band rendered as the first body row, spanning every column. */\n bodyTopRow?: React.ReactNode;\n /** Number of visible leaf columns the `bodyTopRow` cell must span. */\n bodyTopRowColSpan?: number;\n}\n\nconst INDENT_PER_LEVEL_PX = 24;\n\nconst DataTableBody = <T extends IDataTableRow, C extends IDataTableRow = IDataTableRow>({\n config,\n visibleRows,\n columnConfigById,\n columnContentWidths,\n registerRow,\n onCollapseStart,\n detailColumnsState,\n detailSlotWidth,\n showActionsShadow = false,\n dense = false,\n spacious = false,\n fillEnabled = false,\n editMode = false,\n canEditCell,\n bodyTopRow,\n bodyTopRowColSpan,\n}: IDataTableBodyProps<T, C>) => {\n const { t } = useTranslation('ui');\n const expansion = config.expansion;\n const panelExpansionConfig = expansion && isDataTablePanelExpansion(expansion) ? expansion : null;\n const subRowsExpansionConfig = expansion && isDataTableSubRowsExpansion(expansion) ? expansion : null;\n const detailTableExpansionConfig = expansion && isDataTableDetailTableExpansion(expansion) ? expansion : null;\n const tableExpansionConfig = expansion && isDataTableTableExpansion(expansion) ? expansion : null;\n\n const rowClickEnabled = !!config.onRowClick;\n\n // In a spreadsheet-enabled table an editable cell owns its click (it opens the editor),\n // so the row only opens the record from the other cells. A click that bubbled from a\n // portal (a select's listbox) is not inside the row in the DOM and never opens it either.\n const handleRowClick = (event: React.MouseEvent<HTMLTableRowElement>, row: Row<T>) => {\n const target = event.target;\n if (!(target instanceof Node) || !event.currentTarget.contains(target)) return;\n\n if (editMode && canEditCell && target instanceof Element) {\n const cellElement = target.closest<HTMLElement>(`[${GRID_ROW_DATA_ATTR}]`);\n const cellRowId = cellElement?.getAttribute(GRID_ROW_DATA_ATTR);\n const cellColumnId = cellElement?.getAttribute(GRID_COLUMN_DATA_ATTR);\n if (cellRowId && cellColumnId && canEditCell({ rowId: cellRowId, columnId: cellColumnId })) return;\n }\n\n config.onRowClick?.(row.original);\n };\n\n const summaryCells = config.summaryRow?.(visibleRows.map((row) => row.original)) ?? null;\n const summaryContentById = new Map((summaryCells ?? []).map((cell) => [cell.columnId, cell.content]));\n\n const expandTriggerColumnId = expansion?.triggerColumnId ?? null;\n\n const toggleRowExpansion = (row: Row<T>) => {\n if (!expansion || !row.getCanExpand()) return;\n\n const willExpand = !row.getIsExpanded();\n if (!willExpand && subRowsExpansionConfig) {\n onCollapseStart(row.subRows.map((subRow) => subRow.id));\n }\n if (willExpand && tableExpansionConfig && tableExpansionConfig.getChildRows(row.original) === undefined) {\n tableExpansionConfig.loadChildren?.(row.original);\n }\n row.toggleExpanded();\n expansion.onExpandChange?.(row.original, willExpand);\n };\n\n const renderExpandControl = (row: Row<T>) => {\n if (!expansion) return null;\n\n if (!row.getCanExpand()) return <span className=\"inline-block size-8 shrink-0\" />;\n\n return (\n <ControlledButton\n aria-label={t(row.getIsExpanded() ? 'dataTable.collapseRow' : 'dataTable.expandRow')}\n aria-expanded={row.getIsExpanded()}\n defaultHtmlButton\n onClick={(event) => {\n event.stopPropagation();\n toggleRowExpansion(row);\n }}\n className=\"inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-foreground-subtle transition-colors hover:bg-muted-hover hover:text-foreground-muted\"\n >\n <MaterialSymbol icon={row.getIsExpanded() ? 'expand_more' : 'chevron_right'} size={20} />\n </ControlledButton>\n );\n };\n\n const tableBody = (\n <tbody\n className={classNames(\n dataTableOpacityTransitionClass,\n '[&_tr:last-child_td:first-child]:rounded-bl-md [&_tr:last-child_td:last-child]:rounded-br-md [&_tr:last-child_td]:border-b-0',\n )}\n >\n {bodyTopRow && (\n <tr>\n <td colSpan={bodyTopRowColSpan} className=\"border-b border-border bg-surface p-0\">\n {/* Pin the band to the scroller's visible width so it never widens with\n the (possibly overflowing) table and its trailing controls stay in view. */}\n <div className=\"sticky left-0\" style={detailSlotWidth ? { width: detailSlotWidth } : undefined}>\n <div className={dense ? 'px-2 py-2' : 'px-4 py-2.5'}>{bodyTopRow}</div>\n </div>\n </td>\n </tr>\n )}\n\n {visibleRows.map((row) => {\n const isDimmed = config.rowIsDimmed?.(row.original) ?? false;\n const isChild = row.depth > 0;\n const isExpanded = row.getIsExpanded();\n const columnCount = row.getVisibleCells().length;\n\n const showSubRowsLoading =\n !!subRowsExpansionConfig &&\n isExpanded &&\n row.subRows.length === 0 &&\n !!subRowsExpansionConfig.isLoadingChildren?.(row.original);\n\n const showPanelContent = !!panelExpansionConfig && isExpanded;\n const showPanelLoading = showPanelContent && !!panelExpansionConfig.isLoadingContent?.(row.original);\n\n const showDetailTable = !!detailTableExpansionConfig && isExpanded;\n const detailRows = showDetailTable ? detailTableExpansionConfig.getDetailRows(row.original) : undefined;\n const showDetailLoading =\n showDetailTable &&\n (detailRows === undefined || !!detailTableExpansionConfig.isLoadingChildren?.(row.original));\n\n const showNestedTable = !!tableExpansionConfig && isExpanded;\n const nestedRows = showNestedTable ? tableExpansionConfig.getChildRows(row.original) : undefined;\n const showNestedLoading =\n showNestedTable && (nestedRows === undefined || !!tableExpansionConfig.isLoadingChildren?.(row.original));\n\n const visibleCells = row.getVisibleCells();\n const lastCellIndex = visibleCells.length - 1;\n\n return (\n <Fragment key={row.id}>\n <tr\n ref={registerRow(row.id)}\n onClick={rowClickEnabled ? (event) => handleRowClick(event, row) : undefined}\n // A altura na linha vale como MÍNIMO numa tabela: linhas de texto puro e\n // linhas com miniatura ou chip param de alternar de altura, e o olho\n // percorre a coluna sem tropeço. Conteúdo mais alto ainda expande.\n className={classNames('transition-colors', dense ? 'h-9' : 'h-12', {\n 'cursor-pointer': rowClickEnabled,\n 'bg-surface': !isChild,\n 'hover:bg-muted': rowClickEnabled && !isChild,\n 'bg-muted-hover': isChild,\n 'hover:bg-muted-active': isChild && rowClickEnabled,\n })}\n >\n {visibleCells.map((cell, cellIndex) => {\n const columnConfig = columnConfigById.get(cell.column.id);\n const cellContent = flexRender(cell.column.columnDef.cell, cell.getContext());\n const isFirstCell = cellIndex === 0;\n const isActionsCell = cellIndex === lastCellIndex && columnConfig?.type === 'actions';\n const isTriggerColumnCell = !!expandTriggerColumnId && cell.column.id === expandTriggerColumnId;\n const isExpandCell = !!expansion && (expandTriggerColumnId ? isTriggerColumnCell : isFirstCell);\n\n return (\n <DataTableBodyCell\n key={cell.id}\n rowId={row.id}\n columnId={cell.column.id}\n navigable={editMode && columnConfig?.type !== 'actions'}\n fillEnabled={fillEnabled && !!columnConfig?.editable && columnConfig.editable.fillable !== false}\n className={classNames(\n 'border-b border-border-subtle align-middle',\n dense ? 'px-3 py-1.5' : spacious ? 'px-5 py-4' : 'px-4 py-2.5',\n !isFirstCell && !isActionsCell && 'overflow-hidden',\n alignClass(columnConfig?.cellAlign),\n { 'opacity-60': isDimmed && columnConfig?.type !== 'actions' },\n isFirstCell && 'sticky left-0 z-10 bg-inherit',\n isFirstCell && showActionsShadow && FROZEN_LEFT_COLUMN_SHADOW,\n isActionsCell && 'sticky right-0 z-10 bg-inherit',\n isActionsCell && showActionsShadow && FROZEN_COLUMN_SHADOW,\n )}\n style={{\n minWidth:\n columnContentWidths.get(cell.column.id) ??\n resolveColumnMinWidthPx(columnConfig, {\n isFirstColumn: isFirstCell,\n hasColumnToggle: isFirstCell,\n }),\n }}\n >\n {isExpandCell ? (\n <div\n role={isTriggerColumnCell && row.getCanExpand() ? 'button' : undefined}\n tabIndex={isTriggerColumnCell && row.getCanExpand() ? 0 : undefined}\n onClick={\n isTriggerColumnCell && row.getCanExpand()\n ? (event) => {\n event.stopPropagation();\n toggleRowExpansion(row);\n }\n : undefined\n }\n onKeyDown={\n isTriggerColumnCell && row.getCanExpand()\n ? (event) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n event.stopPropagation();\n toggleRowExpansion(row);\n }\n : undefined\n }\n className={classNames('flex min-w-0 items-center gap-1.5', {\n 'cursor-pointer rounded-md': isTriggerColumnCell && row.getCanExpand(),\n })}\n style={{ paddingLeft: isFirstCell ? row.depth * INDENT_PER_LEVEL_PX : undefined }}\n >\n {renderExpandControl(row)}\n <div className=\"min-w-0 flex-1\">{cellContent}</div>\n </div>\n ) : (\n cellContent\n )}\n </DataTableBodyCell>\n );\n })}\n </tr>\n\n {showSubRowsLoading && (\n <tr>\n <td colSpan={columnCount} className=\"border-b border-border px-4 py-3\">\n <LoadingSpinner size={5} color=\"foreground-faint\" />\n </td>\n </tr>\n )}\n\n {showPanelContent && panelExpansionConfig && (\n <tr>\n <td colSpan={columnCount} className=\"border-b border-border p-0\">\n <div className=\"w-full bg-surface py-3\">\n {showPanelLoading ? (\n <div className=\"flex items-center justify-center py-2\">\n <LoadingSpinner size={5} color=\"foreground-faint\" />\n </div>\n ) : (\n panelExpansionConfig.renderExpandedContent(row.original, {\n collapse: () => {\n if (!row.getIsExpanded()) return;\n row.toggleExpanded(false);\n panelExpansionConfig.onExpandChange?.(row.original, false);\n },\n })\n )}\n </div>\n </td>\n </tr>\n )}\n\n {showDetailTable && detailTableExpansionConfig && (\n <tr>\n <td colSpan={columnCount} className=\"border-b border-border bg-muted/70 p-0\">\n <DataTableDetailPanel\n columns={\n typeof detailTableExpansionConfig.columns === 'function'\n ? detailTableExpansionConfig.columns(row.original)\n : detailTableExpansionConfig.columns\n }\n rows={detailRows ?? []}\n loading={showDetailLoading}\n onRowClick={\n detailTableExpansionConfig.onDetailRowClick\n ? (detailRow) => detailTableExpansionConfig.onDetailRowClick?.(detailRow, row.original)\n : undefined\n }\n rowIsDimmed={detailTableExpansionConfig.detailRowIsDimmed}\n emptyMessage={detailTableExpansionConfig.emptyMessage}\n columnsState={detailColumnsState}\n slotWidth={detailSlotWidth}\n />\n </td>\n </tr>\n )}\n\n {showNestedTable && tableExpansionConfig && (\n <tr>\n <td colSpan={columnCount} className=\"border-b border-border bg-muted/70 p-0\">\n <div className=\"sticky left-0\" style={detailSlotWidth ? { width: detailSlotWidth } : undefined}>\n <div className=\"px-3 py-3\">\n <DataTable\n config={tableExpansionConfig.childConfig}\n rows={nestedRows ?? []}\n loading={showNestedLoading}\n />\n </div>\n </div>\n </td>\n </tr>\n )}\n </Fragment>\n );\n })}\n </tbody>\n );\n\n const summaryFooter =\n summaryCells && summaryCells.length > 0 && visibleRows.length > 0 ? (\n <tfoot>\n <tr className=\"bg-muted font-semibold text-foreground-muted\">\n {visibleRows[0]?.getVisibleCells().map((cell, cellIndex) => {\n const columnConfig = columnConfigById.get(cell.column.id);\n const isFirstCell = cellIndex === 0;\n const isActionsCell =\n cellIndex === (visibleRows[0]?.getVisibleCells().length ?? 0) - 1 && columnConfig?.type === 'actions';\n const content = summaryContentById.get(cell.column.id) ?? null;\n\n return (\n <td\n key={cell.column.id}\n className={classNames(\n 'sticky bottom-0 z-10 border-t-2 border-border bg-muted align-middle',\n dense ? 'px-3 py-1.5' : 'px-4 py-2.5',\n alignClass(columnConfig?.cellAlign),\n isFirstCell && 'left-0 z-20',\n isFirstCell && showActionsShadow && FROZEN_LEFT_COLUMN_SHADOW,\n isActionsCell && 'right-0 z-20',\n isActionsCell && showActionsShadow && FROZEN_COLUMN_SHADOW,\n )}\n style={{\n minWidth: resolveColumnMinWidthPx(columnConfig, {\n isFirstColumn: isFirstCell,\n hasColumnToggle: isFirstCell,\n }),\n }}\n >\n {isFirstCell && expansion ? (\n <div className=\"flex min-w-0 items-center gap-1.5\">\n <span className=\"inline-block size-8 shrink-0\" />\n <div className=\"min-w-0 flex-1\">{content}</div>\n </div>\n ) : (\n content\n )}\n </td>\n );\n })}\n </tr>\n </tfoot>\n ) : null;\n\n return (\n <>\n {tableBody}\n {summaryFooter}\n </>\n );\n};\n\nexport default DataTableBody;\n"],"names":["Fragment"],"mappings":"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"}
|
|
@@ -173,7 +173,8 @@ const DataTableBulkEditDialog = ({
|
|
|
173
173
|
value: cellValue,
|
|
174
174
|
onChange: (nextValue) => setDraftValue(row.id, column.id, nextValue),
|
|
175
175
|
disabled: isDisabled,
|
|
176
|
-
errorMessage: cellError
|
|
176
|
+
errorMessage: cellError,
|
|
177
|
+
row
|
|
177
178
|
}
|
|
178
179
|
)
|
|
179
180
|
] }, column.id);
|
|
@@ -231,7 +232,8 @@ const DataTableBulkEditDialog = ({
|
|
|
231
232
|
value,
|
|
232
233
|
onChange: setValue,
|
|
233
234
|
label: t("dataTable.bulkEdit.newValue"),
|
|
234
|
-
errorMessage: sameValueValidationError
|
|
235
|
+
errorMessage: sameValueValidationError,
|
|
236
|
+
rows: targetRows
|
|
235
237
|
}
|
|
236
238
|
)
|
|
237
239
|
] }) : /* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTableBulkEditDialog.js","sources":["../../../../src/components/UI/DataTable/DataTableBulkEditDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport ControlledDialog from '@/components/Form/ControlledDialog';\nimport { ControlledSelect } from '@/components/Form/ControlledSelect';\nimport ControlledViewToggle, { type IControlledViewToggleOption } from '@/components/Form/ControlledViewToggle';\nimport ControlledTooltip from '@/components/UI/ControlledTooltip';\nimport NoDataFound from '@/components/UI/NoDataFound';\n\nimport { areBulkValuesEqual, isBulkValueEmpty } from './bulkEditValues';\nimport DataTableBulkEditRowsPicker from './DataTableBulkEditRowsPicker';\nimport DataTableBulkEditValueEditor from './DataTableBulkEditValueEditor';\nimport type { IDataTableColumnConfig, IDataTableFillChange, IDataTableRow } from './types';\n\ntype TBulkEditMode = 'same' | 'perItem';\ntype TBulkEditScope = 'loaded' | 'picked';\n\ninterface IDataTableBulkEditDialogProps<T extends IDataTableRow> {\n open: boolean;\n onClose: () => void;\n /** Todas as colunas da tabela: só as editáveis viram opção de campo. */\n columns: Array<IDataTableColumnConfig<T>>;\n /** As linhas carregadas: a página atual na lista, o acumulado do scroll nos cards. */\n rows: Array<T>;\n /** Rótulo de cada linha na lista de escolha. Sem ele, a primeira coluna de texto responde. */\n getRowLabel?: (row: T) => string;\n /** Ids já escolhidos fora do diálogo: abre no modo \"escolher\" com eles marcados. */\n initialTargetIds?: ReadonlyArray<string>;\n onApply: (changes: Array<IDataTableFillChange<T>>) => void;\n}\n\n/**\n * Sem `getRowLabel`, o nome sai da primeira coluna que sabe devolver um valor\n * legível. Colunas que só desenham (`render`, sem `accessor`) não respondem, e é\n * por isso que uma tabela cuja identidade mora numa coluna `custom` precisa\n * declarar `getRowLabel`: aqui só sobraria o id.\n */\nconst resolveDefaultRowLabel = <T extends IDataTableRow>(row: T, columns: Array<IDataTableColumnConfig<T>>): string => {\n const label = (columns ?? [])\n .filter((column) => column.type !== 'actions' && !!column.accessor)\n .map((column) => column.accessor?.(row))\n .find((value) => typeof value === 'string' || typeof value === 'number');\n\n return label == null || label === '' ? row.id : String(label);\n};\n\n/**\n * Bulk edit in two modes. \"Same value\" applies one value to one field across the\n * chosen scope; \"Values per item\" opens an item x fields matrix prefilled with\n * the current values, and only the changed cells are written.\n *\n * O diálogo não persiste nada. Ele devolve as células escritas por `onApply`, que\n * o host entrega ao `onFillCells` da fila de commit: um render, um flush e uma\n * requisição em lote, exatamente como o preenchimento por arraste.\n */\nconst DataTableBulkEditDialog = <T extends IDataTableRow>({\n open,\n onClose,\n columns,\n rows,\n getRowLabel,\n initialTargetIds,\n onApply,\n}: IDataTableBulkEditDialogProps<T>) => {\n const { t } = useTranslation('ui');\n\n const editableColumns = useMemo(() => (columns ?? []).filter((column) => !!column.editable), [columns]);\n\n const [mode, setMode] = useState<TBulkEditMode>('same');\n const [columnId, setColumnId] = useState('');\n const [value, setValue] = useState<unknown>('');\n const [perItemColumnIds, setPerItemColumnIds] = useState<Array<string>>([]);\n const [drafts, setDrafts] = useState<Record<string, Record<string, unknown>>>({});\n const [scope, setScope] = useState<TBulkEditScope>('loaded');\n const [pickedIds, setPickedIds] = useState<Array<string>>([]);\n\n // Cada abertura recomeça do zero: um valor ou rascunho sobrando da vez anterior\n // seria aplicado em massa por um clique só. O reset acontece na transição de\n // abrir, nunca por uma nova referência de `columns` ou de `initialTargetIds`,\n // que a tela recria ao marcar uma linha atrás do diálogo.\n const wasOpenRef = useRef(false);\n useEffect(() => {\n if (open === wasOpenRef.current) return;\n wasOpenRef.current = open;\n if (!open) return;\n\n const preselected = Array.from(initialTargetIds ?? []);\n setMode('same');\n setColumnId(editableColumns[0]?.id ?? '');\n setValue('');\n setPerItemColumnIds([]);\n setDrafts({});\n setScope(preselected.length > 0 ? 'picked' : 'loaded');\n setPickedIds(preselected);\n }, [open, editableColumns, initialTargetIds]);\n\n const selectedColumn = useMemo(\n () => editableColumns.find((column) => column.id === columnId),\n [editableColumns, columnId],\n );\n const editable = selectedColumn?.editable;\n\n const selectedPerItemColumns = useMemo(\n () =>\n perItemColumnIds\n .map((id) => editableColumns.find((column) => column.id === id))\n .filter((column): column is IDataTableColumnConfig<T> => !!column),\n [perItemColumnIds, editableColumns],\n );\n\n const resolveLabel = useMemo(\n () => getRowLabel ?? ((row: T) => resolveDefaultRowLabel(row, columns ?? [])),\n [getRowLabel, columns],\n );\n\n // Uma linha que os editores recusam (variante, registro bloqueado) não entra\n // no alcance nem aparece nas listas: prometer a alteração e descartá-la é pior.\n const eligibleRows = useMemo(() => {\n if (mode === 'same') return (rows ?? []).filter((row) => !(editable?.disabled?.(row) ?? false));\n\n const referenceColumns = selectedPerItemColumns.length > 0 ? selectedPerItemColumns : editableColumns;\n return (rows ?? []).filter((row) =>\n referenceColumns.some((column) => !(column.editable?.disabled?.(row) ?? false)),\n );\n }, [mode, rows, editable, selectedPerItemColumns, editableColumns]);\n\n const pickedIdSet = useMemo(() => new Set(pickedIds), [pickedIds]);\n\n const targetRows = useMemo(\n () => (scope === 'loaded' ? eligibleRows : eligibleRows.filter((row) => pickedIdSet.has(row.id))),\n [scope, eligibleRows, pickedIdSet],\n );\n\n // Vazio não aplica no modo \"mesmo valor\": o diálogo abre sem valor, e um Enter\n // distraído limparia o campo em todo o alcance. Na matriz por item, limpar uma\n // célula é uma alteração explícita e legítima.\n const isValueEmpty = isBulkValueEmpty(value);\n\n const sameValueValidationError = useMemo(() => {\n if (mode !== 'same' || !editable?.validate) return null;\n\n const firstInvalid = targetRows.map((row) => editable.validate?.(value, row) ?? null).find((message) => !!message);\n\n return firstInvalid ?? null;\n }, [mode, editable, targetRows, value]);\n\n // Only what actually changed becomes a write: a cell with no draft, or edited\n // back to the current value, stays out.\n const perItemChanges = useMemo(() => {\n if (mode !== 'perItem') return [];\n\n const changes: Array<IDataTableFillChange<T>> = [];\n targetRows.forEach((row) => {\n const rowDraft = drafts[row.id];\n if (!rowDraft) return;\n\n selectedPerItemColumns.forEach((column) => {\n if (!Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return;\n if (column.editable?.disabled?.(row) ?? false) return;\n\n const draftValue = rowDraft[column.id];\n if (areBulkValuesEqual(draftValue, column.editable?.getValue(row) ?? null)) return;\n\n changes.push({ row, columnId: column.id, value: draftValue });\n });\n });\n\n return changes;\n }, [mode, targetRows, drafts, selectedPerItemColumns]);\n\n const perItemValidationError = useMemo(() => {\n const firstInvalid = perItemChanges\n .map((change) => {\n const column = selectedPerItemColumns.find((entry) => entry.id === change.columnId);\n return column?.editable?.validate?.(change.value, change.row) ?? null;\n })\n .find((message) => !!message);\n\n return firstInvalid ?? null;\n }, [perItemChanges, selectedPerItemColumns]);\n\n const setDraftValue = (rowId: string, targetColumnId: string, nextValue: unknown) =>\n setDrafts((current) => ({\n ...current,\n [rowId]: { ...current[rowId], [targetColumnId]: nextValue },\n }));\n\n const resolveCellValue = (row: T, column: IDataTableColumnConfig<T>): unknown => {\n const rowDraft = drafts[row.id];\n if (rowDraft && Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return rowDraft[column.id];\n\n return column.editable?.getValue(row) ?? null;\n };\n\n const isCellDirty = (row: T, column: IDataTableColumnConfig<T>): boolean => {\n const rowDraft = drafts[row.id];\n if (!rowDraft || !Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return false;\n\n return !areBulkValuesEqual(rowDraft[column.id], column.editable?.getValue(row) ?? null);\n };\n\n const modeOptions: Array<IControlledViewToggleOption<TBulkEditMode>> = [\n { value: 'same', label: t('dataTable.bulkEdit.modeSame'), icon: 'stacks' },\n { value: 'perItem', label: t('dataTable.bulkEdit.modePerItem'), icon: 'edit_note' },\n ];\n\n const scopeOptions: Array<IControlledViewToggleOption<TBulkEditScope>> = [\n { value: 'loaded', label: t('dataTable.bulkEdit.scopeLoaded'), icon: 'select_all', count: eligibleRows.length },\n { value: 'picked', label: t('dataTable.bulkEdit.scopePicked'), icon: 'checklist', count: pickedIdSet.size },\n ];\n\n const handleApply = () => {\n if (mode === 'perItem') {\n if (perItemChanges.length === 0) return;\n\n onApply(perItemChanges);\n onClose();\n return;\n }\n\n if (!selectedColumn || targetRows.length === 0) return;\n\n onApply(targetRows.map((row) => ({ row, columnId: selectedColumn.id, value })));\n onClose();\n };\n\n const confirmLabel =\n mode === 'perItem'\n ? perItemChanges.length === 0\n ? t('dataTable.bulkEdit.confirmNoChanges')\n : t('dataTable.bulkEdit.confirmChanges', { count: perItemChanges.length })\n : targetRows.length === 0\n ? t('dataTable.bulkEdit.confirmEmpty')\n : t('dataTable.bulkEdit.confirm', { count: targetRows.length });\n\n const disableConfirm =\n mode === 'perItem'\n ? perItemChanges.length === 0 || !!perItemValidationError\n : !selectedColumn || targetRows.length === 0 || isValueEmpty || !!sameValueValidationError;\n\n const renderPerItemMatrix = () => {\n if (selectedPerItemColumns.length === 0) {\n return <NoDataFound size=\"sm\" variant=\"waiting\" message={t('dataTable.bulkEdit.pickFieldsHint')} />;\n }\n\n if (targetRows.length === 0) {\n return <NoDataFound size=\"sm\" variant=\"waiting\" message={t('dataTable.bulkEdit.noTargets')} />;\n }\n\n return (\n <div className=\"flex min-h-0 flex-col gap-2 rounded-xl border border-border bg-surface-sunken p-3\">\n <span className=\"text-xs text-foreground-muted\">{t('dataTable.bulkEdit.perItemHint')}</span>\n <div className=\"overflow-x-auto\">\n <div className=\"flex min-w-max flex-col gap-2\">\n <div className=\"flex items-center gap-3\">\n <span className=\"w-52 shrink-0 text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.itemColumn')}\n </span>\n {selectedPerItemColumns.map((column) => (\n <span\n key={column.id}\n className=\"w-64 shrink-0 truncate text-xs font-medium uppercase tracking-wide text-foreground-subtle\"\n >\n {column.header}\n </span>\n ))}\n </div>\n\n <div className=\"flex max-h-96 flex-col gap-2 overflow-y-auto pt-1\">\n {targetRows.map((row) => (\n <div key={row.id} className=\"flex items-center gap-3\">\n <ControlledTooltip content={resolveLabel(row)} className=\"z-[9999]\" placement=\"top-start\">\n <span className=\"w-52 shrink-0 truncate text-sm text-foreground\">{resolveLabel(row)}</span>\n </ControlledTooltip>\n {selectedPerItemColumns.map((column) => {\n const cellEditable = column.editable;\n if (!cellEditable) return <span key={column.id} className=\"w-64 shrink-0\" />;\n\n const isDisabled = cellEditable.disabled?.(row) ?? false;\n const cellValue = resolveCellValue(row, column);\n const dirty = isCellDirty(row, column);\n const cellError = dirty ? (cellEditable.validate?.(cellValue, row) ?? null) : null;\n\n return (\n <div key={column.id} className=\"relative w-64 shrink-0\">\n {dirty && (\n <span className=\"absolute -right-1 -top-1 z-10 size-2 rounded-full bg-brand\" aria-hidden />\n )}\n <DataTableBulkEditValueEditor\n editable={cellEditable}\n value={cellValue}\n onChange={(nextValue) => setDraftValue(row.id, column.id, nextValue)}\n disabled={isDisabled}\n errorMessage={cellError}\n />\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n };\n\n return (\n <ControlledDialog\n open={open}\n size={mode === 'perItem' ? 'xl' : 'md'}\n title={t('dataTable.bulkEdit.title')}\n subtitle={mode === 'perItem' ? t('dataTable.bulkEdit.subtitlePerItem') : t('dataTable.bulkEdit.subtitle')}\n onCancel={onClose}\n onConfirm={handleApply}\n confirmLabel={confirmLabel}\n cancelLabel={t('actions.cancel')}\n disableConfirm={disableConfirm}\n >\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.modeLabel')}\n </span>\n <ControlledViewToggle<TBulkEditMode>\n options={modeOptions}\n value={mode}\n onChange={setMode}\n showLabels\n size=\"sm\"\n />\n </div>\n\n {mode === 'same' ? (\n <>\n <ControlledSelect\n className=\"w-full\"\n label={t('dataTable.bulkEdit.field')}\n options={editableColumns.map((column) => ({ value: column.id, label: column.header }))}\n selectedValue={columnId}\n onChangeHook={(selected) => {\n setColumnId(typeof selected === 'string' ? selected : '');\n setValue('');\n }}\n multiple={false}\n searchable\n />\n\n {editable && (\n <DataTableBulkEditValueEditor\n editable={editable}\n value={value}\n onChange={setValue}\n label={t('dataTable.bulkEdit.newValue')}\n errorMessage={sameValueValidationError}\n />\n )}\n </>\n ) : (\n <ControlledSelect\n className=\"w-full\"\n label={t('dataTable.bulkEdit.fields')}\n options={editableColumns.map((column) => ({ value: column.id, label: column.header }))}\n selectedValue={perItemColumnIds}\n onChangeHook={(selected) =>\n setPerItemColumnIds(Array.isArray(selected) ? selected : [selected].filter(Boolean))\n }\n multiple\n searchable\n clearable\n />\n )}\n\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.scopeLabel')}\n </span>\n <ControlledViewToggle<TBulkEditScope>\n options={scopeOptions}\n value={scope}\n onChange={setScope}\n showLabels\n size=\"sm\"\n />\n </div>\n\n {scope === 'picked' && (\n <DataTableBulkEditRowsPicker\n rows={eligibleRows}\n resolveLabel={resolveLabel}\n pickedIds={pickedIds}\n onChange={setPickedIds}\n />\n )}\n\n {mode === 'perItem' && renderPerItemMatrix()}\n </div>\n </ControlledDialog>\n );\n};\n\nexport default DataTableBulkEditDialog;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataTableBulkEditDialog.js","sources":["../../../../src/components/UI/DataTable/DataTableBulkEditDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport ControlledDialog from '@/components/Form/ControlledDialog';\nimport { ControlledSelect } from '@/components/Form/ControlledSelect';\nimport ControlledViewToggle, { type IControlledViewToggleOption } from '@/components/Form/ControlledViewToggle';\nimport ControlledTooltip from '@/components/UI/ControlledTooltip';\nimport NoDataFound from '@/components/UI/NoDataFound';\n\nimport { areBulkValuesEqual, isBulkValueEmpty } from './bulkEditValues';\nimport DataTableBulkEditRowsPicker from './DataTableBulkEditRowsPicker';\nimport DataTableBulkEditValueEditor from './DataTableBulkEditValueEditor';\nimport type { IDataTableColumnConfig, IDataTableFillChange, IDataTableRow } from './types';\n\ntype TBulkEditMode = 'same' | 'perItem';\ntype TBulkEditScope = 'loaded' | 'picked';\n\ninterface IDataTableBulkEditDialogProps<T extends IDataTableRow> {\n open: boolean;\n onClose: () => void;\n /** Todas as colunas da tabela: só as editáveis viram opção de campo. */\n columns: Array<IDataTableColumnConfig<T>>;\n /** As linhas carregadas: a página atual na lista, o acumulado do scroll nos cards. */\n rows: Array<T>;\n /** Rótulo de cada linha na lista de escolha. Sem ele, a primeira coluna de texto responde. */\n getRowLabel?: (row: T) => string;\n /** Ids já escolhidos fora do diálogo: abre no modo \"escolher\" com eles marcados. */\n initialTargetIds?: ReadonlyArray<string>;\n onApply: (changes: Array<IDataTableFillChange<T>>) => void;\n}\n\n/**\n * Sem `getRowLabel`, o nome sai da primeira coluna que sabe devolver um valor\n * legível. Colunas que só desenham (`render`, sem `accessor`) não respondem, e é\n * por isso que uma tabela cuja identidade mora numa coluna `custom` precisa\n * declarar `getRowLabel`: aqui só sobraria o id.\n */\nconst resolveDefaultRowLabel = <T extends IDataTableRow>(row: T, columns: Array<IDataTableColumnConfig<T>>): string => {\n const label = (columns ?? [])\n .filter((column) => column.type !== 'actions' && !!column.accessor)\n .map((column) => column.accessor?.(row))\n .find((value) => typeof value === 'string' || typeof value === 'number');\n\n return label == null || label === '' ? row.id : String(label);\n};\n\n/**\n * Bulk edit in two modes. \"Same value\" applies one value to one field across the\n * chosen scope; \"Values per item\" opens an item x fields matrix prefilled with\n * the current values, and only the changed cells are written.\n *\n * O diálogo não persiste nada. Ele devolve as células escritas por `onApply`, que\n * o host entrega ao `onFillCells` da fila de commit: um render, um flush e uma\n * requisição em lote, exatamente como o preenchimento por arraste.\n */\nconst DataTableBulkEditDialog = <T extends IDataTableRow>({\n open,\n onClose,\n columns,\n rows,\n getRowLabel,\n initialTargetIds,\n onApply,\n}: IDataTableBulkEditDialogProps<T>) => {\n const { t } = useTranslation('ui');\n\n const editableColumns = useMemo(() => (columns ?? []).filter((column) => !!column.editable), [columns]);\n\n const [mode, setMode] = useState<TBulkEditMode>('same');\n const [columnId, setColumnId] = useState('');\n const [value, setValue] = useState<unknown>('');\n const [perItemColumnIds, setPerItemColumnIds] = useState<Array<string>>([]);\n const [drafts, setDrafts] = useState<Record<string, Record<string, unknown>>>({});\n const [scope, setScope] = useState<TBulkEditScope>('loaded');\n const [pickedIds, setPickedIds] = useState<Array<string>>([]);\n\n // Cada abertura recomeça do zero: um valor ou rascunho sobrando da vez anterior\n // seria aplicado em massa por um clique só. O reset acontece na transição de\n // abrir, nunca por uma nova referência de `columns` ou de `initialTargetIds`,\n // que a tela recria ao marcar uma linha atrás do diálogo.\n const wasOpenRef = useRef(false);\n useEffect(() => {\n if (open === wasOpenRef.current) return;\n wasOpenRef.current = open;\n if (!open) return;\n\n const preselected = Array.from(initialTargetIds ?? []);\n setMode('same');\n setColumnId(editableColumns[0]?.id ?? '');\n setValue('');\n setPerItemColumnIds([]);\n setDrafts({});\n setScope(preselected.length > 0 ? 'picked' : 'loaded');\n setPickedIds(preselected);\n }, [open, editableColumns, initialTargetIds]);\n\n const selectedColumn = useMemo(\n () => editableColumns.find((column) => column.id === columnId),\n [editableColumns, columnId],\n );\n const editable = selectedColumn?.editable;\n\n const selectedPerItemColumns = useMemo(\n () =>\n perItemColumnIds\n .map((id) => editableColumns.find((column) => column.id === id))\n .filter((column): column is IDataTableColumnConfig<T> => !!column),\n [perItemColumnIds, editableColumns],\n );\n\n const resolveLabel = useMemo(\n () => getRowLabel ?? ((row: T) => resolveDefaultRowLabel(row, columns ?? [])),\n [getRowLabel, columns],\n );\n\n // Uma linha que os editores recusam (variante, registro bloqueado) não entra\n // no alcance nem aparece nas listas: prometer a alteração e descartá-la é pior.\n const eligibleRows = useMemo(() => {\n if (mode === 'same') return (rows ?? []).filter((row) => !(editable?.disabled?.(row) ?? false));\n\n const referenceColumns = selectedPerItemColumns.length > 0 ? selectedPerItemColumns : editableColumns;\n return (rows ?? []).filter((row) =>\n referenceColumns.some((column) => !(column.editable?.disabled?.(row) ?? false)),\n );\n }, [mode, rows, editable, selectedPerItemColumns, editableColumns]);\n\n const pickedIdSet = useMemo(() => new Set(pickedIds), [pickedIds]);\n\n const targetRows = useMemo(\n () => (scope === 'loaded' ? eligibleRows : eligibleRows.filter((row) => pickedIdSet.has(row.id))),\n [scope, eligibleRows, pickedIdSet],\n );\n\n // Vazio não aplica no modo \"mesmo valor\": o diálogo abre sem valor, e um Enter\n // distraído limparia o campo em todo o alcance. Na matriz por item, limpar uma\n // célula é uma alteração explícita e legítima.\n const isValueEmpty = isBulkValueEmpty(value);\n\n const sameValueValidationError = useMemo(() => {\n if (mode !== 'same' || !editable?.validate) return null;\n\n const firstInvalid = targetRows.map((row) => editable.validate?.(value, row) ?? null).find((message) => !!message);\n\n return firstInvalid ?? null;\n }, [mode, editable, targetRows, value]);\n\n // Only what actually changed becomes a write: a cell with no draft, or edited\n // back to the current value, stays out.\n const perItemChanges = useMemo(() => {\n if (mode !== 'perItem') return [];\n\n const changes: Array<IDataTableFillChange<T>> = [];\n targetRows.forEach((row) => {\n const rowDraft = drafts[row.id];\n if (!rowDraft) return;\n\n selectedPerItemColumns.forEach((column) => {\n if (!Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return;\n if (column.editable?.disabled?.(row) ?? false) return;\n\n const draftValue = rowDraft[column.id];\n if (areBulkValuesEqual(draftValue, column.editable?.getValue(row) ?? null)) return;\n\n changes.push({ row, columnId: column.id, value: draftValue });\n });\n });\n\n return changes;\n }, [mode, targetRows, drafts, selectedPerItemColumns]);\n\n const perItemValidationError = useMemo(() => {\n const firstInvalid = perItemChanges\n .map((change) => {\n const column = selectedPerItemColumns.find((entry) => entry.id === change.columnId);\n return column?.editable?.validate?.(change.value, change.row) ?? null;\n })\n .find((message) => !!message);\n\n return firstInvalid ?? null;\n }, [perItemChanges, selectedPerItemColumns]);\n\n const setDraftValue = (rowId: string, targetColumnId: string, nextValue: unknown) =>\n setDrafts((current) => ({\n ...current,\n [rowId]: { ...current[rowId], [targetColumnId]: nextValue },\n }));\n\n const resolveCellValue = (row: T, column: IDataTableColumnConfig<T>): unknown => {\n const rowDraft = drafts[row.id];\n if (rowDraft && Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return rowDraft[column.id];\n\n return column.editable?.getValue(row) ?? null;\n };\n\n const isCellDirty = (row: T, column: IDataTableColumnConfig<T>): boolean => {\n const rowDraft = drafts[row.id];\n if (!rowDraft || !Object.prototype.hasOwnProperty.call(rowDraft, column.id)) return false;\n\n return !areBulkValuesEqual(rowDraft[column.id], column.editable?.getValue(row) ?? null);\n };\n\n const modeOptions: Array<IControlledViewToggleOption<TBulkEditMode>> = [\n { value: 'same', label: t('dataTable.bulkEdit.modeSame'), icon: 'stacks' },\n { value: 'perItem', label: t('dataTable.bulkEdit.modePerItem'), icon: 'edit_note' },\n ];\n\n const scopeOptions: Array<IControlledViewToggleOption<TBulkEditScope>> = [\n { value: 'loaded', label: t('dataTable.bulkEdit.scopeLoaded'), icon: 'select_all', count: eligibleRows.length },\n { value: 'picked', label: t('dataTable.bulkEdit.scopePicked'), icon: 'checklist', count: pickedIdSet.size },\n ];\n\n const handleApply = () => {\n if (mode === 'perItem') {\n if (perItemChanges.length === 0) return;\n\n onApply(perItemChanges);\n onClose();\n return;\n }\n\n if (!selectedColumn || targetRows.length === 0) return;\n\n onApply(targetRows.map((row) => ({ row, columnId: selectedColumn.id, value })));\n onClose();\n };\n\n const confirmLabel =\n mode === 'perItem'\n ? perItemChanges.length === 0\n ? t('dataTable.bulkEdit.confirmNoChanges')\n : t('dataTable.bulkEdit.confirmChanges', { count: perItemChanges.length })\n : targetRows.length === 0\n ? t('dataTable.bulkEdit.confirmEmpty')\n : t('dataTable.bulkEdit.confirm', { count: targetRows.length });\n\n const disableConfirm =\n mode === 'perItem'\n ? perItemChanges.length === 0 || !!perItemValidationError\n : !selectedColumn || targetRows.length === 0 || isValueEmpty || !!sameValueValidationError;\n\n const renderPerItemMatrix = () => {\n if (selectedPerItemColumns.length === 0) {\n return <NoDataFound size=\"sm\" variant=\"waiting\" message={t('dataTable.bulkEdit.pickFieldsHint')} />;\n }\n\n if (targetRows.length === 0) {\n return <NoDataFound size=\"sm\" variant=\"waiting\" message={t('dataTable.bulkEdit.noTargets')} />;\n }\n\n return (\n <div className=\"flex min-h-0 flex-col gap-2 rounded-xl border border-border bg-surface-sunken p-3\">\n <span className=\"text-xs text-foreground-muted\">{t('dataTable.bulkEdit.perItemHint')}</span>\n <div className=\"overflow-x-auto\">\n <div className=\"flex min-w-max flex-col gap-2\">\n <div className=\"flex items-center gap-3\">\n <span className=\"w-52 shrink-0 text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.itemColumn')}\n </span>\n {selectedPerItemColumns.map((column) => (\n <span\n key={column.id}\n className=\"w-64 shrink-0 truncate text-xs font-medium uppercase tracking-wide text-foreground-subtle\"\n >\n {column.header}\n </span>\n ))}\n </div>\n\n <div className=\"flex max-h-96 flex-col gap-2 overflow-y-auto pt-1\">\n {targetRows.map((row) => (\n <div key={row.id} className=\"flex items-center gap-3\">\n <ControlledTooltip content={resolveLabel(row)} className=\"z-[9999]\" placement=\"top-start\">\n <span className=\"w-52 shrink-0 truncate text-sm text-foreground\">{resolveLabel(row)}</span>\n </ControlledTooltip>\n {selectedPerItemColumns.map((column) => {\n const cellEditable = column.editable;\n if (!cellEditable) return <span key={column.id} className=\"w-64 shrink-0\" />;\n\n const isDisabled = cellEditable.disabled?.(row) ?? false;\n const cellValue = resolveCellValue(row, column);\n const dirty = isCellDirty(row, column);\n const cellError = dirty ? (cellEditable.validate?.(cellValue, row) ?? null) : null;\n\n return (\n <div key={column.id} className=\"relative w-64 shrink-0\">\n {dirty && (\n <span className=\"absolute -right-1 -top-1 z-10 size-2 rounded-full bg-brand\" aria-hidden />\n )}\n <DataTableBulkEditValueEditor\n editable={cellEditable}\n value={cellValue}\n onChange={(nextValue) => setDraftValue(row.id, column.id, nextValue)}\n disabled={isDisabled}\n errorMessage={cellError}\n row={row}\n />\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n };\n\n return (\n <ControlledDialog\n open={open}\n size={mode === 'perItem' ? 'xl' : 'md'}\n title={t('dataTable.bulkEdit.title')}\n subtitle={mode === 'perItem' ? t('dataTable.bulkEdit.subtitlePerItem') : t('dataTable.bulkEdit.subtitle')}\n onCancel={onClose}\n onConfirm={handleApply}\n confirmLabel={confirmLabel}\n cancelLabel={t('actions.cancel')}\n disableConfirm={disableConfirm}\n >\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.modeLabel')}\n </span>\n <ControlledViewToggle<TBulkEditMode>\n options={modeOptions}\n value={mode}\n onChange={setMode}\n showLabels\n size=\"sm\"\n />\n </div>\n\n {mode === 'same' ? (\n <>\n <ControlledSelect\n className=\"w-full\"\n label={t('dataTable.bulkEdit.field')}\n options={editableColumns.map((column) => ({ value: column.id, label: column.header }))}\n selectedValue={columnId}\n onChangeHook={(selected) => {\n setColumnId(typeof selected === 'string' ? selected : '');\n setValue('');\n }}\n multiple={false}\n searchable\n />\n\n {editable && (\n <DataTableBulkEditValueEditor\n editable={editable}\n value={value}\n onChange={setValue}\n label={t('dataTable.bulkEdit.newValue')}\n errorMessage={sameValueValidationError}\n rows={targetRows}\n />\n )}\n </>\n ) : (\n <ControlledSelect\n className=\"w-full\"\n label={t('dataTable.bulkEdit.fields')}\n options={editableColumns.map((column) => ({ value: column.id, label: column.header }))}\n selectedValue={perItemColumnIds}\n onChangeHook={(selected) =>\n setPerItemColumnIds(Array.isArray(selected) ? selected : [selected].filter(Boolean))\n }\n multiple\n searchable\n clearable\n />\n )}\n\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-foreground-subtle\">\n {t('dataTable.bulkEdit.scopeLabel')}\n </span>\n <ControlledViewToggle<TBulkEditScope>\n options={scopeOptions}\n value={scope}\n onChange={setScope}\n showLabels\n size=\"sm\"\n />\n </div>\n\n {scope === 'picked' && (\n <DataTableBulkEditRowsPicker\n rows={eligibleRows}\n resolveLabel={resolveLabel}\n pickedIds={pickedIds}\n onChange={setPickedIds}\n />\n )}\n\n {mode === 'perItem' && renderPerItemMatrix()}\n </div>\n </ControlledDialog>\n );\n};\n\nexport default DataTableBulkEditDialog;\n"],"names":[],"mappings":"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"}
|
|
@@ -4,10 +4,10 @@ import { useTranslation } from "react-i18next";
|
|
|
4
4
|
import ControlledCheckbox from "../../Form/ControlledCheckbox.js";
|
|
5
5
|
import "classnames";
|
|
6
6
|
import "../../Form/ControlledButton.js";
|
|
7
|
-
import "tailwind-merge";
|
|
8
7
|
import "../ControlledMenu.js";
|
|
9
8
|
import "motion/react";
|
|
10
9
|
import "react-dom";
|
|
10
|
+
import "../../../helpers/mergeClasses.js";
|
|
11
11
|
import "@floating-ui/dom";
|
|
12
12
|
import "../../../motion/motionPresets.js";
|
|
13
13
|
import "../../../motion/motionTokens.js";
|
|
@@ -6,11 +6,15 @@ interface IDataTableBulkEditValueEditorProps<T extends IDataTableRow> {
|
|
|
6
6
|
label?: string;
|
|
7
7
|
disabled?: boolean;
|
|
8
8
|
errorMessage?: string | null;
|
|
9
|
+
/** The single row being edited (per-item mode). */
|
|
10
|
+
row?: T;
|
|
11
|
+
/** Every row the value goes to ("same value" mode): only an option every row accepts is offered. */
|
|
12
|
+
rows?: Array<T>;
|
|
9
13
|
}
|
|
10
14
|
/**
|
|
11
15
|
* A value editor derived from the column's `editable` descriptor, the same one
|
|
12
16
|
* the spreadsheet cell opens. Used by the "same value" mode (one editor for the
|
|
13
17
|
* whole scope) and by the per-item matrix (one editor per cell).
|
|
14
18
|
*/
|
|
15
|
-
declare const DataTableBulkEditValueEditor: <T extends IDataTableRow>({ editable, value, onChange, label, disabled, errorMessage, }: IDataTableBulkEditValueEditorProps<T>) => import("react").JSX.Element;
|
|
19
|
+
declare const DataTableBulkEditValueEditor: <T extends IDataTableRow>({ editable, value, onChange, label, disabled, errorMessage, row, rows, }: IDataTableBulkEditValueEditorProps<T>) => import("react").JSX.Element;
|
|
16
20
|
export default DataTableBulkEditValueEditor;
|
|
@@ -2,17 +2,32 @@ import { jsx, Fragment } from "react/jsx-runtime";
|
|
|
2
2
|
import DatePicker from "../../DateTime/DatePicker.js";
|
|
3
3
|
import ControlledInput from "../../Form/ControlledInput.js";
|
|
4
4
|
import { ControlledSelect } from "../../Form/ControlledSelect.js";
|
|
5
|
+
const resolveEditorOptions = (editable, row, rows) => {
|
|
6
|
+
const baseOptions = Array.isArray(editable.options) ? editable.options : [];
|
|
7
|
+
if (!editable.getOptions) return baseOptions;
|
|
8
|
+
if (row) return editable.getOptions(row) ?? baseOptions;
|
|
9
|
+
if (!rows || rows.length === 0) return baseOptions;
|
|
10
|
+
return rows.reduce((sharedOptions, candidateRow) => {
|
|
11
|
+
const rowOptions = editable.getOptions?.(candidateRow);
|
|
12
|
+
if (!rowOptions) return sharedOptions;
|
|
13
|
+
const rowValues = new Set(rowOptions.map((option) => String(option.value)));
|
|
14
|
+
return sharedOptions.filter((option) => rowValues.has(String(option.value)));
|
|
15
|
+
}, baseOptions);
|
|
16
|
+
};
|
|
5
17
|
const DataTableBulkEditValueEditor = ({
|
|
6
18
|
editable,
|
|
7
19
|
value,
|
|
8
20
|
onChange,
|
|
9
21
|
label,
|
|
10
22
|
disabled,
|
|
11
|
-
errorMessage
|
|
23
|
+
errorMessage,
|
|
24
|
+
row,
|
|
25
|
+
rows
|
|
12
26
|
}) => {
|
|
13
27
|
if (editable.renderEditor) {
|
|
14
|
-
return /* @__PURE__ */ jsx(Fragment, { children: editable.renderEditor({ value, onChange, disabled, label }) });
|
|
28
|
+
return /* @__PURE__ */ jsx(Fragment, { children: editable.renderEditor({ value, onChange, disabled, label, row, rows }) });
|
|
15
29
|
}
|
|
30
|
+
const editorOptions = resolveEditorOptions(editable, row, rows);
|
|
16
31
|
const arrayValue = Array.isArray(value) ? value.map(String) : [];
|
|
17
32
|
const stringValue = value == null || Array.isArray(value) ? "" : String(value);
|
|
18
33
|
if (editable.type === "multiselect") {
|
|
@@ -21,7 +36,7 @@ const DataTableBulkEditValueEditor = ({
|
|
|
21
36
|
{
|
|
22
37
|
className: "w-full",
|
|
23
38
|
label,
|
|
24
|
-
options:
|
|
39
|
+
options: editorOptions,
|
|
25
40
|
selectedValue: arrayValue,
|
|
26
41
|
onChangeHook: (selected) => onChange(Array.isArray(selected) ? selected : [selected]),
|
|
27
42
|
multiple: true,
|
|
@@ -37,7 +52,7 @@ const DataTableBulkEditValueEditor = ({
|
|
|
37
52
|
{
|
|
38
53
|
className: "w-full",
|
|
39
54
|
label,
|
|
40
|
-
options:
|
|
55
|
+
options: editorOptions,
|
|
41
56
|
selectedValue: stringValue,
|
|
42
57
|
onChangeHook: (selected) => onChange(typeof selected === "string" ? selected : ""),
|
|
43
58
|
multiple: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTableBulkEditValueEditor.js","sources":["../../../../src/components/UI/DataTable/DataTableBulkEditValueEditor.tsx"],"sourcesContent":["import DatePicker from '@/components/DateTime/DatePicker';\nimport ControlledInput from '@/components/Form/ControlledInput';\nimport { ControlledSelect } from '@/components/Form/ControlledSelect';\n\nimport type { IDataTableEditableConfig, IDataTableRow } from './types';\n\ninterface IDataTableBulkEditValueEditorProps<T extends IDataTableRow> {\n editable: IDataTableEditableConfig<T>;\n value: unknown;\n onChange: (value: unknown) => void;\n label?: string;\n disabled?: boolean;\n errorMessage?: string | null;\n}\n\n/**\n * A value editor derived from the column's `editable` descriptor, the same one\n * the spreadsheet cell opens. Used by the \"same value\" mode (one editor for the\n * whole scope) and by the per-item matrix (one editor per cell).\n */\nconst DataTableBulkEditValueEditor = <T extends IDataTableRow>({\n editable,\n value,\n onChange,\n label,\n disabled,\n errorMessage,\n}: IDataTableBulkEditValueEditorProps<T>) => {\n if (editable.renderEditor) {\n return <>{editable.renderEditor({ value, onChange, disabled, label })}</>;\n }\n\n const arrayValue = Array.isArray(value) ? value.map(String) : [];\n const stringValue = value == null || Array.isArray(value) ? '' : String(value);\n\n if (editable.type === 'multiselect') {\n return (\n <ControlledSelect\n className=\"w-full\"\n label={label}\n options={
|
|
1
|
+
{"version":3,"file":"DataTableBulkEditValueEditor.js","sources":["../../../../src/components/UI/DataTable/DataTableBulkEditValueEditor.tsx"],"sourcesContent":["import DatePicker from '@/components/DateTime/DatePicker';\nimport ControlledInput from '@/components/Form/ControlledInput';\nimport { ControlledSelect } from '@/components/Form/ControlledSelect';\nimport type { IFilterOption } from '@/interfaces/IFilterOption';\n\nimport type { IDataTableEditableConfig, IDataTableRow } from './types';\n\ninterface IDataTableBulkEditValueEditorProps<T extends IDataTableRow> {\n editable: IDataTableEditableConfig<T>;\n value: unknown;\n onChange: (value: unknown) => void;\n label?: string;\n disabled?: boolean;\n errorMessage?: string | null;\n /** The single row being edited (per-item mode). */\n row?: T;\n /** Every row the value goes to (\"same value\" mode): only an option every row accepts is offered. */\n rows?: Array<T>;\n}\n\nconst resolveEditorOptions = <T extends IDataTableRow>(\n editable: IDataTableEditableConfig<T>,\n row: T | undefined,\n rows: Array<T> | undefined,\n): Array<IFilterOption> => {\n const baseOptions = Array.isArray(editable.options) ? editable.options : [];\n if (!editable.getOptions) return baseOptions;\n if (row) return editable.getOptions(row) ?? baseOptions;\n if (!rows || rows.length === 0) return baseOptions;\n\n return rows.reduce<Array<IFilterOption>>((sharedOptions, candidateRow) => {\n const rowOptions = editable.getOptions?.(candidateRow);\n if (!rowOptions) return sharedOptions;\n const rowValues = new Set(rowOptions.map((option) => String(option.value)));\n return sharedOptions.filter((option) => rowValues.has(String(option.value)));\n }, baseOptions);\n};\n\n/**\n * A value editor derived from the column's `editable` descriptor, the same one\n * the spreadsheet cell opens. Used by the \"same value\" mode (one editor for the\n * whole scope) and by the per-item matrix (one editor per cell).\n */\nconst DataTableBulkEditValueEditor = <T extends IDataTableRow>({\n editable,\n value,\n onChange,\n label,\n disabled,\n errorMessage,\n row,\n rows,\n}: IDataTableBulkEditValueEditorProps<T>) => {\n if (editable.renderEditor) {\n return <>{editable.renderEditor({ value, onChange, disabled, label, row, rows })}</>;\n }\n\n const editorOptions = resolveEditorOptions(editable, row, rows);\n\n const arrayValue = Array.isArray(value) ? value.map(String) : [];\n const stringValue = value == null || Array.isArray(value) ? '' : String(value);\n\n if (editable.type === 'multiselect') {\n return (\n <ControlledSelect\n className=\"w-full\"\n label={label}\n options={editorOptions}\n selectedValue={arrayValue}\n onChangeHook={(selected) => onChange(Array.isArray(selected) ? selected : [selected])}\n multiple\n searchable\n clearable\n disabled={disabled}\n />\n );\n }\n\n if (editable.type === 'select') {\n return (\n <ControlledSelect\n className=\"w-full\"\n label={label}\n options={editorOptions}\n selectedValue={stringValue}\n onChangeHook={(selected) => onChange(typeof selected === 'string' ? selected : '')}\n multiple={false}\n searchable\n clearable\n disabled={disabled}\n />\n );\n }\n\n if (editable.type === 'date') {\n const parsed = stringValue ? new Date(stringValue) : undefined;\n const startValue = parsed && !Number.isNaN(parsed.getTime()) ? parsed : undefined;\n\n return (\n <DatePicker\n label={label}\n startValue={startValue}\n onUpdate={(nextValue) => onChange(nextValue ?? '')}\n disabled={disabled}\n />\n );\n }\n\n return (\n <ControlledInput\n label={label}\n value={stringValue}\n onChange={(event) => onChange(event.target.value)}\n inputMode={editable.type === 'number' ? 'decimal' : undefined}\n disabled={disabled}\n error={!!errorMessage}\n errorMessage={errorMessage ?? undefined}\n />\n );\n};\n\nexport default DataTableBulkEditValueEditor;\n"],"names":[],"mappings":";;;;AAoBA,MAAM,uBAAuB,CAC3B,UACA,KACA,SACyB;AACzB,QAAM,cAAc,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,UAAU,CAAA;AACzE,MAAI,CAAC,SAAS,WAAY,QAAO;AACjC,MAAI,IAAK,QAAO,SAAS,WAAW,GAAG,KAAK;AAC5C,MAAI,CAAC,QAAQ,KAAK,WAAW,EAAG,QAAO;AAEvC,SAAO,KAAK,OAA6B,CAAC,eAAe,iBAAiB;AACxE,UAAM,aAAa,SAAS,aAAa,YAAY;AACrD,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,YAAY,IAAI,IAAI,WAAW,IAAI,CAAC,WAAW,OAAO,OAAO,KAAK,CAAC,CAAC;AAC1E,WAAO,cAAc,OAAO,CAAC,WAAW,UAAU,IAAI,OAAO,OAAO,KAAK,CAAC,CAAC;AAAA,EAC7E,GAAG,WAAW;AAChB;AAOA,MAAM,+BAA+B,CAA0B;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA6C;AAC3C,MAAI,SAAS,cAAc;AACzB,WAAO,oBAAA,UAAA,EAAG,UAAA,SAAS,aAAa,EAAE,OAAO,UAAU,UAAU,OAAO,KAAK,KAAA,CAAM,EAAA,CAAE;AAAA,EACnF;AAEA,QAAM,gBAAgB,qBAAqB,UAAU,KAAK,IAAI;AAE9D,QAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,MAAM,IAAI,CAAA;AAC9D,QAAM,cAAc,SAAS,QAAQ,MAAM,QAAQ,KAAK,IAAI,KAAK,OAAO,KAAK;AAE7E,MAAI,SAAS,SAAS,eAAe;AACnC,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV;AAAA,QACA,SAAS;AAAA,QACT,eAAe;AAAA,QACf,cAAc,CAAC,aAAa,SAAS,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,CAAC;AAAA,QACpF,UAAQ;AAAA,QACR,YAAU;AAAA,QACV,WAAS;AAAA,QACT;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAEA,MAAI,SAAS,SAAS,UAAU;AAC9B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV;AAAA,QACA,SAAS;AAAA,QACT,eAAe;AAAA,QACf,cAAc,CAAC,aAAa,SAAS,OAAO,aAAa,WAAW,WAAW,EAAE;AAAA,QACjF,UAAU;AAAA,QACV,YAAU;AAAA,QACV,WAAS;AAAA,QACT;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAEA,MAAI,SAAS,SAAS,QAAQ;AAC5B,UAAM,SAAS,cAAc,IAAI,KAAK,WAAW,IAAI;AACrD,UAAM,aAAa,UAAU,CAAC,OAAO,MAAM,OAAO,QAAA,CAAS,IAAI,SAAS;AAExE,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,UAAU,CAAC,cAAc,SAAS,aAAa,EAAE;AAAA,QACjD;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAEA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,OAAO;AAAA,MACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,MAChD,WAAW,SAAS,SAAS,WAAW,YAAY;AAAA,MACpD;AAAA,MACA,OAAO,CAAC,CAAC;AAAA,MACT,cAAc,gBAAgB;AAAA,IAAA;AAAA,EAAA;AAGpC;"}
|
|
@@ -31,6 +31,7 @@ const DataTableColumnToggleItem = ({ id, header, isVisible, onToggleVisibility }
|
|
|
31
31
|
/* @__PURE__ */ jsx(ControlledTooltip, { content: t("dataTable.reorderColumn"), children: /* @__PURE__ */ jsx(
|
|
32
32
|
ControlledButton,
|
|
33
33
|
{
|
|
34
|
+
"aria-label": t("dataTable.reorderColumn"),
|
|
34
35
|
defaultHtmlButton: true,
|
|
35
36
|
className: "flex cursor-grab touch-none items-center text-foreground-faint hover:text-foreground-muted active:cursor-grabbing",
|
|
36
37
|
...attributes,
|
|
@@ -44,7 +45,7 @@ const DataTableColumnToggleItem = ({ id, header, isVisible, onToggleVisibility }
|
|
|
44
45
|
defaultHtmlButton: true,
|
|
45
46
|
onClick: onToggleVisibility,
|
|
46
47
|
className: "flex flex-1 items-center gap-2 rounded-md px-1.5 py-1 text-left",
|
|
47
|
-
children: /* @__PURE__ */ jsx("span", { className: "pointer-events-none flex items-center", children: /* @__PURE__ */ jsx(ControlledCheckbox, { checked: isVisible, onChange: () => void 0, label: header, size: "sm" }) })
|
|
48
|
+
children: /* @__PURE__ */ jsx("span", { className: "pointer-events-none flex items-center", children: /* @__PURE__ */ jsx(ControlledCheckbox, { checked: isVisible, onChange: () => void 0, label: header, size: "sm", tabIndex: -1 }) })
|
|
48
49
|
}
|
|
49
50
|
)
|
|
50
51
|
]
|
|
@@ -92,6 +93,7 @@ const DataTableColumnMenu = ({
|
|
|
92
93
|
/* @__PURE__ */ jsx(ControlledTooltip, { content: t("dataTable.columns"), children: /* @__PURE__ */ jsx(ControlledMenuHandler, { children: /* @__PURE__ */ jsx(
|
|
93
94
|
ControlledButton,
|
|
94
95
|
{
|
|
96
|
+
"aria-label": t("dataTable.columns"),
|
|
95
97
|
defaultHtmlButton: true,
|
|
96
98
|
className: variant === "toolbar" ? "inline-flex shrink-0 items-center justify-center rounded-lg border border-border bg-surface p-2.5 text-foreground-subtle shadow-sm transition-colors hover:bg-muted-hover hover:text-foreground-muted" : "inline-flex items-center justify-center rounded p-1 text-foreground-faint transition-colors hover:bg-muted-active hover:text-foreground-muted",
|
|
97
99
|
children: /* @__PURE__ */ jsx("span", { className: "flex", children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: "view_column", size: variant === "toolbar" ? 18 : 16 }) })
|