@uxland/primary-shell 7.46.12 → 7.46.14

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 (31) hide show
  1. package/dist/{component-BzmmJiBs.js → component-LekL_wPh.js} +2 -2
  2. package/dist/{component-BzmmJiBs.js.map → component-LekL_wPh.js.map} +1 -1
  3. package/dist/{index-BRK-x41J.js → index-Dm3JkDtL.js} +10199 -10730
  4. package/dist/index-Dm3JkDtL.js.map +1 -0
  5. package/dist/index.js +3 -3
  6. package/dist/index.umd.cjs +206 -232
  7. package/dist/index.umd.cjs.map +1 -1
  8. package/dist/{plugin-B4gkpTjT.js → plugin-CVSTX5Ok.js} +5441 -5430
  9. package/dist/plugin-CVSTX5Ok.js.map +1 -0
  10. package/dist/primary/shell/src/UI/shared-components/primaria-text-editor/primaria-rich-text-editor.d.ts +7 -1
  11. package/dist/primary/shell/src/api/import-data-manager/component/component.d.ts +1 -0
  12. package/dist/primary/shell/src/features/get-user-info/backend-adapter.d.ts +4 -2
  13. package/dist/primary/shell/src/features/get-user-info/pds-token.d.ts +26 -0
  14. package/package.json +3 -3
  15. package/src/UI/components/quick-actions-menu/quick-actions-menu.ts +3 -3
  16. package/src/UI/shared-components/bootstrapper.ts +3 -1
  17. package/src/UI/shared-components/primaria-nav-item/primaria-nav-item.ts +3 -1
  18. package/src/UI/shared-components/primaria-text-editor/primaria-rich-text-editor.ts +34 -21
  19. package/src/api/ecap-event-manager/ecap-event-manager.test.ts +11 -4
  20. package/src/api/import-data-manager/component/component.ts +3 -0
  21. package/src/api/import-data-manager/import-data-manager-impl.test.ts +2 -0
  22. package/src/api/interaction-service/interaction-service-impl.tsx +37 -40
  23. package/src/api/localization/localization.ts +44 -40
  24. package/src/api/region-manager/region-manager.test.ts +1 -1
  25. package/src/api/region-manager/region-manager.ts +13 -3
  26. package/src/features/get-user-info/backend-adapter.ts +30 -18
  27. package/src/features/get-user-info/handler.test.ts +8 -2
  28. package/src/features/get-user-info/pds-token.ts +28 -0
  29. package/src/types/custom.d.ts +30 -0
  30. package/dist/index-BRK-x41J.js.map +0 -1
  31. package/dist/plugin-B4gkpTjT.js.map +0 -1
@@ -1,5 +1,10 @@
1
1
  import { LitElement } from 'lit';
2
2
  import { default as Quill } from 'uxl-quill/dist/quill';
3
+ /**
4
+ * Una opció de la barra d'eines de Quill: el nom solt (`"bold"`), un objecte de configuració
5
+ * (`{ header: 1 }`) o un grup d'opcions, que és com Quill separa els blocs de la barra.
6
+ */
7
+ type ToolbarOption = string | Record<string, unknown> | (string | Record<string, unknown>)[];
3
8
  export declare class PrimariaRichTextEditor extends LitElement {
4
9
  private textChangeTimeout;
5
10
  render(): import('lit').TemplateResult<1>;
@@ -19,7 +24,7 @@ export declare class PrimariaRichTextEditor extends LitElement {
19
24
  _getOptions(): {
20
25
  modules: {
21
26
  toolbar: {
22
- container: never[];
27
+ container: ToolbarOption[];
23
28
  handlers: {
24
29
  redo(): void;
25
30
  undo(): void;
@@ -57,3 +62,4 @@ export declare class PrimariaRichTextEditor extends LitElement {
57
62
  };
58
63
  getCursorPosition(): any;
59
64
  }
65
+ export {};
@@ -5,6 +5,7 @@ export declare class ImportDataManagerModal extends LitElement implements Confir
5
5
  setResult: (result: undefined) => void;
6
6
  setIsValid: (isValid: boolean) => void;
7
7
  confirm: () => void;
8
+ confirmResult: (result: undefined) => void;
8
9
  cancel: () => void;
9
10
  render(): import('lit').TemplateResult<1>;
10
11
  static styles: import('lit').CSSResult;
@@ -1,5 +1,6 @@
1
- import { IUserInfo } from './model';
2
1
  import { shellApi } from '../../api/api';
2
+ import { IUserInfo } from './model';
3
+ import { PdsAccessInfo } from './pds-token';
3
4
  export declare abstract class UserInfoBackendAdapter {
4
5
  abstract getUserInfo(): Promise<IUserInfo>;
5
6
  }
@@ -7,5 +8,6 @@ export declare class GetUserInfoBackendAdapter implements UserInfoBackendAdapter
7
8
  private api;
8
9
  constructor(api: typeof shellApi);
9
10
  getUserInfo(): Promise<IUserInfo>;
10
- getUserId(access_info: any): any;
11
+ private findIdentifier;
12
+ getUserId(accessInfo: PdsAccessInfo | undefined): string | undefined;
11
13
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Els camps del token del PDS que aquest adaptador llegeix.
3
+ *
4
+ * Es declara aquí, i no s'importa `PDSToken` de `@pds/core`, per dues raons: el shell no depèn
5
+ * d'aquell package, i el seu `AccessInfo` no inclou `trace_user_last_name`, que sí que es fa
6
+ * servir. Si algun dia s'unifiquen els dos contractes, aquest fitxer hauria de desaparèixer.
7
+ *
8
+ * Tot és opcional perquè el token no en garanteix cap: fins ara el tipus era `any` i
9
+ * `IUserInfo` prometia `string` a tot arreu, cosa que no era certa en runtime.
10
+ */
11
+ export interface PdsAccessInfo {
12
+ trace_user_given_name?: string;
13
+ trace_user_family_name?: string;
14
+ trace_user_last_name?: string;
15
+ center_code?: string;
16
+ role_type?: string;
17
+ professional_category?: string;
18
+ identifier?: PdsIdentifier[];
19
+ }
20
+ export interface PdsIdentifier {
21
+ type: string;
22
+ value: string;
23
+ }
24
+ export interface PdsToken {
25
+ access_info?: PdsAccessInfo;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxland/primary-shell",
3
- "version": "7.46.12",
3
+ "version": "7.46.14",
4
4
  "description": "Primaria Shell",
5
5
  "author": "UXLand <dev@uxland.es>",
6
6
  "homepage": "https://github.com/uxland/harmonix/tree/app#readme",
@@ -40,14 +40,14 @@
40
40
  "inversify": "^6.0.2",
41
41
  "inversify-inject-decorators": "^3.1.0",
42
42
  "jwt-decode": "^4.0.0",
43
- "lit": "^3.1.0",
43
+ "lit": "^3.2.1",
44
44
  "react": "^19.0.0",
45
45
  "react-dom": "^19.0.0",
46
46
  "reflect-metadata": "^0.2.2",
47
47
  "tslib": "^2.3.0",
48
48
  "uxl-quill": "^1.0.1",
49
49
  "vite": "^7.0.0",
50
- "@primaria/plugins-core": "1.0.20"
50
+ "@primaria/plugins-core": "1.0.21"
51
51
  },
52
52
  "scripts": {
53
53
  "serve": "vite",
@@ -35,11 +35,11 @@ export class QuickActionsMenu extends LitElement {
35
35
 
36
36
  // Obtener los elementos asignados al otro slot
37
37
  const assignedElements = contentSlot?.assignedElements();
38
- if (assignedElements[0])
39
- this._addMenuCustomStyle(contentSlot as HTMLElement, actionMenu as HTMLElement);
38
+ if (assignedElements[0]) this._addMenuCustomStyle(contentSlot as HTMLElement, actionMenu as HTMLElement);
40
39
 
41
40
  // Hacer target al elemento que deseas (por ejemplo, un <p>)
42
- const targetElement = assignedElements.find((el) => el.id === "content");
41
+ // Els components del DSS exposen `show()`, que no és a la interfície `Element`.
42
+ const targetElement = assignedElements.find((el): el is Element & { show(): void } => el.id === "content");
43
43
  if (targetElement) {
44
44
  // Modificar el contenido o estilo del elemento
45
45
  targetElement.show();
@@ -12,5 +12,7 @@ export const useSharedUI = () => {
12
12
  customElement("primaria-rich-text-editor")(PrimariaRichTextEditor);
13
13
  customElement("primaria-nav-tree-menu")(PrimariaNavTreeMenu);
14
14
  customElement("primaria-character-limit-badge")(PrimariaCharacterLimitBadge);
15
- customElement("primaria-region")(PrimariaRegion);
15
+ // @ts-ignore: `PrimariaRegion` estén el mixin `PrimariaRegionHost`, que està tipat com a
16
+ // `any`, així que TypeScript no el reconeix com a `CustomElementClass`.
17
+ customElement("primaria-region")(PrimariaRegion);
16
18
  };
@@ -48,7 +48,9 @@ export class PrimariaNavItem extends regionView(LitElement) {
48
48
 
49
49
  _subscribeEvents() {
50
50
  const subscription = shellApi.broker.subscribe(BROKER_EVENTS.shell.mainViewChanged, (payload: { viewId: string }) => {
51
- this.isActive = payload.viewId === this.view?.id;
51
+ // `ViewDefinition` de @uxland/regions no declara `id`, però el shell només hi registra
52
+ // vistes que en porten.
53
+ this.isActive = payload.viewId === (this.view as { id?: string } | undefined)?.id;
52
54
  });
53
55
  this.subscriptions.push(subscription);
54
56
  }
@@ -7,6 +7,31 @@ import { fixSpellCheckerIssue } from "./utils";
7
7
 
8
8
  fixSpellCheckerIssue(Quill);
9
9
 
10
+ /**
11
+ * Una opció de la barra d'eines de Quill: el nom solt (`"bold"`), un objecte de configuració
12
+ * (`{ header: 1 }`) o un grup d'opcions, que és com Quill separa els blocs de la barra.
13
+ */
14
+ type ToolbarOption = string | Record<string, unknown> | (string | Record<string, unknown>)[];
15
+
16
+ /**
17
+ * Opcions de la barra d'eines que Quill espera com a objecte de configuració en lloc del nom
18
+ * solt. Abans era una cadena d'`else if`.
19
+ */
20
+ const TOOLBAR_OPTION_ALIASES: Record<string, ToolbarOption> = {
21
+ color: { color: [] },
22
+ ol: { list: "ordered" },
23
+ ul: { list: "bullet" },
24
+ subindex: { script: "sub" },
25
+ superindex: { script: "super" },
26
+ // outdent: { indent: "-1" },
27
+ // indent: { indent: "+1" },
28
+ size: { size: ["small", false, "large", "huge"] },
29
+ header: { header: [1, 2, 3, 4, 5, 6, false] },
30
+ background: { background: [] },
31
+ font: { font: [] },
32
+ align: { align: [] },
33
+ };
34
+
10
35
  export class PrimariaRichTextEditor extends LitElement {
11
36
  private textChangeTimeout: number | null = null;
12
37
 
@@ -124,7 +149,8 @@ export class PrimariaRichTextEditor extends LitElement {
124
149
  formats: string;
125
150
 
126
151
  _getOptions() {
127
- let toolbarOptions = [];
152
+ // Sense anotar, `[]` s'infereix com a `never[]` i totes les assignacions posteriors fallen.
153
+ let toolbarOptions: ToolbarOption[] = [];
128
154
  const availableOpts = this.availableOptions;
129
155
  if (this.options === undefined || this.options.length === 0) {
130
156
  toolbarOptions = [
@@ -145,26 +171,13 @@ export class PrimariaRichTextEditor extends LitElement {
145
171
  // ["clean"],
146
172
  ];
147
173
  } else {
148
- toolbarOptions = this.options.split(",");
149
- toolbarOptions.forEach((option) => {
150
- if (availableOpts.indexOf(option) <= -1) toolbarOptions.splice(toolbarOptions.indexOf(option), 1);
151
- });
152
- toolbarOptions = toolbarOptions.map(function (option) {
153
- if (option == "color") return { color: [] };
154
- else if (option == "ol") return { list: "ordered" };
155
- else if (option == "ul") return { list: "bullet" };
156
- else if (option == "subindex") return { script: "sub" };
157
- else if (option == "superindex") return { script: "super" };
158
- // else if (option == 'outdent') return {indent: '-1'};
159
- // else if (option == 'indent') return {indent: '+1'};
160
- else if (option == "size") return { size: ["small", false, "large", "huge"] };
161
- else if (option == "header") return { header: [1, 2, 3, 4, 5, 6, false] };
162
- else if (option == "background") return { background: [] };
163
- else if (option == "font") return { font: [] };
164
- else if (option == "align") return { align: [] };
165
- /* else if (option == 'undo') return {undo: undo_icon};
166
- else if (option == 'redo') return {redo: redo_icon}; */ else return option;
167
- });
174
+ // Abans això filtrava amb un `splice` dins d'un `forEach`, que salta elements quan n'hi ha
175
+ // dos d'invàlids seguits. Amb `filter` el resultat és el que la intenció demanava.
176
+ toolbarOptions = this.options
177
+ .split(",")
178
+ .filter((option) => availableOpts.indexOf(option) > -1)
179
+ // Les opcions amb configuració es tradueixen; la resta passen tal com són.
180
+ .map((option) => TOOLBAR_OPTION_ALIASES[option] ?? option);
168
181
  // if (toolbarOptions.indexOf("clean") < 0) {
169
182
  // toolbarOptions.push("clean");
170
183
  // }
@@ -8,6 +8,13 @@ const createMockGlobalStateManager = (data: Record<string, unknown> = {}) =>
8
8
  clearData: vi.fn(),
9
9
  }) as any;
10
10
 
11
+ /** `mock.calls[0][0]` és `unknown | undefined` amb `noUncheckedIndexedAccess`. */
12
+ const firstPostedMessage = (spy: { mock: { calls: unknown[][] } }): string => {
13
+ const payload = spy.mock.calls[0]?.[0];
14
+ if (typeof payload !== "string") throw new Error("s'esperava un missatge publicat");
15
+ return payload;
16
+ };
17
+
11
18
  describe("EcapEventManagerImpl", () => {
12
19
  const mockPostMessage = vi.fn();
13
20
  const mockConsoleLog = vi.fn();
@@ -31,7 +38,7 @@ describe("EcapEventManagerImpl", () => {
31
38
 
32
39
  eventManager.publish("click", "navigate", payload);
33
40
 
34
- const postedEvent = JSON.parse(mockPostMessage.mock.calls[0][0]);
41
+ const postedEvent = JSON.parse(firstPostedMessage(mockPostMessage));
35
42
  expect(postedEvent.DIAGNOSTICS).toBeUndefined();
36
43
  expect(globalStateManager.getData).not.toHaveBeenCalled();
37
44
  });
@@ -43,7 +50,7 @@ describe("EcapEventManagerImpl", () => {
43
50
 
44
51
  eventManager.publish("PRESCRIPCIO_ACTIVA", "NO_TANCAR", {});
45
52
 
46
- const postedEvent = JSON.parse(mockPostMessage.mock.calls[0][0]);
53
+ const postedEvent = JSON.parse(firstPostedMessage(mockPostMessage));
47
54
  expect(postedEvent.DIAGNOSTICS).toEqual(diagnostics);
48
55
  expect(globalStateManager.getData).toHaveBeenCalledWith(LAST_WORKED_DIAGNOSTICS_KEY);
49
56
  });
@@ -63,7 +70,7 @@ describe("EcapEventManagerImpl", () => {
63
70
 
64
71
  eventManager.publish(eventType, "NO_TANCAR", {});
65
72
 
66
- const postedEvent = JSON.parse(mockPostMessage.mock.calls[0][0]);
73
+ const postedEvent = JSON.parse(firstPostedMessage(mockPostMessage));
67
74
  expect(postedEvent.DIAGNOSTICS).toEqual(diagnostics);
68
75
  });
69
76
 
@@ -73,7 +80,7 @@ describe("EcapEventManagerImpl", () => {
73
80
 
74
81
  eventManager.publish("ORDRE_TRACTAMENT", "NO_TANCAR", {});
75
82
 
76
- const postedEvent = JSON.parse(mockPostMessage.mock.calls[0][0]);
83
+ const postedEvent = JSON.parse(firstPostedMessage(mockPostMessage));
77
84
  expect(postedEvent.DIAGNOSTICS).toEqual([]);
78
85
  });
79
86
 
@@ -10,6 +10,9 @@ export class ImportDataManagerModal extends LitElement implements ConfirmationCo
10
10
  setResult: (result: undefined) => void;
11
11
  setIsValid: (isValid: boolean) => void;
12
12
  confirm: () => void;
13
+ // Injectat pel servei d'interacció igual que la resta; faltava des que es va afegir a la
14
+ // interfície.
15
+ confirmResult: (result: undefined) => void;
13
16
  cancel: () => void;
14
17
 
15
18
  render() {
@@ -11,6 +11,8 @@ import {
11
11
  class MockInteractionService extends PrimariaInteractionService {
12
12
  private mockConfirmResult: { confirmed: boolean; result?: unknown } = { confirmed: true };
13
13
 
14
+ dispose(): void {}
15
+
14
16
  // Mètode per configurar el resultat del mock
15
17
  setMockConfirmResult(result: { confirmed: boolean; result?: unknown }) {
16
18
  this.mockConfirmResult = result;
@@ -1,14 +1,9 @@
1
- import {
2
- ConfirmationOptions,
3
- ConfirmationResult,
4
- ConfirmComponentUI,
5
- PrimariaInteractionService,
6
- } from "./interaction-service";
1
+ import { ConfirmationOptions, ConfirmationResult, ConfirmComponentUI, PrimariaInteractionService } from "./interaction-service";
7
2
  import * as React from "react";
8
3
  import { useEffect, useState, useRef } from "react";
9
4
  import { createRoot } from "react-dom/client";
10
5
  import { ConfirmationMessage } from "./confirmation-message.tsx";
11
- import modalStyles from './modal-styles.css?raw';
6
+ import modalStyles from "./modal-styles.css?raw";
12
7
 
13
8
  const defaultOptions: ConfirmationOptions = {
14
9
  title: "Confirmació",
@@ -19,16 +14,24 @@ const defaultOptions: ConfirmationOptions = {
19
14
  showCloseButton: true,
20
15
  fullCustomization: false,
21
16
  closeOnOutsideClick: false,
22
- hideIcon: false
17
+ hideIcon: false,
23
18
  };
24
19
 
20
+ /**
21
+ * Un diàleg pot ser un component de React o un web component. Només el primer es pot renderitzar
22
+ * com a JSX, i cal un predicat de tipus perquè TypeScript ho estrenyi al punt d'ús: guardar el
23
+ * resultat en un booleà no li serveix.
24
+ */
25
+ const isWebComponentConstructor = (component: unknown): component is new () => HTMLElement =>
26
+ typeof component === "function" && component.prototype instanceof HTMLElement;
27
+
25
28
  export class ParimariaInteractionServiceImpl extends PrimariaInteractionService {
26
29
  private readonly openDialogCancellers = new Set<() => void>();
27
30
 
28
31
  confirm<TData = undefined, TResult = undefined>(
29
32
  data: TData | undefined,
30
33
  componentUI: ConfirmComponentUI<TData, TResult>,
31
- options?: ConfirmationOptions | undefined
34
+ options?: ConfirmationOptions | undefined,
32
35
  ): Promise<ConfirmationResult<TResult | undefined>> {
33
36
  const finalOptions: ConfirmationOptions = { ...defaultOptions, ...(options || {}) };
34
37
  const { openDialogCancellers } = this;
@@ -42,26 +45,25 @@ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService
42
45
  const DialogComponent = componentUI.component;
43
46
  const DialogComponentStyles = componentUI.styles;
44
47
  let componentHeadStyles;
45
- if(DialogComponentStyles) {
48
+ if (DialogComponentStyles) {
46
49
  componentHeadStyles = document.createElement("style");
47
50
  componentHeadStyles.textContent = componentUI?.styles;
48
51
  document.head.appendChild(componentHeadStyles);
49
52
  }
50
53
 
51
54
  const DialogWrapper = () => {
52
- const [result, setResult] = useState<ConfirmationResult<TResult | undefined>>();
55
+ const [result, setResult] = useState<TResult>();
53
56
  const [isValid, setIsValid] = useState(true);
54
- const dialogComponentRef = useRef(null);
57
+ const dialogComponentRef = useRef<HTMLDivElement>(null);
55
58
  const dialogRef = useRef<HTMLDivElement>(null);
56
59
  const modalRef = useRef<HTMLDivElement>(null);
57
60
 
58
- const isWebComponent =
59
- typeof DialogComponent === "function" && DialogComponent.prototype instanceof HTMLElement;
61
+ const isWebComponent = isWebComponentConstructor(DialogComponent);
60
62
 
61
63
  useEffect(() => {
62
- window.addEventListener('keydown', handleKeydown);
64
+ window.addEventListener("keydown", handleKeydown);
63
65
 
64
- return () => window.removeEventListener('keydown', handleKeydown);
66
+ return () => window.removeEventListener("keydown", handleKeydown);
65
67
  }, []);
66
68
 
67
69
  useEffect(() => {
@@ -75,11 +77,11 @@ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService
75
77
  }, []);
76
78
 
77
79
  const handleKeydown = (event: KeyboardEvent) => {
78
- if (event.key === 'Escape') {
80
+ if (event.key === "Escape") {
79
81
  handleClose(false);
80
82
  }
81
83
  };
82
- const handleOutsideClick = (event: Event) => {
84
+ const handleOutsideClick = (event: React.MouseEvent<HTMLDivElement>) => {
83
85
  if (dialogRef.current) {
84
86
  if (event.target === modalRef.current && finalOptions.closeOnOutsideClick) {
85
87
  handleClose(false);
@@ -87,6 +89,9 @@ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService
87
89
  }
88
90
  };
89
91
 
92
+ // L'efecte només ha de crear el web component quan canvia el component o les dades.
93
+ // Afegir-hi els handlers i `isValid` el faria recrear a cada render, perdent l'estat.
94
+ // biome-ignore lint/correctness/useExhaustiveDependencies: recrear-lo perdria l'estat
90
95
  useEffect(() => {
91
96
  if (isWebComponent && dialogComponentRef.current) {
92
97
  const parent = dialogComponentRef.current;
@@ -119,7 +124,7 @@ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService
119
124
  resolvePromise(finalResult, true);
120
125
  };
121
126
 
122
- const resolvePromise = (finalResult: TResult, confirmed: boolean) => {
127
+ const resolvePromise = (finalResult: TResult | undefined, confirmed: boolean) => {
123
128
  if (isClosing) return;
124
129
 
125
130
  isClosing = true;
@@ -132,8 +137,7 @@ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService
132
137
  root.unmount();
133
138
  document.body.removeChild(div);
134
139
  document.head.removeChild(wrapperHeadStyles);
135
- if (DialogComponentStyles)
136
- document.head.removeChild(componentHeadStyles);
140
+ if (DialogComponentStyles) document.head.removeChild(componentHeadStyles);
137
141
  }, 300);
138
142
  };
139
143
 
@@ -160,7 +164,7 @@ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService
160
164
  };
161
165
 
162
166
  const _renderContent = () => {
163
- return isWebComponent ? (
167
+ return isWebComponentConstructor(DialogComponent) ? (
164
168
  <div ref={dialogComponentRef} />
165
169
  ) : (
166
170
  <DialogComponent
@@ -176,7 +180,7 @@ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService
176
180
  );
177
181
  };
178
182
  return (
179
- <div className="modal" ref={modalRef} onClick={handleOutsideClick} >
183
+ <div className="modal" ref={modalRef} onClick={handleOutsideClick}>
180
184
  <div className="dialog" ref={dialogRef}>
181
185
  {!finalOptions.fullCustomization && (
182
186
  <div className="dss-dialog-header">
@@ -188,30 +192,27 @@ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService
188
192
  fill
189
193
  class={`dss-dialog-header-state--${getHeadIconVariantClass()}`}
190
194
  icon={getHeadIcon()}
191
- ></dss-icon>
195
+ />
192
196
  )}
193
197
  {finalOptions.title}
194
198
  </div>
195
199
  </div>
196
200
  {finalOptions.showCloseButton && (
197
201
  <dss-icon-button
198
- class="dss-dialog-header-close"
199
- size="md"
200
- variant="neutral"
201
- icon="close"
202
+ class="dss-dialog-header-close"
203
+ size="md"
204
+ variant="neutral"
205
+ icon="close"
202
206
  onClick={() => handleClose(false)}
203
- ></dss-icon-button>
207
+ />
204
208
  )}
205
209
  </div>
206
210
  )}
207
- <div className={`modal-content ${finalOptions.fullCustomization ? 'full-customization' : ''}`}>
211
+ <div className={`modal-content ${finalOptions.fullCustomization ? "full-customization" : ""}`}>
208
212
  {_renderContent()}
209
213
  </div>
210
214
  {!finalOptions.fullCustomization && (
211
- <div
212
- className="dss-modal-footer"
213
- hidden={!finalOptions.showCancelButton && !finalOptions.showConfirmButton}
214
- >
215
+ <div className="dss-modal-footer" hidden={!finalOptions.showCancelButton && !finalOptions.showConfirmButton}>
215
216
  {finalOptions.showCancelButton && (
216
217
  <dss-button
217
218
  label={finalOptions.cancelButtonText}
@@ -238,17 +239,13 @@ export class ParimariaInteractionServiceImpl extends PrimariaInteractionService
238
239
  );
239
240
  };
240
241
 
241
-
242
242
  const root = createRoot(div);
243
243
  root.render(<DialogWrapper />);
244
244
  });
245
245
  }
246
246
 
247
- confirmMessage(
248
- message: string,
249
- options?: ConfirmationOptions | undefined,
250
- ): Promise<ConfirmationResult> {
251
- return this.confirm(message, {component: ConfirmationMessage}, options);
247
+ confirmMessage(message: string, options?: ConfirmationOptions | undefined): Promise<ConfirmationResult> {
248
+ return this.confirm(message, { component: ConfirmationMessage }, options);
252
249
  }
253
250
 
254
251
  dispose(): void {
@@ -11,44 +11,48 @@ import { getLanguage, getLocales, localizerFactory, setLocales } from "@uxland/l
11
11
  * getCurrentLanguage: () => string,
12
12
  * }>} A promise that resolves to an object with methods for translating strings and getting translations.
13
13
  */
14
- export const createLocaleManager =
15
- (pluginId: string) => (translations: Record<string, Record<string, string>>) => {
16
- const localizer = localizerFactory(getLanguage(), translations, "", true);
17
- setLocales(translations);
18
- return Promise.resolve({
19
- /**
20
- * Translates the given path using the provided variables and the plugin's ID.
21
- *
22
- * @param {string} path - The path to be translated.
23
- * @param {Record<string, string>} [variables] - Optional variables to be used in the translation.
24
- * @return {string} The translated string, or the original path if an error occurs.
25
- */
26
- translate: (path: string, variables?: Record<string, string>) => {
27
- try {
28
- return localizer(`${pluginId}.${path}`, variables);
29
- } catch (e) {
30
- console.error(e);
31
- return path;
32
- }
33
- },
34
- /**
35
- * Retrieves the translations for the current language and plugin.
36
- *
37
- * @return {Record<string, string>} The translations for the current language and plugin.
38
- */
39
- getTranslations: () => {
40
- const translations = getLocales();
41
- const language = getLanguage();
42
- return translations[language][pluginId] || {};
43
- },
14
+ /** Arbre de traduccions: claus imbricades fins a arribar al text. */
15
+ export interface LocalizationMessages {
16
+ [key: string]: string | LocalizationMessages;
17
+ }
44
18
 
45
- /**
46
- * Retrieves the current language.
47
- *
48
- * @return {string} The current language.
49
- */
50
- getCurrentLanguage: () => {
51
- return getLanguage();
52
- },
53
- });
54
- };
19
+ export const createLocaleManager = (pluginId: string) => (translations: Record<string, LocalizationMessages>) => {
20
+ const localizer = localizerFactory(getLanguage(), translations, "", true);
21
+ setLocales(translations);
22
+ return Promise.resolve({
23
+ /**
24
+ * Translates the given path using the provided variables and the plugin's ID.
25
+ *
26
+ * @param {string} path - The path to be translated.
27
+ * @param {Record<string, string>} [variables] - Optional variables to be used in the translation.
28
+ * @return {string} The translated string, or the original path if an error occurs.
29
+ */
30
+ translate: (path: string, variables?: Record<string, string>) => {
31
+ try {
32
+ return localizer(`${pluginId}.${path}`, variables);
33
+ } catch (e) {
34
+ console.error(e);
35
+ return path;
36
+ }
37
+ },
38
+ /**
39
+ * Retrieves the translations for the current language and plugin.
40
+ *
41
+ * @return {Record<string, string>} The translations for the current language and plugin.
42
+ */
43
+ getTranslations: () => {
44
+ const translations = getLocales();
45
+ const language = getLanguage();
46
+ return translations[language][pluginId] || {};
47
+ },
48
+
49
+ /**
50
+ * Retrieves the current language.
51
+ *
52
+ * @return {string} The current language.
53
+ */
54
+ getCurrentLanguage: () => {
55
+ return getLanguage();
56
+ },
57
+ });
58
+ };
@@ -3,7 +3,7 @@ import { createRegionManagerProxy } from "./region-manager";
3
3
  import { BROKER_EVENTS } from "../broker/broker-events";
4
4
 
5
5
  const pluginId = "test-plugin";
6
- const mockView = { id: "view1" };
6
+ const mockView = { id: "view1", factory: () => Promise.resolve(document.createElement("div")) };
7
7
 
8
8
  const createMockRegion = () => ({
9
9
  activate: vi.fn(),
@@ -109,14 +109,24 @@ class RegionManagerProxy implements PrimariaRegionManager {
109
109
  */
110
110
  isViewActive(regionName: string, viewId: string) {
111
111
  const region = this.regionManager.getRegion(regionName);
112
- const isViewActive = region.currentActiveViews.some((view) => view.id === viewId);
112
+ // `ViewDefinition` de @uxland/regions no declara `id`, però el shell només hi registra
113
+ // `HarmonixViewDefinition`, que sí que en porta.
114
+ const isViewActive = region.currentActiveViews.some((view) => (view as HarmonixViewDefinition).id === viewId);
113
115
  return Promise.resolve(isViewActive);
114
116
  }
115
117
  async registerQuickAction(view: HarmonixViewDefinition): Promise<void> {
116
- await this.regionManager.registerViewWithRegion(this.regions.shell.quickActions, `${this.pluginInfo.pluginId}::${view.id}`, view);
118
+ await this.regionManager.registerViewWithRegion(
119
+ this.regions.shell.quickActions,
120
+ `${this.pluginInfo.pluginId}::${view.id}`,
121
+ view,
122
+ );
117
123
  }
118
124
  async registerNavigationMenu(view: HarmonixViewDefinition): Promise<void> {
119
- await this.regionManager.registerViewWithRegion(this.regions.shell.navigationMenu, `${this.pluginInfo.pluginId}::${view.id}`, view);
125
+ await this.regionManager.registerViewWithRegion(
126
+ this.regions.shell.navigationMenu,
127
+ `${this.pluginInfo.pluginId}::${view.id}`,
128
+ view,
129
+ );
120
130
  }
121
131
  async registerMainView(view: HarmonixViewDefinition): Promise<void> {
122
132
  await this.regionManager.registerViewWithRegion(this.regions.shell.main, `${this.pluginInfo.pluginId}::${view.id}`, view);