@umodeapporg/ui 0.5.0 → 0.6.1

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.
Files changed (35) hide show
  1. package/dist/components/Form/ControlledDialogPrimitives.js +4 -1
  2. package/dist/components/Form/ControlledDialogPrimitives.js.map +1 -1
  3. package/dist/components/Form/ControlledDrawer.js +4 -1
  4. package/dist/components/Form/ControlledDrawer.js.map +1 -1
  5. package/dist/components/UI/ControlledMenu.js +4 -1
  6. package/dist/components/UI/ControlledMenu.js.map +1 -1
  7. package/dist/components/UI/ControlledPopover.js +4 -1
  8. package/dist/components/UI/ControlledPopover.js.map +1 -1
  9. package/dist/components/UI/ControlledTooltip.js +4 -2
  10. package/dist/components/UI/ControlledTooltip.js.map +1 -1
  11. package/dist/components/UI/DataTable/DataTableBulkEditDialog.d.ts +3 -3
  12. package/dist/components/UI/DataTable/DataTableBulkEditDialog.js +179 -150
  13. package/dist/components/UI/DataTable/DataTableBulkEditDialog.js.map +1 -1
  14. package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.d.ts +13 -0
  15. package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js +94 -0
  16. package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js.map +1 -0
  17. package/dist/components/UI/DataTable/DataTableBulkEditValueEditor.d.ts +16 -0
  18. package/dist/components/UI/DataTable/DataTableBulkEditValueEditor.js +79 -0
  19. package/dist/components/UI/DataTable/DataTableBulkEditValueEditor.js.map +1 -0
  20. package/dist/components/UI/DataTable/bulkEditValues.d.ts +8 -0
  21. package/dist/components/UI/DataTable/bulkEditValues.js +13 -0
  22. package/dist/components/UI/DataTable/bulkEditValues.js.map +1 -0
  23. package/dist/components/UI/ImageZoomOverlay.js +4 -1
  24. package/dist/components/UI/ImageZoomOverlay.js.map +1 -1
  25. package/dist/components/UI/Kanban/KanbanBoard.js +4 -2
  26. package/dist/components/UI/Kanban/KanbanBoard.js.map +1 -1
  27. package/dist/helpers/portalHost.d.ts +10 -0
  28. package/dist/helpers/portalHost.js +7 -0
  29. package/dist/helpers/portalHost.js.map +1 -0
  30. package/dist/locales/en/ui.json.d.ts +13 -1
  31. package/dist/locales/en/ui.json.js +1 -1
  32. package/dist/locales/index.d.ts +24 -0
  33. package/dist/locales/pt/ui.json.d.ts +13 -1
  34. package/dist/locales/pt/ui.json.js +1 -1
  35. package/package.json +1 -1
@@ -0,0 +1,16 @@
1
+ import { IDataTableEditableConfig, IDataTableRow } from './types';
2
+ interface IDataTableBulkEditValueEditorProps<T extends IDataTableRow> {
3
+ editable: IDataTableEditableConfig<T>;
4
+ value: unknown;
5
+ onChange: (value: unknown) => void;
6
+ label?: string;
7
+ disabled?: boolean;
8
+ errorMessage?: string | null;
9
+ }
10
+ /**
11
+ * A value editor derived from the column's `editable` descriptor, the same one
12
+ * the spreadsheet cell opens. Used by the "same value" mode (one editor for the
13
+ * whole scope) and by the per-item matrix (one editor per cell).
14
+ */
15
+ declare const DataTableBulkEditValueEditor: <T extends IDataTableRow>({ editable, value, onChange, label, disabled, errorMessage, }: IDataTableBulkEditValueEditorProps<T>) => import("react").JSX.Element;
16
+ export default DataTableBulkEditValueEditor;
@@ -0,0 +1,79 @@
1
+ import { jsx, Fragment } from "react/jsx-runtime";
2
+ import DatePicker from "../../DateTime/DatePicker.js";
3
+ import ControlledInput from "../../Form/ControlledInput.js";
4
+ import { ControlledSelect } from "../../Form/ControlledSelect.js";
5
+ const DataTableBulkEditValueEditor = ({
6
+ editable,
7
+ value,
8
+ onChange,
9
+ label,
10
+ disabled,
11
+ errorMessage
12
+ }) => {
13
+ if (editable.renderEditor) {
14
+ return /* @__PURE__ */ jsx(Fragment, { children: editable.renderEditor({ value, onChange, disabled, label }) });
15
+ }
16
+ const arrayValue = Array.isArray(value) ? value.map(String) : [];
17
+ const stringValue = value == null || Array.isArray(value) ? "" : String(value);
18
+ if (editable.type === "multiselect") {
19
+ return /* @__PURE__ */ jsx(
20
+ ControlledSelect,
21
+ {
22
+ className: "w-full",
23
+ label,
24
+ options: Array.isArray(editable.options) ? editable.options : [],
25
+ selectedValue: arrayValue,
26
+ onChangeHook: (selected) => onChange(Array.isArray(selected) ? selected : [selected]),
27
+ multiple: true,
28
+ searchable: true,
29
+ clearable: true,
30
+ disabled
31
+ }
32
+ );
33
+ }
34
+ if (editable.type === "select") {
35
+ return /* @__PURE__ */ jsx(
36
+ ControlledSelect,
37
+ {
38
+ className: "w-full",
39
+ label,
40
+ options: Array.isArray(editable.options) ? editable.options : [],
41
+ selectedValue: stringValue,
42
+ onChangeHook: (selected) => onChange(typeof selected === "string" ? selected : ""),
43
+ multiple: false,
44
+ searchable: true,
45
+ clearable: true,
46
+ disabled
47
+ }
48
+ );
49
+ }
50
+ if (editable.type === "date") {
51
+ const parsed = stringValue ? new Date(stringValue) : void 0;
52
+ const startValue = parsed && !Number.isNaN(parsed.getTime()) ? parsed : void 0;
53
+ return /* @__PURE__ */ jsx(
54
+ DatePicker,
55
+ {
56
+ label,
57
+ startValue,
58
+ onUpdate: (nextValue) => onChange(nextValue ?? ""),
59
+ disabled
60
+ }
61
+ );
62
+ }
63
+ return /* @__PURE__ */ jsx(
64
+ ControlledInput,
65
+ {
66
+ label,
67
+ value: stringValue,
68
+ onChange: (event) => onChange(event.target.value),
69
+ inputMode: editable.type === "number" ? "decimal" : void 0,
70
+ disabled,
71
+ error: !!errorMessage,
72
+ errorMessage: errorMessage ?? void 0
73
+ }
74
+ );
75
+ };
76
+ export {
77
+ DataTableBulkEditValueEditor as default
78
+ };
79
+ //# sourceMappingURL=DataTableBulkEditValueEditor.js.map
@@ -0,0 +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={Array.isArray(editable.options) ? editable.options : []}\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={Array.isArray(editable.options) ? editable.options : []}\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,+BAA+B,CAA0B;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA6C;AAC3C,MAAI,SAAS,cAAc;AACzB,WAAO,oBAAA,UAAA,EAAG,mBAAS,aAAa,EAAE,OAAO,UAAU,UAAU,MAAA,CAAO,EAAA,CAAE;AAAA,EACxE;AAEA,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,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,UAAU,CAAA;AAAA,QAC9D,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,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,UAAU,CAAA;AAAA,QAC9D,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;"}
@@ -0,0 +1,8 @@
1
+ export declare const isBulkValueEmpty: (value: unknown) => boolean;
2
+ /**
3
+ * Compares a cell draft with the row's current value. Editors produce strings
4
+ * while the document may store numbers, so both sides are compared in textual
5
+ * form; empty (null, '', []) is a single state, so editing and reverting does
6
+ * not count as a change.
7
+ */
8
+ export declare const areBulkValuesEqual: (left: unknown, right: unknown) => boolean;
@@ -0,0 +1,13 @@
1
+ const isBulkValueEmpty = (value) => Array.isArray(value) ? value.length === 0 : value == null || value === "";
2
+ const areBulkValuesEqual = (left, right) => {
3
+ if (isBulkValueEmpty(left) && isBulkValueEmpty(right)) return true;
4
+ if (isBulkValueEmpty(left) !== isBulkValueEmpty(right)) return false;
5
+ const leftEntries = (Array.isArray(left) ? left : [left]).map(String);
6
+ const rightEntries = (Array.isArray(right) ? right : [right]).map(String);
7
+ return leftEntries.length === rightEntries.length && leftEntries.every((entry, index) => entry === rightEntries[index]);
8
+ };
9
+ export {
10
+ areBulkValuesEqual,
11
+ isBulkValueEmpty
12
+ };
13
+ //# sourceMappingURL=bulkEditValues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulkEditValues.js","sources":["../../../../src/components/UI/DataTable/bulkEditValues.ts"],"sourcesContent":["export const isBulkValueEmpty = (value: unknown): boolean =>\n Array.isArray(value) ? value.length === 0 : value == null || value === '';\n\n/**\n * Compares a cell draft with the row's current value. Editors produce strings\n * while the document may store numbers, so both sides are compared in textual\n * form; empty (null, '', []) is a single state, so editing and reverting does\n * not count as a change.\n */\nexport const areBulkValuesEqual = (left: unknown, right: unknown): boolean => {\n if (isBulkValueEmpty(left) && isBulkValueEmpty(right)) return true;\n if (isBulkValueEmpty(left) !== isBulkValueEmpty(right)) return false;\n\n const leftEntries = (Array.isArray(left) ? left : [left]).map(String);\n const rightEntries = (Array.isArray(right) ? right : [right]).map(String);\n\n return (\n leftEntries.length === rightEntries.length && leftEntries.every((entry, index) => entry === rightEntries[index])\n );\n};\n"],"names":[],"mappings":"AAAO,MAAM,mBAAmB,CAAC,UAC/B,MAAM,QAAQ,KAAK,IAAI,MAAM,WAAW,IAAI,SAAS,QAAQ,UAAU;AAQlE,MAAM,qBAAqB,CAAC,MAAe,UAA4B;AAC5E,MAAI,iBAAiB,IAAI,KAAK,iBAAiB,KAAK,EAAG,QAAO;AAC9D,MAAI,iBAAiB,IAAI,MAAM,iBAAiB,KAAK,EAAG,QAAO;AAE/D,QAAM,eAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,MAAM;AACpE,QAAM,gBAAgB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG,IAAI,MAAM;AAExE,SACE,YAAY,WAAW,aAAa,UAAU,YAAY,MAAM,CAAC,OAAO,UAAU,UAAU,aAAa,KAAK,CAAC;AAEnH;"}
@@ -6,6 +6,7 @@ import ControlledButton from "../Form/ControlledButton.js";
6
6
  import ControlledImage from "../Form/ControlledImage/index.js";
7
7
  import ControlledTooltip from "./ControlledTooltip.js";
8
8
  import { MaterialSymbol } from "./MaterialSymbol/index.js";
9
+ import { getPortalHost } from "../../helpers/portalHost.js";
9
10
  const ImageZoomOverlay = ({ src, onClose }) => {
10
11
  const { t } = useTranslation("ui");
11
12
  useEffect(() => {
@@ -17,6 +18,8 @@ const ImageZoomOverlay = ({ src, onClose }) => {
17
18
  document.addEventListener("keydown", onKeyDown, true);
18
19
  return () => document.removeEventListener("keydown", onKeyDown, true);
19
20
  }, [onClose]);
21
+ const portalHost = getPortalHost();
22
+ if (portalHost === null) return null;
20
23
  return createPortal(
21
24
  /* @__PURE__ */ jsxs(
22
25
  "div",
@@ -40,7 +43,7 @@ const ImageZoomOverlay = ({ src, onClose }) => {
40
43
  ]
41
44
  }
42
45
  ),
43
- document.body
46
+ portalHost
44
47
  );
45
48
  };
46
49
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"ImageZoomOverlay.js","sources":["../../../src/components/UI/ImageZoomOverlay.tsx"],"sourcesContent":["import React, { useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useTranslation } from 'react-i18next';\n\nimport ControlledButton from '@/components/Form/ControlledButton';\nimport ControlledImage from '@/components/Form/ControlledImage';\nimport ControlledTooltip from '@/components/UI/ControlledTooltip';\nimport { MaterialSymbol } from '@/components/UI/MaterialSymbol';\n\ninterface IImageZoomOverlayProps {\n src: string;\n onClose: () => void;\n}\n\n// Visualização ampliada em tela cheia (portal). Fecha com Esc, clique fora ou\n// no botão de fechar.\nconst ImageZoomOverlay: React.FC<IImageZoomOverlayProps> = ({ src, onClose }) => {\n const { t } = useTranslation('ui');\n\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape') return;\n // Na captura e barrando a propagação: o drawer que abriu esta imagem também escuta Esc no\n // document, e sem isso uma tecla fecharia a imagem E o drawer atrás dela.\n event.stopPropagation();\n onClose();\n };\n document.addEventListener('keydown', onKeyDown, true);\n return () => document.removeEventListener('keydown', onKeyDown, true);\n }, [onClose]);\n\n return createPortal(\n <div\n onClick={onClose}\n // `role=\"dialog\"` não é enfeite: é como `isPressOnLayeredSurface` reconhece esta camada. Sem\n // ele, o drawer por baixo lê o clique no X como \"clicou fora de mim\" e se fecha junto.\n role=\"dialog\"\n aria-modal={true}\n aria-label={t('imageZoom.title')}\n className=\"fixed inset-0 z-[9998] flex items-center justify-center p-6 glass-scrim\"\n >\n <ControlledTooltip content={t('imageZoom.close')} className=\"z-[9999]\">\n <ControlledButton\n defaultHtmlButton\n onClick={onClose}\n className=\"absolute right-4 top-4 flex items-center justify-center rounded-full bg-glass/10 p-2 text-white transition-interactive duration-base ease-enter hover:bg-glass/20 active:scale-90\"\n >\n <MaterialSymbol icon=\"close\" size={24} />\n </ControlledButton>\n </ControlledTooltip>\n <div onClick={(event) => event.stopPropagation()} className=\"flex max-h-full max-w-full\">\n <ControlledImage src={src} className=\"max-h-[90vh] max-w-[90vw] object-contain\" />\n </div>\n </div>,\n document.body,\n );\n};\n\nexport default ImageZoomOverlay;\n"],"names":[],"mappings":";;;;;;;;AAgBA,MAAM,mBAAqD,CAAC,EAAE,KAAK,cAAc;AAC/E,QAAM,EAAE,EAAA,IAAM,eAAe,IAAI;AAEjC,YAAU,MAAM;AACd,UAAM,YAAY,CAAC,UAA+B;AAChD,UAAI,MAAM,QAAQ,SAAU;AAG5B,YAAM,gBAAA;AACN,cAAA;AAAA,IACF;AACA,aAAS,iBAAiB,WAAW,WAAW,IAAI;AACpD,WAAO,MAAM,SAAS,oBAAoB,WAAW,WAAW,IAAI;AAAA,EACtE,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO;AAAA,IACL;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS;AAAA,QAGT,MAAK;AAAA,QACL,cAAY;AAAA,QACZ,cAAY,EAAE,iBAAiB;AAAA,QAC/B,WAAU;AAAA,QAEV,UAAA;AAAA,UAAA,oBAAC,qBAAkB,SAAS,EAAE,iBAAiB,GAAG,WAAU,YAC1D,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,mBAAiB;AAAA,cACjB,SAAS;AAAA,cACT,WAAU;AAAA,cAEV,UAAA,oBAAC,gBAAA,EAAe,MAAK,SAAQ,MAAM,GAAA,CAAI;AAAA,YAAA;AAAA,UAAA,GAE3C;AAAA,UACA,oBAAC,OAAA,EAAI,SAAS,CAAC,UAAU,MAAM,mBAAmB,WAAU,8BAC1D,UAAA,oBAAC,iBAAA,EAAgB,KAAU,WAAU,4CAA2C,EAAA,CAClF;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAEF,SAAS;AAAA,EAAA;AAEb;"}
1
+ {"version":3,"file":"ImageZoomOverlay.js","sources":["../../../src/components/UI/ImageZoomOverlay.tsx"],"sourcesContent":["import React, { useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useTranslation } from 'react-i18next';\n\nimport ControlledButton from '@/components/Form/ControlledButton';\nimport ControlledImage from '@/components/Form/ControlledImage';\nimport ControlledTooltip from '@/components/UI/ControlledTooltip';\nimport { MaterialSymbol } from '@/components/UI/MaterialSymbol';\nimport { getPortalHost } from '@/helpers/portalHost';\n\ninterface IImageZoomOverlayProps {\n src: string;\n onClose: () => void;\n}\n\n// Visualização ampliada em tela cheia (portal). Fecha com Esc, clique fora ou\n// no botão de fechar.\nconst ImageZoomOverlay: React.FC<IImageZoomOverlayProps> = ({ src, onClose }) => {\n const { t } = useTranslation('ui');\n\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape') return;\n // Na captura e barrando a propagação: o drawer que abriu esta imagem também escuta Esc no\n // document, e sem isso uma tecla fecharia a imagem E o drawer atrás dela.\n event.stopPropagation();\n onClose();\n };\n document.addEventListener('keydown', onKeyDown, true);\n return () => document.removeEventListener('keydown', onKeyDown, true);\n }, [onClose]);\n\n const portalHost = getPortalHost();\n if (portalHost === null) return null;\n\n return createPortal(\n <div\n onClick={onClose}\n // `role=\"dialog\"` não é enfeite: é como `isPressOnLayeredSurface` reconhece esta camada. Sem\n // ele, o drawer por baixo lê o clique no X como \"clicou fora de mim\" e se fecha junto.\n role=\"dialog\"\n aria-modal={true}\n aria-label={t('imageZoom.title')}\n className=\"fixed inset-0 z-[9998] flex items-center justify-center p-6 glass-scrim\"\n >\n <ControlledTooltip content={t('imageZoom.close')} className=\"z-[9999]\">\n <ControlledButton\n defaultHtmlButton\n onClick={onClose}\n className=\"absolute right-4 top-4 flex items-center justify-center rounded-full bg-glass/10 p-2 text-white transition-interactive duration-base ease-enter hover:bg-glass/20 active:scale-90\"\n >\n <MaterialSymbol icon=\"close\" size={24} />\n </ControlledButton>\n </ControlledTooltip>\n <div onClick={(event) => event.stopPropagation()} className=\"flex max-h-full max-w-full\">\n <ControlledImage src={src} className=\"max-h-[90vh] max-w-[90vw] object-contain\" />\n </div>\n </div>,\n portalHost,\n );\n};\n\nexport default ImageZoomOverlay;\n"],"names":[],"mappings":";;;;;;;;;AAiBA,MAAM,mBAAqD,CAAC,EAAE,KAAK,cAAc;AAC/E,QAAM,EAAE,EAAA,IAAM,eAAe,IAAI;AAEjC,YAAU,MAAM;AACd,UAAM,YAAY,CAAC,UAA+B;AAChD,UAAI,MAAM,QAAQ,SAAU;AAG5B,YAAM,gBAAA;AACN,cAAA;AAAA,IACF;AACA,aAAS,iBAAiB,WAAW,WAAW,IAAI;AACpD,WAAO,MAAM,SAAS,oBAAoB,WAAW,WAAW,IAAI;AAAA,EACtE,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,aAAa,cAAA;AACnB,MAAI,eAAe,KAAM,QAAO;AAEhC,SAAO;AAAA,IACL;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS;AAAA,QAGT,MAAK;AAAA,QACL,cAAY;AAAA,QACZ,cAAY,EAAE,iBAAiB;AAAA,QAC/B,WAAU;AAAA,QAEV,UAAA;AAAA,UAAA,oBAAC,qBAAkB,SAAS,EAAE,iBAAiB,GAAG,WAAU,YAC1D,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,mBAAiB;AAAA,cACjB,SAAS;AAAA,cACT,WAAU;AAAA,cAEV,UAAA,oBAAC,gBAAA,EAAe,MAAK,SAAQ,MAAM,GAAA,CAAI;AAAA,YAAA;AAAA,UAAA,GAE3C;AAAA,UACA,oBAAC,OAAA,EAAI,SAAS,CAAC,UAAU,MAAM,mBAAmB,WAAU,8BAC1D,UAAA,oBAAC,iBAAA,EAAgB,KAAU,WAAU,4CAA2C,EAAA,CAClF;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAEF;AAAA,EAAA;AAEJ;"}
@@ -2,6 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useState, useCallback, useMemo } from "react";
3
3
  import classNames from "classnames";
4
4
  import { createPortal } from "react-dom";
5
+ import { getPortalHost } from "../../../helpers/portalHost.js";
5
6
  import { useSensors, useSensor, PointerSensor, DndContext, DragOverlay, pointerWithin } from "@dnd-kit/core";
6
7
  import { arrayMove, SortableContext, horizontalListSortingStrategy } from "@dnd-kit/sortable";
7
8
  import { KANBAN_LANE_DRAG_TYPE, KanbanBoardContext } from "./kanbanBoardContext.js";
@@ -65,6 +66,7 @@ const KanbanBoard = ({
65
66
  () => ({ draggingType: activeType, laneSortingEnabled }),
66
67
  [activeType, laneSortingEnabled]
67
68
  );
69
+ const portalHost = getPortalHost();
68
70
  return /* @__PURE__ */ jsx(KanbanBoardContext.Provider, { value: boardContext, children: /* @__PURE__ */ jsxs(
69
71
  DndContext,
70
72
  {
@@ -75,9 +77,9 @@ const KanbanBoard = ({
75
77
  onDragCancel: handleDragCancel,
76
78
  children: [
77
79
  /* @__PURE__ */ jsx("div", { className: classNames("flex min-h-0 flex-1 items-stretch gap-3 overflow-x-auto pb-2", className), children: laneSortingEnabled ? /* @__PURE__ */ jsx(SortableContext, { items: laneIds ?? [], strategy: horizontalListSortingStrategy, children }) : children }),
78
- createPortal(
80
+ portalHost !== null && createPortal(
79
81
  /* @__PURE__ */ jsx(DragOverlay, { children: activeId && activeType === "card" && renderDragOverlay ? /* @__PURE__ */ jsx("div", { className: classNames("rotate-2", overlayClassName), children: renderDragOverlay(activeId) }) : null }),
80
- document.body
82
+ portalHost
81
83
  )
82
84
  ]
83
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"KanbanBoard.js","sources":["../../../../src/components/UI/Kanban/KanbanBoard.tsx"],"sourcesContent":["import { ReactNode, useCallback, useMemo, useState } from 'react';\nimport classNames from 'classnames';\nimport { createPortal } from 'react-dom';\n\nimport {\n CollisionDetection,\n DndContext,\n DragEndEvent,\n DragOverlay,\n DragStartEvent,\n PointerSensor,\n pointerWithin,\n useSensor,\n useSensors,\n} from '@dnd-kit/core';\nimport { arrayMove, horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortable';\n\nimport { KANBAN_LANE_DRAG_TYPE, KanbanBoardContext, TKanbanDragType } from './kanbanBoardContext';\n\n/**\n * A coluna de destino é a que está SOB O CURSOR, não a que mais se sobrepõe ao cartão.\n * A detecção padrão do dnd-kit (`rectIntersection`) compara o retângulo do cartão\n * arrastado com o das colunas, e um cartão mais largo que a folga entre elas cobre duas\n * ao mesmo tempo: soltar caía na coluna errada mesmo com o cursor na certa.\n *\n * Sem coluna sob o cursor não há destino, e soltar na fresta entre duas não move nada.\n * É a resposta previsível: melhor não mover do que escolher uma coluna por proximidade.\n */\nconst laneUnderPointer: CollisionDetection = (args) => pointerWithin(args);\n\n/** Abaixo disto o gesto é um clique, não um arraste. */\nconst DRAG_ACTIVATION_DISTANCE_PX = 6;\n\ninterface IKanbanBoardProps {\n /** Cartão que acompanha o cursor. O host devolve o conteúdo do que está sendo arrastado. */\n renderDragOverlay?: (activeId: string) => ReactNode;\n /**\n * Ids das colunas na ordem atual. Junto com `onLaneReorder`, liga o arraste de colunas:\n * a lane ganha uma alça no cabeçalho e o quadro devolve a nova ordem inteira.\n */\n laneIds?: Array<string>;\n onLaneReorder?: (orderedLaneIds: Array<string>) => void;\n /** Largura do overlay. O padrão casa com a largura útil de uma lane. */\n overlayClassName?: string;\n onDragStart?: (event: DragStartEvent) => void;\n onDragEnd: (event: DragEndEvent) => void;\n onDragCancel?: () => void;\n className?: string;\n children: ReactNode;\n}\n\n/**\n * Casca de um quadro Kanban: o contexto de arraste, a faixa horizontal de colunas e o\n * cartão que segue o cursor. É a ÚNICA implementação de quadro do app; um Kanban novo\n * compõe `KanbanBoard` + `KanbanLane` + `KanbanCard` e traz só o conteúdo do cartão.\n */\nconst KanbanBoard: React.FC<IKanbanBoardProps> = ({\n renderDragOverlay,\n laneIds,\n onLaneReorder,\n overlayClassName = 'w-[17rem]',\n onDragStart,\n onDragEnd,\n onDragCancel,\n className,\n children,\n}) => {\n const [activeId, setActiveId] = useState<string | null>(null);\n const [activeType, setActiveType] = useState<TKanbanDragType | null>(null);\n const laneSortingEnabled = !!laneIds && !!onLaneReorder;\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: DRAG_ACTIVATION_DISTANCE_PX } }),\n );\n\n const handleDragStart = useCallback(\n (event: DragStartEvent) => {\n const isLane = event.active.data.current?.kanbanType === KANBAN_LANE_DRAG_TYPE;\n setActiveId(String(event.active.id));\n setActiveType(isLane ? 'lane' : 'card');\n // Arrastar coluna não é assunto do host: quem reordena é este componente.\n if (!isLane) onDragStart?.(event);\n },\n [onDragStart],\n );\n\n const reorderLanes = useCallback(\n (event: DragEndEvent) => {\n if (!laneIds || !onLaneReorder || !event.over) return;\n\n const fromIndex = laneIds.indexOf(String(event.active.id));\n const toIndex = laneIds.indexOf(String(event.over.id));\n if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return;\n\n onLaneReorder(arrayMove(laneIds, fromIndex, toIndex));\n },\n [laneIds, onLaneReorder],\n );\n\n const handleDragEnd = useCallback(\n (event: DragEndEvent) => {\n const isLane = event.active.data.current?.kanbanType === KANBAN_LANE_DRAG_TYPE;\n setActiveId(null);\n setActiveType(null);\n\n if (isLane) {\n reorderLanes(event);\n return;\n }\n\n onDragEnd(event);\n },\n [onDragEnd, reorderLanes],\n );\n\n const handleDragCancel = useCallback(() => {\n setActiveId(null);\n setActiveType(null);\n onDragCancel?.();\n }, [onDragCancel]);\n\n const boardContext = useMemo(\n () => ({ draggingType: activeType, laneSortingEnabled }),\n [activeType, laneSortingEnabled],\n );\n\n return (\n <KanbanBoardContext.Provider value={boardContext}>\n <DndContext\n sensors={sensors}\n collisionDetection={laneUnderPointer}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n <div className={classNames('flex min-h-0 flex-1 items-stretch gap-3 overflow-x-auto pb-2', className)}>\n {laneSortingEnabled ? (\n <SortableContext items={laneIds ?? []} strategy={horizontalListSortingStrategy}>\n {children}\n </SortableContext>\n ) : (\n children\n )}\n </div>\n\n {/* O overlay é `position: fixed`, e basta UM ancestral com `transform` para ele\n deixar de se posicionar pela viewport e nascer deslocado, longe do cursor. O\n corpo da DataTable, por exemplo, mantém um `translateY(0)` residual de uma\n animação com `forwards`. No portal para o body não existe esse risco. */}\n {createPortal(\n <DragOverlay>\n {/* A coluna arrastada acompanha o cursor pelo próprio transform do sortable,\n então overlay é só para cartão. */}\n {activeId && activeType === 'card' && renderDragOverlay ? (\n <div className={classNames('rotate-2', overlayClassName)}>{renderDragOverlay(activeId)}</div>\n ) : null}\n </DragOverlay>,\n document.body,\n )}\n </DndContext>\n </KanbanBoardContext.Provider>\n );\n};\n\nexport default KanbanBoard;\n"],"names":[],"mappings":";;;;;;;AA4BA,MAAM,mBAAuC,CAAC,SAAS,cAAc,IAAI;AAGzE,MAAM,8BAA8B;AAyBpC,MAAM,cAA2C,CAAC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,CAAC,UAAU,WAAW,IAAI,SAAwB,IAAI;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAI,SAAiC,IAAI;AACzE,QAAM,qBAAqB,CAAC,CAAC,WAAW,CAAC,CAAC;AAC1C,QAAM,UAAU;AAAA,IACd,UAAU,eAAe,EAAE,sBAAsB,EAAE,UAAU,4BAAA,GAA+B;AAAA,EAAA;AAG9F,QAAM,kBAAkB;AAAA,IACtB,CAAC,UAA0B;AACzB,YAAM,SAAS,MAAM,OAAO,KAAK,SAAS,eAAe;AACzD,kBAAY,OAAO,MAAM,OAAO,EAAE,CAAC;AACnC,oBAAc,SAAS,SAAS,MAAM;AAEtC,UAAI,CAAC,OAAQ,eAAc,KAAK;AAAA,IAClC;AAAA,IACA,CAAC,WAAW;AAAA,EAAA;AAGd,QAAM,eAAe;AAAA,IACnB,CAAC,UAAwB;AACvB,UAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,MAAM,KAAM;AAE/C,YAAM,YAAY,QAAQ,QAAQ,OAAO,MAAM,OAAO,EAAE,CAAC;AACzD,YAAM,UAAU,QAAQ,QAAQ,OAAO,MAAM,KAAK,EAAE,CAAC;AACrD,UAAI,cAAc,MAAM,YAAY,MAAM,cAAc,QAAS;AAEjE,oBAAc,UAAU,SAAS,WAAW,OAAO,CAAC;AAAA,IACtD;AAAA,IACA,CAAC,SAAS,aAAa;AAAA,EAAA;AAGzB,QAAM,gBAAgB;AAAA,IACpB,CAAC,UAAwB;AACvB,YAAM,SAAS,MAAM,OAAO,KAAK,SAAS,eAAe;AACzD,kBAAY,IAAI;AAChB,oBAAc,IAAI;AAElB,UAAI,QAAQ;AACV,qBAAa,KAAK;AAClB;AAAA,MACF;AAEA,gBAAU,KAAK;AAAA,IACjB;AAAA,IACA,CAAC,WAAW,YAAY;AAAA,EAAA;AAG1B,QAAM,mBAAmB,YAAY,MAAM;AACzC,gBAAY,IAAI;AAChB,kBAAc,IAAI;AAClB,mBAAA;AAAA,EACF,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,eAAe;AAAA,IACnB,OAAO,EAAE,cAAc,YAAY;IACnC,CAAC,YAAY,kBAAkB;AAAA,EAAA;AAGjC,SACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,cAClC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,oBAAoB;AAAA,MACpB,aAAa;AAAA,MACb,WAAW;AAAA,MACX,cAAc;AAAA,MAEd,UAAA;AAAA,QAAA,oBAAC,SAAI,WAAW,WAAW,gEAAgE,SAAS,GACjG,UAAA,qBACC,oBAAC,iBAAA,EAAgB,OAAO,WAAW,IAAI,UAAU,+BAC9C,SAAA,CACH,IAEA,UAEJ;AAAA,QAMC;AAAA,8BACE,aAAA,EAGE,UAAA,YAAY,eAAe,UAAU,oBACpC,oBAAC,OAAA,EAAI,WAAW,WAAW,YAAY,gBAAgB,GAAI,4BAAkB,QAAQ,EAAA,CAAE,IACrF,MACN;AAAA,UACA,SAAS;AAAA,QAAA;AAAA,MACX;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ;"}
1
+ {"version":3,"file":"KanbanBoard.js","sources":["../../../../src/components/UI/Kanban/KanbanBoard.tsx"],"sourcesContent":["import { ReactNode, useCallback, useMemo, useState } from 'react';\nimport classNames from 'classnames';\nimport { createPortal } from 'react-dom';\n\nimport { getPortalHost } from '@/helpers/portalHost';\nimport {\n CollisionDetection,\n DndContext,\n DragEndEvent,\n DragOverlay,\n DragStartEvent,\n PointerSensor,\n pointerWithin,\n useSensor,\n useSensors,\n} from '@dnd-kit/core';\nimport { arrayMove, horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortable';\n\nimport { KANBAN_LANE_DRAG_TYPE, KanbanBoardContext, TKanbanDragType } from './kanbanBoardContext';\n\n/**\n * A coluna de destino é a que está SOB O CURSOR, não a que mais se sobrepõe ao cartão.\n * A detecção padrão do dnd-kit (`rectIntersection`) compara o retângulo do cartão\n * arrastado com o das colunas, e um cartão mais largo que a folga entre elas cobre duas\n * ao mesmo tempo: soltar caía na coluna errada mesmo com o cursor na certa.\n *\n * Sem coluna sob o cursor não há destino, e soltar na fresta entre duas não move nada.\n * É a resposta previsível: melhor não mover do que escolher uma coluna por proximidade.\n */\nconst laneUnderPointer: CollisionDetection = (args) => pointerWithin(args);\n\n/** Abaixo disto o gesto é um clique, não um arraste. */\nconst DRAG_ACTIVATION_DISTANCE_PX = 6;\n\ninterface IKanbanBoardProps {\n /** Cartão que acompanha o cursor. O host devolve o conteúdo do que está sendo arrastado. */\n renderDragOverlay?: (activeId: string) => ReactNode;\n /**\n * Ids das colunas na ordem atual. Junto com `onLaneReorder`, liga o arraste de colunas:\n * a lane ganha uma alça no cabeçalho e o quadro devolve a nova ordem inteira.\n */\n laneIds?: Array<string>;\n onLaneReorder?: (orderedLaneIds: Array<string>) => void;\n /** Largura do overlay. O padrão casa com a largura útil de uma lane. */\n overlayClassName?: string;\n onDragStart?: (event: DragStartEvent) => void;\n onDragEnd: (event: DragEndEvent) => void;\n onDragCancel?: () => void;\n className?: string;\n children: ReactNode;\n}\n\n/**\n * Casca de um quadro Kanban: o contexto de arraste, a faixa horizontal de colunas e o\n * cartão que segue o cursor. É a ÚNICA implementação de quadro do app; um Kanban novo\n * compõe `KanbanBoard` + `KanbanLane` + `KanbanCard` e traz só o conteúdo do cartão.\n */\nconst KanbanBoard: React.FC<IKanbanBoardProps> = ({\n renderDragOverlay,\n laneIds,\n onLaneReorder,\n overlayClassName = 'w-[17rem]',\n onDragStart,\n onDragEnd,\n onDragCancel,\n className,\n children,\n}) => {\n const [activeId, setActiveId] = useState<string | null>(null);\n const [activeType, setActiveType] = useState<TKanbanDragType | null>(null);\n const laneSortingEnabled = !!laneIds && !!onLaneReorder;\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: DRAG_ACTIVATION_DISTANCE_PX } }),\n );\n\n const handleDragStart = useCallback(\n (event: DragStartEvent) => {\n const isLane = event.active.data.current?.kanbanType === KANBAN_LANE_DRAG_TYPE;\n setActiveId(String(event.active.id));\n setActiveType(isLane ? 'lane' : 'card');\n // Arrastar coluna não é assunto do host: quem reordena é este componente.\n if (!isLane) onDragStart?.(event);\n },\n [onDragStart],\n );\n\n const reorderLanes = useCallback(\n (event: DragEndEvent) => {\n if (!laneIds || !onLaneReorder || !event.over) return;\n\n const fromIndex = laneIds.indexOf(String(event.active.id));\n const toIndex = laneIds.indexOf(String(event.over.id));\n if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return;\n\n onLaneReorder(arrayMove(laneIds, fromIndex, toIndex));\n },\n [laneIds, onLaneReorder],\n );\n\n const handleDragEnd = useCallback(\n (event: DragEndEvent) => {\n const isLane = event.active.data.current?.kanbanType === KANBAN_LANE_DRAG_TYPE;\n setActiveId(null);\n setActiveType(null);\n\n if (isLane) {\n reorderLanes(event);\n return;\n }\n\n onDragEnd(event);\n },\n [onDragEnd, reorderLanes],\n );\n\n const handleDragCancel = useCallback(() => {\n setActiveId(null);\n setActiveType(null);\n onDragCancel?.();\n }, [onDragCancel]);\n\n const boardContext = useMemo(\n () => ({ draggingType: activeType, laneSortingEnabled }),\n [activeType, laneSortingEnabled],\n );\n\n const portalHost = getPortalHost();\n\n return (\n <KanbanBoardContext.Provider value={boardContext}>\n <DndContext\n sensors={sensors}\n collisionDetection={laneUnderPointer}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n <div className={classNames('flex min-h-0 flex-1 items-stretch gap-3 overflow-x-auto pb-2', className)}>\n {laneSortingEnabled ? (\n <SortableContext items={laneIds ?? []} strategy={horizontalListSortingStrategy}>\n {children}\n </SortableContext>\n ) : (\n children\n )}\n </div>\n\n {/* O overlay é `position: fixed`, e basta UM ancestral com `transform` para ele\n deixar de se posicionar pela viewport e nascer deslocado, longe do cursor. O\n corpo da DataTable, por exemplo, mantém um `translateY(0)` residual de uma\n animação com `forwards`. No portal para o body não existe esse risco. */}\n {portalHost !== null &&\n createPortal(\n <DragOverlay>\n {/* A coluna arrastada acompanha o cursor pelo próprio transform do sortable,\n então overlay é só para cartão. */}\n {activeId && activeType === 'card' && renderDragOverlay ? (\n <div className={classNames('rotate-2', overlayClassName)}>{renderDragOverlay(activeId)}</div>\n ) : null}\n </DragOverlay>,\n portalHost,\n )}\n </DndContext>\n </KanbanBoardContext.Provider>\n );\n};\n\nexport default KanbanBoard;\n"],"names":[],"mappings":";;;;;;;;AA6BA,MAAM,mBAAuC,CAAC,SAAS,cAAc,IAAI;AAGzE,MAAM,8BAA8B;AAyBpC,MAAM,cAA2C,CAAC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,CAAC,UAAU,WAAW,IAAI,SAAwB,IAAI;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAI,SAAiC,IAAI;AACzE,QAAM,qBAAqB,CAAC,CAAC,WAAW,CAAC,CAAC;AAC1C,QAAM,UAAU;AAAA,IACd,UAAU,eAAe,EAAE,sBAAsB,EAAE,UAAU,4BAAA,GAA+B;AAAA,EAAA;AAG9F,QAAM,kBAAkB;AAAA,IACtB,CAAC,UAA0B;AACzB,YAAM,SAAS,MAAM,OAAO,KAAK,SAAS,eAAe;AACzD,kBAAY,OAAO,MAAM,OAAO,EAAE,CAAC;AACnC,oBAAc,SAAS,SAAS,MAAM;AAEtC,UAAI,CAAC,OAAQ,eAAc,KAAK;AAAA,IAClC;AAAA,IACA,CAAC,WAAW;AAAA,EAAA;AAGd,QAAM,eAAe;AAAA,IACnB,CAAC,UAAwB;AACvB,UAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,MAAM,KAAM;AAE/C,YAAM,YAAY,QAAQ,QAAQ,OAAO,MAAM,OAAO,EAAE,CAAC;AACzD,YAAM,UAAU,QAAQ,QAAQ,OAAO,MAAM,KAAK,EAAE,CAAC;AACrD,UAAI,cAAc,MAAM,YAAY,MAAM,cAAc,QAAS;AAEjE,oBAAc,UAAU,SAAS,WAAW,OAAO,CAAC;AAAA,IACtD;AAAA,IACA,CAAC,SAAS,aAAa;AAAA,EAAA;AAGzB,QAAM,gBAAgB;AAAA,IACpB,CAAC,UAAwB;AACvB,YAAM,SAAS,MAAM,OAAO,KAAK,SAAS,eAAe;AACzD,kBAAY,IAAI;AAChB,oBAAc,IAAI;AAElB,UAAI,QAAQ;AACV,qBAAa,KAAK;AAClB;AAAA,MACF;AAEA,gBAAU,KAAK;AAAA,IACjB;AAAA,IACA,CAAC,WAAW,YAAY;AAAA,EAAA;AAG1B,QAAM,mBAAmB,YAAY,MAAM;AACzC,gBAAY,IAAI;AAChB,kBAAc,IAAI;AAClB,mBAAA;AAAA,EACF,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,eAAe;AAAA,IACnB,OAAO,EAAE,cAAc,YAAY;IACnC,CAAC,YAAY,kBAAkB;AAAA,EAAA;AAGjC,QAAM,aAAa,cAAA;AAEnB,SACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,cAClC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,oBAAoB;AAAA,MACpB,aAAa;AAAA,MACb,WAAW;AAAA,MACX,cAAc;AAAA,MAEd,UAAA;AAAA,QAAA,oBAAC,SAAI,WAAW,WAAW,gEAAgE,SAAS,GACjG,UAAA,qBACC,oBAAC,iBAAA,EAAgB,OAAO,WAAW,IAAI,UAAU,+BAC9C,SAAA,CACH,IAEA,UAEJ;AAAA,QAMC,eAAe,QACd;AAAA,8BACG,aAAA,EAGE,UAAA,YAAY,eAAe,UAAU,oBACpC,oBAAC,OAAA,EAAI,WAAW,WAAW,YAAY,gBAAgB,GAAI,4BAAkB,QAAQ,EAAA,CAAE,IACrF,MACN;AAAA,UACA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA,GAEN;AAEJ;"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The host element every overlay portals into, or null where there is no DOM.
3
+ *
4
+ * `createPortal` runs during RENDER, so reading `document.body` inline throws
5
+ * `ReferenceError: document is not defined` on a server render (Next.js App
6
+ * Router renders every page on the server first). The overlays this affects are
7
+ * interactive by nature and have nothing to show before hydration, so the server
8
+ * render skips them and the client mounts them on the first paint.
9
+ */
10
+ export declare function getPortalHost(): HTMLElement | null;
@@ -0,0 +1,7 @@
1
+ function getPortalHost() {
2
+ return typeof document === "undefined" ? null : document.body;
3
+ }
4
+ export {
5
+ getPortalHost
6
+ };
7
+ //# sourceMappingURL=portalHost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portalHost.js","sources":["../../src/helpers/portalHost.ts"],"sourcesContent":["/**\n * The host element every overlay portals into, or null where there is no DOM.\n *\n * `createPortal` runs during RENDER, so reading `document.body` inline throws\n * `ReferenceError: document is not defined` on a server render (Next.js App\n * Router renders every page on the server first). The overlays this affects are\n * interactive by nature and have nothing to show before hydration, so the server\n * render skips them and the client mounts them on the first paint.\n */\nexport function getPortalHost(): HTMLElement | null {\n return typeof document === 'undefined' ? null : document.body;\n}\n"],"names":[],"mappings":"AASO,SAAS,gBAAoC;AAClD,SAAO,OAAO,aAAa,cAAc,OAAO,SAAS;AAC3D;"}
@@ -153,7 +153,19 @@ declare const _default: {
153
153
  "selectAll_other": "Select the {{count}} visible",
154
154
  "confirm_one": "Apply to {{count}} item",
155
155
  "confirm_other": "Apply to {{count}} items",
156
- "confirmEmpty": "Pick the items"
156
+ "confirmEmpty": "Pick the items",
157
+ "subtitlePerItem": "Pick the fields and adjust each item's value; only what changed will be saved.",
158
+ "modeLabel": "How to apply",
159
+ "modeSame": "Same value",
160
+ "modePerItem": "Values per item",
161
+ "fields": "Fields",
162
+ "confirmChanges_one": "Apply {{count}} change",
163
+ "confirmChanges_other": "Apply {{count}} changes",
164
+ "confirmNoChanges": "No changes",
165
+ "pickFieldsHint": "Pick at least one field to edit per item.",
166
+ "noTargets": "No item in the chosen scope.",
167
+ "perItemHint": "Change only the cells you want. The rest stay as they are.",
168
+ "itemColumn": "Item"
157
169
  }
158
170
  },
159
171
  "dateRangePicker": {
@@ -5,7 +5,7 @@ const controlledDrawer = { "resizeHandle": "Drag to adjust the width" };
5
5
  const controlledSelect = { "loadingOptions": "Loading options", "noDataToShow": "No data to show", "moreItems": "+{{count}}" };
6
6
  const controlledTabs = { "renameTab": "Rename tab", "tabDisabled": "This tab is disabled", "enableTab": "Enable tab", "mustKeepOneEnabled": "At least one tab must remain enabled", "disableTab": "Disable tab", "enableTabShort": "Enable tab" };
7
7
  const createTask = { "newTask": "New task", "createTask": "Create Task", "newTaskTitle": "New Task", "taskName": "Title", "createdSuccess": "Task created successfully for {{date}}", "taskNamePlaceholder": "What is the task?", "taskType": "Task Type", "deadline": "Due date", "description": "Description", "descriptionPlaceholder": "Add a description...", "assignees": "Assignees", "saveTask": "Save Task", "cancel": "Cancel", "createdSuccessNoDate": "Task created successfully", "titleRequired": "Title is required", "taskTypeRequired": "Task type is required", "sidebarTitle": "Details", "rich": { "bold": "Bold", "italic": "Italic", "strikethrough": "Strikethrough", "link": "Link", "bulletList": "Bullet list", "numberedList": "Numbered list", "quote": "Quote", "indent": "Increase indent", "outdent": "Decrease indent", "textColor": "Text colour", "colors": { "default": "Default", "brand": "Purple", "info": "Blue", "success": "Green", "warning": "Amber", "danger": "Red", "accent": "Pink", "secondary": "Teal", "muted": "Grey" } }, "newTaskSubtitle": "Set the assignee and the due date", "createError": "Could not create the task", "requiredHint": "Fill in the title and the task type to save", "deadlinePlaceholder": "Select a date" };
8
- const dataTable = { "empty": "No records found", "error": "We could not load this list", "columns": "Columns", "viewList": "List", "viewCards": "Cards", "reorderColumn": "Reorder column", "restoreDefaultOrder": "Restore default order", "resizeColumn": "Resize column", "restoreDefaultWidths": "Restore default widths", "viewGrouped": "Grouping", "viewKanban": "Kanban", "addRow": "Add row", "removeRow": "Remove row", "editCell": "Edit", "save": "Save", "fillHandle": "Drag to fill", "editMode": { "enter": "Spreadsheet editing", "exit": "Exit editing", "inactiveHint": "Turns on cell editing: keyboard navigation, selection and drag to fill", "activeHint": "Cell editing is on. Clicking a row opens the record again once you exit" }, "bulkEdit": { "title": "Bulk edit", "subtitle": "Pick the field, the new value and which items it applies to.", "open": "Bulk edit", "hint": "Applies the same value to several items at once", "field": "Field", "newValue": "New value", "scopeLabel": "Apply to", "scopeLoaded": "All loaded", "scopePicked": "Pick items", "searchRows": "Search the list", "noRowsFound": "No item matches the search.", "selectAll_one": "Select the visible item", "selectAll_other": "Select the {{count}} visible", "confirm_one": "Apply to {{count}} item", "confirm_other": "Apply to {{count}} items", "confirmEmpty": "Pick the items" } };
8
+ const dataTable = { "empty": "No records found", "error": "We could not load this list", "columns": "Columns", "viewList": "List", "viewCards": "Cards", "reorderColumn": "Reorder column", "restoreDefaultOrder": "Restore default order", "resizeColumn": "Resize column", "restoreDefaultWidths": "Restore default widths", "viewGrouped": "Grouping", "viewKanban": "Kanban", "addRow": "Add row", "removeRow": "Remove row", "editCell": "Edit", "save": "Save", "fillHandle": "Drag to fill", "editMode": { "enter": "Spreadsheet editing", "exit": "Exit editing", "inactiveHint": "Turns on cell editing: keyboard navigation, selection and drag to fill", "activeHint": "Cell editing is on. Clicking a row opens the record again once you exit" }, "bulkEdit": { "title": "Bulk edit", "subtitle": "Pick the field, the new value and which items it applies to.", "open": "Bulk edit", "hint": "Applies the same value to several items at once", "field": "Field", "newValue": "New value", "scopeLabel": "Apply to", "scopeLoaded": "All loaded", "scopePicked": "Pick items", "searchRows": "Search the list", "noRowsFound": "No item matches the search.", "selectAll_one": "Select the visible item", "selectAll_other": "Select the {{count}} visible", "confirm_one": "Apply to {{count}} item", "confirm_other": "Apply to {{count}} items", "confirmEmpty": "Pick the items", "subtitlePerItem": "Pick the fields and adjust each item's value; only what changed will be saved.", "modeLabel": "How to apply", "modeSame": "Same value", "modePerItem": "Values per item", "fields": "Fields", "confirmChanges_one": "Apply {{count}} change", "confirmChanges_other": "Apply {{count}} changes", "confirmNoChanges": "No changes", "pickFieldsHint": "Pick at least one field to edit per item.", "noTargets": "No item in the chosen scope.", "perItemHint": "Change only the cells you want. The rest stay as they are.", "itemColumn": "Item" } };
9
9
  const dateRangePicker = { "selectPeriod": "Select a period", "nowSelectEndDate": "Now select the end date" };
10
10
  const dates = { "today": "Today", "yesterday": "Yesterday", "tomorrow": "Tomorrow", "thisWeek": "This week", "lastWeek": "Last week", "thisMonth": "This month", "lastMonth": "Last month", "selectStartDate": "Select start date", "selectEndDate": "Select end date", "clearSelection": "Clear selection", "clearPeriod": "Clear selected period", "periodSelectedSuccess": "Period selected successfully" };
11
11
  const detailsTable = { "size": "Size", "quantity": "Qty" };
@@ -156,6 +156,18 @@ export declare const uiLocales: {
156
156
  confirm_one: string;
157
157
  confirm_other: string;
158
158
  confirmEmpty: string;
159
+ subtitlePerItem: string;
160
+ modeLabel: string;
161
+ modeSame: string;
162
+ modePerItem: string;
163
+ fields: string;
164
+ confirmChanges_one: string;
165
+ confirmChanges_other: string;
166
+ confirmNoChanges: string;
167
+ pickFieldsHint: string;
168
+ noTargets: string;
169
+ perItemHint: string;
170
+ itemColumn: string;
159
171
  };
160
172
  };
161
173
  dateRangePicker: {
@@ -651,6 +663,18 @@ export declare const uiLocales: {
651
663
  confirm_one: string;
652
664
  confirm_other: string;
653
665
  confirmEmpty: string;
666
+ subtitlePerItem: string;
667
+ modeLabel: string;
668
+ modeSame: string;
669
+ modePerItem: string;
670
+ fields: string;
671
+ confirmChanges_one: string;
672
+ confirmChanges_other: string;
673
+ confirmNoChanges: string;
674
+ pickFieldsHint: string;
675
+ noTargets: string;
676
+ perItemHint: string;
677
+ itemColumn: string;
654
678
  };
655
679
  };
656
680
  dateRangePicker: {
@@ -153,7 +153,19 @@ declare const _default: {
153
153
  "selectAll_other": "Selecionar os {{count}} visíveis",
154
154
  "confirm_one": "Aplicar a {{count}} item",
155
155
  "confirm_other": "Aplicar a {{count}} itens",
156
- "confirmEmpty": "Selecione os itens"
156
+ "confirmEmpty": "Selecione os itens",
157
+ "subtitlePerItem": "Escolha os campos e ajuste o valor de cada item; só o que mudou será salvo.",
158
+ "modeLabel": "Como aplicar",
159
+ "modeSame": "Mesmo valor",
160
+ "modePerItem": "Valores por item",
161
+ "fields": "Campos",
162
+ "confirmChanges_one": "Aplicar {{count}} alteração",
163
+ "confirmChanges_other": "Aplicar {{count}} alterações",
164
+ "confirmNoChanges": "Nenhuma alteração",
165
+ "pickFieldsHint": "Selecione ao menos um campo para editar por item.",
166
+ "noTargets": "Nenhum item no alcance escolhido.",
167
+ "perItemHint": "Altere apenas as células desejadas. As demais permanecem como estão.",
168
+ "itemColumn": "Item"
157
169
  }
158
170
  },
159
171
  "dateRangePicker": {
@@ -5,7 +5,7 @@ const controlledDrawer = { "resizeHandle": "Arraste para ajustar a largura" };
5
5
  const controlledSelect = { "loadingOptions": "Carregando opções", "noDataToShow": "Não há dados para mostrar", "moreItems": "+{{count}}" };
6
6
  const controlledTabs = { "renameTab": "Renomear aba", "tabDisabled": "Esta aba está desabilitada", "enableTab": "Habilitar aba", "mustKeepOneEnabled": "É necessário manter ao menos uma aba habilitada", "disableTab": "Desabilitar tab", "enableTabShort": "Habilitar tab" };
7
7
  const createTask = { "newTask": "Nova tarefa", "createTask": "Criar Tarefa", "newTaskTitle": "Nova Tarefa", "taskName": "Título", "createdSuccess": "Tarefa criada com sucesso para {{date}}", "taskNamePlaceholder": "Qual é a tarefa?", "taskType": "Tipo de Tarefa", "deadline": "Prazo", "description": "Descrição", "descriptionPlaceholder": "Adicione uma descrição...", "assignees": "Responsáveis", "saveTask": "Gravar Tarefa", "cancel": "Cancelar", "createdSuccessNoDate": "Tarefa criada com sucesso", "titleRequired": "O título é obrigatório", "taskTypeRequired": "O tipo de tarefa é obrigatório", "sidebarTitle": "Detalhes", "rich": { "bold": "Negrito", "italic": "Itálico", "strikethrough": "Tachado", "link": "Link", "bulletList": "Lista com marcador", "numberedList": "Lista numerada", "quote": "Citação", "indent": "Aumentar recuo", "outdent": "Diminuir recuo", "textColor": "Cor do texto", "colors": { "default": "Padrão", "brand": "Roxo", "info": "Azul", "success": "Verde", "warning": "Âmbar", "danger": "Vermelho", "accent": "Rosa", "secondary": "Turquesa", "muted": "Cinza" } }, "newTaskSubtitle": "Defina o responsável e o prazo", "createError": "Não foi possível criar a tarefa", "requiredHint": "Informe o título e o tipo de tarefa para gravar", "deadlinePlaceholder": "Selecionar data" };
8
- const dataTable = { "empty": "Nenhum registro encontrado", "error": "Não conseguimos carregar esta lista", "columns": "Colunas", "viewList": "Lista", "viewCards": "Cards", "reorderColumn": "Reordenar coluna", "restoreDefaultOrder": "Restaurar ordem padrão", "resizeColumn": "Redimensionar coluna", "restoreDefaultWidths": "Restaurar largura padrão", "viewGrouped": "Agrupamento", "viewKanban": "Kanban", "addRow": "Adicionar linha", "removeRow": "Remover linha", "editCell": "Editar", "save": "Salvar", "fillHandle": "Arraste para preencher", "editMode": { "enter": "Editar em planilha", "exit": "Sair da edição", "inactiveHint": "Ativa a edição em célula: navegação por teclado, seleção e preenchimento por arraste", "activeHint": "Edição em célula ativa. O clique na linha volta a abrir o registro quando você sair" }, "bulkEdit": { "title": "Editar em massa", "subtitle": "Escolha o campo, o novo valor e a quais itens aplicar.", "open": "Editar em massa", "hint": "Aplica um mesmo valor a vários itens de uma vez", "field": "Campo", "newValue": "Novo valor", "scopeLabel": "Aplicar a", "scopeLoaded": "Todos os carregados", "scopePicked": "Escolher itens", "searchRows": "Buscar na lista", "noRowsFound": "Nenhum item corresponde à busca.", "selectAll_one": "Selecionar o item visível", "selectAll_other": "Selecionar os {{count}} visíveis", "confirm_one": "Aplicar a {{count}} item", "confirm_other": "Aplicar a {{count}} itens", "confirmEmpty": "Selecione os itens" } };
8
+ const dataTable = { "empty": "Nenhum registro encontrado", "error": "Não conseguimos carregar esta lista", "columns": "Colunas", "viewList": "Lista", "viewCards": "Cards", "reorderColumn": "Reordenar coluna", "restoreDefaultOrder": "Restaurar ordem padrão", "resizeColumn": "Redimensionar coluna", "restoreDefaultWidths": "Restaurar largura padrão", "viewGrouped": "Agrupamento", "viewKanban": "Kanban", "addRow": "Adicionar linha", "removeRow": "Remover linha", "editCell": "Editar", "save": "Salvar", "fillHandle": "Arraste para preencher", "editMode": { "enter": "Editar em planilha", "exit": "Sair da edição", "inactiveHint": "Ativa a edição em célula: navegação por teclado, seleção e preenchimento por arraste", "activeHint": "Edição em célula ativa. O clique na linha volta a abrir o registro quando você sair" }, "bulkEdit": { "title": "Editar em massa", "subtitle": "Escolha o campo, o novo valor e a quais itens aplicar.", "open": "Editar em massa", "hint": "Aplica um mesmo valor a vários itens de uma vez", "field": "Campo", "newValue": "Novo valor", "scopeLabel": "Aplicar a", "scopeLoaded": "Todos os carregados", "scopePicked": "Escolher itens", "searchRows": "Buscar na lista", "noRowsFound": "Nenhum item corresponde à busca.", "selectAll_one": "Selecionar o item visível", "selectAll_other": "Selecionar os {{count}} visíveis", "confirm_one": "Aplicar a {{count}} item", "confirm_other": "Aplicar a {{count}} itens", "confirmEmpty": "Selecione os itens", "subtitlePerItem": "Escolha os campos e ajuste o valor de cada item; só o que mudou será salvo.", "modeLabel": "Como aplicar", "modeSame": "Mesmo valor", "modePerItem": "Valores por item", "fields": "Campos", "confirmChanges_one": "Aplicar {{count}} alteração", "confirmChanges_other": "Aplicar {{count}} alterações", "confirmNoChanges": "Nenhuma alteração", "pickFieldsHint": "Selecione ao menos um campo para editar por item.", "noTargets": "Nenhum item no alcance escolhido.", "perItemHint": "Altere apenas as células desejadas. As demais permanecem como estão.", "itemColumn": "Item" } };
9
9
  const dateRangePicker = { "selectPeriod": "Selecione um período", "nowSelectEndDate": "Agora selecione a data final" };
10
10
  const dates = { "today": "Hoje", "yesterday": "Ontem", "tomorrow": "Amanhã", "thisWeek": "Esta semana", "lastWeek": "Semana passada", "thisMonth": "Este mês", "lastMonth": "Mês passado", "selectStartDate": "Selecione a data inicial", "selectEndDate": "Selecione a data final", "clearSelection": "Limpar Seleção", "clearPeriod": "Limpar Período Selecionado", "periodSelectedSuccess": "Período selecionado com sucesso" };
11
11
  const detailsTable = { "size": "Tam", "quantity": "Qtd" };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umodeapporg/ui",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "sideEffects": [