@bigbinary/neeto-atoms 1.0.127 → 1.0.129

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.
@@ -48,6 +48,10 @@ function resolveErrorMessage(errorObject) {
48
48
  if (Array.isArray(data.errors)) return data.errors.join("\n");
49
49
  if (data.noticeCode)
50
50
  return translateNoticeCode(String(data.noticeCode), data);
51
+ if (Array.isArray(data.errorCodes)) {
52
+ const message = data.errorCodes.filter((code) => typeof code === "string").map((code) => translateNoticeCode(code, data)).join("\n");
53
+ if (message) return message;
54
+ }
51
55
  }
52
56
  if (axiosLike.isAxiosError && axiosLike.message) return axiosLike.message;
53
57
  if (errorObject instanceof Error) return errorObject.message;
@@ -262,4 +266,4 @@ const Toastr = {
262
266
  };
263
267
 
264
268
  export { Toastr as T };
265
- //# sourceMappingURL=Toastr-FXUexjdi.js.map
269
+ //# sourceMappingURL=Toastr-Dhl8V1V2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toastr-Dhl8V1V2.js","sources":["../src/components/Toastr/utils.ts","../src/components/Toastr/ToastrBody.tsx","../src/components/Toastr/Toastr.tsx"],"sourcesContent":["import i18next from \"i18next\";\nimport { isValidElement, type ReactNode } from \"react\";\n\nimport type { ToastrConfig, ToastrErrorInput, ToastrMessage } from \"./Toastr\";\n\nconst activeToasts = new Set<string>();\n\nfunction toastKey(\n type: string,\n message: ReactNode,\n buttonLabel?: string\n): string | null {\n if (typeof message !== \"string\") return null;\n return `${type}::${message}::${buttonLabel ?? \"\"}`;\n}\n\nfunction getContentDir(): \"ltr\" | \"rtl\" {\n if (typeof document === \"undefined\") return \"ltr\";\n return (\n (document.documentElement.getAttribute(\"dir\") as \"ltr\" | \"rtl\") ??\n (document.body.getAttribute(\"dir\") as \"ltr\" | \"rtl\") ??\n \"ltr\"\n );\n}\n\n// A `noticeCode` payload carries the interpolation values the key expects\n// alongside it, e.g. `{ noticeCode: \"resource.clone\", entity: \"Form\" }` for\n// \"{{entity}} has been cloned successfully.\"\n//\n// `replace` scopes the payload to interpolation, so a backend field that\n// collides with an i18next option (`ns` forces a namespace and resolves the\n// key to nothing, `lng` forces a language) cannot alter key resolution.\n// `count` is the one option that still has to be promoted, since pluralised\n// keys resolve their `_one`/`_other` suffix from it.\nfunction translateNoticeCode(\n noticeCode: string,\n values: Record<string, unknown>\n): string {\n return i18next.t(noticeCode, { count: values.count, replace: values });\n}\n\nfunction resolveMessage(message: ToastrMessage): ReactNode {\n if (typeof message === \"string\") return message;\n if (isValidElement(message)) return message;\n // Arrays of ReactNodes (e.g. [<a/>, \" \", <b/>]) render natively via {message}.\n if (Array.isArray(message)) return message;\n if (message && typeof message === \"object\") {\n const obj = message as {\n noticeCode?: string;\n customMessage?: string;\n notice?: string;\n };\n if (obj.customMessage) return obj.customMessage;\n if (obj.notice) return obj.notice;\n if (obj.noticeCode) {\n return translateNoticeCode(\n obj.noticeCode,\n obj as Record<string, unknown>\n );\n }\n }\n return String(message);\n}\n\nfunction resolveErrorMessage(errorObject: ToastrErrorInput): ReactNode {\n if (typeof errorObject === \"string\") return errorObject;\n\n if (errorObject && typeof errorObject === \"object\") {\n // Axios-style error\n const axiosLike = errorObject as {\n response?: { data?: Record<string, unknown> };\n message?: string;\n isAxiosError?: boolean;\n };\n\n if (axiosLike.response?.data) {\n const data = axiosLike.response.data;\n // Not stringified: callers pass React elements here to embed links in an\n // error toast, and the copy affordance already skips non-string messages.\n if (data.customMessage) return data.customMessage as ReactNode;\n if (data.error) return String(data.error);\n if (Array.isArray(data.errors)) return data.errors.join(\"\\n\");\n if (data.noticeCode)\n return translateNoticeCode(String(data.noticeCode), data);\n if (Array.isArray(data.errorCodes)) {\n const message = data.errorCodes\n .filter((code): code is string => typeof code === \"string\")\n .map(code => translateNoticeCode(code, data))\n .join(\"\\n\");\n\n if (message) return message;\n }\n }\n\n if (axiosLike.isAxiosError && axiosLike.message) return axiosLike.message;\n\n // Native Error\n if (errorObject instanceof Error) return errorObject.message;\n\n // ToastrMessage (string, React element, array, or notice-shape object)\n return resolveMessage(errorObject as ToastrMessage);\n }\n\n return \"Something went wrong.\";\n}\n\nasync function copyToClipboard(text: string): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text);\n\n return true;\n } catch {\n // Clipboard API is unavailable in insecure contexts; fall back to execCommand.\n try {\n const textArea = document.createElement(\"textarea\");\n textArea.value = text;\n textArea.style.position = \"fixed\";\n textArea.style.opacity = \"0\";\n document.body.appendChild(textArea);\n textArea.focus();\n textArea.select();\n\n const isCopied = document.execCommand(\"copy\");\n textArea.remove();\n\n return isCopied;\n } catch {\n return false;\n }\n }\n}\n\nfunction parseConfig(args: unknown[]): ToastrConfig {\n if (args.length === 0) return {};\n\n const first = args[0];\n if (typeof first === \"object\" && first !== null && !Array.isArray(first)) {\n return first as ToastrConfig;\n }\n\n // Legacy positional args: buttonLabel, onClick, customConfig, showIcon\n const [buttonLabel, onClick, customConfig, showIcon] = args as [\n string?,\n (() => void)?,\n Record<string, unknown>?,\n boolean?,\n ];\n\n return {\n ...(buttonLabel ? { buttonLabel } : {}),\n ...(onClick ? { onClick } : {}),\n ...(showIcon !== undefined ? { showIcon } : {}),\n ...(customConfig ?? {}),\n };\n}\n\nexport {\n activeToasts,\n copyToClipboard,\n getContentDir,\n parseConfig,\n resolveErrorMessage,\n resolveMessage,\n toastKey,\n};\n","import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport {\n CircleCheckIcon,\n InfoIcon,\n OctagonXIcon,\n TriangleAlertIcon,\n XIcon,\n type LucideIcon,\n} from \"lucide-react\";\nimport { useTranslation } from \"react-i18next\";\n\nimport { cn } from \"src/shadcn/lib/utils\";\n\nimport { copyToClipboard } from \"./utils\";\n\ntype ToastrType = \"success\" | \"info\" | \"warning\" | \"error\";\n\nconst ICONS: Record<ToastrType, LucideIcon> = {\n success: CircleCheckIcon,\n info: InfoIcon,\n warning: TriangleAlertIcon,\n error: OctagonXIcon,\n};\n\nconst VARIANT_CLASSES: Record<ToastrType, string> = {\n success:\n \"bg-success-solid text-success-solid-foreground border-success-solid/30\",\n error: \"bg-error-solid text-error-solid-foreground border-error-solid/30\",\n warning:\n \"bg-warning-solid text-warning-solid-foreground border-warning-solid/30\",\n info: \"bg-info-solid text-info-solid-foreground border-info-solid/30\",\n};\n\nexport interface ToastrBodyProps {\n type: ToastrType;\n message: ReactNode;\n /** Plain-text version of the message; renders a copy-to-clipboard affordance when set. */\n copyableMessage?: string;\n showIcon?: boolean;\n dismissible?: boolean;\n buttonLabel?: string;\n onAction?: () => void;\n onDismiss: () => void;\n}\n\nconst COPIED_FEEDBACK_DURATION_MS = 2000;\n\nconst ToastrBody = ({\n type,\n message,\n copyableMessage,\n showIcon = true,\n dismissible = true,\n buttonLabel,\n onAction,\n onDismiss,\n}: ToastrBodyProps) => {\n const Icon = ICONS[type];\n const { t } = useTranslation();\n const [isCopied, setIsCopied] = useState(false);\n const copiedTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined\n );\n\n useEffect(() => () => clearTimeout(copiedTimeoutRef.current), []);\n\n const handleCopy = async () => {\n if (!copyableMessage || !(await copyToClipboard(copyableMessage))) return;\n\n setIsCopied(true);\n clearTimeout(copiedTimeoutRef.current);\n copiedTimeoutRef.current = setTimeout(\n () => setIsCopied(false),\n COPIED_FEEDBACK_DURATION_MS\n );\n };\n\n return (\n <div\n role={type === \"error\" || type === \"warning\" ? \"alert\" : \"status\"}\n data-testid={`toastr-${type}-container`}\n className={cn(\n \"flex w-full items-center gap-3 rounded-md border px-4 py-3 text-sm shadow-lg\",\n VARIANT_CLASSES[type]\n )}\n >\n {showIcon && (\n <Icon\n aria-hidden\n data-testid={`${type}-toast-icon`}\n className=\"size-4 shrink-0\"\n />\n )}\n <span className=\"flex-1\">\n {message}\n {copyableMessage && (\n <>\n {\" \"}\n <button\n type=\"button\"\n data-testid={`toastr-${type}-copy-button`}\n className=\"text-xs italic underline-offset-2 opacity-80 transition-opacity hover:underline hover:opacity-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2\"\n onClick={handleCopy}\n >\n {isCopied\n ? t(\"neetoatoms.toastr.copied\", \"Copied!\")\n : t(\"neetoatoms.toastr.copyMessage\", \"Copy this error message\")}\n </button>\n </>\n )}\n </span>\n {buttonLabel && (\n <button\n type=\"button\"\n onClick={onAction}\n data-testid={`toastr-${type}-button`}\n className=\"shrink-0 text-sm font-medium underline-offset-2 hover:underline\"\n >\n {buttonLabel}\n </button>\n )}\n {dismissible && (\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n onClick={onDismiss}\n data-testid=\"toastr-close-button\"\n className=\"shrink-0 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2\"\n >\n <XIcon aria-hidden className=\"size-4\" />\n </button>\n )}\n </div>\n );\n};\n\nexport { ToastrBody };\n","import type { ReactNode } from \"react\";\nimport { toast } from \"sonner\";\n\nimport { ToastrBody } from \"./ToastrBody\";\nimport {\n activeToasts,\n getContentDir,\n parseConfig,\n resolveErrorMessage,\n resolveMessage,\n toastKey,\n} from \"./utils\";\n\nexport interface ToastrConfig {\n /** Label for an action button inside the toast. */\n buttonLabel?: string;\n /** Callback when the action button is clicked. */\n onClick?: () => void;\n /** Whether to show the type icon in the toast. */\n showIcon?: boolean;\n /** Whether the toast can be dismissed (shows X button + enables swipe). Defaults to true. */\n dismissible?: boolean;\n /** Whether to show the copy-to-clipboard affordance in error toasts. Defaults to true. */\n showCopyButton?: boolean;\n /** Additional sonner toast options (duration, position, etc.). */\n [key: string]: unknown;\n}\n\nexport type ToastrMessage =\n | ReactNode\n | { noticeCode?: string; customMessage?: string; notice?: string };\n\nexport type ToastrErrorInput =\n | string\n | Error\n | {\n response?: { data?: Record<string, unknown> };\n message?: string;\n isAxiosError?: boolean;\n }\n | ToastrMessage;\n\ntype SonnerType = \"success\" | \"info\" | \"warning\" | \"error\";\n\nfunction showToast(\n type: SonnerType,\n message: ReactNode,\n config?: ToastrConfig\n): string | number | undefined {\n const {\n buttonLabel,\n onClick,\n showIcon,\n dismissible = true,\n showCopyButton = true,\n onDismiss: userOnDismiss,\n onAutoClose: userOnAutoClose,\n ...restConfig\n } = config ?? {};\n\n const key = toastKey(type, message, buttonLabel);\n\n if (key !== null && activeToasts.has(key)) return undefined;\n if (key !== null) activeToasts.add(key);\n\n // Error toasts stay until dismissed so the message can be read and copied.\n const options: Record<string, unknown> = {\n ...(type === \"error\" && { duration: Infinity }),\n ...restConfig,\n position: getContentDir() === \"rtl\" ? \"bottom-right\" : \"bottom-left\",\n dismissible,\n onDismiss: (t: unknown) => {\n if (key !== null) activeToasts.delete(key);\n (userOnDismiss as ((t: unknown) => void) | undefined)?.(t);\n },\n onAutoClose: (t: unknown) => {\n if (key !== null) activeToasts.delete(key);\n (userOnAutoClose as ((t: unknown) => void) | undefined)?.(t);\n },\n };\n\n const copyableMessage =\n showCopyButton && type === \"error\" && typeof message === \"string\"\n ? message\n : undefined;\n\n return toast.custom(\n id => (\n <ToastrBody\n buttonLabel={buttonLabel}\n copyableMessage={copyableMessage}\n dismissible={dismissible}\n message={message}\n showIcon={showIcon}\n type={type}\n onAction={() => {\n try {\n onClick?.();\n } finally {\n toast.dismiss(id);\n }\n }}\n onDismiss={() => toast.dismiss(id)}\n />\n ),\n options\n );\n}\n\nconst Toastr = {\n success(message: ToastrMessage, ...args: unknown[]) {\n return showToast(\"success\", resolveMessage(message), parseConfig(args));\n },\n\n info(message: ToastrMessage, ...args: unknown[]) {\n return showToast(\"info\", resolveMessage(message), parseConfig(args));\n },\n\n show(message: ToastrMessage, ...args: unknown[]) {\n return showToast(\"info\", resolveMessage(message), parseConfig(args));\n },\n\n warning(message: ToastrMessage, ...args: unknown[]) {\n return showToast(\"warning\", resolveMessage(message), parseConfig(args));\n },\n\n error(errorObject: ToastrErrorInput, ...args: unknown[]) {\n const msg = resolveErrorMessage(errorObject);\n return showToast(\"error\", msg, parseConfig(args));\n },\n};\n\nexport { Toastr };\n"],"names":["i18next","CircleCheckIcon","InfoIcon","TriangleAlertIcon","OctagonXIcon","XIcon"],"mappings":"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"}
@@ -8019,14 +8019,28 @@ const SelectionCheckbox = ({
8019
8019
  ariaLabel
8020
8020
  }) => {
8021
8021
  const { t } = reactI18next.useTranslation();
8022
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
8023
- primitives_Checkbox.Checkbox,
8022
+ const shiftKeyRef = React.useRef(false);
8023
+ return /* @__PURE__ */ jsxRuntime.jsx(
8024
+ "div",
8024
8025
  {
8025
- checked,
8026
- onCheckedChange,
8027
- "aria-label": ariaLabel ?? t("neetoatoms.dataTable.selectRow", "Select row")
8026
+ className: "flex w-full items-center justify-center",
8027
+ onMouseDown: (event) => {
8028
+ if (event.shiftKey) event.preventDefault();
8029
+ },
8030
+ children: /* @__PURE__ */ jsxRuntime.jsx(
8031
+ primitives_Checkbox.Checkbox,
8032
+ {
8033
+ checked,
8034
+ "aria-label": ariaLabel ?? t("neetoatoms.dataTable.selectRow", "Select row"),
8035
+ onCheckedChange: (value) => onCheckedChange(!!value, shiftKeyRef.current),
8036
+ onClick: (event) => {
8037
+ shiftKeyRef.current = event.shiftKey;
8038
+ if (event.shiftKey) event.currentTarget.focus();
8039
+ }
8040
+ }
8041
+ )
8028
8042
  }
8029
- ) });
8043
+ );
8030
8044
  };
8031
8045
 
8032
8046
  const useTableSelection = ({
@@ -8055,7 +8069,8 @@ const useTableSelection = ({
8055
8069
  }
8056
8070
  if (onRowSelect) {
8057
8071
  const keys = rowSelectionToKeys(newSelection);
8058
- const selectedRows = data.filter((row) => keys.includes(getRowId(row)));
8072
+ const keySet = new Set(keys);
8073
+ const selectedRows = data.filter((row) => keySet.has(getRowId(row)));
8059
8074
  onRowSelect(keys, selectedRows);
8060
8075
  }
8061
8076
  },
@@ -8067,25 +8082,66 @@ const useTableSelection = ({
8067
8082
  return;
8068
8083
  }
8069
8084
  }, []);
8085
+ const rangeAnchorRef = React.useRef(null);
8086
+ const onRowCheckedChange = React.useCallback(
8087
+ ({
8088
+ row,
8089
+ table,
8090
+ checked,
8091
+ shiftKey
8092
+ }) => {
8093
+ const rows = table.getRowModel().rows;
8094
+ const currentIndex = rows.findIndex(({ id }) => id === row.id);
8095
+ const anchorIndex = rows.findIndex(
8096
+ ({ id }) => id === rangeAnchorRef.current
8097
+ );
8098
+ const currentSelection = table.getState().rowSelection;
8099
+ const hasSelectionOnPage = rows.some(({ id }) => currentSelection[id]);
8100
+ const isRangeSelection = shiftKey && anchorIndex !== -1 && currentIndex !== -1 && hasSelectionOnPage;
8101
+ if (!isRangeSelection) {
8102
+ rangeAnchorRef.current = checked && row.getCanSelect() ? row.id : null;
8103
+ row.toggleSelected(checked);
8104
+ return;
8105
+ }
8106
+ const startIndex = Math.min(anchorIndex, currentIndex);
8107
+ const endIndex = Math.max(anchorIndex, currentIndex);
8108
+ const rangeRows = rows.slice(startIndex, endIndex + 1).filter((rangeRow) => rangeRow.getCanSelect());
8109
+ const shouldSelect = rangeRows.some(({ id }) => !currentSelection[id]);
8110
+ const nextSelection = { ...currentSelection };
8111
+ rangeRows.forEach(({ id }) => {
8112
+ if (shouldSelect) {
8113
+ nextSelection[id] = true;
8114
+ } else {
8115
+ delete nextSelection[id];
8116
+ }
8117
+ });
8118
+ rangeAnchorRef.current = shouldSelect ? rows[endIndex].id : null;
8119
+ table.setRowSelection(nextSelection);
8120
+ },
8121
+ []
8122
+ );
8070
8123
  const selectionColumn = React.useMemo(() => {
8071
8124
  if (!enableRowSelection) return null;
8072
8125
  return {
8073
8126
  id: "_selection",
8074
8127
  header: ({ table }) => SelectionCheckbox({
8075
8128
  checked: table.getIsAllPageRowsSelected() || table.getIsSomePageRowsSelected() && "indeterminate",
8076
- onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
8129
+ onCheckedChange: (value) => {
8130
+ rangeAnchorRef.current = null;
8131
+ table.toggleAllPageRowsSelected(!!value);
8132
+ },
8077
8133
  ariaLabel: "Select all"
8078
8134
  }),
8079
- cell: ({ row }) => SelectionCheckbox({
8135
+ cell: ({ row, table }) => SelectionCheckbox({
8080
8136
  checked: row.getIsSelected(),
8081
- onCheckedChange: (value) => row.toggleSelected(!!value),
8137
+ onCheckedChange: (value, shiftKey) => onRowCheckedChange({ row, table, checked: value, shiftKey }),
8082
8138
  ariaLabel: "Select row"
8083
8139
  }),
8084
8140
  size: 56,
8085
8141
  enableSorting: false,
8086
8142
  enableResizing: false
8087
8143
  };
8088
- }, [enableRowSelection]);
8144
+ }, [enableRowSelection, onRowCheckedChange]);
8089
8145
  return {
8090
8146
  rowSelection,
8091
8147
  onRowSelectionChange,
@@ -9330,4 +9386,4 @@ exports.useColumnVisibility = useColumnVisibility;
9330
9386
  exports.useTablePagination = useTablePagination;
9331
9387
  exports.useTableSelection = useTableSelection;
9332
9388
  exports.useTableSort = useTableSort;
9333
- //# sourceMappingURL=DataTable-dKha6uAk.js.map
9389
+ //# sourceMappingURL=DataTable-BXNU34g-.js.map